去年我改造一套客服知识库系统时,前后试用过三种截然不同的Agent工作流写法。最初是单个Prompt硬扛,后来拆成多个Agent串行处理,再后来因为用户意图分叉太严重,不得不引入路由分发。整个过程踩了不少坑,也把Agent工作流架构设计里最典型的三种模式——增强型、链式、路由式——都试了个遍。这篇文章不打算讲教科书式的定义,而是从实际业务演进的角度,把三种架构的运作原理、适用场景、选型方法和落地经验一次说透。
如果你正在做AI应用,已经发现单个Prompt搞不定真实业务流程,或者你准备用Agent框架搭一个多步骤系统,这篇文章会非常对口。里面没有空泛的概念,全是可以直接拿去用的结构设计思路、参数约定和避坑清单。
1. 为什么单个Prompt搞不定业务:工作流架构出现的必然性
1.1 一个真实项目的演进轨迹
先说那个客服系统的故事。第一版其实很简单:把知识库文档切片后做向量检索,用户提问时把检索结果拼进Prompt,让大模型统一回答。看起来挺顺理成章,但上线之后问题全暴露了。
用户问"我的订单三天没到"时,模型只能基于知识库给一段标准话术;但如果用户接着问"能不能帮我催一下",系统就卡住了——因为它既查不到这个用户的订单信息,也没有办法触发任何内部工单动作。再往后,用户问"我买了两件衣服,其中一件不合适想退,另一件还在路上,能一起处理吗",这种多意图混合提问,单次推理根本接不住。
这就是工作流架构出现的根本原因:单个Prompt的推理能力边界是固定的,上下文长度有限,信息获取依赖外部系统,决策路径没法根据用户反馈动态调整。一旦业务流程超过三个环节,或者必须联动真实系统,就需要把"一次推理"拆成"多次推理的组合",并对这些组合进行结构化管理。
从第一版改造开始,我先后尝试了三种思路:
- 给单个Agent配上检索、查单、建单等工具,让它在一次对话里自己决定调用顺序——这是增强型架构。
- 把"理解需求→查物流→生成回复→质检"拆成四个独立环节,上一环节输出作为下一环节输入——这是链式架构。
- 在入口处先判断用户意图属于"售后处理""订单咨询"还是"产品推荐",再分别投递到不同处理路径——这是路由式架构。
三种模式不是互斥的,真实系统里往往是混合使用,这点后面专门讲。
1.2 先厘清两个概念:工作流与Agent到底指什么
在做架构设计之前,必须把概念先理清楚,不然讨论起来会乱。
业内大致有个共识:Workflow是预先定义好的代码路径,由程序控制大模型何时被调用、以什么顺序调用;Agent则是把决策权交给大模型,让它根据当前观察和工具反馈动态决定下一步做什么。两者不是对立的,而是一条光谱的两端。
这篇文章标题里的"Agent工作流",我理解是一个统称——指由Agent能力注入的、具备一定智能决策能力的流程编排系统。增强型架构更靠近Agent那一端,链式和路由式更靠近Workflow那一端,但每一个环节内部都可以嵌入大模型决策。
设计时最忌讳的是把三者混为一谈。增强型解决的是"一个实体怎么应对不确定任务",链式解决的是"固定任务怎么稳定执行",路由式解决的是"不同任务怎么分叉到不同路径"。一句话说清楚:增强型管单点智能,链式管过程稳定,路由式管入口分流。
1.3 架构设计要回答的三个本质问题
不管选哪种架构,本质上都只是在回答三个问题:
- 谁来做决策——是Agent自主判断下一步,还是代码流程里写死下一步?
- 信息如何流转——节点之间传递什么格式的数据?是纯文本、JSON还是对象?
- 失败如何隔离——某个环节出错了,是整个流程重跑,还是只重跑局部,或者直接降级?
把这三个问题想清楚,选型就成功了一半。接下来逐个看三种架构分别是怎么回答这些问题的。
2. 增强型架构:一个Agent配一套工具箱的运行方式
2.1 增强型的核心循环:观察、决策、行动、反思
增强型架构的核心思路是一个大脑,多把工具。它不预先划分流程阶段,而是把任务整体交给一个Agent,由它在循环里反复决策:
- 观察当前用户输入和已有的中间结果
- 决策是直接回答,还是需要调用某个工具获取信息
- 如果调用工具,解析工具的返回结果
- 反思结果是否满足需求,不满足则继续下一轮循环,满足则结束
这种循环在业界有个经典范式叫ReAct,即Reason与Act交替进行。OpenAI的function calling、各大Agent框架里的Tool Use,本质都是在落实这个循环。
我实际搭建时的结构大致是这样:
class Tool: name: str description: str parameters: dict # JSON Schema execute: Callable class EnhancedAgent: def __init__(self, llm, tools: list[Tool]): self.llm = llm self.tools = {tool.name: tool for tool in tools} def run(self, user_input: str, max_steps: int = 5): messages = [{"role": "user", "content": user_input}] for _ in range(max_steps): response = self.llm.chat(messages, tools=self.tools) if not response.tool_calls: return response.content for call in response.tool_calls: tool = self.tools[call.name] result = tool.execute(**call.arguments) messages.append({ "role": "tool", "tool_call_id": call.id, "content": result }) return "已达到最大执行轮次,返回当前进度"看起来不复杂,但这个循环撑起了很多产品。比如之前客服系统里的"查单Agent":用户说订单号,Agent调用查单工具拿到底层数据,如果物流异常,再调用工单工具创建催办单,最后汇总成一段自然语言回复。整个流程不需要代码里写死"先查再催",模型自行判断。
2.2 工具层设计细节:描述词直接决定调用质量
踩过最大的坑是:工具的描述写得敷衍,Agent分不清该用哪个工具。
有一次我同时给了"查询订单"和"查询物流"两个工具,唯一的区别是description里一个写"查询订单基本信息",一个写"查询物流进度"。看起来没什么问题,但线上经常出现用户问"我的东西到哪儿了",Agent却调用了"查询订单",拿到一堆不含物流轨迹的数据,然后一本正经地编了一段物流状态。
后来我把描述重写了一遍,把触发场景、输入要求、返回内容都写清楚:
name: query_logistics description: > 当用户询问包裹当前所在位置、预计送达时间、物流轨迹、运输进度时使用。 典型问法包括"我的快递到哪了""物流怎么没更新""显示什么状态"。 输入为订单号或快递单号,返回物流轨迹列表、当前节点、状态说明。改完之后调用准确率提升非常明显。工具描述本质上是给模型的"函数说明书",越像给新同事写的交接文档越好,千万别当成注释来写。另外,参数设计也别偷懒,尽量用枚举值约束,比如status字段只允许"pending/shipped/delivered/exception",模型就不容易自由发挥。
2.3 增强型适合什么场景,盲区在哪
增强型最适合两类任务:一类是需要实时数据或外部动作的任务,比如查天气、订会议室、查库存;另一类是探索性、开放性的任务,比如"帮我分析这组数据并给出几个优化方向",因为路径不固定,只能靠Agent自己探索。
但它也有明显盲区。
第一,多分支决策容易跑偏。当业务流程有很多个分叉点时,增强型Agent虽然理论上能处理,但每一步都有选错概率,几步叠加之后成功率衰减很快。我遇到过用户问"我想退款但不确定能不能退,你帮我评估一下",Agent自查了一下政策就给出了结论,其实它没有调用订单详情工具,也没有核对用户实际购买的商品条款——属于典型的"跳步"行为。
第二,上下文污染问题。多轮工具调用后,中间结果全部堆在messages里,token越积越多,后面的决策反而被干扰。对工具返回结果要做截断,只保留关键字段,别把完整业务对象直接塞回对话。
第三,延迟不可控。每多一轮循环就多一次大模型调用,用户体验从"秒回"变成"3秒、5秒、8秒"。增强型架构的循环次数必须设置上限,而且最好每轮判断一下"当前信息是否足够回答",能早收就早收。
3. 链式架构:把任务拆成可跟踪的流水线节点
3.1 链式的本质:用稳定结构约束不确定性
链式架构的思路正好和增强型相反——不用一个Agent干所有事,而是把任务切成几个固定阶段,每个阶段由一个独立Prompt或Agent负责,前一节点的输出作为后一节点的输入。
打个比方,增强型是全科医生一个人问诊、开检查、看报告、开药;链式则是工厂流水线,每个工位只负责一道工序,零件按顺序流过。
之所以需要这种结构,是因为很多业务任务虽然内部复杂,但整体路径是相对稳定的。比如生成周报,必然经过"收集数据→归纳要点→撰写正文→格式检查";处理退单,必然经过"校验条件→计算退款金额→发起退款→通知用户"。这种稳定路径,如果全交给Agent自由发挥,每次处理方式都不一致,反而难维护;写成链式之后,每个环节可以单独调优、单独替换,出问题也能精准定位到第几个环节。
链式设计有一个核心原则:单输入单输出。每个节点只做一件事,输入输出都要有明确边界。如果某个节点既要查数据又要写文案又要审核质量,那它就不是一个节点,而是一个隐藏的增强型Agent,应该继续拆分或考虑换架构。
3.2 一个完整链式案例:从需求到报告的四级流水线
我做过一个内部项目:用户提交一段自然语言需求(比如"分析一季度华东区销售下滑原因"),系统自动生成一份分析报告。最初用的是单个增强型Agent,结果每次产出的报告结构都不一样,质量忽高忽低。后来改成链式架构,稳定了很多。
流水线分四段:
- 需求解析节点:把用户模糊描述转换成结构化的需求规格,包括分析维度、时间范围、业务域、输出格式要求。
- 数据检索节点:根据需求规格查询数据库和文档库,输出一段过滤后的关键数据摘要。
- 分析撰写节点:基于数据摘要,按照固定提纲生成报告正文。
- 质量校验节点:检查报告是否覆盖需求要点、数据是否有前后矛盾、格式是否合规,不通过则返回撰写节点重写一次。
每一节点之间都传递结构化的JSON,而不是随便一大段文本:
{ "node": "requirement_parse", "output": { "dimensions": ["区域", "品类"], "time_range": "2025-01-01~2025-03-31", "business_context": "华东区销售", "focus": "下滑原因分析", "output_format": "markdown_report" } }这样做的最大好处是,后续节点拿到的是结构化参数,而非需要二次解析的自然语言,稳定性立刻上来了。第二个好处是,某个节点出错时能快速定位:比如报告数据显示"数据为空",直接锁定到数据检索节点,重新跑这一段就行,不用整条链路重来。
3.3 链式的数据契约与错误定位
在链式架构里,节点间的数据契约比Prompt本身更重要。我见过不少团队在链式上栽跟头,不是Prompt写得不好,而是节点间传的是纯文本,下游节点没法稳定解析。
建议每个节点的输出都用JSON Schema定义,并在代码里加一层校验,不满足Schema就自动重试一次,重试仍失败则终止流程并返回该节点的诊断信息。
链式的重试策略也要提前想好。整条链路重跑代价太大,所以要有"局部重跑"机制。比如报告案例里,质量校验不通过,只把"撰写节点"和"质量校验节点"作为一个子循环重跑,前置的检索结果可以复用。为了支撑这一点,每个节点最好做幂等设计——同一输入重复执行不产生副作用,这样重跑不会污染数据。
还要注意,链式架构总延迟是各节点延迟之和,串行链路越长,用户等待越久。一般建议控制在4~6个节点以内;如果节点超过6个,要考虑部分节点并行化,或对中间结果做缓存,减少重复计算。
4. 路由式架构:入口分流,让最合适的路径处理请求
4.1 路由的两种触发维度:类目与条件
路由式架构解决的是另一个问题:进来的请求类型太杂,统一流程处理效果差,需要先分流再处理。
医院分诊台的逻辑是最形象的类比。病人不会直接去手术室,而是先去分诊台,护士问两句症状,判断该去内科、骨科还是急诊,然后再走对应流程。Agent工作流里的路由器就是这个分诊台。
路由触发大致分两种:
- 类目路由:根据用户意图类型分流。客服系统里最常见——"退货咨询"走退款流程,"物流查询"走物流链路,"产品推荐"走推荐Agent。
- 条件路由:根据某个业务字段或前置判断分流。比如用户请求里带了"加急"标记,就送入加急处理路径;否则走常规路径。财务审批、风险控制系统里经常用到。
类目路由和条件路由经常组合使用:先用条件路由做硬性判断(比如是否VIP、是否涉及高风险操作),再用类目路由做意图分类,最后进入下游具体处理节点。
4.2 路由器的三种技术实现方案对比
路由器的实现方式有粗有细,我三种都用过,各有优劣。
第一种:纯规则路由。用关键词、正则、或业务字段硬匹配。优点是零成本、延迟低、可解释性强;缺点是覆盖不了语义表达变化。用户说"我的东西还没到"和"物流显示签收但我没收到"表达的是不同意图,纯规则会漏。适合作为第一层过滤,把最典型的请求快速分流掉。
第二种:小模型分类路由。微调一个轻量分类模型,输入用户文本,输出意图标签。优点是速度快、成本低、准确率可控;缺点是需要标注样本,并且新意图出现时要重新训练。适合意图类别固定、样本量充足的场景。
第三种:大模型零样本路由。让LLM根据预设的意图列表,给输入打标签。做法通常是用一个轻量级大模型,配合约束输出格式:
router_prompt = """ 你是意图分类器。从以下类目中选择一个最匹配用户问题的标签: - aftersale: 退货、退款、换货、售后维修 - logistics: 物流更新、送达时间、运输轨迹 - recommend: 产品推荐、选购建议、功能对比 - complaint: 投诉、纠纷、态度不满 仅输出标签本身,不要输出任何解释。 用户问题:{user_input} """ label = llm.complete(router_prompt).strip()大模型路由的优势是零样本、适应新意图只需要改prompt;缺点是每次分类都有一次模型调用,成本随流量上涨,而且latency会叠加在用户请求路径上。
实际项目里我通常用规则+大模型混合:先跑规则层,把能明确覆盖的高频意图分流掉;剩下的模糊请求才交给LLM分类。这样80%的流量走规则,只有20%付模型调用的成本,整体性能和费用都更能接受。
4.3 路由失败的兜底设计
路由是整个系统的入口,错误代价很高:分错路径,下游做得再好用户也不满意。所以路由必须有兜底。
第一个兜底是置信度阈值。如果LLM分类时给出的概率低于设定阈值(比如0.6),不要硬分,而是进入"模糊识别"路径。
第二个兜底是默认路径。路由失败时必须有一条默认路径兜住,通常是人工客服或一个能处理通用问题的增强型Agent,而不是直接报错。
第三个兜底是回流数据。路由器的每次判断都要记录日志,包括输入、预测标签、用户后续行为(是否满意、是否转人工)。用这些数据定期回流到分类Prompt或训练集里,路由准确率才能持续提升。这一点特别容易被忽略,但恰恰是路由架构保持长期可靠的关键。
5. 三种架构怎么选:复杂度与收益的权衡
5.1 选型前先回答三个问题
很多人一上来就问"我这需求用哪种架构好",其实答案藏在对业务的分析里。我习惯先让团队回答三个问题:
- 任务路径是稳定的还是开放的?如果步骤顺序相对固定,选链式;如果路径随用户输入变化很大,选增强型。
- 请求类型是单一的还是多元的?只有一种类型,不用路由;差异极大且处理策略不同,必须路由。
- 单一Agent的失败率能接受吗?增强型最灵活但成功率天花板有限,链式最稳但灵活性差。当业务要求高确定性,比如财务、医疗、合规相关,即使路径有点变化,也要优先用链式或路由来约束决策过程。
这三组问题回答完,大体架构方向就出来了。下表可以做一个快速参考:
| 业务特征 | 优先考虑 | 不建议直接上 |
|---|---|---|
| 单领域,需要实时外部数据 | 增强型 | 链式(路径太死) |
| 多步骤,步骤顺序固定 | 链式 | 增强型(结果不稳定) |
| 多意图,处理策略差异大 | 路由式(作为入口) | 单一链路 |
| 高合规、可审计要求 | 链式或路由+约束 | 纯粹增强型 |
| 探索性、数据驱动决策 | 增强型 | 链式(过度约束) |
5.2 混合架构才是常态,不用纠结"选一种"
现实系统很少只用一种架构,往往是三种嵌套组合。
以一个中大型客服系统为例:最外层是路由式架构,先判断用户意图;退货退款分支内部是一条链式流水线(校验条件→计算金额→执行退款→通知);而"产品推荐"分支则是一个增强型Agent,因为它需要不断检索商品库、对比参数、追问用户偏好。
这种混合结构的好处是每层只承担一种职责,复杂度被拆到可以单独维护的粒度。演进路径通常也是渐进的:先单Agent试跑,发现某部分路径不稳定,拆成链式;发现入口流量分叉太大,加路由器。不要一开始就建一个巨大的混合架构,先让业务跑起来,用数据说话,再逐步加结构。
5.3 成本、延迟与维护性的三角权衡
架构选型不只是准确率问题,三个隐性代价必须提前算清楚:
- 调用成本:增强型一次任务可能调用3~5次模型,链式几个节点串联也是多次调用,路由本身还要多一次分类调用。同样的用户请求,不同架构的成本可能差出好几倍。
- 延迟体验:增强型循环轮数不可控,链式总延迟是各节点之和,路由会增加一次前置分类耗时。对延迟敏感的场景,每条路径都要测一下P95,而不是只看理想情况。
- 维护成本:增强型只有一个Agent要维护,但行为不可预测;链式和路由需要维护节点契约、路由逻辑和回流数据,前期投入更大,但后期稳定性更高。
我的经验是,把业务分成"高确定性要求"和"高灵活性要求"两块,高确定性场景哪怕多付一点调用成本也要上结构化架构,高灵活性场景可以容忍一定失败率,保持增强型的轻量。两边标准分开,不要一把尺子量到底。
6. 落地经验:五个反复出现的坑位与处理方式
6.1 坑位:路由类目越拆越细,分类成功率反而崩了
做路由式架构时,原本只分5类,后来业务方要求拆成15类。结果LLM分类准确率肉眼可见地往下掉——类目之间出现大量语义重叠,比如"退货运费问题"和"运费争议处理"模型根本分不清;而且标注数据被摊薄到每个类目,样本严重不足。
解套的办法是两层分类:入口只保留5个大类,每类内部再用规则或小模型细分。入口分类界面越干净,路由越稳。类目之间还要做互斥检查,保证每一条输入只能命中一个类目。
6.2 坑位:链式节点间传纯文本,下游解析全靠碰运气
第一次搭链式时图省事,节点之间直接传自然语言文本,结果下游节点的输出时好时坏——今天能解析,明天改一个标点就崩。核心问题是让大模型同时承担"生成内容"和"维护格式"两件事,容易互相干扰。
后来全部改成结构化输出,每个节点强制输出一个JSON对象,并加一层运行时校验。现在只要链式某个环节出问题,日志里直接能看到是哪个字段缺失,不用靠猜。
6.3 坑位:增强型工具返回结果不截断,上下文越滚越乱
增强型Agent的工具调用结果如果直接全部塞回messages,跑不了几轮上下文就快满了,而且后面的推理会被无关字段干扰。比如查订单返回了30个字段,模型真正需要的只有"状态、金额、地址"三个。
解决办法是做一层结果清洗:工具返回后先取必要字段,甚至用一句话摘要替代原始JSON,再放回上下文。这样既节省token,也降低了模型"看花眼"的概率。
6.4 坑位:没有可观测性,出故障只能盲猜
Agent类系统最难排查的问题就是"模型做出了一个不合理的决策"。如果没有日志,故障复盘基本靠猜。
我后来在所有节点入口和出口都打了结构化日志,把输入、输出、模型调用耗时、token消耗、工具调用明细全部记录在案。排查问题时直接按trace_id拉全链路日志,看模型在哪一步开始偏离预期。这一项投入,比多调十次prompt都有用。
6.5 坑位:失败与重试语义没有提前定义
无论是增强型还是链式,都会遇到大模型输出格式非法、工具调用超时、下游API返回500等异常。最忌讳的是没有统一的重试策略,到处写try-except,行为彼此不一致。
建议提前约定一套统一规则:网络超时重试2次;模型输出校验失败重试1次;业务逻辑异常直接终止并进入兜底路径;超过总时限走降级回复。把重试次数、超时时间、降级文案都配置化,而不是散落在代码里。
另外一个小技巧:给每个Agent起一个带故障场景的中文别名。比如"物流查询Agent"在异常时的别名叫"物流查询兜底",日志里一眼就能看出来流程走的是正常路径还是降级路径。这个做法成本为零,排查问题时却能省下大量时间。
三种架构我都从头到尾实践过,说不上哪种更好,只能说架构本身是约束业务复杂度的工具,能稳定解决业务问题的架构,就是当下最合适的选择。如果这篇文章能帮你在设计Agent工作流时少走几个弯路,把更多精力花在业务本身,那就不算白写了。