这两年做多智能体项目,最深的体会就是:模型能力决定系统上限,但真正决定你项目能不能落地、敢不敢上线的,是“控制”这件事。说得直接一点,你用三五个大模型 Agent 去协作处理一个业务流程,最难的不是让每个 Agent 变聪明,而是让它们不跑偏、不打架、不失控、不把用户数据搞得一团糟。这篇文章我就围绕“多智能体开发最难的地方是控制”这个点,把我在实际项目里踩过的坑、拆过的架构、用过的控制手段,完整梳理一遍。适合正在做或准备做多智能体应用的开发者、架构师,以及被“Agent 失控”折磨过的工程团队参考。
1. 先拆清楚:多智能体里的“控制”到底控什么
多智能体系统不是新鲜概念,分布式人工智能研究了很多年。但大模型时代的多智能体,和传统 MAS 有个本质区别:每个智能体的“决策内核”变成了一颗不可完全预测的大模型。以前做多智能体,每个 Agent 的行为逻辑是确定性的代码,输入确定,输出基本确定。现在换成 LLM 作为 Agent 的“大脑”,同一个问题,你问它十次,它可能给出十种不同的回答。
这就是“控制”难度飙升的根源。你可以用提示词约束它,但提示词是软的,是概率性的约束,不是硬性的逻辑保证。你说“不准调用删除接口”,它大概率不会调用,但可能有一天它就“理解错”了,构造了一个带有删除语义的调用。这类不确定性,决定了多智能体系统的控制不能只靠一个 prompt 解决,得从架构、状态、工具、权限、审计多个层面层层设防。
1.1 模型给你上限,控制决定你能不能摸到上限
很多团队一开始做多智能体,都是被“Agent 自主规划、自主执行”这个愿景吸引的。觉得只要给模型配上工具、写好角色设定,它就能像人一样拆解任务、调用工具、完成任务。真做起来才发现,初期 Demo 跑得挺漂亮,一放到真实业务流程里就问题百出。
我见过一个有代表性的场景:一个多智能体采购助手,设计的时候有需求分析 Agent、供应商比价 Agent、订单执行 Agent。Demo 环节一切正常,各种供应商数据、价格比较、自动推荐,演示效果拉满。结果一上测试环境,需求分析 Agent 把一个“咨询采购流程”的用户问题,错误地转成了“发起采购流程”,下游的订单执行 Agent 直接调用了“生成采购订单”接口,差点把测试环境的假订单发出去了。后来排查发现,根因是路由环节缺少意图置信度阈值,低置信度的需求直接流转到了下一环。
这类事故说明一个问题:模型的上限决定了这个系统“最聪明”的时候能做得多好,但控制能力决定了它在各种意外情况下不出错的下限。对生产系统来说,下限远比上限重要。
1.2 控制的两个层次:单智能体自我控制与系统级编排控制
多智能体里的“控制”,我习惯拆成两个层次来理解。
第一个层次是单个智能体的自我控制。这包括提示词设计、上下文窗口管理、行为边界约束、工具调用规范。简单说,就是让每个 Agent 在单独工作在时就处于一个“受控状态”。第二个层次是系统级编排控制。多个 Agent 协作时,谁来发起任务、任务怎么拆解、结果怎么汇总、冲突怎么仲裁、状态怎么流转,这些都是编排层要管的事。很多失控事故,恰恰发生在这一层。单个 Agent 都很稳,组合起来却陷入死循环、互相推翻结论、重复执行任务,这就是编排层控制没做好。
用团队做个类比。你招了一批聪明人,每个人单独干活都很能干,但如果不设分工、不立流程、不做互相 review 的机制,这群聪明人凑在一起开会,大概率会把一个简单问题讨论成一个无法收场的复杂问题。多智能体系统也是这个道理,甚至更严重。因为人至少还有会议主持人和组织架构来兜底,而 Agent 协作如果编排器不够强势,几个大模型对话起来是收不住的。
1.3 为什么“控制”是公认最难啃的骨头
控制难,说到底是因为它属于一个“跨学科”问题。你既要懂 NLP、懂提示词工程、懂大模型的脾气,又要懂软件工程、懂分布式系统、懂状态管理,还得懂业务流程本身。这不是某一个单一领域的知识能搞定的。
另外,恰恰因为模型是非确定性的,传统软件工程里的很多控制手段在这里会部分失效。比如你写一个确定性的工作流,A 步骤执行完必然走到 B 步骤,谁来打断都不好使。但多智能体系统里,每个节点的输入输出都是不稳定的,你很难用一个固定流程图穷尽所有情况。所以你会看到,业界的多智能体框架越来越强调 harness、编排器、状态机、人工介入节点,本质上都是想从不同维度把这种不确定性兜住。
2. 多智能体控制的核心拆解:七个不能失控的环节
我在实际项目里总结下来,多智能体系统的“控制”可以拆成七个具体环节。每个环节都可能出幺蛾子,也都需要有对应的控制手段。下面逐个说清楚。
2.1 行为边界的控制:让智能体不做不该做的事
先说行为边界。这是最基层的控制,也是很多人做提示词时最容易忽略的部分。大家写系统提示词,喜欢写“你是一个智能客服助手”“你擅长回答用户问题”,但对“你不能做什么”写得含糊不清。
行为边界控制要做的是明确画出红线。举个例子,我之前做过一个企业内部的采购助手。在 Agent 的 system prompt 里,除了写清楚它的职责,还必须明确列出禁止事项:不可修改产品价格、不可绕过审批直接下单、不可删除订单记录、当用户问题超出权限范围时必须转人工。这些禁止项越具体越好,不要只写“谨慎操作”这种没有操作性的空话。
但仅靠提示词里的禁止项是不够的,更硬的手段是“工具暴露面控制”。什么意思?就是你在给 Agent 配置工具时,只暴露当前角色完成本职工作必需的最少工具。不需要查库存的 Agent,就不给它查库存的工具;不需要下单的 Agent,就绝对不能有下单的权限。很多项目出事,出在把所有工具都暴露给所有 Agent,相当于给所有人都发了总经理的权限卡。
这个思路和权限管理里“最小权限原则”是一脉相承的。你在系统里给普通员工配门禁卡,不会让他能进财务室、机房、档案室。多智能体系统同样是一个组织,每个 Agent 都是组织里的员工,它的工具列表就是它的门禁卡,必须按需授权。
2.2 协作与任务分配的控制:谁来做、做什么、何时做
多智能体协作最常见的模式有几种:路由分发、编排器-执行器、会议讨论式、层级汇报式。每种模式对“控制”的要求都不同,我一个个说。
路由分发是最简单的模式。一个入口 Agent 判断用户意图,直接把任务分给对应的专业 Agent。这个模式的控制重点在路由准确率上,一旦意图识别错了,后面的环节全都会跟着错。所以一定要给路由环节加置信度判断,低置信度不流转,转人工确认,这是我在前文那个事故案例里得到的教训。
编排器-执行器模式是我个人最推荐、也是实际项目里用得最稳的一种。编排器不直接执行任务,它负责任务拆解、下发、回收结果、复核质量。“不清楚的任务先拆解、再下发;下发的任务要有明确验收标准;回收的结果要经过质量校验才能进入下一环”,这三条是编排器的核心职责。
会议式多智能体协作很有意思,多个 Agent 围绕一个问题讨论、投票、达成共识。效果挺好的,但控制难度也是最高的。讨论轮次必须设上限,否则几个 Agent 能互相辩论几十轮停不下来,token 消耗直接爆炸。还要防止“多数派压制少数派”,不要让所有 Agent 都来自同一个模型、用同一个系统提示词,不然讨论出来的结论就是“同一种声音的自我重复”,没有真实多样性。
层级汇报式则是把人类组织的金字塔结构搬过来。最高层 Agent 定战略目标,中层拆解任务,基层执行。控制重点在层与层之间的信息传递,上级给下级的任务描述必须精确到可独立完成的程度,下级反馈给上级的结果必须结构化,不能是一大段上下文。
2.3 上下文与记忆的控制:控制不住上下文,就控制不住质量
这是多智能体开发里另一个常见的失控点。大模型的上下文窗口是有限的,但业务流程是连续的。当多个 Agent 在同一个会话里协作时,消息会在 Agent 之间来回传递,如果不加控制,上下文里的无效信息会越来越多,直到把真正关键的信息淹没。
我把这种现象叫“上下文污染”。举个例子:一个内容创作多智能体系统里,创意 Agent 负责构思选题,写作 Agent 负责写稿,审核 Agent 负责把关。创意 Agent 很活跃,输出了大量发散性的想法,这些想法全部如实传递给了写作 Agent。写作 Agent 的上下文里有 80% 是删减后不需要的素材,写出来的初稿就偏离主线。审核 Agent 一看,初稿和用户需求对不上,打回重写,写作 Agent 又基于同样受污染的上下文再写一遍,还是不对。来回三次,系统陷入低效循环。
解决上下文污染的思路有几个。一是每条消息在传递前要做“降噪压缩”,把长对话压缩成结构化摘要,只保留事实、决策和待办事项。二是 Agent 之间的通信要尽量传递结构化消息而非自由文本,比如定义一个 Task 数据类,包含任务描述、输入参数、约束条件、期望输出,而不要让 Agent 面对一长串聊天记录自行理解。三是确实需要长期记忆的,用外部向量库或摘要记忆模块管理,不要全塞进当前上下文里。
2.4 状态的记录与恢复:从“无状态对话”到“有状态系统”
大模型本身是无状态的,对话时你给它什么上下文,它就基于什么回答。但一个真实业务流程是有状态的。用户在第三步取消订单,第五步的售后 Agent 不能还对着一张“已取消”的订单做发货处理。多智能体系统要把这种状态管理起来,才能避免逻辑混乱。
我的做法是给系统引入显式状态机。每个业务流程定义好状态集合和状态转移条件。比如采购流程,状态可以是:草稿、待审批、审批通过、已下单、已收货、已取消。只有处于“待审批”状态的订单才能触发“审批通过”这个转移,否则直接拒绝操作。所有 Agent 的读写操作都基于这个状态机来校验。
引入状态机还有一个好处,就是“可恢复性”。假设一个订单已经走到“已下单”状态,但下游 Agent 在执行后续操作时崩了。如果没有状态机,你只能从头再跑一遍或者人工介入去理清“现在到底到哪一步了”。有了状态机,系统服务重启后可以直接从该状态继续执行,不需要重放所有历史消息。
2.5 工具与外部动作的控制:API 调用是最后一道闸门
大模型 Agent 能做什么,不是由“聪明不聪明”决定的,而是由你给它接了什么工具决定的。所以,工具接入层是多智能体系统里最重要的安全闸门。
工具控制要从两个层面做。第一个层面是工具准入。前面提过最小权限原则,每个 Agent 只能挂载职责对应的工具。第二个层面是工具调用复核。即使 Agent 生成了某个工具调用请求,也要有一个前置校验层来检查参数合法性。我做采购助手项目时,在“生成采购订单”这个动作前加了一层校验:订单金额超过一定数值、或者供应商不在合作名单里,系统直接拦截,转入人工审批。
这个过程很容易理解,类比一下就是:一个员工有权限提交采购申请,但不代表他的每笔采购都能自动通过,财务还要审核。多智能体系统里,这个“财务”就是你在工具调用前插入的校验逻辑。
另外,要特别重视工具调用的幂等性设计。Agent 可能因为网络超时或自身重试逻辑,对同一个工具调用两次。如果这个工具是“发送邮件”或者“创建订单”,重复调用就会出大问题。工具实现层面要尽量做幂等,或者在接受请求时查重,保证同一个任务 ID 只能成功执行一次。
2.6 执行节奏与并发的控制
多 Agent 系统里,并发执行既是效率利器,也是失控放大器。多个 Agent 同时执行不同子任务,最后汇总结果,这是很自然的并行设计。但并行就会引入竞态条件、资源竞争、先后依赖这些问题。
几个典型的并发失控场景:多个 Agent 同时往同一份文档里写入内容,互相覆盖;一个 Agent 依赖另一个 Agent 的输出,但两者被错误地并行调度;多个 Agent 共享同一个外部接口限额,并发一高就触发限流,全部失败。
解决并发控制的核心思路是明确任务依赖图。有依赖关系的任务必须串行,无依赖的任务可以并行。系统里要有统一的调度器来管理这些任务的状态,而不是让 Agent 自己决定“我什么时候跑”。这就像开餐厅,后厨可以同时炒好几道菜,但必须按点菜单的顺序和依赖来协调出菜节奏,不能每个厨师想炒什么就炒什么。
还有一点是全局的输出节奏控制。如果系统允许 Agent 动态地向用户输出内容,可能出现两个 Agent 同时往聊天窗口里“抢话”。这时候就需要一个统一的输出管理器,所有 Agent 的对外消息都经过它排序后发布,保证信息有序呈现。
2.7 可观测性与审计:看不见的控制不是控制
没有监控的系统就是没有安全带的车。多智能体系统尤其需要可观测性,因为它的决策链路长、参与节点多、故障定位难。
我的项目里会为每个任务分配一个全局唯一的 Trace ID,所有 Agent 的处理过程、工具调用、中间结果、耗时、 token 消耗都挂在这个 Trace ID 下。排查问题的时候,直接根据任务 ID 拉出整条处理链路的日志,哪个环节出错了、哪里耗时异常、哪个 Agent 做了什么决策,一目了然。
审计更是硬需求。凡是涉及资金、数据变更、对外发送消息的操作,都要有完整的审计日志。谁发起的、为什么发起、依据是什么、经过谁审批、结果是什么,这些信息必须可追溯。我之前接手过一个项目,上线后才开始补审计日志,结果每次出问题都靠人工翻聊天记录,痛苦不堪。这块真的应该在一开始就设计好,后面补的代价通常是数倍。
3. 落地一个可控的多智能体系统:从架构到代码
原则讲了不少,接下来聊实操。我拿一个实际的例子来说明怎么把“控制”落到代码里。场景是一个多智能体客服工单系统:用户提问题,系统判断问题类型,分发给对应的处理 Agent,复杂工单进入人工审核。
3.1 架构选型用 harness 模式,控制别交给模型
先说为什么选编排器模式,而不是自由协商式的多智能体。前面我提到,模型是非确定性的,而生产系统需要的是可控性。自由协商式系统里,每个 Agent 都是平级的,它们之间通过自然语言交互来协调工作,这样的系统很有未来感,但对工程化来说太难控制。Agent 之间可能因为理解偏差产生分歧,然后不断互相纠正,陷入无意义的循环。
harness 架构的思路是“控制反转”。核心流程、状态流转、任务分配这些关键控制点,全部由代码和配置管理,而不是放给模型通过自然语言来协商。Agent 在 harness 里更像是执行器,它只负责完成自己被分配的任务,任务之间的组织关系由编排器把控。
我认同这种思路的最大原因就一个:可调试。控制逻辑在代码里,出了问题我可以断点调试、写单测;控制逻辑若在模型对话里,出问题只能靠猜。生产系统要的是确定性,哪条路径可控就走哪条路径。
这里顺便提一嘴,现在市面上的多智能体框架,比如基于 harness 理念的 LangGraph、CrewAI、AutoGen 等,本质上都是给开发者提供了编排控制的基础设施。我不建议在项目初期自己造轮子,先用成熟的框架把流程跑通,理解它的控制机制,再按业务需求去扩展。
3.2 用状态机管住核心流程
在核心业务流程上,我不会让 Agent 自由发挥,而是用状态机严格定义。举个例子,客服工单的状态流转可以定义如下:
- 新建(New):用户提交问题后创建
- 分发中(Routing):系统正在判断问题类型
- 处理中(In Progress):处理 Agent 正在解决问题
- 待人工(Need Human):系统或 Agent 判定需要人工介入
- 已解决(Resolved):问题已处理完成,等待用户确认
- 已关闭(Closed):用户确认或超时自动关闭
状态转移规则要写得很严格,比如“处理中”状态的工单,不能直接跳到“已关闭”,必须先经过“已解决”或“待人工”。这样做的好处是无论前面的 Agent 讨论多么天马行空,最终落到工单状态上的变更必须按规矩来。状态机就是多智能体系统里的“交通规则”,让每个 Agent 不能想怎么变就怎么变。
3.3 一段最小编排器的伪代码示例
为了让思路更直观,我写一段简化版的编排器示例,你们感受一下控制点在代码里长什么样。这里不是完整生产代码,只展示核心控制逻辑。
class Orchestrator: def __init__(self): self.task_queue = Queue() self.task_status = {} # task_id -> status self.worker_map = {} # agent_name -> handler def route_task(self, user_request: str): # 控制点1:意图识别置信度 intent, confidence = self.intent_classifier(user_request) if confidence < 0.7: # 低置信度不自动流转,转人工 self.transfer_to_human(user_request) return # 控制点2:任务拆解与分配 subtasks = self.decompose(intent, user_request) for st in subtasks: task_id = self.create_task(st) worker = self.select_worker(intent, st) self.task_queue.put((task_id, worker, st)) def run(self): # 控制点3:依赖顺序与并发控制 for task_id, worker, st in self.task_queue: # 有依赖的子任务,等待上游完成 if st.depends_on and not self.is_done(st.depends_on): self.task_queue.put((task_id, worker, st)) continue # 保持在执行状态,避免重复调度 self.task_status[task_id] = "running" self.worker_map[worker].execute(st) self.task_status[task_id] = "done" def transfer_to_human(self, user_request): # 控制点4:人工介入兜底 self.create_ticket(user_request, priority="high", flag="manual_review")这段代码里,我把意图路由、任务拆分、依赖检查、人工兜底全部显式放在编排器里,Agent 只负责执行自己的子任务。我在实际项目里面还加了“执行上限”字段,每个 Worker 最多执行 N 次就强制停止,防止死循环。
3.4 把不确定性拦截在关键动作之前
前面代码里的人工兜底就是一个思路:越关键的动作,越不要完全信任模型。在真实业务里,有些操作属于“不可逆操作”,发送邮件、转账、删除数据、对外发公告,这类动作绝不能只靠 Agent 自动判断。
我通常的做法是分层控制:低风险动作,比如“查询订单状态”,Agent 自动执行没问题;中风险动作,比如“修改收货地址”,设置了二次确认,让 Agent 把修改前后的信息展示给用户确认后再执行;高风险动作,比如“退款”“删除账号”,一律转人工审批。
这里有个经验心得:在 Agent 工具封装层做控制,远好过在提示词里做控制。举个例子,“退款”这个动作,我在工具函数入口写了一段校验逻辑,先检查操作者 token 是否有退款权限,再检查退款金额是否在可用额度内,任意一项不满足就直接抛异常,Agent 从模型层根本调不动这个工具,从而形成硬性约束。
4. 常见失控问题与排查实录
把这一节放在后面,是因为排查失控问题必须建立在对控制体系的理解上。没有控制理念的人,遇到 Agent 失控会去调 prompt、换模型,而真正的问题往往出现在系统层。总结几个我在实战中遇到的典型失控症状和排查思路。
4.1 症状:Agent 突然答非所问,回复内容跟当前问题毫无关系
这类问题大概率是上下文污染导致的。我们在一个客服项目里遇到过,Agent 聊着聊着,突然开始回复另一个用户的问题。查日志发现,这个 Agent 在处理当前请求时,上下文里混入了上一个会话的残留消息,原因是消息队列在切换会话时没有清理干净旧消息。
排查建议:先看这个 Agent 收到的最新一轮输入上下文里到底有什么。如果混入了其他会话内容,优先检查上下文拼接逻辑,是不是在会话结束时遗漏了清理或隔离。千万不要先去调 prompt,因为根因根本不在模型理解层面。
4.2 症状:两个 Agent 陷入无限循环对话,互相纠正对方,停不下来
这也是多智能体系统里的经典失控场景。排查思路很简单:看编排器有没有给 Agent 之间的对话设置轮次上限。如果没有,那必然有概率陷入死循环,因为 Agent A 说“这不是我负责的”,Agent B 回“那请你转给负责的人”,Agent A 又回“我无法转移”,Agent B 又回“请你联系管理员”……就这样永远聊不完。
解决手段是强硬的:所有 Agent 之间的交互轮数必须有硬性上限。达到上限后,强制中断,把当前状态上报给编排器,由编排器决定是转人工还是重新分配。另外,给每个 Agent 设置“职责边界声明”,当发现自己无法处理时,直接触发“transfer”动作而不是继续对话。
4.3 症状:同一个任务被多个 Agent 重复执行,产生重复数据
这种情况往往是任务分配时缺少幂等控制。排查方法:查任务执行记录,看同一个 task_id 是否被多个 Worker 同时领取和使用。在代码层面最容易出的问题就是 Worker 里自己写了重试逻辑,任务执行超时后,Agent 没收到成功确认,就自动又发起一次同样的子任务。
解决思路:所有任务必须有全局唯一 ID;任务执行前先查任务状态表;执行完成后立刻标记完成状态;重试时必须携带原 task_id,如果该任务已完成,直接返回已完成结果而非重新执行。
4.4 症状:Agent 调用了“绝不应该调用”的工具,比如删除了数据
这类事故的根源通常不是 Agent 太“调皮”,而是你给了它过度权限。排查方法很直接:看这个 Agent 的工具列表里为什么会出现删除类工具。九成情况是开发图省事,把通用工具集挂给了所有 Agent。
根治方案就是我前面说的“最小工具权限”。每个 Agent 的工具列表单独配置,配置项就是它的能力边界。删除类、写库类、发送类操作统一走审批链,在工具封装层加白名单校验。这里也提醒一句:不要迷信模型的“安全意识”,模型的安全护栏在复杂工具场景下并不稳定,工程层硬约束才是真正可靠的。
4.5 症状:单次任务的 token 消耗飙升,成本控制不住
很多多智能体项目上线后被成本搞崩过。排查这一类问题时,可以先看 Trace 里的 token 消耗分布:到底是哪个 Agent 消耗最多,是上下文太长,还是重试次数太多,还是 Agent 之间的消息循环导致消耗叠加。
常见的成本失控原因有:上下文无限累积不做压缩、重试次数过多、多 Agent 之间来回传递长文本没有降采样、并行度过高导致单位时间消耗激增。控制手段包括:设置单任务 token 预算上限,超预算强制终止;对传递的中间结果做摘要化处理;限制单 Agent 的最大重试次数;合理控制并行任务数。
5. 控制不是为了限制,而是为了让系统真正可交付
最后谈谈我个人的体会。很多人对多智能体的“控制”有误解,觉得控制多了就是给 Agent 戴上了枷锁,限制了它的“智能”。我一开始也有这种执念,总想着让 Agent 更自主、更灵活。后来被现实教育过几次,才彻底想明白:多智能体系统的价值不在于单个 Agent 多聪明,而在于整个系统能否可靠地完成业务目标。
控制这件事,本质上是在给不确定性兜底。你没法让一个非确定性的系统完全确定,但你可以通过架构分层、状态机、权限控制、人工兜底这些手段,把不确定性限制在一个可控的范围内。在这个范围内,Agent 可以自由发挥;超出这个范围,系统强制介入。这就像给一个天才员工足够的发挥空间,但公司的底线红线他不能碰,关键决策必须走审批流程。你不会觉得这是限制,你会觉得这是管理。
现在做多智能体项目,我习惯把“控制设计”当成一个重要模块来做,跟 Agent 本身的开发同步进行。先定义状态机,再设计编排器,再配置工具权限,最后才去写提示词。一套流程走下来,系统稳定性提升非常明显,调试成本也大幅下降。
额外分享一个好用的小技巧:在每个 Agent 的系统提示词结尾,加一句“如果你发现自己无法完成当前任务,或对任务目标有疑问,请立即停止操作,并调用 escalate_to_human 工具转人工处理”。就这么一句话,能避免很多 Agent 硬着头皮乱操作的场面,因为模型在没有把握的时候,恰恰是依赖“确定性”指令的。让它停下来,把控制权交还给系统,比让它继续试探要安全得多。
多智能体开发的路还很长,但“可控”永远是底牌。系统再聪明,不可控就不可用。希望这篇文章能帮你们少踩一些我踩过的坑。