给C++工业软件搭建 Agent
这篇文章介绍如何给C++/Qt写的桌面软件搭建自身的agent,让agent直接操控软件干活,这不是skill/mcp,也不需要软件提供cli,尤其适合工业软件加入agent
这篇文章基于我维护的一个开源的工作流和数据分析软件,项目地址:github:data-workbench,国内镜像:gitee:data-workbench
data-workbench 是我开源的一个数据分析软件,C++17 加 Qt 写界面层,内嵌 Python 跑 pandas 和 numpy,主要给科研实验数据做清洗、分析、绘图,内嵌工作流,可以执行一些固有数据分析任务,软件最开始,是为了实现简单的数据分析,避免操作pandas、numpy、matplotlib等各种各样花里胡哨的函数。同时呢,集成了一个工作流,这样重复性的数据分析,能变成一个固有的流程。但随着AI的发展,这些功能已经可有可无,于是我给它补上了 Agent 功能,通过内部 Agent 对话直接操作软件自身功能,可以查数据、让ai画图、画完的图用户自己可以手动调整,可以生成分析报告等等。
Agent 分析数据自动出报告的效果。

Agent 自动绘图的效果。

给这个桌面软件添加Agent的功能,我手写的代码很少。真正干活的是 zcode、kimi code、qwen code、deepseek harness 这几个 coding agent(我穿插着用),模型主力是 glm-5.2,中间还体验过 qwen3.8-plus、deepseek-v4-flash,前前后后差不多两亿 token 就可以搭建一个相对完备的agent,花费也就几百块钱而已,后续就是工具的调整和完善。
这篇文章主要基于 data-workbench 这个C++项目,介绍如何让 AI 替你在你的某个项目中加入agent功能。最后会提供一组提示词,通过这组提示词即可让ai基于你的项目搭建出一个agent,目前我已经在其它项目测试过,无论是java写的还是c#写的项目都可以很轻松地搭建
任意软件增加Agent功能
我对 Agent 的理解,核心就是行动循环和提示词注入。 Agent 就是在调模型,解析它想调什么工具,执行,把结果塞回消息列表,再调模型,直到它认为完成。实际就一个while循环,内部一直调模型直至结束。各种 Agent 框架,langgraph 也好自己手写也好,都是在给这个循环加工程保障。
现在能控制大模型的只有文本,因此,无论skill还是mcp,对模型而言最终都是提示词的一种注入。大模型是无状态的,实际开发 Agent 你会发现每次请求都是一个大大的json,把所有会话拼接起来,让大模型能"知道"之前的对话信息,你可以塞入之前的对话,你也可以加入其它的东西,例如Agents.md的内容
一个 Agent 核心是提供的工具,尤其是垂直领域 Agent ,例如工业软件,你提供了什么工具,就决定了 Agent 能操作软件的哪些功能。这里会面临一个问题,软件的功能非常多的时候,尤其是工业软件,可能会要暴露非常多的工具,这是后续需要解决的问题
工具包含两段内容,一段是 JSON Schema,告诉模型有哪些功能、参数是什么。另一段是执行器,模型要调用时,程序去执行,把结果作为一条消息返回。
目前data-workbench提供了 23 个C++工具(DAAgentTools插件21个 + DAPaperAgent插件2个),工具的执行是C++端执行的,且工具全部通过插件注入,可以自由扩展------插件化注入机制后面单独一节细讲
data-workbench中增加agent的架构和方法
data-workbench 是桌面软件,C++ 生态里没有趁手的 agent 框架,生态最丰富的是Python,data-workbench 选用的是Python的langgraph,把 Agent 的推理循环、上下文管理、重试、子 agent 编排,全部跑在一个 Python 子进程里。工具执行全部留在 C++ 主进程。两边用 stdin/stdout 管道通信,一行一条 JSON。
#mermaid-svg-0KBELbmiTlpANnP7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0KBELbmiTlpANnP7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0KBELbmiTlpANnP7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0KBELbmiTlpANnP7 .error-icon{fill:#552222;}#mermaid-svg-0KBELbmiTlpANnP7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0KBELbmiTlpANnP7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0KBELbmiTlpANnP7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0KBELbmiTlpANnP7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0KBELbmiTlpANnP7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0KBELbmiTlpANnP7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0KBELbmiTlpANnP7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0KBELbmiTlpANnP7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0KBELbmiTlpANnP7 .marker.cross{stroke:#333333;}#mermaid-svg-0KBELbmiTlpANnP7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0KBELbmiTlpANnP7 p{margin:0;}#mermaid-svg-0KBELbmiTlpANnP7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0KBELbmiTlpANnP7 .cluster-label text{fill:#333;}#mermaid-svg-0KBELbmiTlpANnP7 .cluster-label span{color:#333;}#mermaid-svg-0KBELbmiTlpANnP7 .cluster-label span p{background-color:transparent;}#mermaid-svg-0KBELbmiTlpANnP7 .label text,#mermaid-svg-0KBELbmiTlpANnP7 span{fill:#333;color:#333;}#mermaid-svg-0KBELbmiTlpANnP7 .node rect,#mermaid-svg-0KBELbmiTlpANnP7 .node circle,#mermaid-svg-0KBELbmiTlpANnP7 .node ellipse,#mermaid-svg-0KBELbmiTlpANnP7 .node polygon,#mermaid-svg-0KBELbmiTlpANnP7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0KBELbmiTlpANnP7 .rough-node .label text,#mermaid-svg-0KBELbmiTlpANnP7 .node .label text,#mermaid-svg-0KBELbmiTlpANnP7 .image-shape .label,#mermaid-svg-0KBELbmiTlpANnP7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-0KBELbmiTlpANnP7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0KBELbmiTlpANnP7 .rough-node .label,#mermaid-svg-0KBELbmiTlpANnP7 .node .label,#mermaid-svg-0KBELbmiTlpANnP7 .image-shape .label,#mermaid-svg-0KBELbmiTlpANnP7 .icon-shape .label{text-align:center;}#mermaid-svg-0KBELbmiTlpANnP7 .node.clickable{cursor:pointer;}#mermaid-svg-0KBELbmiTlpANnP7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0KBELbmiTlpANnP7 .arrowheadPath{fill:#333333;}#mermaid-svg-0KBELbmiTlpANnP7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0KBELbmiTlpANnP7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0KBELbmiTlpANnP7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0KBELbmiTlpANnP7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0KBELbmiTlpANnP7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0KBELbmiTlpANnP7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0KBELbmiTlpANnP7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0KBELbmiTlpANnP7 .cluster text{fill:#333;}#mermaid-svg-0KBELbmiTlpANnP7 .cluster span{color:#333;}#mermaid-svg-0KBELbmiTlpANnP7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0KBELbmiTlpANnP7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0KBELbmiTlpANnP7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-0KBELbmiTlpANnP7 .icon-shape,#mermaid-svg-0KBELbmiTlpANnP7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0KBELbmiTlpANnP7 .icon-shape p,#mermaid-svg-0KBELbmiTlpANnP7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0KBELbmiTlpANnP7 .icon-shape .label rect,#mermaid-svg-0KBELbmiTlpANnP7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0KBELbmiTlpANnP7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0KBELbmiTlpANnP7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0KBELbmiTlpANnP7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Python 子进程
C++ 主进程 Qt
stdin 写
user_msg / tool_result
stdout 读
token / tool_call / message_end
界面 / 数据工作区 / 绘图区 / 工具执行
DAAgentBridge
QProcess + JSON Lines 协议
langgraph 推理循环 / 上下文压缩 / 重试
langchain-openai → OpenAI 兼容 API 流式
这种架构和开发语言无关,无论你是c/c++、c#、java写的应用都可以简单快速实现,唯一劣势是打包时要带个python环境(data-workbench本来就内嵌Python跑pandas,子进程直接复用这套环境,连这个劣势也不存在)
工具执行留在 C++ 是因为本身就是要让agent操作软件自身,因此工具都是你要改造的软件本身来执行。LLM 那边只拿到工具 schema,真正执行时经管道 RPC 回调到主进程,直接就是当前软件界面上的真实对象。这是工业软件做 agent 最舒服的一点,你的工具层就是你的业务层,中间不需要任何胶水。
推理放子进程也是更好的隔离。LLM 思考过程不会影响GUI线程,就算 Python 子进程崩了,主程序还活着,桥接层检测到异常退出会自动重启它,从磁盘上的会话记录恢复历史,重发最后一条用户消息,用户最多感觉到思考时间变长而已
目前 data-workbench 使用的协议本身很简单。C++ 往 stdin 写 user_msg、tool_result、load_session、reconfigure 这些,Python 往 stdout 写 token、tool_call、message_end、usage、error 等。stdout 只许走协议,日志全部走 stderr
聊天面板一定得使用web技术,否则很丑陋,因为你要不停对markdown进行流式渲染,web技术是目前唯一解,data-workbench是C++写的GUI,聊天面板用 QWebEngine 加 QWebChannel 和一个内嵌的web聊天界面通讯。具体实现还做了很多工程化的处理,想了解可以通过源码查看
实际开发agent需要考虑的点
实际上让 Agent 搭建 Agent 很快就能完成,但细节的调教是很花费时间的。给data-workbench嵌入agent功能只用了2天(当时用的qwen code/zcode + glm-5.2),从web界面、跨进程通信、行动循环到工具封装,几轮任务就完成得很好,但后续实际调试中会遇到很多细节问题需要逐一调整。
- 重连和容错
这点非常关键,大模型会有各种可能返回的异常问题,例如没钱了,例如请求太多受限了,这些都是实际工程问题,往往第一遍ai给你写的时候是会忽略的问题,需要后续测试补充上
- 上下文压缩和管理
在 Agent 功能跑通后,上下文的管理很关键,其中压缩是一个关键点,ai接口是无记忆状态的,让用户感觉到有记忆完全取决于你的上下文怎么拼接,而上下文的拼接都是由客户端来决定的,同时还会涉及到压缩问题,什么时候开始压缩,怎么压缩都比较有讲究,这些最好的思路就是参考当前开源的agent
- 防循环卡死
Agent 有时会反复调用同一个工具,同样的参数,一遍一遍,安安静静把 token 烧光。这也是工程性问题,初次开发agent非常容易忽略
插件化注入领域Agent
一个好的软件,一定是模块化和插件化的,你的软件提供的是基础平台,领域(业务)功能是通过插件注入的,这样才能满足不同领域(业务)的需求,也能轻松扩展各种功能。Agent功能也不例外
data-workbench的agent能力分两层。平台层是C++封装的DAAgent模块,只做和领域无关的事,负责推理桥接、工具执行调度、权限门、会话持久化、子agent编排等工作,主要就是处理python进程信息和c++端程序通信和调度。为了满足具体业务功能的需求,所有的工具和提示词都是可以通过插件注入的。为此data-workbench设计了一个插件接口DAAgentInterface
DAAgentInterface开了四个注入口:
| 注入口 | 注入什么 | 例子 |
|---|---|---|
registerTool |
单个工具(schema + 执行器) | 可注入各种软件操作的工具,例如工程加载、数据查询、建图加曲线等 |
registerSystemPrompt |
一段命名的系统提示词片段 | 可注入各种业务场景特定的提示词,选择不同的agent可以以不同的提示词开始工作 |
registerBuiltinAgent |
一整个领域agent(提示词 + 配套工具) | 可以直接提供一个完整的agent工具到界面中,例如./plugins/DAPaperAgent论文撰写助手 |
registerBuiltinSubagent |
一个子agent定义 | 可以注册一个调研subagent,或者注册一个探索subagent |
#mermaid-svg-hNErnk3U1341WLh6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-hNErnk3U1341WLh6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hNErnk3U1341WLh6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hNErnk3U1341WLh6 .error-icon{fill:#552222;}#mermaid-svg-hNErnk3U1341WLh6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hNErnk3U1341WLh6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hNErnk3U1341WLh6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hNErnk3U1341WLh6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hNErnk3U1341WLh6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hNErnk3U1341WLh6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hNErnk3U1341WLh6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hNErnk3U1341WLh6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hNErnk3U1341WLh6 .marker.cross{stroke:#333333;}#mermaid-svg-hNErnk3U1341WLh6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hNErnk3U1341WLh6 p{margin:0;}#mermaid-svg-hNErnk3U1341WLh6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hNErnk3U1341WLh6 .cluster-label text{fill:#333;}#mermaid-svg-hNErnk3U1341WLh6 .cluster-label span{color:#333;}#mermaid-svg-hNErnk3U1341WLh6 .cluster-label span p{background-color:transparent;}#mermaid-svg-hNErnk3U1341WLh6 .label text,#mermaid-svg-hNErnk3U1341WLh6 span{fill:#333;color:#333;}#mermaid-svg-hNErnk3U1341WLh6 .node rect,#mermaid-svg-hNErnk3U1341WLh6 .node circle,#mermaid-svg-hNErnk3U1341WLh6 .node ellipse,#mermaid-svg-hNErnk3U1341WLh6 .node polygon,#mermaid-svg-hNErnk3U1341WLh6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hNErnk3U1341WLh6 .rough-node .label text,#mermaid-svg-hNErnk3U1341WLh6 .node .label text,#mermaid-svg-hNErnk3U1341WLh6 .image-shape .label,#mermaid-svg-hNErnk3U1341WLh6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-hNErnk3U1341WLh6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hNErnk3U1341WLh6 .rough-node .label,#mermaid-svg-hNErnk3U1341WLh6 .node .label,#mermaid-svg-hNErnk3U1341WLh6 .image-shape .label,#mermaid-svg-hNErnk3U1341WLh6 .icon-shape .label{text-align:center;}#mermaid-svg-hNErnk3U1341WLh6 .node.clickable{cursor:pointer;}#mermaid-svg-hNErnk3U1341WLh6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hNErnk3U1341WLh6 .arrowheadPath{fill:#333333;}#mermaid-svg-hNErnk3U1341WLh6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hNErnk3U1341WLh6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hNErnk3U1341WLh6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hNErnk3U1341WLh6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hNErnk3U1341WLh6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hNErnk3U1341WLh6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hNErnk3U1341WLh6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hNErnk3U1341WLh6 .cluster text{fill:#333;}#mermaid-svg-hNErnk3U1341WLh6 .cluster span{color:#333;}#mermaid-svg-hNErnk3U1341WLh6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-hNErnk3U1341WLh6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hNErnk3U1341WLh6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-hNErnk3U1341WLh6 .icon-shape,#mermaid-svg-hNErnk3U1341WLh6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hNErnk3U1341WLh6 .icon-shape p,#mermaid-svg-hNErnk3U1341WLh6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hNErnk3U1341WLh6 .icon-shape .label rect,#mermaid-svg-hNErnk3U1341WLh6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hNErnk3U1341WLh6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hNErnk3U1341WLh6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hNErnk3U1341WLh6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 插件层
平台层 DAAgent
registerTool / registerSystemPrompt
registerBuiltinAgent / registerTool / registerSystemPrompt
推理桥接 / 工具执行调度 / 权限门 / 会话持久化 / 子agent编排
DAAgentTools 插件
21个工具 + 提示词片段
DAPaperAgent 插件
论文撰写助手agent + 2个文献工具
data-workbench的具体agent能力目前通过两个插件提供,一个是提供工具让agent调用,名为DAAgentTools插件(源码位于./plugins/DAAgentTools),一个名为DAPaperAgent,是做的论文写作的agent,演示了系统提示词的注入和特有工具的注入(源码位于./plugins/DAPaperAgent)。后续我会开发更多的插件,注入到软件中,例如信号处理插件、工作流编排插件等等。这里简单介绍我现在做好的两个插件:
DAAgentTools插件注入21个工具:数据类(list_data-列举所有数据、query_data-查询数据、get_column_stats-获取列数据的基础统计信息等5个)、绘图类(create_chart-创建绘图、add_curve-添加曲线、set_axis-设置坐标轴样式等11个)、文件报告类3个、脚本执行类2个,外加一段系统提示词,教agent怎么在data-workbench里引用图表。
DAPaperAgent插件注入的是一个完整agent------"论文撰写助手":设计了六阶段论文写作流程的提示词,外加search_literature-文献搜索、verify_doi-文献信息确认两个文献工具,确保ai获取的文献是真实文献。插件装进plugins目录,平台上就多了一个会写论文的agent,主程序仅仅提供框架,不做业务。
-
自定义论文写作agent

-
论文写作agent提供了search_literature-文献搜索、verify_doi-文献信息确认两个文献工具,确保文献正确

这么做的好处有三个。第一,领域(业务)团队不碰主程序。想给软件加个特殊行业的agent不需要了解主程序的agent如何实现,只需要了解如何注册工具和提示词即可,插件支持热插拔,可以把不需要的工具或者插件剔除。第二,工具就在C++主进程内执行,工具层就是业务层,直接操作软件接口,没有序列化开销,没有RPC胶水,这是MCP/skill这类体外方案做不到的,程序能暴露多少接口ai就能操作多少内容,理论上整个程序所有接口ai都可以操作。第三,所有插件注入的工具执行时也统一接受平台权限管理,插件开发者也无需管理权限的问题。
怎么让 AI 替你把 agent 造出来
前面说过,这套带权限、带子 agent、带会话管理、可以直接操作软件自身的agent功能,我没有手写多少代码,用qwen code/zcode/kimi code这些搭配glm-5.2轻松搞定,现在有glm-5.3或kimi-k3理论上会做的更好。这里讲讲给到coding ai的提示词,及注意的地方,这样任何软件按照这套提示词都可以增加agent功能
你首先需要了解一个完整的 agent 由哪些能力组成,最好去看看开源的agent是怎么做的,这里可以看看我另外一篇文章专门拆解了7个开源agent:拆了七大开源 Agent 的源码,最高分竟然不是 Codex
第一次任务并不需要一次全做,但得知道每一项的存在和它解决的问题,在完成框架搭建后,一步一步补充即可
| 能力 | 解决什么问题 | 必要性 |
|---|---|---|
| 推理循环 | 调 LLM、解析 tool_calls、执行、回传、继续 | 必要 |
| 工具系统 | 声明式工具 + JSON Schema + 注册执行 | 必要 |
| 通信与隔离 | 推理不阻塞主程序,双向流式协议 | 必要 |
| 对话 UI | markdown 渲染、工具调用折叠、双向桥接 | 必要 |
| 人机交互 HITL | agent 能在对话流里向用户提问,回答后继续 | 必要 |
| 系统提示词 | 分段拼接、运行时注入、防注入转义 | 必要 |
| 配置与安全 | LLM 配置、连接测试、apikey凭据加密 | 必要 |
| 上下文管理 | token 估算、阈值压缩、摘要重组历史 | 必要 |
| 循环防卡死 | 最大步数、循环检测、配对兜底 | 必要 |
| 重试与恢复 | 429、断网、限流下退避重试 | 必要 |
| 会话管理 | 多会话、持久化、崩溃后恢复 | 按需 |
| 权限系统 | 每工具允许/询问/拒绝、路径保护 | 按需 |
| 子 agent 调度 | 派发独立子任务、收窄工具与权限 | 按需 |
| skill支持 | 让agent支持自定义skill | 按需 |
| mcp支持 | 让agent支持mcp协议 | 按需 |
| 记忆 | 让agent自我总结记忆 | 按需 |
agent 是个大工程,可以通过下面提示词让一个code agent基于你自身的系统制定一个计划,有经验的架构师好好审查该计划进行完善,再让ai执行,这里建议使用grill me技能,让ai先制定完善的方案,再执行
markdown
为 {平台名} 新增 AI Agent 能力,让用户在 {使用场景} 通过自然语言对话,调用大模型对 {数据} 进行 {分析、可视化、报告生成等能力}。
第一步,识别我当前系统的技术架构和现有能力。桌面端还是 Web 端、什么框架、什么构建系统、现有的数据与可视化能力如何,据此选择与之匹配的隔离方式、通信方式、渲染方式。下面每条都是平台中立的设计要求,只说目标与原因,具体机制由你按我当前架构选,在计划里说明理由,与我的架构冲突先和我确认。
一、推理与宿主隔离。LLM 推理、API 调用、工具编排放在不阻塞主程序的执行单元里,桌面端就是子进程或独立线程。不使用时零开销,执行单元出问题主程序要能恢复到可用状态。
二、框架与 LLM 接入。选一个原生支持「向用户提问、用户选择后继续推理」的 agent 框架,Python 后端 LangGraph 是成熟候选,其它栈给出两三个候选和取舍。LLM 走 OpenAI 兼容 API 加流式,做 provider 抽象,不绑死任何一家 SDK。
三、流式输出。逐 token 到 UI,chunk 里的 tool_call 按 index 累积拼接,不能逐 chunk 当完整参数。渲染增量追加并做防抖,工具调用和结果在对话流里折叠展示。
四、人机交互。提问直接出现在对话流里,不弹模态窗,用户选择后答案回传继续推理,支持多轮提问。
五、工具体系。统一接口,例如:getToolSpec 返回 JSON Schema,execute 返回 JSON 结果。注意大数据集返回摘要而非全量,失败返回 success false 不抛异常,带超时。
六、系统提示词分段拼装,平台基础加领域扩展加工具规格自动注入,外部注入的内容转义后再拼,防 prompt 注入。
七、分层架构。通用能力做平台模块,领域能力经注册注入,不把领域逻辑写进通用模块。
八、配置与安全。api key 加密存储,不写日志不打控制台。文件工具限制在工作目录,路径词法规范化防越界,拒绝敏感文件。
九、错误处理。每个 tool.call 必须有配对的 tool.result,中断、超时、取消都要合成错误结果兜底,否则下一轮请求会被 provider 拒绝,对话卡死。
十、分阶段交付,每阶段可独立验证。先模块骨架,再核心循环,再对话 UI,再生命周期,再内置工具,再设置页。每阶段实现前先给我看设计,确认后再做。
总之,计划阶段别省
计划批准后按阶段推进。实施阶段每一段提示词的套路都一样:先让 AI 识别你现在的代码结构和数据流,再给设计要求,最后带验收标准。上面这段是制定计划用的,各实施阶段的提示词照这个套路写即可。
雏形出来后,再逐步增加相关的功能,如上面能力地图提到的上下文管理 、循环防卡死 、重试与恢复 、权限 、子 agent等等功能,在结合你软件自身增加一些配置类功能。
结语
data-workbench 完全开源,仓库位于 github.com/czyt1988/data-workbench或gitee.com/czyt1988/data-workbench,文档在 czyt1988.github.io/data-workbench。支持任意 OpenAI 兼容 API
工业软件搭 agent 并不难。工业软件功能非常多,按现有思路搭建会面临工具爆炸问题:一个大型软件要给agent暴露非常多的工具。data-workbench目前的答案是子agent加工具白名单收窄------主agent只留调度类和通用工具,领域工具下沉给子agent,模型某一时刻面对的工具数量降下来了。上百个工具对当前大模型来说不至于失控,但长对话后是否还能准确调用如此多工具依然有待验证,这条路能走多远也需要更多实践检验