ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从单体到图结构:智能体图工程设计与多智能体协作实践

从单体到图结构:智能体图工程设计与多智能体协作实践 1. 从单体到群体为什么智能体需要图工程1.1 一个让我彻底改变认知的踩坑经历去年下半年我接手了一个内部知识问答智能体的项目需求听起来很朴素用户提问系统检索文档大模型生成答案。我当时的做法非常典型——一个 ReAct 循环一个工具调用列表一个提示词模板跑通了就上线。前两周效果还行到了第三周问题开始集中爆发多跳问题答不对、需要对比两份文档时只会各说各话、遇到需要先算再查再总结的任务直接卡死。我盯着日志看了整整两天最后意识到一件事——问题不在模型能力而在我把所有逻辑都塞进了一个循环里。这就是单体智能体的天花板。它像一个什么活都接的个体户能力边界取决于那一个提示词能承载多少上下文。而真实业务里的任务往往是先分类、再检索、再校验、再生成、最后自检这样一条有分支、有回路、有并行的工作链。把这条链压扁成一个循环等于让一个人同时扮演前台、会计、审核和经理出错是必然的。Graph Engineering图工程要解决的正是这件事把智能体的行为从一个循环升级为一张有向图节点是能力单元边是流转规则状态在图上流动。个体智能变成系统智能靠的不是换一个更强的模型而是换一种组织方式。1.2 个体智能与系统智能的本质差别我用一个生活化的类比来说明。个体智能像一位经验丰富的全科医生什么都能看一点但遇到复杂病例容易漏诊系统智能像一家医院分诊台、专科门诊、检验科、药房、复诊环节各司其职每个环节只做自己最擅长的事通过流程把整体能力放大。医院的整体诊疗水平不取决于某一个医生有多强而取决于流程设计是否合理、信息传递是否无损。落到技术层面这个差别体现在四个维度上我整理成一张表方便对照维度个体智能单体 Agent系统智能图结构 Agent控制流单一循环线性推进有向图支持分支、并行、回路状态管理全部塞进对话历史显式状态对象按节点读写错误恢复靠模型自我纠错可设计重试节点、回退边、人工介入点可观测性只能看最终输出每个节点可单独打点、评估、替换扩展方式改提示词越改越臃肿加节点、加边局部替换不影响全局这张表里最关键的一行是可观测性。单体智能体出问题时你只能看到输入和输出中间发生了什么全靠猜。而图结构把每一步都暴露出来哪个节点拖慢了、哪个节点判断错了、哪条边走岔了一目了然。这一点在工程化落地阶段是决定性的——你不能优化你看不见的东西。1.3 为什么是现在三个条件同时成熟图工程这个概念不是凭空冒出来的它踩在了三个同时成熟的支点上。第一是模型能力的稳定化。早期模型连结构化输出都不稳定你让它返回一个 JSON 都经常多带解释文字根本没法做节点间的状态传递。现在主流模型对函数调用、结构化输出的支持已经相当可靠节点之间的数据契约才立得住。第二是编排框架的成熟。以 LangGraph 为代表的图编排框架把状态、节点、边、检查点这几个抽象做成了标准件开发者不用再从零手写调度器。这类框架的核心价值不是省代码而是把一套经过验证的工程范式固化下来。第三是业务复杂度的倒逼。早期智能体做的是问答摘要这类单步任务单体足够。现在需求变成了帮我完成一份竞品分析报告自动处理这批工单这类任务天然是多步骤、多角色、多校验的单体架构根本扛不住。我的判断是2026 年之后评价一个智能体项目是否专业看的不是它用了哪个模型而是它的图设计得是否合理。模型是发动机图是传动系统发动机再好传动系统拉胯车也跑不起来。2. 图工程的核心构件节点、边与状态2.1 状态设计整张图的地基如果只能给一条建议我会说先把状态设计对再谈其他。状态是图工程里最容易做错、也最影响后续一切的东西。状态本质上是一个在节点之间流转的数据对象。每个节点读取它需要的字段写入它产出的字段。设计状态时我踩过的最大坑是什么都往里塞——把原始文档、中间推理、工具返回、用户历史全堆进一个巨大的字典结果节点之间耦合严重改一个字段到处报错。我的做法是按生命周期分层。把状态字段分成三类输入层用户原始输入、会话标识、外部参数全程只读。工作层检索结果、中间推理、工具调用记录节点间读写任务结束即丢弃。输出层最终答案、置信度、引用来源只在收尾节点写入。这样分层之后每个节点只需要声明自己读哪些、写哪些耦合度立刻降下来。下面是一个我用得比较顺的状态定义示例用 Python 的 TypedDict 表达from typing import TypedDict, Annotated from operator import add class AgentState(TypedDict): # 输入层全程只读 user_query: str session_id: str # 工作层节点间流转 retrieved_docs: list[dict] reasoning_trace: Annotated[list[str], add] # 累加式写入 tool_calls: list[dict] # 输出层收尾写入 final_answer: str confidence: float citations: list[str]这里有个细节值得说reasoning_trace用了Annotated[list[str], add]意思是多个节点写入时自动做列表拼接而不是覆盖。这个机制在并行节点场景下特别有用——两个节点同时往同一个字段写不会互相覆盖。这是图工程里一个非常实用但容易被忽略的设计很多人第一次做并行分支时数据莫名丢失就是栽在这里。2.2 节点设计单一职责是铁律节点是图里的执行单元可以是一次模型调用、一次工具调用、一段纯代码逻辑甚至是一个人工审核的等待点。节点设计我遵循一条铁律一个节点只做一件事且这件事能用一句话说清楚。反面例子是我早期做的一个处理节点它同时负责意图识别、文档检索和答案生成。听起来省事实际上这个节点既没法单独评估也没法单独替换出了错根本定位不到是哪一步的问题。后来我把它拆成三个节点每个节点单独打点问题立刻现形——原来是意图识别把对比类问题误判成了查询类导致检索策略用错了。节点的粒度怎么把握我的经验是看可测试性。如果一个节点你没法为它单独写一个输入输出明确的测试用例那它大概率太大了需要拆。反过来如果两个节点永远一起出现、从不单独使用那它们可能该合并。2.3 边设计控制流的真正表达边决定了节点之间怎么流转这是图工程区别于普通工作流引擎的核心。边分几种类型各有各的用途普通边A 执行完无条件进入 B用于固定顺序。条件边根据状态里的某个字段决定走哪条分支用于路由。回路边从后面的节点指回前面的节点用于重试和迭代。并行边一个节点同时触发多个下游节点用于并发处理。条件边是使用频率最高的。举个我实际项目里的例子一个客服智能体需要先判断用户意图然后路由到不同的处理链。这个路由逻辑就写在条件边里def route_by_intent(state: AgentState) - str: intent state.get(intent, unknown) if intent refund: return refund_flow elif intent technical: return tech_support_flow elif intent complaint: return escalation_flow return general_flow graph.add_conditional_edges( classify_intent, route_by_intent, { refund_flow: handle_refund, tech_support_flow: handle_tech, escalation_flow: escalate, general_flow: handle_general, } )这里有个实操心得路由函数的返回值一定要穷举所有可能并且给一个兜底分支。我见过太多项目因为路由函数漏了一种情况导致状态卡在某个节点再也走不下去整个图直接挂死。兜底分支不是可选项是必选项。2.4 检查点让图具备记忆和恢复能力检查点Checkpoint是图工程里被低估最严重的能力。它的作用是在每个节点执行后把当前状态持久化下来带来三个直接好处断点续跑、人工介入、时间旅行调试。断点续跑很好理解长任务中途挂了不用从头再来。人工介入是指你可以在某个节点设置一个暂停点等人工确认后再继续——这在需要审核的场景里是刚需。时间旅行调试是我用得最多的任务跑完后我可以回放任意一个节点的状态快照看当时模型到底看到了什么、输出了什么定位问题效率提升非常明显。检查点的存储选型上开发阶段用内存或 SQLite 就够了生产环境建议用支持并发写入的数据库。这里要注意一个坑检查点会显著增加存储开销如果状态里塞了大量文档原文快照体积会爆炸。我的做法是状态里只存文档 ID 和摘要原文放外部存储需要时再取。3. 多智能体协作从一张图到多张图3.1 什么时候该拆成多智能体单张图能解决的问题不要拆成多智能体。这是我用血泪换来的原则。多智能体带来的协调成本、状态同步成本、调试成本都是指数级上升的。那什么时候必须拆我总结出三个信号第一角色之间存在根本性的提示词冲突。比如一个智能体要严格按事实回答另一个要发挥创意生成方案这两种人格塞进一个提示词会互相打架拆开反而各自清晰。第二子任务可以独立评估和迭代。如果检索质量可以单独度量、单独优化那它就该是一个独立的智能体有自己的评估指标和迭代节奏。第三需要不同的工具集和权限。一个智能体只能读数据库另一个可以写数据库权限隔离在工程上是硬需求拆开是最自然的实现方式。3.2 三种主流协作拓扑多智能体的协作结构我实践下来主要用三种拓扑各有适用场景拓扑结构适用场景典型问题主管制一个主管智能体调度多个工人智能体任务可分解、需要统一决策主管成为瓶颈流水线智能体按顺序接力处理步骤明确、依赖线性单点故障传导对等协商多个智能体互相讨论达成共识需要多视角、有争议容易陷入循环主管制是我用得最多的。主管智能体不干具体活只负责拆解任务、分派、汇总。它的提示词里核心就三件事任务分解规则、分派依据、结果整合方式。这里的关键是主管不能太聪明——如果主管自己也开始做具体任务整个结构就退化成单体了。对等协商要慎用。我做过一个需要多视角评估的方案评审智能体三个智能体互相讨论结果经常陷入你说得对但我补充一点的无限循环。后来我加了一个硬性的轮次上限和一个仲裁节点才把这个问题压住。任何可能形成回路的协作结构都必须有终止条件这是铁律。3.3 状态在多个智能体之间怎么传多智能体最大的工程难点是状态传递。我的做法是共享一个全局状态但每个智能体只被允许读写自己的命名空间。这样既保证了信息可达又避免了互相污染。具体实现上全局状态里给每个智能体划一块子字典智能体 A 只能写state[agent_a]下的字段读的时候可以读全局。这个约束看起来简单但它把谁能改什么这件事显式化了调试时能快速定位是哪个智能体改坏了数据。还有一个细节是消息传递的格式统一。智能体之间传的不是自然语言而是结构化的消息对象包含发送方、接收方、意图、载荷、时间戳。用自然语言传消息看起来灵活实际上会让下游智能体不得不做额外的解析既慢又不可靠。4. 工程化落地评估、可观测与成本控制4.1 图结构智能体的评估怎么做单体智能体的评估很简单给一批输入看输出对不对。图结构智能体的评估要复杂得多因为错误可能发生在任何一个节点。我的评估体系分三层端到端评估看最终结果这是最基本的。节点级评估看每个关键节点的输出质量比如检索节点的召回率、路由节点的准确率。路径评估看整个执行路径是否合理比如一个本该走退款流程的请求有没有走错分支。节点级评估是图工程带来的独特价值。因为节点是独立的你可以为每个节点准备专门的测试集。我通常会给路由节点准备几百条标注好的意图样本每次改路由逻辑就跑一遍准确率掉了立刻能发现。这在单体架构里是做不到的。4.2 可观测性让每一步都可见可观测性我按三个维度搭建日志、指标、追踪。日志记录每个节点的输入输出和耗时格式统一成结构化 JSON方便检索。指标关注几个关键数字节点平均耗时、路由分支分布、重试次数、失败率。追踪则是把一次完整请求的所有节点串成一条链路用 trace_id 关联。这里有个独家经验路由分支分布这个指标特别有价值。如果某个分支的命中率突然从 20% 涨到 60%往往意味着上游的意图识别出了问题或者用户行为发生了变化。这个信号比单纯的错误率更早暴露问题。4.3 成本控制的几个实操手段图结构智能体天然比单体贵因为节点多了、调用多了。控制成本我有几个手段缓存高频路径。很多请求走的是完全相同的路径把路径的中间结果缓存起来命中时直接返回。小模型做路由大模型做生成。路由和分类这类任务用小模型完全够用把大模型留给真正需要它的生成节点。并行化能并的节点。检索多个数据源这种操作完全可以并行总耗时取决于最慢的那个而不是累加。我实测下来这三个手段组合使用成本能压到原来的三分之一左右而效果几乎无损。关键是要先测量再优化不要凭感觉砍节点砍错了效果掉得比成本快。5. 常见问题与排查实录5.1 状态卡死与无限循环这是图工程最高频的问题。表现是任务跑着跑着不动了或者日志里同一个节点反复出现。根因通常是回路边缺少终止条件或者条件边的路由函数返回了未定义的分支。排查方法很直接看追踪链路找到重复出现的节点检查它的入边条件。如果是回路确认循环计数器有没有正确递增如果是路由确认路由函数的返回值是否都在边映射表里。我的预防措施是给每个回路强制加一个最大迭代次数超过就强制跳出并记录告警。5.2 节点间数据丢失表现是下游节点读不到上游写的字段。常见原因有三个字段名拼写不一致、状态定义里没声明该字段、并行写入时被覆盖。排查时先打印每个节点执行前后的完整状态快照对比差异。如果是并行覆盖检查是否用了累加式的注解。我现在的习惯是状态字段全部集中定义在一个文件里所有节点引用同一份定义从源头杜绝拼写问题。5.3 并行节点的结果合并并行节点跑完后需要合并结果这里容易出问题。如果两个节点都往同一个列表写没有累加注解就会互相覆盖。如果合并逻辑写在某个节点里要确保这个节点在所有并行分支都完成后才执行。我的做法是用一个专门的汇聚节点它的入边来自所有并行分支框架会等所有分支完成后再触发它。合并逻辑就写在这个节点里清晰且不会出错。5.4 问题速查表现象可能原因排查方向任务卡死不动回路无终止条件检查循环计数器与最大迭代走错分支路由函数逻辑错误打印路由输入与返回值字段读不到状态未声明或拼写错对比状态定义与节点读写并行结果丢失缺少累加注解检查 Annotated 配置成本异常高大模型用在了简单节点分析各节点调用分布效果不稳定节点粒度过粗拆分节点单独评估5.5 几条踩坑换来的经验第一先画图再写代码。我现在的习惯是拿张纸把节点和边画出来确认逻辑通了再动手。跳过这一步直接写代码返工率极高。第二每个节点都要能单独跑。给节点写一个独立的测试入口输入一个构造好的状态看输出对不对。这个习惯能省下大量联调时间。第三日志要带 trace_id 和节点名。没有这两个字段的日志在排查多节点问题时基本没用。第四不要过早优化图结构。先用最直白的结构跑通再根据实际瓶颈调整。我见过有人一上来就设计了一个十几节点的复杂图结果一半节点从没被走到过。6. 一个完整的图结构智能体搭建示例6.1 需求与图设计我拿一个实际做过的技术文档问答智能体来演示。需求是用户提问技术问题系统检索内部文档判断检索结果是否充分不充分就改写查询重试充分则生成带引用的答案最后自检答案是否有据可依。这张图的节点和边是这样的入口节点接收问题进入查询改写节点然后检索节点接着是一个判断节点——如果检索结果相关度低于阈值走回路回到查询改写如果达标进入生成节点生成后进入自检节点自检不通过则回到生成重试通过则输出。6.2 关键节点的实现要点查询改写节点的核心是让模型把口语化问题转成适合检索的关键词组合。这里我用的提示词很克制只要求输出关键词不要求解释。生成节点要求模型严格基于检索到的文档作答并在每个论断后标注来源编号。自检节点检查答案里的每个论断是否都能在检索文档里找到支撑找不到就标记出来触发重试。判断节点的阈值设定是个经验活。我一开始设了 0.7结果大量正常问题被判定为不充分反复重试。后来降到 0.5并改成取 top3 文档的平均分稳定性好了很多。阈值没有标准答案要靠实际数据调。6.3 回路与重试的边界控制这张图有两个回路检索不充分回改写自检不通过回生成。两个回路都必须有次数上限。我的设置是改写最多重试 2 次生成最多重试 1 次。超过上限就走兜底分支返回当前最好的结果并附上未能完全确认的提示。这个设计很重要。没有上限的回路在生产环境就是定时炸弹某个刁钻的问题可能让系统无限循环既烧钱又拖垮服务。兜底不是失败是工程上的必要妥协。7. 我对图工程未来的一点个人判断做了一年多的图结构智能体我最大的体会是这个领域的门槛不在模型而在设计。模型能力会持续变强但把能力组织成可靠系统的能力是需要工程师一点点积累的。图工程本质上是一种系统设计思维它要求你既懂业务逻辑又懂状态管理还得有分布式系统的那套工程直觉。我现在看一个智能体项目第一眼看的不是它用了什么模型而是它的图长什么样、状态怎么设计、回路有没有边界。这三点过关了项目基本就稳了。至于未来我猜图会越来越动态——不是工程师画死的而是根据任务类型自动生成或调整的。但那是下一步的事眼下把静态图设计扎实已经能解决绝大多数实际问题了。最后分享一个小技巧如果你刚开始接触图工程别急着上框架先用一个字典当状态、一个函数当节点、一个 if-else 当边手写一个最小的图跑通。理解了状态怎么流、边怎么控再上框架会顺畅得多。框架帮你省的是样板代码但状态和边的设计思路得你自己长在脑子里。
返回列表