拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

多智能体Agent架构:从Demo到生产系统的工程实践

多智能体Agent架构:从Demo到生产系统的工程实践

我见过太多Agent项目,Demo惊艳全场,一上生产系统就满地找牙。这个现象跟Agent的架构设计强相关,尤其是多智能体协作,很多坑在Demo阶段根本不会暴露出来。今天想借这个标题,把我自己从零做Agent、再到拆成多智能体、最后落到生产系统的完整思路和踩坑记录整理一遍。这篇文章适合两类人:一是已经用LangChain、LangGraph这类框架做出过Demo、正准备往工程化方向走的开发者;二是手里有几个Agent原型、想搞清楚“多智能体到底怎么拆、怎么通信、怎么编排”的架构师。内容不会只讲概念,会把每个决策背后的为什么讲清楚,也会给可以直接抄作业的配置和步骤。

1. Agent架构为什么不能照搬Demo代码

1.1 Demo和真正的Agent系统,差的不是代码量

先看一个最常见的Demo长什么样:一个Python脚本,从控制台读用户输入,拼一个prompt丢给大模型,模型决定调用某个函数,函数跑完再给模型结果,最后打印答案。整个过程在内存里完成,状态变量放在局部,失败就Ctrl+C重跑,模型密钥写死在环境变量里。这套东西在演示时没有任何问题,但几乎每个环节到生产都会变成事故。

我自己整理过一个对照表,每次做技术方案评审都会拿出来用:

维度Demo阶段生产系统要求
用户规模只有你一个人多用户同时访问,会话必须隔离
状态存储内存变量持久化存储,可恢复、可追溯
可观测性print和断点全链路trace、结构化日志、指标监控
失败处理重跑一遍自动重试、降级兜底、人工干预
安全假设输入可信恶意输入、提示注入、越权调用都必须防
成本几乎可以忽略每任务成本能被量化、被控制
评估肉眼看输出离线回归集 + 线上指标双轨验证

这里说的“架构”,不是简单的代码目录结构,而是整个系统的运行时骨架:调用链怎么走、状态流怎么动、容错边界画在哪、后续演进怎么扩展。Demo代码往往是一条直线,生产系统则必须是一张网,网上的每个节点都要有兜底逻辑。

1.2 Agent的“能力边界”到底在哪里

做生产系统前,建议先把Agent的能力边界想清楚。不是所有任务都适合交给Agent自由发挥,模型有推理能力的上限,工具有执行能力的边界,记忆有获取和更新的成本。这三个因素共同决定了Agent能扛住什么场景。

一个很典型的例子:让Agent做“查询天气并推荐穿衣”很容易,但让Agent做“根据上百页财报判断公司经营风险”就很难,不是模型不会推理,而是上下文塞不下、检索不精准、输出不可验证。生产系统不会等Agent硬着头皮跑完,而是在任务进入之前就先做分流判断:哪些进Agent主流程、哪些走规则引擎、哪些直接回复“抱歉无法处理”。这个“拒绝策略”在Demo阶段几乎没人写,但生产环境必须有,否则边界外的任务会把token消耗和用户等待时间一起拖垮。

1.3 从“函数调用”到“系统设计”的思维转变

Demo的本质是“本地触发”:本地调用、本地输出、本地观察。生产系统的本质是“服务”:外部请求进来,经过鉴权、限流、排队,再由Agent处理,结果写回存储,整个过程可能异步完成。

思维转变发生在几个细节上。入口不再只是一个main函数,而是要处理HTTP请求、消息队列、定时任务等多个来源;模型返回的不再是直接展示的文本,而是要解析出结构化动作;工具调用失败不能再直接抛异常,而是要转成模型能理解并继续执行的错误信息。我见过很多团队把这个转变理解成“加一个FastAPI壳子”,但真正的差别在于状态、失败和并发这些非功能性的东西,这些才是生产系统的地基。

2. 单体Agent架构的核心模块拆解

2.1 五层结构:接入层、编排层、模型层、记忆层、工具层

不管最终要不要拆成多智能体,先把单体Agent的分层做扎实是最稳妥的路径。我习惯把Agent系统拆成五层,每一层只做一件事:

  • 接入层:统一处理来自API、IM机器人、Webhook、定时任务的请求,完成用户认证、参数校验,转换成内部统一的任务格式。
  • 编排层:决定“下一步干什么”,持有Agent的主循环逻辑。最常见的是ReAct模式,也就是“思考-行动-观察”循环;复杂任务可以用Plan-and-Execute,先整体规划再逐步执行。
  • 模型层:封装所有大模型调用,统一请求和响应的数据结构,负责流式输出、重试、结构化解码、token计量。
  • 记忆层:管理短期记忆(当前对话上下文)和长期记忆(跨会话的知识、用户偏好、历史事实)。
  • 工具层:注册Agent可调用的外部能力,包括搜索、代码执行、API调用、数据库查询,统一鉴权、参数校验和结果清洗。

这五层对应到代码上,就是五个清晰的功能边界,互相之间不建议跨层引用。很多容易崩的Agent项目,问题就出在编排层直接写模型调用、模型层又掺杂业务状态,最后改一个需求牵一发动全身。

2.2 模型层:别把大模型当成唯一的“数据库”

生产环境里最常被低估的是模型层。很多Demo直接在业务代码里调openai.chat.completions.create,看起来简单,但一旦涉及换模型、加流式、做重试,代码就到处都是补丁。

模型层的核心价值是“适配和隔离”。适配是指对不同供应商的模型做统一接口,支持把同一个请求路由到不同模型;隔离是指业务代码永远只和本地的LLMClient打交道,不直接依赖某个SDK。这样做的好处很实际:某天你发现GPT-4太贵、换一个便宜的模型,只需要改模型层的路由配置,业务编排一行都不用动。

结构化输出是另一个必须提前考虑的工程点。不要指望模型返回的JSON永远是合法JSON,生产环境一定要用强制手段。目前最可靠的是function calling或者JSON mode,部分模型还能用constrained decoding。我在项目中的做法是:所有Agent决策都定义为强类型的JSON Schema,解析失败就重试一次,再失败就进入降级分支。这个机制在Demo阶段可有可无,但在生产环境是每天的日常。

2.3 记忆与上下文管理:上下文窗口不是内存条

很多人把上下文窗口当成内存条,觉得只要没到上限就随便塞。实际跑过生产就知道,token变多之后,模型会忽略中间的关键信息,也就是所谓的“迷失在中间”。而且每次请求都要重发全部历史,延迟和成本线性上涨,最后账单比Agent的产出还吓人。

记忆层要做的是分级管理。当前轮对话的上下文控制在窗口的30%以内;超过部分做摘要压缩,把对话历史转成阶段性结论;跨会话的用户偏好、知识片段存进向量库,按需检索召回;业务状态(比如订单号、审批流程ID)则从外部存储查询,不塞进系统提示词。

这里分享一个我自己实践过的策略:每轮对话结束时,把当前状态打包成一个快照,包括任务目标、已完成步骤、未决问题、关键实体。下一轮不是重发全部原始消息,而是把这个快照作为场景起点。这一招能把长对话的token开销砍掉一半以上,还能缓解上下文污染。

2.4 工具层:每次工具调用都是一次外部IO

工具层是最像传统后端工程的地方。Agent怎么描述工具、怎么传参数、怎么处理返回值,都应该有严格的规范,而不是让模型自由发挥。

我的做法是给每个工具写一份“运行手册”,里面包含:工具功能描述、参数Schema、触发条件、错误代码、示例返回。模型通过function calling拿到这份手册后决定是否调用。参数校验必须在模型层之后、真实IO之前做,防止模型捏造或者传错参数。工具返回值要标准化,成功、失败、部分成功都要有明确的机器可读状态。

还有一个容易踩的坑:工具权限。给Agent挂一个“能执行任意命令”的终端工具,和把服务器root密码贴在工位上没有区别。生产环境要坚持最小权限原则,高危操作单独加审批流,工具的调用记录要进审计日志。近几年MCP(Model Context Protocol)这类标准协议越来越普及,建议新工具优先按MCP方式暴露,天然具备权限边界和统一描述格式。

3. 多智能体:拆、分、聊、管

3.1 什么时候才需要多智能体,以及什么时候不需要

不是所有项目都该上多智能体。我见过的最离谱的误用,是三个Agent互相争论“这个用户问题该怎么分类”,折腾了8000多个token,结果一个简单分类函数几毫秒就能跑完。

多智能体真正适用的场景有几个特征:任务本身可以拆成多个职责边界清晰的子任务;子任务之间存在信息并行或流水线协作;单个Agent的上下文装不下所有专业知识和工具;不同角色需要独立的状态和记忆。比如工单处理系统就很典型,一个Agent负责客户沟通,另一个负责查知识库,还有一个负责生成最终解决方案,它们各自维护自己的工具和上下文,互不干扰。

反过来,如果任务就是一个问答、一次单工具调用、或者需要严格保持全局一致性的事务,就别拆。多智能体带来的消息风暴、通信延迟、状态一致性协调成本,最终都是从用户能感知的响应时间和错误率里扣钱的。

3.2 三种主流编排模式对比

多智能体的编排模式,基本逃不出下面三种:

编排模式工作方式适用场景主要风险
Supervisor(主管)一个主管Agent负责任务分解、分配和结果汇总,worker执行具体子任务任务边界清晰、需要收敛决策主管成为性能瓶颈和单点故障
Peer-to-peer(对等)多个Agent地位平等,相互之间直接发消息协作高度动态的协作任务消息无序、容易变成“聊天室”
Hierarchical(层级)上级主管拆任务,子主管再拆,worker在底层执行大规模、复杂任务链路深、延迟放大、追踪困难

Supervisor模式最适合绝大多数业务系统。它实现的不是“多智能体”,而是“多角色”:主管Agent更像一个项目经理,它不干活,只负责拆解、追踪和验收。LangGraph里的supervisor节点、AutoGen的group chat两种方案我都试过,前者更适合工程落地,因为状态机和条件转移是显式的。

Peer-to-peer模式看着高级,但坑极多。多个Agent对“下一步做什么”有不同判断,如果没有终止条件,会互相唤醒、无限循环。除非你的场景天然是开放式的多角色讨论,否则不建议作为主干方案。

3.3 多智能体通信协议:像设计微服务一样设计Agent

多智能体系统确实很像微服务,但这个类比里最重要的一点常被忽视:微服务之间用定义好的API契约通信,多智能体之间也得有消息契约,而不是直接把自然语言文档抛来抛去。

我建议每个跨Agent消息都带上元信息:消息ID、发送方、接收方(或订阅主题)、任务ID、消息类型(请求、响应、事件、命令)、时间戳、负载格式版本。发送方把结果写成结构化事件发布到总线,订阅方按需消费,这就是典型的事件驱动架构。好处是节点之间解耦,还能回放重试。如果直接点对点调用,系统会退化成一张无法理清的调用网。

多Agent的状态管理也要借鉴分布式系统的思路。共享记忆不建议所有Agent都读同一个全局变量区,而是按Agent分区,再通过消息做同步。写冲突要用版本号兜底。某个Agent在处理期间失败重启了,状态从哪恢复、已经发出的消息会不会重复执行,这些问题必须在设计阶段给出答案。

3.4 多智能体协作中的一致性、冲突与优先级

两个Agent意见相左的时候谁说了算?生产环境不能靠模型临场辩论,要提前定规则。

我的经验是三层兜底:第一层,每个Agent在prompt里写清楚自己的决策边界和可接受的不确定级别;第二层,主管Agent对所有结果做最终裁决;第三层,系统层面设置超时和默认路由,超时未决自动进入人工处理队列。

外部系统调用的一致性问题更隐蔽。比如一个Agent负责生成邮件内容,另一个Agent负责发送邮件,网络抖动导致发送结果丢失,重试时就会重复发邮件。解决办法是给每笔外部操作生成幂等键,接收方靠幂等键去重。这个细节在Demo阶段没人会想,但它直接决定了生产事故的数量级的。

4. Demo到生产,必须补齐的六项工程能力

4.1 可观测性:删除所有print,换成结构化trace

Agent系统的调试难度远超传统服务,因为每一步都有模型主观判断,错误不会像普通异常那样有清晰的堆栈。生产环境的Agent必须做到让每一轮决策可以被回放:模型收到什么prompt、输出了什么、选择了哪个工具、传了什么参数、拿到什么结果、下一步转移到了哪个节点。

我现在的做法是给每个任务分配一个trace_id,从入口一直贯穿到所有模型调用和工具调用。祖业测日志、指标、收费数据全部挂在trace_id下面。工具选型上,LangSmith很方便,但更通用的是OpenTelemetry,把Agent的LLM调用和工具调用都手动埋点,这样能复用公司已有的可观测性基础设施。不要迷信某一款工具,关键是把事件模型定好。

4.2 可靠性与容错:模型是会失败的,工具是会挂的

Demo阶段调用模型失败的概率很低,所以你感觉不到重试机制的必要性。到了生产环境,模型服务超时、限流、返回畸形内容几乎是每天都会发生的。我的建议是三层容错:超时控制、重试退避、降级兜底。

参数可以参考这个经验表:

调用对象超时时间重试策略降级动作
大模型LLM调用30秒最多2次,指数退避返回预设兜底话术
外部工具API10秒最多1次把错误状态交给模型决策
消息队列消费5秒3次,退避进入死信队列人工处理

Agent主循环本身也要有步数上限。我习惯设置为10到15步,超过就终止并生成阶段性总结,而不是无限跑下去。很多所谓“Agent死循环”,本质就是缺这一步。

4.3 安全与护栏:防注入、防越权、防泄漏

Agent的开放性和安全性天然冲突,生产环境必须把安全当成第一需求。最常见的威胁是提示注入:用户输入里的一段恶意文本,试图让Agent忽略系统提示、执行危险操作。防注入不能靠一句“Ignore previous instructions”,而是要分层防御。

第一层,入口做输入过滤,识别并隔离明显的命令注入和敏感信息请求;第二层,系统提示词中加入安全规则,明确哪些动作永远不允许执行;第三层,工具调用按最小权限配置,高危操作(如发邮件、删除数据、支付操作)单独决策,甚至加一道人工审批;第四层,所有Agent输出过一遍敏感信息检查,防止把数据库里的隐私内容合成进回复。这个领域现在演进非常快,搜索“Agent安全”能看到的资料已经不少,但核心还是把权限边界画清楚,而不是指望模型凭空变安全。

4.4 成本控制:token不只是钱,也是延迟

很多Demo项目根本没有成本概念,一个任务轻松烧掉几万token,感觉无所谓。上生产之后第一笔账单就让人清醒。要控制成本,首先得能算清账:任务成本等于输入token单价乘数量,加上输出token单价乘数量,再加上工具调用次数乘以单次调用成本(包括外部API的费用和内部算力开销)。

实操上有三板斧。第一板斧是缓存,最有效的是语义缓存:用户问题经过向量化后和近期问题比对,相似度超过阈值就直接复用上一次的回答,大模型一次都不需要调用。第二板斧是模型分级,简单分类或者信息抽取用便宜的小模型,复杂推理才调用旗舰模型。第三板斧是上下文瘦身,也就是前面讲的快照摘要机制,这个在长会话场景下成本差异非常显著。我自己项目里的经验,优化后单任务token开销能下降60%以上。

4.5 评估体系:没有评测集,你根本不知道上线后是变好还是变坏

Demo靠感觉,生产靠指标。没有固定的评估集,你会发现每次调优都像是在碰运气。上线前,建议先建一个黄金评测集:收集200到500条真实任务,每一条标注期望结果和允许的行为边界(比如是否必须调用某个工具、是否允许拒绝回答)。

评估分成两个维度。结果评估,直接判断最终输出的质量,可以人工标注,也可以用强模型当裁判(LLM-as-judge);过程评估,关注模型是走了几步才完成任务、是否调用错误工具、是否卡在死循环、是否产出违规内容。上线后还要盯线上指标:任务成功率、用户主动反馈率、平均对话轮数、单任务成本。评测集和线上指标构成了双轨,任何prompt调整或者模型升级都要先跑回归,再决定要不要上线。

4.6 部署与发布:灰度、回滚、配置分离

很多人把Agent部署想象成部署普通Web服务,实际上更复杂的地方在于:一个版本的行为由模型权重和prompt共同决定,任何一个变化都需要纳管。

模型版本要做映射,一个产品版本明确绑定模型ID和prompt版本;prompt要像代码一样进仓库,每次修改都有Diff记录可供回滚。发布时建议先做灰度,让新版本处理5%到10%的线上流量,对比旧版本的任务成功率和成本数据。配置分离也很关键,模型API key、工具URL、超时时间、步数上限这些参数,全部放配置中心,不能散落在代码和部署脚本里。这样出问题时,调整一个配置就能快速止血,而不是重新发版。

5. 实操案例:一个客服工单助手如何从Demo进化到生产

5.1 原始Demo:一个跑通了的Python函数

我拿自己做过的一个客服工单助手举例。最早的Demo是一个两百行左右的Python脚本:接收用户问题,拼接一个包含客服话术规范的prompt,调用大模型,模型判断需要查工单库,就执行一个search_ticket()函数,把结果再丢回模型生成回答。

这个Demo在人肉演示时效果不错,但上线第一天就暴露了三个问题:并发用户稍多,脚本直接阻塞;会话状态在内存里,用户换一台设备就丢了历史;模型偶尔把一个不存在的工单编号编造成“已处理”,没有任何校验。这三个问题都属于Demo不背锅、生产躲不掉。

5.2 第一次重构:服务化、持久化、队列化

第一次重构做了三件事。第一,用FastAPI包了一层HTTP服务,每个用户请求带session_id;第二,对话记录和任务状态写进Redis,超时后异步持久化到数据库;第三,引入消息队列(RabbitMQ),用户请求先进入队列,Worker进程从队列拉任务,处理完回调结果。

改完后并发问题解决了,但架构上还是“单智能体”:一个Worker里跑的是一个完整Agent,知识库检索、工单分类、答复生成全在一个上下文里。表现是prompt越来越长,模型在多个任务间来回切换,经常出现分类错误。

5.3 第二次重构:拆成主管加三个专用Agent

第二次重构决定拆多智能体。整体结构是这样的:

  • Router/Dispath Agent(主管):负责理解用户意图,决定把任务分配给哪个下游Agent,并汇总最终回复。
  • Classifier Agent(分类专用):只做一件事,判断工单的紧急程度和类别,输出一个标准分类结果。
  • Knowledge Agent(知识检索专用):负责搜索内部知识库,召回相关文档,输出引用列表。
  • Solution Agent(方案生成专用):拿到分类结果和知识片段后,生成最终答复方案。

编排框架选的是LangGraph,因为它的状态图和条件边很直观,适合主管模式的落地。核心代码结构是这样:

from langgraph.graph import StateGraph, END g = StateGraph(WorkflowState) g.add_node("dispatcher", dispatcher_agent.run) g.add_node("classifier", classifier_agent.run) g.add_node("knowledge_search", knowledge_agent.run) g.add_node("solution_generator", solution_agent.run) g.add_edge("dispatcher", "classifier") g.add_edge("dispatcher", "knowledge_search") g.add_edge("classifier", "solution_generator") g.add_edge("knowledge_search", "solution_generator") g.add_edge("solution_generator", END) app = g.compile()

这里有两个关键决策。第一,分类和知识检索设计成并行执行,因为两者互不依赖,并行能节省用户感知的响应时间;第二,Solution Agent只有在拿到分类结果和知识片段之后才启动,保证生成答案有依据,不会凭空编造。

拆完之后效果最明显的不是准确率,而是稳定性和可调试性:每个Agent都有独立的prompt和工具,问题定位范围大幅缩小。分类出错了,直接看Classifier Agent的trace;知识召回不准,只调Knowledge Agent的检索策略。

6. 常见问题与排查技巧实录

6.1 踩过的坑和对应解法

下面这些问题是多智能体项目里最常出现的,我按真实项目经验整理成速查表:

现象根因排查与解法
两个Agent互相回复,退化成聊天室没有行为边界和终止条件每个Agent指定明确职责,定义输出schema;主循环设最大轮数
模型虚构工具执行结果工具返回未被严格校验工具层每一步做返回值校验,失败状态必须传给模型
用户上下文串了会话状态未隔离所有记忆和缓存key必须带上session_id
同一笔任务邮件发了两遍重试机制覆盖了外部副作用每次外部操作生成幂等键,接收端去重
模型升级后行为突变缺少评估集回归固定黄金评测集,上线前全量回归
Agent卡在一个节点反复调用同一个工具工具结果没有消除触发条件给工具调用增加“调用历史”反馈,禁止相同参数重复执行

6.2 再多说几条独家经验

第一条,给每个Agent决策的过程都生成独立trace_id。排查多Agent问题时,最大的痛苦是不知道哪一步决策出了问题。有了trace_id,就能把模型输入、工具参数、输出结果串成一条完整证据链。

第二条,消费队列消息前先做幂等。幂等表里记录每个task_id的处理状态,重复消费直接跳过。这个机制能挡住大量分布式环境下的重复执行风险。

第三条,Agent的主循环要设步数上限,这个前面提过,但我还想强调:上限值最好小一点。一开始用20步,实际跑下来一个简单的工单任务平均只需要4到5步,超过10步的任务基本都是走偏了,不如提前终止。

第四条,把“人工审批”作为一个工具暴露给Agent。当Agent面对高危操作时,它不应该自己决定执行,而是调用一个approval工具,推送审批请求给人工。等人工确认后,Agent再继续后续流程。这个模式对比“自动执行”来说安全性提升巨大,而且不会阻塞整体架构。

我个人的体会是,Agent系统从Demo到生产,本质上是一场从“无限循环”到“有界服务”的转变。Demo里你可以容忍模型跑偏、容忍结果不确定、容忍token超支,生产环境里每一样都会变成事故或者账单。如果让我重新做一个Agent项目,我会从第一天就建评测集、埋成本监控、画好权限边界,而不是等系统上线后,再追着事故去补这些能力。希望这篇拆解能帮你在做架构决策时省几个月的弯路。

返回列表