
最近和不少做 Agent 的同学聊天发现大家都在纠结同一个问题到底用单 Agent 还是多 Agent。这个话题几乎是所有 Agent 项目的必经之问但大多数讨论都停留在概念层面——什么角色分工、编排、记忆共享听起来头头是道真落到自己写代码、上生产就完全不是一码事了。我们团队维护了一套跑在生产 Kubernetes 集群上的故障排查系统专门处理线上告警和异常事件从收到一条服务 A 错误率飙升开始AI 自动去拉日志、查监控、看调用链最后给出根因分析和恢复建议。这套系统前后经历了单 Agent 和多 Agent 两种形态踩过的坑基本覆盖了上面提到的所有概念。今天我不聊抽象理论就把这套系统的演进过程和真实事故摊开来讲看完你对多 Agent 还是单 Agent这个问题应该会有自己的答案。1. 先交代背景我们这套生产排查系统遇到了什么问题1.1 系统定位与最初的单 Agent形态先说说这套系统是干什么的。我们在 K8s 集群上维护着几十个微服务每天都会收到来自监控平台的各种告警比如 Pod 重启、错误率上升、接口 P99 延迟恶化、磁盘水位告高。过去这些告警要先由值班工程师人工去看 Grafana、翻 Kibana、查调用链运气好十分钟定位运气不好折腾一两个小时。这套排查系统的目标就是把告警到初步根因判断这一段自动化。系统输入是一个标准化的告警事件包括服务名、异常类型、时间窗口、告警原文输出是一份诊断报告包含环境状态摘要、日志异常模式、调用链关键变化、根因假设、置信度和恢复建议。第一阶段我们做的是典型的单 Agent一个 LLM Agent挂上若干工具函数用最朴素的 ReAct 循环跑。核心结构大概长这样class DiagnoseAgent: def __init__(self, llm): self.llm llm self.tools { get_pod_status: get_pod_status, query_logs: query_logs, query_metric: query_metric, get_trace: get_trace, list_recent_deployments: list_recent_deployments, } async def run(self, incident: Incident) - Report: messages build_system_prompt(incident) for step in range(MAX_STEPS): decision await self.llm.function_call(messages, self.tools) if decision.name submit_report: return parse_report(decision.arguments) result await self.tools[decision.name](**decision.arguments) messages.append({ role: tool, name: decision.name, content: truncate(result, 20000), })单 Agent 的好处是直观所有中间结果都在同一个上下文里模型天然记得前面看过什么工具调用顺序也是模型自己决定。第一版上线后效果其实不差简单场景比如某个 Pod 被 OOMKilled 反复重启模型几个工具调用就能给出正确答案用户反馈也还行。1.2 单 Agent 从够用到不够用瓶颈到底出在哪问题是从工具列表膨胀开始的。排查需求越来越多我们给 Agent 接入了日志关键字检索、日志聚合统计、指标多窗口对比、调用链下钻、K8s 事件查询、发布记录查询等工具总数从 8 个涨到 20 多个。第一个崩溃点就是工具选择错误变多。工具少的时候模型选错一次很快能自己纠正工具一多它经常在query_logs和query_log_stats之间犹豫或者在不需要看链路的时候强行调了一次 get_trace。不是模型变笨了而是函数列表太长后描述里的边界变得模糊模型每走一步都要做一个 20 选 1 的决策准确率自然下降。第二个问题是系统提示词越补越臃肿。为了让 Agent 知道什么时候该用哪个工具、查日志时时间窗口怎么对齐、结论必须带证据我们在 system prompt 里不断追加规则。最后 prompt 接近两千字模型在长上下文中越来越头重脚轻——前面的规则它还记得但真正执行时经常顾此失彼。第三个问题是上下文塞满后Agent 会忘记早期的关键信息。排查过程里拉日志经常会产生大量中间文本虽然做了截断但几轮工具调用下来早期那个 Pod 的状态信息可能就被挤出了注意力范围。我们曾复现过一种非常尴尬的情况Agent 在前面已经发现发布后 2 分钟才开始报错后面分析日志时又得出结论报错和发布同时发生——它自己打自己脸。到这一步瓶颈已经非常清楚了不是模型能力不够而是把不同专业领域的判断都塞进同一个长上下文里让同一个角色用一套规则做所有决策这件事本身就有上限。于是我们开始认真考虑多 Agent 方案。2. 向多 Agent 演进拆分逻辑和职责边界2.1 为什么拆成四个 Agent 而不是八个很多人拆 Agent 是按工具拆比如一个 Agent 管日志一个 Agent 管理指标一个 Agent 管 K8s 事件。我们最初也这么想过后来发现不对工具只是手段真正的边界应该按业务环节切。我们复盘了有经验工程师的排查方法论发现步骤高度一致先看环境和资源状态Pod 是否 CrashLoop、Node 是否有压力、有没有新发布再查日志错误关键字、堆栈、日志节奏再看链路调用方、依赖方、耗时分布最后综合所有事实做根因判断。这四步的专业背景完全不同环境判断依赖 K8s 知识日志分析依赖检索与文本模式归纳链路分析依赖分布式追踪体系根因汇总则依赖全局推理。硬塞给同一个 Agent它就需要在一颗大脑里维护四套专业知识这恰恰是单 Agent 崩溃的根源。所以最终拆成四个Agent职责专属工具集输入输出ClusterAgent集群资源与状态判断Pod/Node/Deployment 状态、K8s 事件、发布记录受检工作负载与时间窗口结构化环境状态摘要LogAgent日志检索与异常模式归纳日志关键字检索、时间窗口统计、上下文捞取服务名、关键字、时间范围异常日志模式、堆栈片段、出现频率TraceAgent调用链与依赖变化分析Trace 查询、Span 统计、接口耗时分布服务名/接口、时间窗口链路关键路径、异常 Span、依赖变化AnalyzerAgent根因汇总与报告生成读取三个 Agent 的结构化结果、检索预案库受控的中间事实集合根因结论、置信度、恢复建议四个 Agent 对应排查闭环的四个环节每个 Agent 的 prompt 只聚焦自己的专业域工具控制在 6 个以内上下文长度也大幅缩短。为什么是四个而不是八个因为每多一个 Agent 就多一次上下文切换、多一层编排开销、多一倍的调试难度。我们在内部实验里试过把 ClusterAgent 再拆成节点 Agent和负载 Agent效果不仅没提升反而因为两个 Agent 的观察视角不一致经常得出互相矛盾的中间结论。能合并就合并这是多 Agent 设计的第一条纪律。2.2 编排层与协作方式共享黑板模式的具体实现多 Agent 之间怎么协作我们在早期架构评审时讨论过两种方案一种是让 Agent 之间互相发消息聊天式协作另一种是引入编排层统一调度。前者听起来很Agent但生产环境里几乎不可控你不知道 A 什么时候该找 B不知道消息会不会循环也没办法对谁给谁发了什么做预算。我们最后选了更朴素的方案——编排层 共享黑板Shared Blackboard模式。核心思路是子 Agent 之间不直接对话所有中间结果都以结构化数据的形式写入一个共享状态对象编排层控制写入顺序和读取权限。简化后的编排逻辑长这样async def run_case(case: IncidentCase) - Report: board SharedBoard(case_idcase.id, read_only_after_writeTrue) # 第一步环境状态必须最先完成 env_result await cluster_agent.run(case.workload, case.window) board.write(cluster, env_result, max_size2000, immutableTrue) # 第二步日志和链路可以并行 log_task asyncio.create_task( log_agent.run(case.workload, board.read(cluster), case.window) ) trace_task asyncio.create_task( trace_agent.run(case.workload, case.window) ) log_result, trace_result await asyncio.gather(log_task, trace_task) board.write(log, log_result, max_size3000, immutableTrue) board.write(trace, trace_result, max_size3000, immutableTrue) # 第三步根因汇总 report await analyzer_agent.run( cluster_summaryboard.read(cluster), log_summaryboard.read(log), trace_summaryboard.read(trace), ) return report这里最关键的设计是结构化黑板而不是自然语言对话记录。每个子 Agent 的输出都是带字段的 JSON而不是一段话。这么做有三个直接好处第一AnalyzerAgent 读取的是精确提炼过的事实而不是被废话包裹的文本第二每个 Agent 的输入输出都有明确的 schema可以自动校验、自动缓存、自动追溯第三token 成本可控——board 里永远只保存压缩后的精华而不是原始工具返回。我们在预研阶段对比过 LangGraph、CrewAI 这类框架也调研了各种 agent 编排方案最后没有直接用在生产上。不是说框架不好而是我们这个场景的编排逻辑其实只有三四步引入重量级框架反而要适配它的生命周期和状态管理。自研一个不到两百行的轻量编排层行为完全可控出了问题也容易排查。这一点在后面踩坑时帮了大忙——因为我们能直接看到 board 里每一步写入的内容而不是在黑盒框架里猜。3. 多 Agent 跑在生产上的真实事故与排查过程理论设计再美好上了生产该翻车还是翻车。我们多 Agent 版本灰度上线后前后出了好几起事故每一起都让我对多 Agent有了更清醒的认识。下面挑三个最有代表性的把完整排查链路写出来。3.1 事故一上下文不同步导致结论穿越现象有一次线上订单服务响应变慢告警触发后系统给出的报告结论是新发布导致超时。但值班同学核实后发现发布时间是 9:50超时高峰从 10:02 才开始两个事件根本不在一根时间轴上。报告里的因果链是错的。排查步骤先看报告明细发现 ClusterAgent 给出的环境摘要里写着10:00 检查到 3 个新 Pod 运行正常LogAgent 查到的日志里10:02 开始大量 timeout。单独看都没问题拼在一起就产生了误导。拉出编排层的执行 trace看每个 Agent 的真实执行时间。发现 ClusterAgent 先执行完并写入了 board随后 LogAgent 和 TraceAgent 并行执行。问题出在这LogAgent 的某个工具函数内部为了补充上下文顺手调用了 board.set(cluster, ...) 的通用写入接口覆盖了原本 ClusterAgent 写入的环境摘要。根因找到了SharedBoard 虽然设计了 immutable 接口但子 Agent 的工具函数里还保留着一个旧版的通用 set 方法没有完全移除。LogAgent 拿到的是一个已经被自己意外修改过的 cluster 字段时间戳还停留在它自己的执行窗口于是 AnalyzerAgent 拿到的环境状态和日志事实来自两个不同的时间切片。修复方案分两层。第一层是机制board 的字段改为写时复制线上彻底移除所有通用写入接口只允许编排层写入子 Agent 只能通过显式命名的只读接口读取每条写入自动附带不可变的时间戳。第二层是策略AnalyzerAgent 增加一道时间线校验——任何作为因果依据的观测事实必须有明确时间戳两个事实之间如果时间窗口完全不重叠不允许直接建立因果关系。这次事故让我明白多 Agent 的上下文一致性不是天然存在的。单 Agent 里模型凭借注意力机制勉强维持时间线一致多 Agent 里如果共享状态处理不严格一致性会以更隐蔽的方式崩坏——不是忘记而是被修改。3.2 事故二子 Agent 失败导致整个排查链路中断现象某次线上排查LogAgent 连续三次工具调用超时正好赶上 ES 集群在做大查询编排层直接把整个 case 标记为失败用户什么结论都没拿到。事后统计发现上线多 Agent 后case 的整体失败率从单 Agent 阶段的不足 2% 涨到了 8% 左右而且主要集中在某一个子任务不稳定的时段。排查步骤先看失败案例分布发现并不是 AnalyzerAgent 不行而是所有失败都发生在子 Agent 环节。LogAgent 最严重TraceAgent 次之。看编排层异常日志报错是工具调用的 LockTimeout 被直接抛出没有任何捕获状态机把 case 置为 failed。再看状态机设计才发现问题我们最初把排查流程设计成全有或全无——任何一个环节失败整个 case 就失败。这个设计思路是从传统接口幂等逻辑带过来的对确定性系统没问题但放到 LLM 系统上完全不适用。修复方案是给编排层加了三道保险第一道每个子 Agent 的单步工具调用加超时熔断30 秒没返回就终止该工具并让 Agent 换一条路径第二道子任务支持退避重试最多重试两次第三道也是最关键的——允许 degraded 模式。当 LogAgent 完全不可用时编排层不再让整个 case 失败而是把告警原文里自带的一段日志 snippet 传给 AnalyzerAgent同时标注日志链路异常结论置信度下降。状态机从全有或全无改成完整模式 - 降级模式 - 手工模式。这里我要强调一个在生产上悟出来的道理AI 排查的价值不是每次都必须给出完美结论而是即使拿不到完整数据也要基于现有事实给出可操作的信息并明确标注不确定性。讽刺的是单 Agent 天然具备这个能力——模型可以在工具全挂的情况下自己总结已有信息收尾而多 Agent 如果编排层不做降级设计反而把这个能力丢掉了。多 Agent 确实更专业但更专业的前提是所有零件都正常工作生产系统里这个前提永远不成立。3.3 事故三token 成本翻了三倍响应却更慢了现象灰度两周后我们拉成本账单发现同样一个 case多 Agent 平均消耗 token 从单 Agent 的 150k 涨到了 400kP95 响应时间从 40 秒涨到 90 秒。花了更多钱速度还更慢了这肯定是哪里设计出了差错。逐项分析后找到三个原因第一上下文重复传递。ClusterAgent 已经把 Pod 状态提炼成 800 字的 JSON 摘要了但编排层传给 LogAgent 和 TraceAgent 时把原始告警事件 payload 完整塞了一遍里面有大量无用字段。第二并行 Agent 的过度输出。LogAgent 和 TraceAgent 并行执行本身没问题但各自吐出了很长的中间分析两个加起来四五千字AnalyzerAgent 根本不需要这么多信息。第三个原因比较隐蔽——我们在每个子 Agent 的 prompt 里都写了请先逐步分析再给出结论这句通用指令结果每个 Agent 都把它那种专业领域的推理过程完整写了出来这一步本来可以用更短的内部推理完成。优化方案也是三个方向Board 里保存的是提炼视图而不是完整输出。子 Agent 的原始输出先经过一个轻量的裁剪器只保留关键字段和 topN 结果AnalyzerAgent 只能看到摘要级信息。每个子 Agent 的输出模板改为严格的 schema 约束明确写到输出不超过 800 字只包含必要字段字段级限制长度。并行 Agent 的中间结果在进入 board 前由编排层做一次非 LLM 的字段过滤直接在代码层裁剪不经过大模型零成本。做完这三项单 case token 从 400k 降回 220kP95 响应时间降回 55 秒左右。后来把模型升级到指令遵循能力更强的新版本又降了 25%。这次事故给我的感受是多 Agent 不是多花钱买质量的必然逻辑。如果编排层设计粗糙花出去的钱一大半买的是熵——重复上下文、冗长输出、无效推理。单 Agent 的成本模型很透明多 Agent 的成本需要从设计层就精打细算否则越高配越亏。另外还有一个容易被忽略的问题可解释性。单 Agent 只有一个完整 transcript虽然长但你至少能从头翻到尾。多 Agent 天然把链路切成一段段如果从一开始不把每个子 Agent 的输入输出记录到 trace 里等用户问你这个结论怎么来的时你根本答不上来。这逼着我们把可观测性当成第一优先级来建设。4. 多 Agent 与单 Agent 的选型决策框架4.1 一张表看透核心差异经历了这几轮事故后我们内部把两套方案的差异整理成一张对照表每次新项目评审直接拿这张表说话维度单 Agent多 Agent上下文一致性全程共享天然一致但长上下文会遗忘需人工设计共享状态做好是不可变结构化做不好就是时间线错乱专业知识隔离所有领域的 prompt 混在一起互相干扰每个专业域独立 prompt互不污染并行能力弱本质串行决策可并行拉取多个独立数据源缩短部分耗时故障容忍度模型可以根据已有信息绕路收尾子任务失败可能拖垮全链路必须做降级设计Token 成本较低模型自己控制节奏通常高 2-3 倍需要强烈的输出长度约束可解释性一个完整 transcript勉强能追溯需要额外建设 trace 基建否则不可解释决策质量工具多时工具超过 15 个明显下滑分域决策更稳但编排层本身会引入新误差调优与维护维护一个 prompt维护多套 prompt、多套评估集、一套编排层上线周期最快一两周可跑通至少翻倍需要额外投入架构设计这张表最核心的一个观察是多 Agent 的优势集中在专业隔离和并行能力但付出的代价是一致性、可观测性、成本、维护四个方面的复杂度同时上升。你做技术选型时本质上就是在权衡这两组得失。4.2 我现在的判断标准五个问题定方案经历过单 Agent 和多 Agent 的完整对比后我现在接手一个新项目会按照下面五个问题来决策而不是先入为主地选一个问题一这个任务能不能被清晰地切成专业子域如果子域之间互相强烈依赖比如分析这段代码的逻辑正确性这种整体性任务硬拆成多 Agent 反而会破坏全局视野。能切分且子域相对独立才是多 Agent 的基础。问题二工具数量是否超过了 15 个工具越多单 Agent 的选择准确率下降越明显。工具在 10 个以内且边界清晰单 Agent 完全够用。超过 15 个且工具分属不同专业领域才值得拆。问题三是否需要并行拉取多个独立数据源如果诊断链路天然是串行的先 A 后 BB 依赖 A那并行优势发挥不出来多 Agent 只增加成本。只有存在多个同时进行的独立子任务时拆开才有意义。问题四允许部分结论 不确定性标注吗如果甲方或业务方要求每次都必须给出完整结论你只能选单 Agent 这种靠模型硬兜底的模式或者花大力气把多 Agent 的降级机制做得非常完善。我们生产环境接受降级所以多 Agent 才可能跑起来。问题五团队有没有精力维护多套系统多 Agent 意味着每个子 Agent 都要单独调 prompt、单独建评估集、单独监控效果还要维护编排层。团队只有一个人建议老老实实单 Agent 起步把效果跑出来再演进。最后再补充一种被很多人忽略的中间形态单 Agent 子专家工具。也就是主 Agent 仍然只有一个但某些工具函数内部封装了微型子 Agent 的逻辑。比如分析日志异常模式这个工具内部其实是一个针对日志分析优化的 LLM 调用它对主 Agent 暴露的是一个干净的输入输出接口。这种方案兼顾了可编排性和实现简单度也是我现在最推荐起步的架构。我们后续计划把排查系统逐步收敛成这种形态。5. 如果从头再来一套更稳的 Agent 排查系统设计清单5.1 统一 schema 比自然语言协作重要一百倍多 Agent 系统里最容易犯的错误是让子 Agent 用自然语言向其他 Agent汇报。我也见过一些 Agent 框架把子 Agent 的输出设计成人话文本看起来友好实则灾难——下一层 Agent 每次都要从一段话里自己提取关键信息提取错了还没法校验。正确做法是给每个 Agent 定义严格的输入输出契约。以我们的 AnalyzerAgent 为例输出 schema 长这样{ report_id: case-20250126-001, root_cause_hypothesis: { title: 最近发布导致连接池耗尽, confidence: 0.72, evidence: [ { fact: 10:02 开始 error_rate 从 0.1% 升至 12%, source: metric, timestamp: 2025-01-26T10:05:00Z }, { fact: 发布窗口 09:48-09:52新版本连接池上限由 200 降至 50, source: deployment, timestamp: 2025-01-26T09:52:00Z } ] }, alternative_hypotheses: [], suggested_actions: [扩容连接池并回滚新版本], uncertainty_notes: 日志 Agent 降级部分证据缺失 }统一 schema 带来的收益是实打实的可以自动校验字段合法性和时间戳逻辑可以在每一步做自动缓存同样的 case 第二次跑直接复用中间结果更重要的是评估集可以自动化跑——我们把历史告警事故整理成几百个带标准答案的 case直接比较输出 JSON 和答案的匹配度而不是靠人眼读大模型生成的报告。没有统一 schema这套评估机制根本建不起来。5.2 可观测性设计让 Agent 行为可追踪排查系统本身就是一个可观测性产品结果我们自己的 Agent 系统在一开始反而没有可观测性。出了事故需要靠重新跑一遍看看会不会复现来定位问题这在多 Agent 阶段是完全不可接受的。后来我们把每个 case 从接收告警到输出报告的完整生命周期都串联到 trace 里用 case_id 贯穿。每个子 Agent 的输入、输出、每个工具调用的参数和返回摘要都作为一个 span 写入可观测性后端类似这样with tracer.start_as_current_span(agent.step) as span: span.set_attribute(agent.name, LogAgent) span.set_attribute(step.id, step_id) span.set_attribute(input_schema_version, v3) tool_result await run_tool(call.name, call.arguments) span.set_attribute(tool.result_summary, summarize(tool_result)) span.set_attribute(output.fields, list(call.output.keys()))这套链路跑通后复盘一个坏 case 就变成了纯粹的工程问题拉出这条 trace看到底是哪个 Agent 收到了什么输入、做了什么调用、产出了什么字段。大多数问题一眼就能定位。这也是我们后来敢把 Agent 系统做到生产级的基础没有可观测性多 Agent 就是一个大型不可调试黑盒上线越久越可怕。5.3 兜底降级多 Agent 失败时退回单 Agent最后一条经验也最实用多 Agent 系统必须保留一条单 Agent 逃生通道。我们生产上维护着一套精简版单 Agent的 prompt 和工具集比完整版弱一些但不依赖编排层。当多 Agent 链路连续失败达到阈值或者编排服务自身出现异常时流量自动切换到单 Agent 路径。用户拿到的结论可能没有那么精细但至少系统可用。状态机设计也很简单full mode多 Agent 全量- degraded mode单 Agent 精简- manual mode人工介入。这条逃生通道的价值在线上体现得极其明显。有一次编排服务因为发布配置错误挂了 20 分钟这 20 分钟里所有排查 case 全部走了单 Agent 路径用户侧几乎没有感知。我们统计过在加了降级机制后系统整体可用性从 99% 提升到了 99.9% 以上。做 Agent 系统的人常常把注意力都放在让 AI 变聪明上但在生产环境里决定用户体验的往往是这些不起眼的兜底设计。最后分享一点我现阶段的心得。多 Agent 不是银弹它的真正价值不在多个角色分工协作这个听起来很酷的比喻里而在于把一个又长又混杂的多上下文决策拆成多个短上下文、单一领域的决策并且强制每一步产出结构化结果。能做到这一点单 Agent 也能做得很好做不到这一点拆十个 Agent 只会把混乱放大十倍。就像我们这套排查系统最终并不是多好 vs 单好的胜负而是找到了一种混合形态核心编排保留单 Agent 的简洁可靠分析环节借用了多 Agent 的专业隔离降级时又能退回最朴素的模式。如果你也在做 Agent 项目建议不要被架构名词带着走多想想你的真实任务有没有清晰边界、能不能接受部分结果、有没有精力维护复杂度这三个问题答案也就清楚了。我后续可以再把每个 Agent 的 prompt 模板、编排层完整状态机、以及评估集的搭建方法单独拆出来写评论区见。