基于CangjieMagic的RAG技术赋能智能问答系统

article/2025/6/17 0:39:16

目录

引言

示例程序分析

代码结构剖析

导入模块解读

智能体配置详情

提示词模板说明

主程序功能解析

异步聊天功能实现

检索信息展示

技术要点总结

ollama 本地部署nomic-embed-text

 运行测试

结语


引言

这段时间一直在学习CangjieMagic。前几天完成了在CangjieMagic智能体框架中集成华为云的DeepSeek服务-CSDN博客,今天研究了一下本地部署。CangjieMagic支持两种本地部署:Ollma和Llama.cpp。CangjieMagic提供了一个有趣的例子markdown_qa,它根据本项目的文档实现问答助手。今天就来测试一下它。

示例程序分析

例子的链接在:项目目录预览 - CangjieMagic:基于仓颉编程语言构建的 LLM Agent 开发框架,其主要特点包括:Agent DSL、支持 MCP 协议,支持模块化调用,支持任务智能规划。 - GitCode

这段代码是一个使用 Magic DSL 框架开发的智能问答机器人程序,它能借助 RAG 技术从指定文档里获取信息来回答问题。下面是对代码的详细解释:

代码结构剖析

package magic.examples.markdown_qa
  • 此程序属于magic.examples.markdown_qa包,这表明它是 Magic 框架示例的一部分。

导入模块解读

import magic.dsl.*
import magic.prelude.*
import magic.config.Config
import magic.model.ModelManagerimport log.LogLevel
  • 导入了 Magic DSL 框架的核心功能,像 DSL 注解、基础函数、配置类以及模型管理工具等都包含在内。
  • 同时还导入了日志级别配置模块。

智能体配置详情

@agent[model: "deepseek:deepseek-chat",executor: "naive",rag: {source: "./docs/tutorial.md",mode: "static"}
]
class QABot { ... }
  • 这是一个基于 DeepSeek-chat 模型的智能体,它采用了简单的执行器。
  • 配置了 RAG(检索增强生成)功能,会从./docs/tutorial.md这个 Markdown 文档中获取知识,并且使用静态模式,这意味着文档内容会被预加载。

提示词模板说明

@prompt[pattern: ERA] (expectation: "代码块被标签 ```cangjie 和 ```包裹",role: "简单问答助手",action: "搜索文档获取知识并回答问题"
)
  • 运用 ERA(期望 - 角色 - 行动)模式构建提示词模板。
  • 期望生成的代码块使用特定的 Cangjie 标记。
  • 该智能体的角色是作为简单问答助手,其主要行动是搜索文档并生成回答。

主程序功能解析

main () {Config.logLevel = LogLevel.INFOConfig.defaultEmbeddingModel = ModelManager.createEmbeddingModel("ollama:nomic-embed-text")
  • 把日志级别设定为 INFO,这样可以显示执行过程中的信息。
  • 配置了嵌入模型,用于生成文本的向量表示,这里使用的是 Ollama 平台的 nomic-embed-text 模型。

异步聊天功能实现

    let bot = QABot()let aresp = bot.asyncChat("Agent RAG 怎么编写")for (chunk in aresp) {print(chunk)}
  • 创建了 QABot 实例,并异步询问 "Agent RAG 怎么编写" 这个问题。
  • 采用流式输出的方式打印回答内容。

检索信息展示

    if (let Some(info) <- aresp.execInfo) {for (info in info.retrievalInfo) {for (doc in info.retrieval.sources) {println(doc.metadata)}}}
}
  • 展示了回答所依据的文档元数据,这体现了 RAG 的可解释性。
  • 能看到哪些文档片段被用作了回答的参考。

技术要点总结

  1. RAG 技术:该程序将检索和大语言模型相结合,利用本地文档来增强回答的准确性。
  2. 异步处理:采用异步聊天接口asyncChat,支持流式响应,提升了用户体验。
  3. 模块化设计
    • 智能体配置与业务逻辑是分离的。
    • 提示词工程采用了标准化的模板。
  4. 模型生态集成
    • 支持 DeepSeek 等 LLM 模型。
    • 与 Ollama 平台的嵌入模型进行集成。
  5. 知识来源:以静态 Markdown 文档作为知识来源,适合特定领域的问答场景。

这个程序展示了如何使用 Magic DSL 框架开发一个基于文档的智能问答系统,它具备可配置、可扩展以及可解释的特点。

ollama 本地部署nomic-embed-text

首先下载安装Ollma:

developer@developer:~/IDEProjects$ curl https://ollama.com/install.sh | sh% Total    % Received % Xferd  Average Speed   Time    Time     Time  CurrentDload  Upload   Total   Spent    Left  Speed
100  5503    0  5503    0     0   7219      0 --:--:-- --:--:-- --:--:--  7212>>> Cleaning up old version at /usr/local/lib/ollama
>>> Installing ollama to /usr/local
100 13281    0 13281    0     0  16854      0 --:--:-- --:--:-- --:--:-- 16854
>>> Downloading Linux amd64 bundle
######################################################################## 100.0%
>>> Creating ollama user...
>>> Adding ollama user to render group...
>>> Adding ollama user to video group...
>>> Adding current user to ollama group...
>>> Creating ollama systemd service...
>>> Enabling and starting ollama service...
Created symlink /etc/systemd/system/default.target.wants/ollama.service → /etc/systemd/system/ollama.service.
>>> The Ollama API is now available at 127.0.0.1:11434.
>>> Install complete. Run "ollama" from the command line.
WARNING: No NVIDIA/AMD GPU detected. Ollama will run in CPU-only mode.
developer@developer:~/IDEProjects$ 

安装成功后它会自动启动服务器。

 这个实例需要‌ Nomic-embed-text模型。Nomic-embed-text ‌是一个基于 Sentence Transformers 库的句子嵌入模型,专门用于特征提取和句子相似度计算。该模型在多个任务上表现出色,特别是在分类、检索和聚类任务中,能够生成高质量的句子嵌入,这些嵌入在语义上非常接近,从而在相似度计算和分类任务中表现优异‌。

执行下面的语句下载该模型:

ollama pull nomic-embed-text

 运行测试

然后就可以修改程序适配华为版的DeepSeek,具体做法参考:在CangjieMagic智能体框架中集成华为云的DeepSeek服务-CSDN博客。

这个程序实际上先用本地的Nomic-embed-text模型提取文档中有用的部分,然后把它和用户问题一起发给DeepSeek。下面是简单的原理示意。

  "messages": [{"role": "system","content": "## Expectation\n代码块被标签 ```cangjie 和 ```包裹\n## Role\n简单问答助手\n## Action\n搜索文档获取知识并回答问题\n\n\n# Retrieved Content\n除了系统提示词,外部知识也可以增强 Agent 的解决问题的能力。 Agent 能够从各类知识源中提取必要和有用的信息。\n目前,Agent 的 `rag` 属性表明外部知识的数据源,它接受多个数据源配置,每个数据源包含如下的键值对:\n| 属性  | 属性值 | 说明 |\n|---|---|---|\n| `source`  | `String \\| Expr`  | 数据源 |\n| `mode`  | `String`  | 使用模式,支持 `\"static\"` 和 `\"dynamic\"` 两种;默认为 `\"static\"` |\n| `description`  | `String`  | 可进一步描述数据源,帮助 Agent 更加精准地获取数据 |\n属性 `source` 表明数据的实际来源,支持两种类型:\n- 合法路径指向*预置的文件类型*\n    - 当前支持的文件类型包括 markdown, Sqlite 数据库\n- 类型为 `Retriever` 的表达式\n```cangjie\n@agent[\n  rag: { source: \"path/to/some.db\" }\n]\nclass Foo { }\n```\n⚠️注意:使用 Sqlite 数据库的功能需要配置 `cfg.toml` 中 `sqlite = \"enable\"`,且由于数据库使用了 Sqlite,所以需要安装三方依赖。详见 [third_party_libs.md](./third_party_libs.md)\n[使用示例](../src/examples/mini_rag/main.cj)\n除了通过 `@agent` 定义 Agent 之外,当前框架内置如下的几种 Agent。\n目前,我们使用宏 `@agent` 修饰 `class` 类型来定义一个 Agent 类型。\n```cangjie\n@agent class Foo { }\n```\n宏 `@agent` 支持如下属性。具体属性可参考相应章节内容\n| 属性名 | 值类型 | 说明 |\n|-------|-------|-------|\n| `description` | `String` | Agent 的功能描述;默认未设置时,将由 LLM 从提示词中自动总结出 |\n| `model` | `String` | 配置使用到的 LLM 模型服务;默认使用 gpt-4o |\n| `tools` | `Array` | 配置能够使用的外部工具 |\n| `mcp` | `Array` | 配置接入的 MCP 服务器 |\n| `rag` |   `Map` | 配置外部的知识源 |\n| `memory` |  `Bool` | 是否使用记忆,即保存 Agent 的多次问答记录(目前记忆仅支持 in-memory 非持久化数据);默认为 `false` |\n| `executor` | `String` | 规划模式;默认为 `react` |\n| `temperature` | `Float` | Agent 使用 LLM 时的 temperature 值;默认为 `0.5` |\n| `enableToolFilter` | `Bool` | 启用工具过滤功能,Agent 在执行前会自动根据输入问题选择合适的工具集合;默认 `false` |\n| `dump` | `Bool` | 调试代码用,是否打印 Agent 变换后的 AST;默认为 `false` |\n工具可以理解为 Agent 执行过程中能够执行的代码。当前 Agent 工具有两个来源:\n- 使用 DSL 直接编写的工具函数\n- 由 MCP 服务器提供的工具(MCP 服务器可视为*一组工具的集合*)。\n---end of Retrieved Content---\n\n\n\n\n"},{"role": "user","content": "Agent RAG 怎么编写"}],

DeepSeek收到我们提供的数据和用户的问题,就会生成解答。

下面是日志中解答的部分。 

| `temperature` | `Float` | Agent 使用 LLM 时的 temperature 值;默认为 `0.5` |
| `enableToolFilter` | `Bool` | 启用工具过滤功能,Agent 在执行前会自动根据输入问题选择合适的工具集合;默认 `false` |
| `dump` | `Bool` | 调试代码用,是否打印 Agent 变换后的 AST;默认为 `false` |
工具可以理解为 Agent 执行过程中能够执行的代码。当前 Agent 工具有两个来源:
- 使用 DSL 直接编写的工具函数
- 由 MCP 服务器提供的工具(MCP 服务器可视为*一组工具的集合*)。
---end of Retrieved Content---2025/05/30 17:33:17.562631 INFO logger QABot: Chat Message[ user ]: Agent RAG 怎么编写
嗯,用户问的是如何编写Agent的RAG部分。我需要先回忆一下之前学过的相关知识。根据检索到的内容,RAG属性在Agent中用于配置外部知识源。首先,RAG接受多个数据源配置,每个数据源需要指定source、mode和description等属性。source可以是文件路径或者Retriever表达式。比如,用户可能想连接一个数据库或者Markdown文件。例如,使用source: "path/to/some.db"来指定SQLite数据库。但要注意,使用SQLite需要配置cfg.toml中的sqlite选项为enable,并且安装相关依赖。mode有static和dynamic两种模式,默认是static。static可能是指数据源在初始化时加载,而dynamic可能在运行时动态获取。需要确认用户是否需要实时更新的数据,从而选择合适的模式。description用来进一步描述数据源,帮助Agent更准确地获取信息。这部分可能需要用户提供详细的描述,比如数据内容或结构。然后,用户需要在@agent宏中设置rag属性。例如,@agent[rag: { source: "data.md" }]来引入Markdown文件作为知识源。如果使用多个数据源,可能需要用数组形式,但检索到的内容里提到rag是Map类型,可能需要确认是否支持多个配置。另外,用户可能需要知道如何结合工具和RAG。比如,在工具函数中调用检索到的内容,或者在回答问题时引用这些数据。示例中的代码片段展示了如何将数据库作为RAG源,用户可能需要参考示例代码来编写自己的Agent。还要注意,使用SQLite数据库需要安装第三方库,用户可能需要查看third_party_libs.md文档。此外,示例中的main.cj文件可能提供了具体的实现方法,建议用户参考。总结来说,编写Agent的RAG需要配置rag属性,指定数据源路径或表达式,选择合适的模式,添加描述,并确保依赖项正确安装。用户可能需要根据具体的数据类型和需求调整配置,同时参考提供的示例代码进行实现。在 Cangjie DSL 中编写 Agent 的 RAG 功能需要以下步骤:1. **基本结构定义**
使用 `@agent` 宏修饰 class 来定义 Agent:```cangjie
@agent[rag: {source: "path/to/your_data.md",  // 数据源路径mode: "static",                 // 使用模式description: "产品知识库"        // 数据源描述}
]
class MyAssistant {// Agent 的具体实现...
}
```2. **关键参数配置**:
- `source`:支持两种形式
```cangjie
// 文件路径形式(支持 markdown/sqlite)
source: "data/product_manual.db"// Retriever 表达式形式
source: Retriever.new("https://api.example.com/knowledge-base")
```- `mode` 模式选择:
```cangjie
mode: "static"   // 静态加载(默认)
mode: "dynamic"  // 动态实时检索
```3. **多数据源配置**(使用数组):
```cangjie
@agent[rag: [{ source: "internal_knowledge.db",description: "公司内部技术文档"},{source: Retriever.new(API_ENDPOINT),mode: "dynamic",description: "实时市场数据接口"}]
]
```4. **SQLite 数据库注意事项**:
- 在 `cfg.toml` 中启用:
```toml
sqlite = "enable"
```
- 需要安装依赖:
```bash
pip install sqlalchemy datasets
```5. **典型使用场景示例**:
```cangjie
// 客户服务 Agent 示例
@agent[rag: {source: "customer_service_qa.db",description: "包含常见问题解答和产品故障处理方案"},memory: true
]
class CustomerServiceBot {// 会自动继承 RAG 检索能力// 在对话处理中可直接引用知识库内容
}
```完整示例可参考框架中的 `/src/examples/mini_rag/main.cj` 实现。建议先通过 markdown 文件进行原型验证,再逐步接入数据库等结构化数据源。
---end---
[(Header 1, 用户教程), (Header 2, 外部知识)]
[(Header 1, 用户教程), (Header 2, 常用 API 介绍), (Header 3, 知识图谱), (Header 4, 使用示例)]
[(Header 1, 用户教程), (Header 2, 常用 API 介绍), (Header 3, 内置 Agent)]
[(Header 1, 用户教程), (Header 2, Agent 定义)]
[(Header 1, 用户教程), (Header 2, MCP 协议和工具)]cjpm run finished

结语

RAG模型的优势在于可实现即时的知识更新,从而提供更高效和精准的信息服务。 CangjieMagic在这方面做得很不错,值得进一步挖掘。


http://www.hkcw.cn/article/JoJQcgiYuL.shtml

相关文章

【速通RAG实战:进阶】18、如何利用LLM记忆功能,实现一对一的个性化服务

一、赛博记忆的本质:从数据到数字人格的进化 (一)核心概念与技术定位 赛博记忆(Cyber Memory)是通过AI技术将个人多源数据(文本、图像、生物特征等)转化为可交互的动态记忆系统,其终极目标是构建可进化的数字人格,实现记忆的存储、重构与智能响应。与传统数据存储不…

C++深入类与对象

在上一篇中提到了构造函数&#xff0c;那么这篇再来提一下构造函数&#xff0c;编译器自动生成的默认构造函数对于内置类型不做处理&#xff0c;自定义类型会调用它自己的构造函数。对于自己写的构造函数&#xff0c;之前是在函数体中初始化&#xff0c;当然不止这一种初始化&a…

创新型老年综合评估实训室建设方案:提升评估实训精准性

随着人口老龄化加剧&#xff0c;老年综合评估人才需求激增&#xff0c;建设创新型老年综合评估实训室&#xff0c;是提升评估实训精准性、培育专业人才的关键路径。点击获取实训室建设方案 一、建设背景与意义 &#xff08;一&#xff09;行业发展需求 1、老龄化社会对老年综…

凤凰古城举行端午抢鸭子活动 欢乐江中庆佳节

5月31日,湖南凤凰古城沱江上举行了一场备受期待的“抢鸭子”大赛。500只精心挑选的鸭子被投入江中,其中一些鸭子颈项间系着鲜艳红绸带,象征好运。随着一声悠长的吆喝声响起,早已准备好的参赛者们纷纷跃入清凉的江水中。现场瞬间变成了欢乐的海洋,游客和市民一同沉浸在浓厚…

【深度学习】16. Deep Generative Models:生成对抗网络(GAN)

Deep Generative Models&#xff1a;生成对抗网络&#xff08;GAN&#xff09; 什么是生成建模&#xff08;Generative Modeling&#xff09; 生成模型的主要目标是从数据中学习其分布&#xff0c;从而具备“生成”数据的能力。两个关键任务&#xff1a; 密度估计&#xff0…

卢伟冰:诋毁本身就是一种仰望 小米汽车成功之道

小米集团合伙人、总裁卢伟冰昨晚谈到了小米汽车的成功。他表示,无论是SU7的热销还是YU7获得更高关注和期待,都基于强大的产品力。小米以“十倍投入做一辆好车”的决心,赢得了千万网友的认可。卢伟冰强调,小米汽车的成功体现了小米的价值观、模式和方法论。他还引用了莫言的…

西安交大原校长徐通模逝世 沉痛悼念与缅怀

沉痛悼念并深切缅怀徐通模同志。中国共产党优秀党员、西安交通大学原校长、我国动力工程与工程热物理领域著名专家、西安交通大学能源与动力工程学院热能工程系徐通模教授,因病医治无效,于2025年5月31日下午14时16分在西安逝世,享年86岁。徐通模同志1939年11月生于江苏如皋,…

性能优化 - 理论篇:常见指标及切入点

文章目录 引言一、 Java 性能优化的核心思路二、为什么要度量&#xff1f;三、常用性能衡量指标详解3.1 吞吐量与响应速度3.2 响应时间的具体度量&#xff1a;平均响应时间与百分位数3.3 并发量3.4 秒开率&#xff08;页面秒开&#xff09;3.5 正确性&#xff08;功能可用性&am…

Java代码重构:如何提升项目的可维护性和扩展性?

Java代码重构&#xff1a;如何提升项目的可维护性和扩展性&#xff1f; 在Java开发领域&#xff0c;随着项目规模的不断扩大和业务需求的频繁变更&#xff0c;代码的可维护性和扩展性逐渐成为了项目成功的关键因素。代码重构作为一种优化代码质量的重要手段&#xff0c;能够在…

【数据集】全球无缝高分辨率1 km 月均地表温度和气温(2001-2020)

目录 数据概述一、输入数据(Input Data)二、模型处理(Modeling and Processing)2.1 清空缺值重建(Clear-sky Ts Reconstruction)2.2 多云条件下地表温度重建(Cloudy-sky Ts Reconstruction)2.3 近地表气温估算(Ta Estimation)2.4 准确性验证与对比三、输出结果与产品…

Vue能启动但访问空白?并报”export ‘default’ (imported as ‘Vue’) was not found in ‘vue’

场景 如图&#xff0c;vue项目的node_modules下载顺利&#xff0c;启动也顺利&#xff0c;但是访问却为空白页面 虽然页面是空白&#xff0c;但是通过浏览器控制台可以看出并非简单的空白&#xff0c;确实有不兼容问题在里面 分析问题 从上图浏览器控制台可以看出&#xff0c…

go|channel源码分析

文章目录 channelhchanmakechanchansendchanrecvcomplieclosechan channel 先看一下源码中的说明 At least one of c.sendq and c.recvq is empty, except for the case of an unbuffered channel with a single goroutine blocked on it for both sending and receiving usin…

詹俊:国米表现太糟糕了,统治力差距明显

在欧冠决赛中,巴黎圣日耳曼对阵国际米兰,半场结束时巴黎以2-0领先。知名解说员詹俊对比赛进行了点评。詹俊指出,国米差点三球落后,他们在中前场缺乏逼抢力度,使得大巴黎能够轻松控球并通过短传渗透轻易进入进攻区域。大巴黎不仅擅长传控还能快速反击,展现了强大的统治力。…

黄健翔谈大巴黎5-0国米夺欧冠冠军 比赛成一边倒态势

6月1日凌晨,欧冠决赛落幕,巴黎圣日耳曼以5-0战胜国际米兰。赛后,黄健翔在社交媒体上分享了他的看法。他提到自己在赶往机场的途中,边吃早餐边回想刚结束的比赛。他对国米本场比赛的表现感到困惑,特别想了解国米在这场比赛中的战术策略究竟是如何制定的,导致全队多个位置和…

【KWDB 创作者计划】_探秘浪潮KWDB数据库:从时间索引到前沿技术

探秘浪潮KWDB数据库&#xff1a;从时间索引到前沿技术 文章目录 探秘浪潮KWDB数据库&#xff1a;从时间索引到前沿技术引言1.浪潮KWDB数据库时间索引深度解析1.1时间索引工作原理1.2时间索引创建与管理实践 2.浪潮KWDB数据库前沿产品技术纵览2.1多模融合存储引擎2.2就地计算技术…

Linux系统-基本指令(4)

文章目录 touch 指令&#xff08;2-2.25.00&#xff09;知识点&#xff08;普通文件和文本文件&#xff09;mkdir 指令知识点&#xff08;tree指令&#xff09;rm 指令man 指令&#xff08;3-0.00.00&#xff09;知识点&#xff08;Linux下&#xff0c;一切皆为文件&#xff09…

光电设计大赛智能车激光对抗方案分享:低成本高效备赛攻略

一、赛题核心难点与备赛痛点解析 全国大学生光电设计竞赛的 “智能车激光对抗” 赛题&#xff0c;要求参赛队伍设计具备激光对抗功能的智能小车&#xff0c;需实现光电避障、目标识别、轨迹规划及激光精准打击等核心功能。从历年参赛情况看&#xff0c;选手普遍面临三大挑战&a…

力扣 208.实现Trie(前缀树)

文章目录 题目介绍题解 题目介绍 题解 解析&#xff1a; 初始化&#xff1a;创建一棵 26 叉树&#xff0c;一开始只有一个根节点 root。26 叉树的每个节点包含一个长为 26 的儿子节点列表 son&#xff0c;以及一个布尔值 end&#xff0c;表示是否为终止节点。 insert&#xf…

WiFi万能钥匙鲲鹏服务器部署 TiDB 集群实战指南

作者&#xff1a; TiDBer_yangxi 原文来源&#xff1a; https://tidb.net/blog/15a234d0 一、环境准备 1. 硬件要求 服务器架构 &#xff1a;鲲鹏服务器&#xff08;ARM架构&#xff09;&#xff0c;TiDB 官方明确支持 ARM 架构服务器部署 推荐配置 &#xff08;生产环…

Java多线程并发常见问题与解决方案

1. 使用不当:竞争与死锁 synchronized保证了同一时刻只有一个线程访问同步块,但过度使用会导致线程争用、性能瓶颈,甚至死锁。当多个线程在不同顺序上请求多个锁时,容易产生循环等待而死锁。 下面示例演示了两个线程互相持有对方需要的锁导致的死锁情况: Object lockA…