ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AI Agent工程化落地:AgentOps、评估体系与多智能体架构拆解

AI Agent工程化落地:AgentOps、评估体系与多智能体架构拆解

简介:这是一份聚焦生成式AI智能体的谷歌白皮书中英文对照版,面向具备AI与软件开发基础、从事智能体设计开发或运维的工程师、技术管理者及研究人员。内容围绕模型、工具与交互协调三大组件展开,系统讨论AgentOps、能力、轨迹与最终响应评估及人机协同评估,并延伸至多智能体架构的分层、协作、对等模式与汽车AI案例;同时覆盖Agentic RAG、Google企业级智能体平台及“从智能体到承包商”的合同化机制,兼顾理论框架、评估方法与企业落地思路。资源包共1个PDF文件,大小约2.17MB,中英文对照便于术语核对与原文理解。已有1509人学习,适合系统建立智能体架构、评估与多智能体协作的整体认知,并结合评估工具、开发平台和案例梳理企业级部署与运维要点。

1. 这份 AI Agent 白皮书该摆在什么位置:一份写给开发者的 102 级手册

把 Agent 推上生产的团队,最后卡住的通常不是模型够不够聪明,而是评估做不出来、故障复现不了、成本压不下去。这份 2025 年 2 月定稿的 AI Agent 白皮书,定位很明确——它是入门篇之后的 102 级配套读物,中英文对照排版,全书围绕 AgentOps、评估体系、多智能体架构三条主线展开,还带了一个车载场景的完整落地案例。适合已经把 ReAct 跑通过一遍、正准备把 Agent 接进真实业务系统的工程师;纯新手读起来会有点跳,因为开篇就假设你知道什么是工具调用。后面几章按「能抄作业」的方式拆一遍,顺便标出哪些地方翻译腔重、哪些图必须对着英文原文看。

2. 智能体的三件套怎么落到工程上:模型、工具与编排层

白皮书开篇给出的定义很克制:Agent 是一个被工程化构造出来的应用,通过感知环境、并借助手头工具采取行动来达成特定目标。这句话里藏着一个容易被忽略的限定词——「被工程化构造」。一个裸模型加一个函数调用接口,不算 Agent;真正让它成为 Agent 的,是那个把推理、行动、观察串成闭环的编排层。这一章按模型、工具、编排层三块拆,重点放在编排层,因为前两块是选型问题,编排层是设计问题。

2.1 编排层才是分水岭:为什么「模型 + 工具」撑不起一个 Agent

白皮书把模型称作 Agent 框架里的核心决策单元,承担基于指令的推理和逻辑判断。这个定位意味着模型本身不保存任务状态,它只是被反复调用的一次次「思考」。状态保存在编排层:记忆里存过往轮次,scratchpad 里存本轮推理轨迹,规划模块决定下一步是继续调工具还是收口给用户。三者缺一,Agent 就会退化成一次性的问答封装。

选型上,白皮书的说法是从通用模型到多模态、再到微调模型都可以,取决于需求。落到实际工程,我一般按三个维度切:任务是否需要读图读表(多模态)、工具调用格式是否要求极稳定的结构化输出(微调或强约束解码)、单轮推理链是否超过七八步(长上下文与推理成本)。前两个维度决定模型档位,第三个维度决定你要不要上规划模块。

推理技术这一层,白皮书点了 ReAct、思维链(CoT)、思维树(ToT)三个名字,但没说清什么时候用哪个。血泪经验是这样的:

技术适用场景代价常见误用
CoT单步复杂判断,如工单分类、条款比对输出 token 涨 2~4 倍用在多步工具调用上,链一长就跑偏
ReAct需要边查边改的任务,如检索问答、数据核对轮次不可控,需设上限不设 max_steps,遇到工具报错就死循环
ToT分支决策,如方案比选、路径规划调用量呈树状放大在简单任务上硬套,成本翻十倍收益为零

实际项目里 ReAct 是主力,CoT 用在 ReAct 的单个 thought 内部做细化,ToT 只在少数需要「先想三条路再选一条」的场景开。白皮书把三者并列写,容易让人以为是三选一的架构选项,其实它们处在不同粒度上。

还有一层是记忆。白皮书把记忆放在编排层里一句话带过,但工程上记忆是最容易失控的部分:短期记忆(本轮 scratchpad)必须做截断,长期记忆(跨会话画像)必须做写入策略,否则上下文窗口会被历史对话撑爆。常见做法是给 scratchpad 设一个字符预算,超出就把最早的工具返回结果压缩成一行摘要,保留结论、丢掉原始 payload。

2.2 用 ReAct 写一个最小编排循环:代码骨架与四个关键参数

不依赖任何特定厂商 SDK 的骨架最能说明问题。下面这段是一个可以直接跑的 ReAct 循环,模型客户端用一个抽象接口表示,换成自家的推理服务即可。

# react_loop.py —— 最小 ReAct 编排骨架,不绑定具体模型厂商 import json from dataclasses import dataclass, field @dataclass class AgentState: goal: str # 用户目标,全程不变,用于最终收口判断 scratchpad: str = "" # 本轮推理轨迹,受 MAX_PAD_CHARS 约束 history: list = field(default_factory=list) # 已完成的 (action, observation) 对 steps: int = 0 MAX_STEPS = 8 # 硬上限,超过强制收口,防止死循环 MAX_PAD_CHARS = 6000 # scratchpad 字符预算,超出触发压缩 TOOL_TIMEOUT_S = 10 # 单个工具调用超时 TOOL_RETRY = 1 # 仅对幂等工具开启重试 def build_prompt(state: AgentState, tools_spec: str) -> str: # 工具清单每轮都重放,格式必须是稳定的 JSON Schema 描述 return f"""目标:{state.goal} 可用工具:{tools_spec} 已完成步骤:{json.dumps(state.history, ensure_ascii=False)} 推理轨迹:{state.scratchpad} 请输出 JSON:{{"thought": "...", "action": "工具名或 finish", "action_input": {{...}}}}""" def compress_pad(state: AgentState) -> None: # 压缩策略:保留最近两轮完整内容,更早的 observation 只留首行结论 if len(state.scratchpad) <= MAX_PAD_CHARS: return keep_tail = state.scratchpad[-MAX_PAD_CHARS // 2:] state.scratchpad = "[早期步骤已压缩]\n" + keep_tail def run_agent(goal: str, tools: dict, llm) -> str: state = AgentState(goal=goal) tools_spec = json.dumps({k: v["schema"] for k, v in tools.items()}, ensure_ascii=False) while state.steps < MAX_STEPS: raw = llm.invoke(build_prompt(state, tools_spec), temperature=0.2) try: decision = json.loads(raw) # 解析失败直接进入下一轮,不抛给用户 except json.JSONDecodeError: state.scratchpad += "\n[格式错误,请只输出合法 JSON]" state.steps += 1 continue if decision["action"] == "finish": return decision["action_input"].get("answer", "") name, args = decision["action"], decision.get("action_input", {}) if name not in tools: # 幻觉工具名,回灌错误信息让它自己纠正 obs = f"工具 {name} 不存在,可用工具:{list(tools)}" else: try: obs = tools[name]["fn"](**args, timeout=TOOL_TIMEOUT_S) except Exception as e: obs = f"工具执行失败:{type(e).__name__}: {e}" state.history.append({"action": name, "args": args, "obs": str(obs)[:800]}) state.scratchpad += f"\nThought: {decision['thought']}\nAction: {name}\nObservation: {str(obs)[:800]}" state.steps += 1 compress_pad(state) return "任务未在步数上限内完成,请缩小问题范围后重试。"

逻辑上分四段:构造提示、解析决策、执行工具、回写观察。提示里工具清单每轮完整重放而不是只放增量,是因为模型没有跨轮记忆之外的机制去「想起」某个工具,省这几百 token 换来的是显著更低的工具名幻觉率。

四个参数值得单独说。MAX_STEPS是后悔药,设多少取决于任务链长度,检索类任务 5~8 步够用,跨系统核对类 12 步以上;超过上限不要静默返回空,要返回一句明确的未完成提示,否则前端会显示成「已完成」但内容是空的。MAX_PAD_CHARS直接决定单次调用成本,6000 字符大约对应两千多 token,调大之前先算一遍单任务成本。TOOL_TIMEOUT_S必须设,不设的话一个挂住的下游接口能把整个 Agent 拖死。TOOL_RETRY只对查询类工具开,写库类工具重试一次就可能产生重复记录,这类翻车在第 5 章会细说。

解析失败的处理方式也值得注意:直接把格式错误回灌进 scratchpad,让模型自我纠正,比在代码里写一堆正则去「抢救」输出更稳。抢救逻辑往往只覆盖了你见过的那几种坏格式,模型下次会给你新的。

2.3 工具层的三种形态:扩展、函数、数据存储的选型边界

白皮书把工具分成扩展(Extension)、函数(Function)、数据存储(Data Store)三类,这个分法比常见的「API / 本地函数 / 向量库」更贴近职责划分,但中译版读起来容易混——尤其「扩展」这个词,英文原意是桥接 API 与 Agent 的那层适配器。

先给结论性的选型边界:

形态本质什么时候用什么时候别用
扩展把外部 API 的鉴权、重试、分页封装成 Agent 可直接调用的单元下游是成熟 REST 服务,且需要 OAuth 之类复杂鉴权下游接口本身不稳定、错误码语义混乱
函数一段自包含的代码模块,跑在你自己的进程里需要在调用前后做数据整形、校验、脱敏逻辑需要访问内网资源而 Agent 部署在外部环境
数据存储向量库、文档库、结构化库,提供动态事实来源答案依赖最新业务数据,且模型知识已过期数据量很小、直接塞进提示更省事

三种形态最容易出问题的地方不在选型,而在描述。工具描述是写给模型看的接口文档,不是写给同事看的注释。我看到过的典型翻车是把描述写成「查询订单信息」六个字,模型根本不知道要传订单号还是手机号、不知道返回里有没有物流状态,于是反复试探,步数烧光。

好的工具描述包含四件事:这个工具解决什么问题、必填参数的含义与格式、返回值的结构、什么情况下不该调用它。第四点几乎没人写,但它能显著降低误调用率。比如一个「发送短信」的工具,描述里加一句「仅在用户明确确认发送内容后调用」,误触发率能降一个量级。

数据存储这一层的坑在切分和时效。白皮书提到 Agentic RAG 时说得很清楚:更好的检索来自更好的搜索策略,而不是更大的向量库。文档切分粒度、是否保留表格结构、增量更新怎么触发重建索引,这些决定了后面所有推理的质量上限。我一般会把「检索到的原文片段」和「模型改写后的查询」都打进日志,出问题时能立刻判断是检索没召回,还是召回了但模型没用上。

工具数量也需要控制。给一个 Agent 挂三十个工具,工具选择的准确率会明显下滑,因为模型在长清单里做区分的成本很高。超过十五个工具就该考虑分组,或者拆成多个专职 Agent 用路由分发——这也正是白皮书后半部分讲多智能体架构的现实动因之一。

3. AgentOps 落地:从 Ops 谱系到可埋点的成功指标

白皮书对 AgentOps 的定义是 GenAIOps 的一个子类,专注于 Agent 的高效运营化,额外承担工具管理、Agent 大脑提示(目标、画像、指令)与编排、记忆、任务分解这几块。这个定义本身没什么争议,真正有价值的是它给出的谱系图——把 DevOps、MLOps、FMOps、PromptOps、RAGOps、AgentOps 排成一条依赖链,明确说了新实践不替代旧实践。

3.1 六种 Ops 的依赖关系:哪些能力可以直接继承

很多团队做 Agent 运维时从零搭一套,结果是重复造轮子还造得不好。谱系图的意义在于告诉你哪些能力可以直接从上一代继承。

层级关注对象新增的核心能力可直接继承的部分
DevOps确定性软件CI/CD、版本控制、监控告警——
MLOpsML 模型数据版本、模型训练流水线、漂移监控全部 DevOps 能力
FMOps预训练/微调基础模型模型卡、微调数据治理、推理资源调度全部 MLOps 能力
PromptOps提示提示存储、血缘、元数据(含评分)、集中模板注册表、提示优化器版本控制、灰度发布
RAGOps检索增强离线数据准备(清洗、切分、向量化、相似检索、重排)+ 生成侧增强与接地提示管理、评估框架
AgentOps智能体内外部工具管理、Agent 大脑提示与编排、记忆、任务分解鉴权、限流、配额、异常处理、可观测性

这张表最有用的地方是最后一行。白皮书特别强调,Agent 调工具用的往往就是非 Agent 软件调的那批 API,所以身份认证、密钥管理、安全、隐私、异常处理、限流、配额、可扩展性这些照样是硬要求,而且需要在 API 设计阶段就考虑,不是套在 Agent 外面补一层。

实际落地时我见过一个典型误判:团队认为 Agent 是「新东西」,于是单独建了一套告警体系,结果工具侧的超时告警和原有 API 网关的告警重复触发,值班同学一晚上收到四遍同样的信息。正确做法是工具调用的底层指标复用原有网关的埋点,Agent 层只补编排相关的指标。

PromptOps 那一层也常被跳过。提示没有集中注册表,典型后果是同一个系统里三个服务各存一份「客服系统提示」,改了一处另外两处继续用旧版,线上表现时好时坏,排查时怀疑模型、怀疑检索,就是不怀疑提示不一致。集中模板注册表加血缘记录,能省掉大量这类玄学问题。

3.2 A/B 实验埋点:业务北极星指标与 Agent 过程指标怎么分层

白皮书里有个很好的思维实验:假设你在生产环境给新 Agent 开 A/B,实验组上 Agent、对照组不上,你要用哪些指标判断实验组更好?这个问题逼着人区分两类指标——业务指标(收入、转化、用户参与度)是北极星,但它通常不在 Agent 自身的范围内;Agent 指标(任务完成率、步数、工具成功率)是你能直接优化和归因的东西。

分层埋点我一般这么设计:

层级指标采集位置用途
业务层任务转化率、人工接管率、单均成本业务系统判断值不值得继续投入
任务层任务完成率、平均步数、超步数上限比例编排循环判断 Agent 本身是否在进步
步骤层工具选择准确率、参数正确率、工具错误率工具调用包装器定位具体退化点
质量层最终响应事实性、格式合规率评估流水线离线回归与线上抽检
成本层单任务 token、单任务时延 P50/P95模型客户端容量规划与预算控制

关键在「超步数上限比例」这个指标,它比平均步数敏感得多。平均步数从 4.2 涨到 4.6 可能只是任务分布变了,但超上限比例从 2% 涨到 9% 基本可以确定是某个工具的返回格式改了或者描述写坏了,直接去看步骤层日志就能定位。

埋点代码最忌讳写得太重。下面这个函数在循环里每步调一次,字段够用就好:

# telemetry.py —— 每个步骤打一条 span,字段控制在 12 个以内 import time, uuid, json, logging log = logging.getLogger("agent.span") def emit_span(state, step_index, decision, obs, latency_ms, tokens): log.info(json.dumps({ "trace_id": state.trace_id, # 一次任务一个,跨步骤串起来 "step": step_index, "action": decision.get("action"), # 工具名,finish 也记 "action_args_keys": sorted(decision.get("action_input", {}).keys()), # 只记 key 不记值,防泄漏 "obs_len": len(str(obs)), # 观察长度,用于观察上下文膨胀 "obs_ok": not str(obs).startswith("工具执行失败"), "latency_ms": latency_ms, "tokens": tokens, "step_limit_hit": step_index >= 7, # 接近上限时提前预警 }, ensure_ascii=False))

参数上有个细节:action_args_keys只记参数名的 key,不记值。参数值里经常有手机号、地址、订单金额,直接落日志就是一次数据合规事故。需要复现时靠 trace_id 去业务系统查原始入参,而不是让日志成为一份脱敏不彻底的副本。

obs_ok用字符串前缀判断是个权宜之计,更稳的做法是让工具包装器统一返回{"ok": bool, "data": ..., "error": ...}结构,编排层和埋点都读这个字段。前期偷懒用字符串约定,后期接入第二种工具时一定会后悔。

还有一个指标白皮书没提但线上很要命:首次工具调用的时延。用户感知的等待主要发生在前两三步,后面步骤即使慢,用户已经在看进度反馈了。把 P95 首次工具时延压到两秒以内,比把总时延优化 30% 的体感收益大得多。

3.3 提示、工具 schema 与记忆的版本管理

AgentOps 的版本控制和普通服务不太一样,因为它有三个独立演进的版本维度:提示、工具 schema、记忆结构。三者任一改动都可能让线上表现突变,而这三者的变更往往由不同角色发起。

我习惯的目录结构是这样的,重点是每次变更必须同时留一份变更说明和一份基线评估结果:

agent_config/ ├── prompts/ │ ├── system.20250210.v3.txt │ └── system.20250210.v3.md # 变更说明:改了什么、为什么、基线分数 ├── tools/ │ ├── order_query.schema.v2.json # 参数增减必须升版本,不能直接改 │ └── order_query.impl.v2.py └── memory/ ├── short_term.policy.v1.json # 截断策略、压缩策略 └── long_term.schema.v1.json # 写入字段、过期时间

三条硬规则。第一,工具 schema 的破坏性变更(删参数、改类型、改必填性)必须升大版本并且新旧并行一段时间,因为模型侧的提示里缓存了旧 schema,切换需要灰度。第二,提示文件的命名里带日期,便于按时间回滚,文件名里不带「final」「new」这种词——我见过一个仓库里七个system_final.txt。第三,记忆结构的变更必须做数据迁移方案,长期记忆里存着用户画像,schema 一改,老数据读出来就是脏的。

白皮书在 AgentOps 部分还提了任务分解,这块的版本管理容易被忽略。分解策略(先拆子任务还是边做边拆)本质上也是配置,改一次就等于换了套系统,同样需要基线评估兜底。

4. 智能体评估的三个切面:能力、轨迹与最终响应

评估是这份白皮书里分量最重的部分,从 Agent Evaluation 一路写到多智能体评估,中间拆出能力评估、轨迹与工具使用评估、最终响应评估、人机协同评估四块。这个拆法是对的,因为 Agent 的失败模式太多样了——有的任务做对了但绕了十步,有的每步都对最后汇总错了,有的答案对但工具调用越权了。用单一指标衡量,永远只能看到一角。

4.1 能力评估:把「会不会用工具」拆成可判定项

能力评估回答的是「这个 Agent 具备哪些本领」,听起来抽象,落下去必须变成可判定的二元项,否则没法算分。白皮书的思路是先定义能力清单,再为每项能力设计测试用例。我一般拆成四类:

能力项判定方式典型失败表现
工具选择给定意图,第一步调用的工具是否在期望集合内该查订单的去查了物流
参数构造必填参数是否齐全、格式是否合法日期传成「昨天」而不是 ISO 格式
多步规划完成任务的步数是否在参考区间内反复调用同一工具刷步数
边界识别超出能力范围时是否明确拒答或转人工硬编一个看起来像样的答案

边界识别这一项最容易被漏掉,但它在生产环境里的价值最高。一个不会拒答的 Agent,错误答案会以很高的置信度流到用户手里,而错误答案的修复成本远高于「暂时无法处理,已转人工」。

测试用例的构造有个实用技巧:不要从「正确路径」出发写用例,从工具的错误返回出发写。把每个工具可能返回的 4xx、5xx、空结果、超时都造一遍,看 Agent 是重试、换路还是收口。这批用例的通过率,往往比正常路径的通过率更能预测线上表现。

能力评估的另一个坑是用例集泄漏。同一批用例被反复用来调提示、调工具描述,几轮之后分数很好看,但那只是过拟合。常见做法是留一个从不参与调试的留出集,只在版本发布前跑一次,两次发布之间的分数变化才有参考意义。

4.2 轨迹评估:单步正确不等于任务完成

轨迹评估是 Agent 特有的,传统模型评估没有这一层。白皮书把轨迹和工具使用放在一起讲,因为它们本质上是同一件事的两个视角:轨迹是过程,工具使用是过程中的关键动作。

轨迹层面我关注五个信号:总步数、重复调用、无效调用、恢复能力、终止原因。前三个是效率问题,后两个是可靠性问题。

「恢复能力」这个信号特别好用,定义是:工具返回错误后,Agent 在接下来两步内是否采取了不同的动作。如果它原样重试同一个工具同一个参数,说明它根本没读 observation;如果它换了个工具但目标不变,说明它有基本的纠错意识。这个指标在版本对比时比任务完成率更早出现分化,适合做早期预警。

批量跑轨迹评估的脚本大概是这个形状,核心是把每一步的决策和执行结果都落到结构化记录里,再做规则判定:

# eval_trajectory.py —— 离线跑批,输出每条用例的轨迹诊断 import json from collections import Counter def diagnose(trace: list[dict]) -> dict: """trace 是一串 step 记录,每步含 action / args / obs_ok""" actions = [s["action"] for s in trace if s["action"] != "finish"] pairs = [(s["action"], json.dumps(s["args"], sort_keys=True)) for s in trace] dup = sum(c - 1 for c in Counter(pairs).values() if c > 1) # 完全相同的调用算重复 recover = 0 for i, s in enumerate(trace[:-2]): if not s["obs_ok"]: nxt = trace[i + 1] if (nxt["action"], json.dumps(nxt["args"], sort_keys=True)) != pairs[i]: recover += 1 # 失败后换了动作即算一次恢复 errs = sum(1 for s in trace if not s["obs_ok"]) return { "steps": len(actions), "duplicate_calls": dup, "tool_errors": errs, "recover_rate": round(recover / errs, 3) if errs else None, "terminated_by": trace[-1]["action"] if trace else "empty", } def run_batch(cases, agent, threshold): rows = [] for c in cases: trace = agent.run_with_trace(c["input"]) # 需要编排层支持返回完整 trace d = diagnose(trace) d["case_id"] = c["id"] d["passed"] = d["steps"] <= threshold and d["duplicate_calls"] == 0 rows.append(d) return rows

recover_rate为 None 表示整条轨迹没遇到工具错误,这类用例要从恢复率统计里剔除,否则会把「没机会恢复」算成「恢复失败」,把指标拉低。这个细节在第一次算分时几乎必然踩到。

duplicate_calls用的是「动作 + 参数」的完全匹配,比只比动作名严格。有些任务确实需要连续两次查同一个接口但参数不同(比如翻页),按动作名判重会误伤。反过来,如果 Agent 把同一份长文本每次都完整塞进参数里,完全匹配能抓出来,这是省成本的关键。

passed的判定条件写得很粗,只看步数和重复。生产环境里还要加「终止原因必须是 finish 而非步数上限」以及「关键工具必须被调用过」。后者可以防一种很隐蔽的作弊:Agent 不调任何工具,直接凭模型知识编了个答案,任务看着完成了,实际全是幻觉。

4.3 最终响应评估与人机协同打分

最终响应评估看起来最简单——比对答案对不对,实际最难,因为很多任务的正确答案不是字符串。白皮书给了三个方向:事实性、相关性、完整性,工程上还要加格式合规。

事实性可以用检索接地的方式判:把响应里的事实性陈述抽出来,逐条回查权威数据源,能否支撑。相关性靠模型裁判,给一段评分标准和几个示例,让它打 1~5 分。完整性最难自动化,我一般用「要点覆盖率」近似:预先标注该任务必须覆盖的 N 个要点,统计命中几个。这个方法粗糙但可解释,业务方看得懂,比一个黑盒分数更容易推动改进。

模型裁判要注意两个偏差。一是位置偏差,A/B 对比时把两个响应交换顺序各打一次分,取平均。二是长度偏差,裁判模型倾向于给长答案高分,所以评分标准里必须明确写「冗余信息扣分」。不加这条,优化方向会跑偏成「把所有相关的内容都堆上去」。

人机协同评估是白皮书单列的一节,核心观点是自动化评估不能完全替代人。落地方式我建议做成抽检流水线:线上每天按固定比例抽一批真实会话,让标注同学按统一 rubric 打分,同时记录他们的判断理由。理由字段比分数值钱,它是后续调整自动评估标准的依据。

标注一致性要算,两个标注同学对同一批样本的 Cohen's kappa 低于 0.6,说明 rubric 本身写得有问题,先改 rubric 再谈模型好坏。这一步跳过的话,后面所有的评估结论都建立在一套互相矛盾的标注上。

4.4 多智能体评估的归属难题

单 Agent 评估的链路是清楚的:输入、轨迹、输出,三段都能归因。多 Agent 一上来就把这个链路打散了——任务失败了,是路由 Agent 分错了,是子 Agent 执行错了,还是汇总 Agent 拼接错了?

白皮书在讨论多智能体时提到几个挑战:组件间通信、任务分配的合理性、系统级的可观测性。评估上的应对办法我常用两条。

第一条是分层评估。每个子 Agent 单独跑自己的能力用例,保证单个组件的下限;再跑端到端用例,只看最终结果。两套分数分开看,不要混成一个总分。总分好看但子 Agent 分数参差,说明路由逻辑在掩盖某些子 Agent 的缺陷,换个任务分布就会暴露。

第二条是轨迹归属标注。在 trace 里给每个步骤打上执行者标签,端到端失败时先看失败步骤归属哪个 Agent,再看这个 Agent 在分层评估里的分数。如果它自己的分数很高但端到端里表现差,问题多半出在上下文传递——上游给的输入缺了关键信息,或者格式不符合下游预期。

上下文传递是多智能体系统里最隐蔽的故障源。两个子 Agent 之间的交接内容如果是自然语言,信息损耗极大;如果用结构化字段,又容易因为 schema 演进不同步而丢字段。常见做法是交接内容用「结构化主干 + 自然语言补充」,主干字段有严格的 schema 校验,补充部分给下游做背景理解。

5. 避坑与排查:多智能体落地时最容易翻车的五个地方

前面几章讲的都是「应该怎么做」,这一章讲「实际上会怎么坏」。下面五条都是我在真实项目里反复见到的,按现象、原因、解决三段写,方便直接对照排查。

5.1 工具层与编排层的三类翻车

第一条:工具描述写得太简,模型反复试探。

现象:单任务平均步数从 4 涨到 9,日志里能看到 Agent 连续用不同参数调用同一个查询工具,像是不知道要传什么。

原因:工具描述只写了一句话,参数说明缺失,模型只能靠猜测填参数,第一次填错拿到空结果,就换个参数再试。这类试探在步数上限内会一直持续,用户的体感是「卡了很久然后说找不到」。

解决:把工具描述补成四段式——用途、必填参数含义与格式、返回结构、禁用条件。补完之后先在离线用例上跑一遍,看平均步数有没有回落。如果回落不明显,说明问题不在描述而在参数设计,比如必填参数太多,考虑给默认值或者拆成两个工具。

第二条:记忆无限增长,成本和时延同时失控。

现象:同一个功能上线两周后,单任务 token 消耗涨了三倍,P95 时延从 3 秒涨到 11 秒,但代码没改过。

原因:长期记忆只写不读、只增不删,每轮都全量塞进提示。用户用得越久,历史越长,提示越大。这种劣化是渐进的,监控上不会报警,只会慢慢变贵。

解决:给记忆加三件事——写入时的字段白名单(只存对后续任务有用的)、检索时的相关性过滤(按当前任务召回 top-k,不全量塞)、过期策略(比如画像类记忆 90 天未命中就归档)。上线后加一个「单任务提示 token」的监控曲线,涨势一旦抬头就能立刻发现。

第三条:工具重试没有幂等保护,产生了重复副作用。

现象:用户反馈收到了两条一样的短信,或者订单被创建了两次。

原因:编排层对工具失败做了重试,但重试的是写操作。第一次调用实际上成功了,只是响应超时,Agent 以为失败,重试一次,下游就多了一条记录。这个问题在测试环境几乎不会出现,因为测试环境的超时不会真的发生。

解决:工具注册时给每个工具打上idempotent标记,只有标记为真的工具才允许自动重试。写操作要么不重试,要么带上幂等键(由编排层生成并在重试时复用同一个键),让下游做去重。这一条不改,迟早出事故。

5.2 评估与协作流程的两类翻车

第四条:离线评估 95 分,上线一周跌到 60。

现象:发布前评估集上表现很好,灰度到 20% 流量后任务完成率断崖式下跌,但错误日志里没有任何异常。

原因:评估集是从早期人工构造的样本里来的,句式规整、意图明确;线上真实输入有错别字、有多意图混杂、有大量「帮我看看那个东西怎么样了」这类指代不明的表达。分布不一致,分数就没有可比性。

解决:评估集必须掺真实样本。我一般要求线上抽检样本占评估集的三成以上,并且每月更新一次。另外加一个「分布漂移监控」——统计线上输入的平均长度、工具调用分布,和评估集对比,偏差超过阈值就提示补样本。这件事没有一次性解决方案,只能持续维护。

第五条:多智能体互相等待,任务挂死不报错。

现象:端到端任务偶发超时,trace 里能看到两个 Agent 之间来回交接了十几次,每次交接内容都差不多。

原因:交接没有终止条件。A 把任务交给 B,B 觉得信息不全又交回给 A 补充,A 补充完再交给 B,B 仍然认为不全——循环成立,因为没有一方负责判定「信息已经够了」。

解决:给交接加三样东西。交接次数上限(比如同一对 Agent 之间最多 3 次);交接时必须携带「已确认信息清单」,下游只能要求清单外的信息;指定一个最终裁决者,超过上限直接由裁决者收口,哪怕信息不全也要给出带不确定性的答复。第三点很关键,宁可给出「部分信息缺失」的明确答复,也不要让用户对着转圈等十分钟。

6. Agentic RAG 与协作模式选型:把架构图变成可验证的配置

白皮书后半段有两个内容密度很高的部分,一个是 Agentic RAG,一个是车载场景里的多智能体模式。前者讲的是检索增强的演进方向——从「一次检索 + 一次生成」变成「改写查询、迭代检索、自我批判、必要时换检索源」;后者把层级、钻石、对等、协作、响应混合、自适应循环这几种模式摊开,配了具体的车载场景。两件事合起来其实回答同一个问题:什么时候该加一个 Agent。

我的判断标准很土但有效:看这个子任务是否需要独立的状态和独立的工具集。需要,就拆;只是「步骤多一点」,就别拆,加一步 ReAct 更省事。拆错的代价是多智能体评估的归属难题全部落到你头上。

模式结构特征适用场景必须配的终止条件
层级一个主控拆任务,子 Agent 执行子任务边界清晰、可并行主控汇总轮次上限
钻石先分发再汇聚,中间有交叉验证需要多视角核对的事实类任务交叉验证通过即收口
对等Agent 之间直接协商能力对等、需要谈判的场景协商轮次上限 + 裁决者
协作多个 Agent 共享中间产物内容生产类,如文案 + 校对产物收敛判定
响应混合多路结果由混合器加权输出多来源答案需要融合混合器置信度阈值
自适应循环根据中间结果动态改变编排任务路径高度不确定全局步数 + 成本双上限

实操上有个技巧值得单独说:把每次线上失败的 trace 存下来,改造成回归用例,放进离线评估集。注意要保留完整的 step 记录而不是只留最终答案,因为你要验证的是「这个版本会不会重复同样的错误动作」。我一般会给失败 trace 加三个标签——失败类型(工具、规划、幻觉、超时)、责任 Agent、是否可自动判定。只把「可自动判定」的那部分放进回归集,其余的转人工抽检,否则回归集会越滚越大,跑一次要两小时。

踩过最深的一次坑是:某个版本的提示调整让任务完成率涨了四个点,团队很高兴地全量了,两周后发现单任务成本涨了六成——因为新提示让 Agent 更倾向于「多查一次保险」。从那以后我每次做提示或编排变更,都把「任务完成率」和「单任务 token」两条曲线强制放在同一张图上看,任何一条恶化超过 15% 都不允许全量。这条习惯救过我不止一次。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进