做Agent的朋友们,我猜你多多少少被同一个问题折腾过:单个大模型直接跑任务,简单问题还行,一旦任务超过两三步,就开始掉链子——中间结果跑偏、上下文越滚越长、明明说好要调用工具结果它自己"脑补"答案。我最初做Agent工作流也走过一段弯路,天真地以为把LLM接上几个工具就是Agent了,结果线上连着出事故,最后老老实实回来研究架构。直到我把整个系统拆成增强型、链式、路由式三种模型去重新设计,思路才真正打开。这篇就把我在这三种架构上的设计思路、实现细节和踩过的坑整理出来,希望能帮你少走点弯路。
1. 三种架构到底在解决什么问题
1.1 一个让所有Agent开发者都头疼的现场
先看一个典型场景。用户在对话框里说:"帮我分析这个季度的销售数据,出一份报告,并针对下降的品类给建议。"
直接丢给一个裸奔的LLM调用,你会看到什么?
第一,模型可能不知道该先查数据库还是该先读文件,它可能直接开始"编"一个季度销售数据出来。第二,就算让它调用查询工具,查完数据之后它又把原始目标给忘了,报告里讲了一堆有的没的,就是没有"下降品类建议"。第三,整个过程中没有清晰的决策依据,模型输出里夹着幻觉数据,你还很难定位是哪个环节出了问题。
这些问题真不是模型不够聪明,而是缺少一层"工作流架构"。所谓Agent工作流架构,本质上是回答三件事:任务要怎么拆、状态怎么传、决策点放在哪。增强型、链式、路由式,就是三种最常见的拆分方式。
1.2 增强型、链式、路由式:一句话理解加对比表
我用一句话先把三者的气质说清楚:
- 增强型架构:一个Agent干到底,但它会反思、会调用工具、会翻历史记录。
- 链式架构:任务排排坐,前一步的输出喂给下一步,流程基本不变。
- 路由式架构:入口先做判断,把不同任务分发给不同处理分支,走哪条路不确定。
光这么说还是有点虚,做对比的时候我习惯把"任务特征"和"成本特点"放在一起看,因为这两个因素决定选型。
| 维度 | 增强型 | 链式 | 路由式 |
|---|---|---|---|
| 核心思想 | 单Agent + 工具 + 记忆,在循环中完成任务 | 多步骤固定顺序,前一步输出是后一步输入 | 动态分类/决策,把输入送到不同处理分支 |
| 适合任务 | 需要外部工具、检索、记忆的复杂单任务 | 流程相对固定、步骤明确且可预期的任务 | 输入类型差异大、系统内部有多个子任务 |
| 架构复杂度 | 中 | 低到中 | 中到高 |
| 调试难度 | 中,依赖循环日志 | 低,链路清晰 | 高,路由判定不好归因 |
| Token成本 | 循环调用,上下文可能膨胀,偏高 | 线性调用,成本较可控 | 路由层有额外开销,分支越多成本越高 |
| 典型应用 | 带知识库检索的问答Agent | 数据清洗到分析报告生成流水线 | 多类型客服工单自动分流 |
这张表是我的选型底稿。实际项目里很少只用一种架构,但先把纯态看明白,后面组合才不慌。
1.3 选型前必须回答的三个问题
与其拿着架构硬套业务,不如先问自己三个问题。
第一个问题:任务的处理路径是固定的吗?如果用户进来十个请求,九个的处理步骤都是"先做A,再做B,最后C",那你应该优先考虑链式。路径在运行时才动态变化的,才需要路由式。
第二个问题:任务是否需要持续依赖外部工具或者旧信息?比如查库存、搜文档、翻聊天记录,只要单Agent需要频繁"看外部世界",增强型的循环结构就是必需的,它让模型每走一步都能拿到新的观测结果。
第三个问题:你更想要稳定性还是灵活性?链式最稳,出了问题好排查;路由式最灵活,但分类错了会连锁错误。稳不稳这件事,项目后期比前期更敏感,别等接线上才想明白。
需要说明的是,这三个问题不是孤立的。实际情况中,一个任务可能既有固定流程又有动态分支,这时候通常不是纯单一架构能解决的,核心思路是分层组合。
2. 增强型架构:让单个Agent真正具备干活能力
2.1 增强型架构的运行回路
增强型架构的核心不是"一个大模型",而是"一个大模型在一个循环里不断干活"。每轮循环做四件事:理解当前任务和目标、决定下一步动作、执行动作(可能是调用工具)、把动作结果放回上下文里继续思考。
这个回路写出来没那么玄乎,大概长这样:
steps = 0 while steps < max_steps: action = llm_plan(task, context, tools) if action.type == "call_tool": result = execute_tool(action.tool_name, action.args) context.append(result) # 把工具返回结果放回上下文 elif action.type == "finish": return action.answer steps += 1这个模式和常见的ReAct思路是一致的:推理和行动交替进行。模型不是一次性给出最终答案,而是把"思考下一步"和"执行动作"交替推进。你别小看这个循环,它意味着修复了一个老Agent最严重的毛病——没有反馈回路。
没有回路的时候,模型说"我要查一下订单状态",然后就没有然后了。有了回路,模型查到订单状态,看到结果是"已发货",它才会继续问"那你是否需要我帮你修改收货地址"。这个"看一眼结果再决定下一步"的能力,才是增强型比普通Prompt调用高级的地方。
2.2 工具接入:比你想的更考验提示词功力
增强型架构里,工具API的接入质量,直接决定循环能不能收敛。我见过不少项目,工具也接了,模型也调了,就是频繁出错,最后发现是工具描述写得太敷衍。
接口文档里给工具起名字,尽量用语义化的词。search_order就比func1_get命中率高得多。参数描述一定要写清楚必填项、默认值和取值范围。模型判断要传什么参数,全靠这些描述,别让它猜。
更关键的是工具描述里的"使用时机"部分。只写"查询订单信息"远远不够,要写"仅当用户明确询问订单状态、物流信息或配送进度时使用;当用户询问售后政策时不要使用"。这句话看着啰嗦,但能解决大量误调用问题。模型和人对工具的理解,天然有偏差,你不把边界写清楚,模型就会替你做决定。
一个工具Schema示例:
{ "name": "search_sales_data", "description": "查询指定时间段内的销售汇总数据,仅在用户询问销售数据、经营报表、业绩趋势时使用", "parameters": { "type": "object", "properties": { "start_date": {"type": "string", "description": "开始日期,格式YYYY-MM-DD"}, "end_date": {"type": "string", "description": "结束日期,格式YYYY-MM-DD"} }, "required": ["start_date", "end_date"] } }这里有一个实操细节:工具返回结果要做精简。很多内部系统返回的是几百行的日志、嵌套对象,你把这些全塞进上下文,模型根本看不完,还会把上下文窗口撑爆。我的习惯是让工具返回摘要字段,把最重要的数字、状态、错误码提取出来,其余细节放进附件或详情接口里,等模型需要了再取。
2.3 记忆和上下文管理
增强型的第二个增强点是记忆。记忆分两层:短期记忆和长期记忆。短期记忆就是当前会话上下文,模型靠它记住任务目标;长期记忆是跨会话的,用向量库或摘要库存历史结论,模型需要时再检索出来。
实际跑起来最头疼的是上下文膨胀。每轮循环都会追加工具返回、模型思考,跑个十几轮之后,窗口就满了。我的办法是三级策略:窗口内保留最近N轮完整记录,更早的用摘要覆盖,工具返回结果只保留结论字段。这样既能保留关键信息,又不至于让Token费用涨到天上去。
还有一个小技巧:相同查询做缓存。销售数据聚合、知识库搜索这种工具,结果基本是稳定的,我建议加一层简单的缓存,key是参数序列化后的哈希值,过期时间按业务定。增强型Agent循环里经常会出现"同一个工具被反复调用"的情况,有缓存能省不少成本。
2.4 增强型架构的适用边界
不强的地方也得讲清楚。增强型架构高度依赖模型的推理能力,如果底模本身就弱,循环只会让它越错越离谱。它也不适合延迟敏感的场景,因为每轮循环都是一次LLM调用加一次工具调用,动辄几秒甚至十几秒,想做毫秒级交互基本没戏。
所以我一般建议:使用增强型架构时,一定要给循环设置步数上限,5到10步比较合适。再给它一个明确的"结束"动作类型,让模型知道做完之后必须有收尾动作,不能永远思考下去。没有这两个限制,线上很容易出现一个Agent反复横跳,把预算烧光。
3. 链式架构:把复杂任务拆成可控流水线
3.1 链式架构的两种主流形态
链式架构大家应该不陌生,它的思路就是流水线:任务拆成多个有顺序的步骤,每个步骤由一个模型或工具完成,前一步的输出作为后一步的输入。我做链式设计时,一般分两种形态。
第一种是顺序链,步骤是严格A到B到C的。比如做一个数据分析报告:数据提取、数据清洗、统计分析、内容生成、格式校验。中间每一步都必须做完,不能跳步。这种链的好处是流程稳定,每一层职责单一,出问题能快速锁死到具体环节。
第二种是分支链,中间有if/else判断。比如内容生产流程里,第一步先判断"用户要的是长文还是短文",然后走两条子链,长文链和短文链各自有不同的步骤。分支链本质上是在链式里加入了一个小的决策点,它还不是完整的路由架构,因为分支本身的数量和路径是预先定死的。
3.2 链式设计的三个关键参数
我在设计链式工作流时,最关注三个参数,这三个参数踩坑率最高。
步骤粒度。拆太细,每一步都是跨服务调用和LLM调用,延迟累加、失败面扩大;拆太粗,一步里的任务复杂度太高,模型hold不住。我的判断标准是:单个步骤尽量让模型在一次Prompt加一个输出Schema内完成,并且输出是可以被程序校验的。能校验的步骤才是好步骤,不能校验的步骤就是隐患。
状态传递。链式架构里,前一步的输出直接决定后一步能否正常工作。所以中间数据的结构必须稳定。我习惯给每个中间结果定义一个JSON Schema,比如"数据分析结果"固定包含summary、tables、insights三个字段,下游只认这个结构。模型输出后先做一次Schema校验,不合格就重试或标记失败,绝不让脏数据流到下一层。
失败重试策略。每一步都要问:能不能重试,重试几次,失败后是中止整个流程还是降级到兜底方案。我默认设置是重试1到2次,重试之间有指数退避。重试仍然失败就直接返回错误给用户,并附上失败环节的日志,方便排查。
3.3 链式工作流的调试与容错
链式的调试,核心就一句话:让每一步都留下脚印。
我要求所有中间产物落盘,要么写文件,要么存日志库。这样每一步的输入输出都能回放,哪里出了问题一目了然。曾经有一个数据清洗步骤,结果偶尔缺失字段,排查了很久才发现是该步骤在某种边界条件下返回了空列表。如果没有中间产物回放,这种问题根本不可能快速定位。
容错方面还有一个好用的技巧:幂等设计。很多步骤本身就是纯函数式的,同样的输入会产生同样的输出。我在步骤执行前先检查缓存,命中的话直接复用上次结果。这个设计有两个好处,一是节省重复调用成本,二是断点重跑时能跳过已完成部分,调试效率高很多,特别是跑长链路的时候,不用每次从头开始。
链式还有一个容易被忽略的点:每步耗时和Token消耗要单独记录。链路一旦变长,成本热点往往藏在某个不起眼的步骤里。有了单步监控,你才能知道是"数据提取"花了80%的预算,还是"内容生成"拖了后腿。
4. 路由式架构:动态决策型任务的调度中枢
4.1 路由式架构的工作机制
路由式架构和前面两种最大的区别在于:它明确地把"决策"和"执行"分开了。入口是一个路由器,它只做一件事——根据输入内容判断该走哪条路,然后转发。每条路上跑的可以是一个增强型Agent,也可以是一个链式工作流,甚至另一个路由。
拿客服系统举个例子。用户消息进来,路由器先判断:这是售后问题、售前咨询、技术支持,还是单纯闲聊。判断完之后,售后问题转给售后Agent,技术支持转给技术支持Agent。这里的"用户输入类型多样"就是典型的路由式场景。
核心逻辑在于:你不是让一个Agent处理所有类型的请求,而是让一个Agent处理"该不该转"这个简单问题,让专业分支处理自己的具体问题。意图识别比完整回答简单得多,所以把识别环节单独拎出来,能有效提升整体系统的专业度。
4.2 硬路由与软路由:两种实现路线对比
路由判定这件事,做法上有硬路由和软路由两条路线。我一段时间里纠结过,后来发现它们各有明显定位。
| 对比项 | 硬路由 | 软路由 |
|---|---|---|
| 工作方式 | 分类器选出唯一分支,其他分支不执行 | 多个分支并行执行,最后按投票或评分合并 |
| 速度 | 快,只跑一条链 | 慢,所有分支都跑 |
| 成本 | 省,只算一份执行费用 | 贵,成倍增加 |
| 准确性 | 依赖分类器的判断质量,有误判风险 | 多分支交叉验证,整体准确率更高 |
| 适用场景 | 路径清晰、量大、对延迟敏感 | 高风险、歧义大、需要多角度确认 |
硬路由适合意图明确的场景。比如"查余额"这种请求,分类器敢拍板就让它走余额分支,干净利落。软路由适合那种连人都觉得有歧义的请求,比如用户说"我想处理一下订单",你拿不准他是要退货还是催发货,那就让退货分支和催发货分支各跑一遍,再让一个聚合模型投票选最优结果。效果是稳,代价是合计算力,所以通常只用在业务价值高的请求上,不会全量启用。
4.3 路由式架构落地的四个要点
路由式架构落地时,有一些容易踩的细节,我整理成四个要点。
第一,路由层的输出必须是固定枚举。路由器Agent的输出不能是一段散文,必须是结构化指令,比如{"intent": "after_sales", "confidence": 0.85}。后续分支靠这个枚举值做分流,不是靠个LLM猜。如果模型输出了一个枚举里不存在的值,那这个分支谁能接得住?
第二,必须有兜底分支。用户意图再五花八门,也不能让请求找不到处理者。我习惯单独设一个default分支,专门接收那些路由把握不准的请求。这个分支可能是让用户修改表述,也可能是转人工,但一定得存在。
第三,路由决策要留日志。记录原始输入、判定结果、置信度、实际走的分支、最终输出。别嫌这一步啰嗦,它能让你在线下复盘"为什么这个用户被分到了技术分支结果他实际想退款"。路由日志是整个系统持续优化的燃料,没日志的Agent系统是没有生命力的。
第四,每个分支的输入输出结构要统一。路由器只负责转发,不需要关心分支内部实现。为此,所有分支Agent暴露的接口必须对齐,比如统一输入为{"question": "...", "context": "..."},统一输出为{"reply": "...", "action": "..."}。接口统一了,后续替换分支、增加新分支的成本才会低。
5. 实测总结:三种架构的选型路线与组合方案
5.1 从场景倒推架构的选择路径
架构选型不是凭感觉,我一般按一条固定路径来推。
先把业务场景拉出来,看输入端的多样性。如果系统需要处理的目标类型明显很多,而且彼此之间处理逻辑完全不一样,那就先考虑路由式,入口必须先分流。如果输入相对单一,再看处理流程是否固定。流程固定且步骤多,链式是最优解;流程不固定或者需要看中间结果决定下一步,增强型更合适。
还有一个判断辅助:你需要这个Agent"回想"之前做过的事吗?需要外部检索和记忆,那无论选链式还是路由式,最终叶子部分大概率还是增强型结构。你可以把这个判断路径当成一个经验规则用,先按它走,等日志数据攒多了再调整。
5.2 组合架构实例:一个分层客服系统的设计
纯粹的一种架构很难撑起复杂系统,真实场景里更多是组合使用。我做一个分层客服系统的实例说明。
最外层是路由层,负责意图识别,把请求分为"售后、售前、技术支持、闲聊"。技术这个分支内部还有一个小路由,区分真正的Bug反馈和用户不会操作的问题。售后分支用的是增强型,它需要查订单状态、调退款接口、查询历史对话,这些动作没有循环的能力根本做不了。售前分支用的是链式,流程稳定:先收集需求,再生成商品推荐,最后创建订单草稿。闲聊分支走最普通的LLM直答,因为它不需要工具和流程,堆复杂架构反而浪费成本。
这个分层设计的好处很明显:每个架构只出现在它最擅长的位置。路由层只做决定,不做复杂执行,保持轻量;增强型躲在后端查数据时不打扰用户;链式把售前流程做得可控,不会说了一半就跑偏。整体系统既能处理复杂的售后问题,又能稳定走完售前链路。
5.3 常见问题与排查技巧速查
最后整理一份我在实战中经常遇到的问题清单,可以直接照着排查。
| 现象 | 可能原因 | 排查与解决 |
|---|---|---|
| Agent反复调用同一个工具 | 上下文里没有记录已执行动作,模型重复规划 | 每轮循环后把"已调用工具列表+结论"写回上下文,加缓存做拦截 |
| 中间输出解析失败 | 模型输出的JSON格式不合法,或在JSON里混入解释文字 | 用JSON Schema做校验,失败自动重试一次;Prompt里明确只输出JSON |
| 路由经常分错类 | 路由Prompt缺少示例,枚举不完整,用户表述模糊 | 给few-shot示例,增加"不确定选兜底",把路由日志拉出来分析 |
| Token消耗过大 | 工具返回结果太大,历史上下文无节流 | 工具结果摘要化,窗口外数据压缩,设置循环最大步数 |
| 链路超时 | 某个工具调用慢,或模型陷入长思考 | 给工具调用设置超时,失败快速返回;为LLM调用设置最大等待时间和重试上限 |
这些问题里面,我最想强调的还是路由分类和工具误调用的排查。因为它们的出错点不在最终输出,而是在过程判断里,过程一错,后面全错,而且表现是随机的。我的做法是:所有判定类、调用类动作全部埋点,别省这一步。排查共性问题的时候,能靠日志说清楚的事情,就不要靠猜。
我自己这几年做Agent工作流下来,最大的体会是:不要一上来就追求最复杂的架构。先跑通一个最简单能用的版本,让日志替你说话——是流程乱了,是上下文爆了,还是分类不准,再针对性地把链式、增强型、路由式加进去。架构始终是服务任务的,不是用来炫技的。多给系统留日志和中间产物,这些是你后续迭代最值钱的资产。