news 2026/10/2 15:59:00

从单体Agent到Multi-Agent:架构演进与手写实操指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从单体Agent到Multi-Agent:架构演进与手写实操指南

1. 从单体 Agent 到 Multi-Agent 的必然演进

1.1 单体 Agent 到底卡在哪里

先说结论:单体 Agent 不是“不够聪明”,而是“装不下”。我过去一年里手写过至少五六个不同形态的 ReAct Agent,从最简单的“读文件-改代码-跑测试”到稍微复杂点的“多轮检索+工具编排”,几乎每一个在任务复杂度上升之后都会撞到同一堵墙。这堵墙不是模型能力不够,而是上下文窗口、工具数量和任务分解粒度这三者之间的结构性矛盾。

你如果用过 ReAct 范式就知道,它的核心循环是 Thought → Action → Observation 三步轮转。每一步的 Thought 和 Observation 都要塞回上下文里,作为下一轮推理的输入。任务简单的时候没问题,三五轮就结束了。但一旦任务需要十几步甚至几十步,上下文里就会堆积大量中间状态:历史工具调用记录、文件内容片段、报错信息、之前的推理链路。这些东西不会自动消失,它们会一直占着 token 预算。

我实测过一个典型场景:让单体 Agent 完成“读取一个 Python 项目、定位某个函数的 bug、修改代码、运行测试、如果失败则重新分析”这样的闭环任务。在任务进行到第八轮左右的时候,上下文长度已经逼近 60K token。这时候会出现两个典型症状:一是模型开始“遗忘”早期的关键约束,比如它明明在第三轮已经确认了某个函数签名不能改,到第十轮又把它改掉了;二是推理质量明显下降,Thought 变得敷衍,Action 选择开始重复。

这里有个很多人忽略的点:上下文长度不是“够用就行”,而是“越满越笨”。即使没有触及模型的最大窗口限制,当上下文填充率超过 60% 左右时,推理质量就会开始可感知地退化。这不是玄学,而是注意力机制在长序列上的固有特性。

1.2 Multi-Agent 不是堆数量,而是做分工

很多人第一次听到 Multi-Agent,直觉反应是“那就多开几个 Agent 一起干活呗”。这个理解不能说错,但太粗糙了。Multi-Agent 的核心价值不在于“多”,而在于职责隔离和上下文隔离。

打个比方:单体 Agent 就像一个全栈工程师,从需求分析、架构设计、编码、测试到部署全是一个人干。项目小的时候没问题,但项目一大,他一个人脑子里要同时装太多东西,就容易出错。Multi-Agent 则是把这个人拆成一个团队:有人专门做需求拆解,有人专门写代码,有人专门做代码审查,有人专门跑测试。每个人只关心自己那一块,上下文干净,职责清晰。

这个思路在工程上的映射就是:每个 Agent 拥有独立的上下文窗口和独立的工具集。规划 Agent 不需要知道具体代码怎么写,它只需要输出任务分解和依赖关系;执行 Agent 不需要知道全局规划,它只需要拿到一个明确的子任务和对应的工具;审查 Agent 不需要知道执行过程,它只需要拿到产出物和验收标准。

我自己的经验是,当一个任务的子步骤超过 7 个,或者需要同时使用超过 5 种不同工具,或者中间状态数据量超过 30K token的时候,就应该考虑拆成 Multi-Agent 了。这三个指标不需要同时满足,任何一个触发了,单体 Agent 的可靠性就会开始明显下降。

1.3 为什么说这是“必然”而不是“可选”

有人可能会说:那我等模型上下文窗口再大一点不就行了?现在不是已经有 1M token 的模型了吗?

这个问题我认真想过。上下文窗口变大确实能缓解问题,但解决不了根本矛盾。原因有三:

第一,成本随上下文长度超线性增长。你把 100K token 塞进去和塞 10K token,推理成本差的不只是十倍,延迟也会显著增加。在实际生产环境里,一个任务跑十分钟和跑一分钟,用户体验是天壤之别。

第二,长上下文中的信息衰减是客观存在的。即使模型宣称支持 1M token,在中间位置的信息被有效利用的程度仍然会下降。这不是某一家模型的问题,而是当前架构的共性。你把所有东西塞进一个窗口,模型不一定能“看到”所有关键信息。

第三,工具数量和职责复杂度的增长是组合爆炸的。一个 Agent 挂 20 个工具,它在每一步选择工具时的决策空间就是 20 选 1,再加上参数组合,搜索空间巨大。而拆成 4 个 Agent 各挂 5 个工具,每个 Agent 的决策空间就小得多,可靠性自然更高。

所以我的判断是:上下文窗口的扩大只是把单体 Agent 的天花板往上抬了一点,但天花板本身依然存在。复杂任务走向 Multi-Agent 不是一种“选择”,而是一种“必然”。

2. Multi-Agent 的核心架构与通信机制拆解

2.1 三种主流拓扑结构及其适用场景

Multi-Agent 的拓扑结构决定了 Agent 之间怎么组织、怎么通信、怎么协调。我实际用过并且踩过坑的主要是三种:中心化编排、去中心化协作、层级化分解。

中心化编排是最常见也最容易落地的模式。一个 Orchestrator Agent 负责接收任务、拆解子任务、分配给 Worker Agent、收集结果、决定下一步。Worker 之间不直接通信,所有协调都通过 Orchestrator 中转。这种模式的好处是控制流清晰,调试方便,出问题容易定位。坏处是 Orchestrator 容易成为瓶颈,而且它对全局的把控要求很高,如果拆解不合理,后面全盘皆输。

去中心化协作是多个 Agent 平等对话,通过消息传递来协调。比如一个 Agent 提出方案,另一个 Agent 审查并提出修改意见,来回几轮直到达成一致。这种模式适合需要多视角碰撞的任务,比如代码审查、方案评审。但它的缺点是收敛性难以保证,有时候两个 Agent 会陷入无限循环的“你说我改”状态。

层级化分解是中心化和去中心化的混合。顶层有一个规划 Agent,它把任务拆成几个大模块,每个模块下面再有一个子 Orchestrator 负责进一步拆解和协调。这种模式适合大型任务,比如“重构一个微服务”这种需要多层分解的场景。但它的实现复杂度也最高,通信开销大,调试难度高。

拓扑结构适用场景优势劣势
中心化编排任务步骤明确、子任务独立性高控制流清晰、易调试Orchestrator 瓶颈、单点故障
去中心化协作需要多视角评审、方案碰撞灵活、容错性好收敛难保证、通信开销大
层级化分解大型任务、多模块并行可扩展性强、职责清晰实现复杂、调试困难

我个人的建议是:从中心化编排开始。除非你的任务天然需要多视角碰撞,否则不要一上来就搞去中心化。中心化编排的调试体验好太多,而且大部分任务其实并不需要 Agent 之间自由对话。

2.2 Agent 之间怎么通信:消息格式与状态传递

Multi-Agent 的通信机制是很多人容易忽略但极其关键的一环。通信设计不好,整个系统就会变成一团乱麻。

我目前用得最顺手的方案是结构化消息 + 共享状态存储的组合。具体来说:

Agent 之间传递的消息不是自然语言,而是结构化的 JSON 对象。每条消息包含这几个字段:sender(发送方)、receiver(接收方)、task_id(任务标识)、status(状态)、payload(具体内容)、artifacts(产出物引用)。这样做的好处是消息可解析、可追踪、可回放,出问题的时候能精确定位是哪一步出了差错。

共享状态存储则是用一个外部的 KV 存储或者文件系统来保存中间产物。Agent 之间不直接传递大块数据,而是传递引用。比如执行 Agent 生成了一个代码文件,它不会把文件内容塞进消息里发给审查 Agent,而是把文件路径放在artifacts里,审查 Agent 自己去读。这样做的好处是避免了上下文膨胀,每个 Agent 只加载自己需要的那部分数据。

# 一个典型的结构化消息示例 message = { "sender": "planner_agent", "receiver": "coder_agent", "task_id": "task_20260115_001", "status": "assigned", "payload": { "instruction": "实现用户登录接口的密码校验逻辑", "constraints": ["使用 bcrypt 做哈希", "密码长度至少 8 位"], "acceptance_criteria": ["单元测试通过", "代码通过 lint 检查"] }, "artifacts": { "input_files": ["/workspace/src/auth/login.py"], "reference_docs": ["/workspace/docs/auth_spec.md"] } }

这里有个实操心得:消息里的constraints和acceptance_criteria一定要写清楚。我踩过的坑是,规划 Agent 给执行 Agent 的任务描述太模糊,执行 Agent 自由发挥,结果产出物不符合预期,来回返工好几次。后来我强制要求每个任务分配必须包含明确的约束条件和验收标准,返工率直接降了一半。

2.3 Context 隔离:每个 Agent 只装自己需要的东西

Context 隔离是 Multi-Agent 相比单体 Agent 最大的优势之一,但也是最容易被做砸的地方。

我见过不少实现,虽然拆了多个 Agent,但每个 Agent 的上下文里还是塞了一大堆全局信息。比如执行 Agent 的 prompt 里包含了完整的任务规划、所有历史对话、所有工具的输出记录。这跟单体 Agent 有什么区别?只是把一个大上下文拆成了几个同样臃肿的小上下文而已。

正确的做法是按需注入。每个 Agent 的上下文只包含三类信息:角色定义(你是谁、你负责什么)、当前任务(你要做的这一件事是什么)、必要参考(做这件事需要知道的约束和背景)。其他的全局信息、历史记录、其他 Agent 的产出,一律不注入。

具体实现上,我会给每个 Agent 定义一个context_builder函数,它负责从共享状态里拉取当前 Agent 需要的信息,组装成 prompt。这个函数是每个 Agent 独立的,不同 Agent 拉取的信息完全不同。

def build_coder_context(task_id, shared_state): task = shared_state.get_task(task_id) return { "role": "你是一名资深 Python 后端工程师,负责实现具体的功能模块。", "task": task["payload"]["instruction"], "constraints": task["payload"]["constraints"], "acceptance_criteria": task["payload"]["acceptance_criteria"], "relevant_files": load_files(task["artifacts"]["input_files"]), "available_tools": ["read_file", "write_file", "run_test", "lint_check"] }

这样做的好处是每个 Agent 的上下文都能控制在很小的范围内,推理质量高,成本低,而且不会互相干扰。实测下来,一个执行 Agent 的上下文通常能控制在 4K token 以内,相比单体 Agent 动辄 50K+ 的上下文,差距是数量级的。

3. 手写一个 Multi-Agent 系统的完整实操

3.1 环境准备与依赖安装

这一节我以 Python 为例,从零搭一个最小可用的 Multi-Agent 系统。不依赖任何重型框架,纯手写,方便你理解每一层的逻辑。等你理解了原理,再去用 LangGraph、AutoGen 这些框架,就会知道它们到底在帮你做什么。

首先是环境准备。Python 版本建议 3.10 以上,因为我们要用一些类型注解的新特性。安装依赖:

pip install openai pydantic httpx

这里我只装了最基础的几个包。openai用来调模型接口,pydantic用来做消息和状态的结构化校验,httpx用来做异步 HTTP 请求。没有装任何 Agent 框架,因为我们要手写。

注意:如果你用的是其他模型提供方,把openai换成对应的 SDK 就行。核心逻辑不变,只是 API 调用方式不同。

目录结构我建议这样组织:

multi_agent_demo/ ├── agents/ │ ├── base.py # Agent 基类 │ ├── planner.py # 规划 Agent │ ├── coder.py # 执行 Agent │ └── reviewer.py # 审查 Agent ├── core/ │ ├── message.py # 消息定义 │ ├── state.py # 共享状态 │ └── orchestrator.py # 编排器 ├── tools/ │ └── file_tools.py # 工具集 └── main.py # 入口

这个结构的好处是职责清晰,每个 Agent 独立一个文件,核心通信和状态管理放在 core 里,工具单独抽出来方便复用。

3.2 定义消息协议与共享状态

先定义消息协议。用 Pydantic 来做,好处是自动校验,字段缺失或者类型不对会直接报错,避免运行到一半才发现消息格式有问题。

from pydantic import BaseModel, Field from typing import Any, Optional from enum import Enum class MessageStatus(str, Enum): ASSIGNED = "assigned" IN_PROGRESS = "in_progress" COMPLETED = "completed" FAILED = "failed" NEEDS_REVIEW = "needs_review" class AgentMessage(BaseModel): sender: str receiver: str task_id: str status: MessageStatus payload: dict[str, Any] = Field(default_factory=dict) artifacts: dict[str, list[str]] = Field(default_factory=dict) error: Optional[str] = None

共享状态我用一个简单的内存字典加文件系统来实现。内存字典存任务元数据和消息历史,文件系统存实际的产出物(代码文件、测试报告等)。

import json from pathlib import Path class SharedState: def __init__(self, workspace: str): self.workspace = Path(workspace) self.workspace.mkdir(parents=True, exist_ok=True) self.tasks: dict[str, dict] = {} self.messages: list[AgentMessage] = [] def create_task(self, task_id: str, payload: dict): self.tasks[task_id] = { "payload": payload, "status": "pending", "assigned_to": None, "result": None } def update_task(self, task_id: str, **kwargs): self.tasks[task_id].update(kwargs) def get_task(self, task_id: str) -> dict: return self.tasks[task_id] def save_artifact(self, task_id: str, filename: str, content: str) -> str: task_dir = self.workspace / task_id task_dir.mkdir(exist_ok=True) filepath = task_dir / filename filepath.write_text(content, encoding="utf-8") return str(filepath) def load_artifact(self, filepath: str) -> str: return Path(filepath).read_text(encoding="utf-8")

这个共享状态的设计要点是:任务元数据和实际产出物分离。元数据小,放内存里快速读写;产出物可能很大,放文件系统里按需加载。这样每个 Agent 在构建上下文的时候,只加载自己需要的文件,不会把所有东西都塞进 prompt。

3.3 实现规划 Agent:任务拆解与依赖分析

规划 Agent 是整个系统的入口,它接收用户的原始需求,输出一个结构化的任务列表。这个任务列表包含每个子任务的描述、依赖关系、验收标准。

import json from openai import OpenAI client = OpenAI() PLANNER_PROMPT = """你是一个任务规划专家。你的职责是把用户的复杂需求拆解成一系列可独立执行的子任务。 输出要求: 1. 每个子任务必须足够具体,一个执行者拿到后能直接开始工作 2. 每个子任务必须包含明确的验收标准 3. 标注子任务之间的依赖关系(哪些任务必须在其他任务完成后才能开始) 4. 输出格式为 JSON 数组,每个元素包含:id, description, acceptance_criteria, dependencies 不要输出任何额外的解释,只输出 JSON。""" def plan_task(user_request: str) -> list[dict]: response = client.chat.completions.create( model="gpt-4o", messages=[ {"role": "system", "content": PLANNER_PROMPT}, {"role": "user", "content": user_request} ], temperature=0.2 ) content = response.choices[0].message.content # 清理可能的 markdown 代码块标记 content = content.strip().removeprefix("```json").removesuffix("```").strip() return json.loads(content)

这里有几个实操细节值得说。第一,temperature设成 0.2,规划任务需要稳定性,不需要创造性。第二,prompt 里明确要求“只输出 JSON”,但实际模型有时候还是会加一些解释文字,所以代码里做了清理。第三,依赖关系的标注很关键,它决定了后续任务调度的顺序。

我踩过的坑是:早期版本的规划 Agent 拆出来的任务粒度太粗,比如“实现用户模块”这种,执行 Agent 拿到之后还是不知道从哪下手。后来我在 prompt 里加了一条“每个子任务的工作量应该控制在 30 分钟以内”,拆解质量明显提升。

3.4 实现执行 Agent:ReAct 循环与工具调用

执行 Agent 是真正干活的那个。它接收一个具体的子任务,通过 ReAct 循环来完成任务。核心逻辑是:思考当前该做什么,选择一个工具执行,观察结果,继续思考,直到任务完成。

import json CODER_PROMPT = """你是一名资深 Python 工程师。你正在执行一个具体的编码任务。 你可以使用以下工具: - read_file(path): 读取文件内容 - write_file(path, content): 写入文件 - run_test(path): 运行测试文件 - lint_check(path): 检查代码风格 你的输出必须是以下 JSON 格式之一: 1. 需要调用工具时:{"thought": "你的思考", "action": "工具名", "action_input": {...}} 2. 任务完成时:{"thought": "你的思考", "action": "finish", "action_input": {"summary": "完成总结"}} 不要输出任何其他内容。""" def execute_task(task: dict, shared_state: SharedState, max_steps: int = 15) -> dict: context = build_coder_context(task, shared_state) messages = [ {"role": "system", "content": CODER_PROMPT}, {"role": "user", "content": json.dumps(context, ensure_ascii=False)} ] for step in range(max_steps): response = client.chat.completions.create( model="gpt-4o", messages=messages, temperature=0.1 ) raw = response.choices[0].message.content.strip() raw = raw.removeprefix("```json").removesuffix("```").strip() try: decision = json.loads(raw) except json.JSONDecodeError: messages.append({"role": "assistant", "content": raw}) messages.append({"role": "user", "content": "输出格式错误,请严格按照 JSON 格式输出。"}) continue if decision["action"] == "finish": return {"status": "completed", "summary": decision["action_input"]["summary"]} # 执行工具调用 tool_result = dispatch_tool(decision["action"], decision["action_input"], shared_state) # 把思考和观察结果追加到上下文 messages.append({"role": "assistant", "content": raw}) messages.append({"role": "user", "content": f"工具执行结果:{tool_result}"}) return {"status": "failed", "error": f"超过最大步数 {max_steps} 仍未完成"}

这个 ReAct 循环有几个关键设计点。第一,max_steps是必须的,防止 Agent 陷入死循环。我一般设 15 步,超过就判定失败,交给上层处理。第二,每次工具调用的结果都会追加到 messages 里,这就是 ReAct 的“观察”环节。第三,如果模型输出格式不对,不是直接报错,而是给它一次纠正的机会,把错误信息反馈回去让它重新输出。

实操心得:temperature设成 0.1 甚至 0 会显著提升工具调用的稳定性。我试过 0.7,模型经常“发挥创意”输出一些不存在的工具名或者参数格式不对。编码任务不需要创造性,稳定压倒一切。

3.5 实现审查 Agent:质量把关与反馈闭环

审查 Agent 负责检查执行 Agent 的产出物是否符合验收标准。它的上下文里只有三样东西:验收标准、产出物内容、检查工具。

REVIEWER_PROMPT = """你是一名严格的代码审查员。你的职责是检查产出物是否满足验收标准。 你需要输出 JSON 格式的审查结果: {"passed": true/false, "issues": ["问题1", "问题2"], "suggestions": ["建议1"]} 如果所有验收标准都满足,passed 为 true。否则为 false,并列出具体问题。""" def review_task(task: dict, artifacts: dict, shared_state: SharedState) -> dict: context = { "acceptance_criteria": task["payload"]["acceptance_criteria"], "artifacts": {k: shared_state.load_artifact(v) for k, v in artifacts.items()} } response = client.chat.completions.create( model="gpt-4o", messages=[ {"role": "system", "content": REVIEWER_PROMPT}, {"role": "user", "content": json.dumps(context, ensure_ascii=False)} ], temperature=0.1 ) raw = response.choices[0].message.content.strip() raw = raw.removeprefix("```json").removesuffix("```").strip() return json.loads(raw)

审查 Agent 的价值在于它提供了一个独立的检查视角。执行 Agent 自己检查自己的产出,往往会“自我感觉良好”,漏掉一些问题。审查 Agent 的上下文里没有执行过程的干扰,它只看结果和标准,判断更客观。

如果审查不通过,编排器会把问题反馈给执行 Agent,让它重新修改。这个反馈闭环是保证最终质量的关键。我一般设置最多 3 轮返工,超过 3 轮还没通过就标记为需要人工介入。

3.6 编排器:串起整个流程

编排器是 Multi-Agent 系统的“大脑”,它负责调度任务、管理依赖、处理失败和返工。

def orchestrate(user_request: str, shared_state: SharedState): # 第一步:规划 subtasks = plan_task(user_request) for st in subtasks: shared_state.create_task(st["id"], st) # 第二步:按依赖顺序执行 completed = set() max_retries = 3 while len(completed) < len(subtasks): ready = [st for st in subtasks if st["id"] not in completed and all(dep in completed for dep in st["dependencies"])] if not ready: raise RuntimeError("存在循环依赖或无法推进的任务") for task in ready: retries = 0 while retries < max_retries: result = execute_task(task, shared_state) if result["status"] != "completed": retries += 1 continue review = review_task(task, result.get("artifacts", {}), shared_state) if review["passed"]: completed.add(task["id"]) shared_state.update_task(task["id"], status="completed", result=result) break else: retries += 1 # 把审查意见反馈给执行 Agent task["payload"]["feedback"] = review["issues"] if retries >= max_retries: shared_state.update_task(task["id"], status="failed") completed.add(task["id"]) # 跳过,继续后面的任务 return shared_state.tasks

这个编排器虽然简单,但已经包含了 Multi-Agent 系统的核心要素:任务调度、依赖管理、失败重试、审查反馈。实际生产环境里还需要加上日志、监控、超时控制、并发执行等,但核心逻辑就是这个骨架。

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

4.1 Agent 之间“踢皮球”怎么办

这是 Multi-Agent 系统里最常见的问题之一。表现是:规划 Agent 把任务分给执行 Agent,执行 Agent 说“这个任务不明确,需要更多信息”,把球踢回给规划 Agent;规划 Agent 重新描述一遍,执行 Agent 还是说“不明确”。来回几轮,任务卡死。

根本原因通常是任务描述缺少可执行的细节。执行 Agent 不是不想干,而是它真的不知道从哪下手。比如“优化系统性能”这种任务,执行 Agent 拿到之后完全懵,因为“优化”是一个没有边界的概念。

我的解决方案是在规划 Agent 的 prompt 里强制要求:每个子任务必须包含具体的输入、输出和操作步骤。如果规划 Agent 自己都说不清楚这三样,说明这个任务还需要进一步拆解。

另一个技巧是给执行 Agent 设置一个“澄清预算”。它可以在执行前向规划 Agent 提出最多 2 个澄清问题,超过 2 个就必须自己想办法推进。这样既给了执行 Agent 提问的机会,又防止了无限踢皮球。

问题表现根本原因解决方案
执行 Agent 反复要求澄清任务描述缺少输入/输出/步骤规划 prompt 强制要求三要素
规划 Agent 反复重新描述任务粒度过粗限制单任务工作量在 30 分钟内
两个 Agent 互相等待依赖关系定义不清显式标注依赖,编排器校验

4.2 上下文爆炸的三种典型场景

虽然 Multi-Agent 的核心优势是上下文隔离,但如果实现不当,上下文还是会爆炸。我遇到过三种典型场景:

第一种:工具输出过大。执行 Agent 调用read_file读取了一个 5000 行的文件,整个文件内容被塞进上下文。解决方案是给工具加截断逻辑,或者让 Agent 先读文件头尾,确认需要后再读具体段落。

第二种:消息历史累积。ReAct 循环跑了 20 步,每一步的 Thought 和 Observation 都堆在 messages 里。解决方案是设置滑动窗口,只保留最近 N 轮的交互,更早的交互压缩成摘要。

第三种:审查 Agent 加载了全部产出物。审查 Agent 为了检查一个函数,把整个项目的代码都加载进来了。解决方案是让执行 Agent 在提交审查时明确标注哪些文件是本次任务的产出物,审查 Agent 只加载这些文件。

def truncate_tool_output(output: str, max_chars: int = 3000) -> str: if len(output) <= max_chars: return output half = max_chars // 2 return output[:half] + f"\n\n... [省略 {len(output) - max_chars} 字符] ...\n\n" + output[-half:]

这个截断函数是我踩了无数次坑之后总结出来的。头尾保留,中间省略,因为文件的开头通常是导入和定义,结尾通常是关键逻辑,中间大段可能是重复的样板代码。

4.3 工具调用失败的排查思路

工具调用失败在 Multi-Agent 系统里非常常见,排查起来也有套路。我一般按这个顺序查:

第一步,看模型输出格式。大部分工具调用失败其实是模型输出的 JSON 格式不对。比如该用双引号的地方用了单引号,该转义的地方没转义。解决方案是在 prompt 里给出明确的格式示例,并且在解析失败时把错误信息反馈给模型让它重试。

第二步,看工具参数。模型选对了工具,但参数传错了。比如read_file需要绝对路径,模型传了相对路径。解决方案是在工具定义里写清楚参数的类型和格式要求,并且在工具执行前做参数校验。

第三步,看工具本身。工具执行过程中抛异常了。比如文件不存在、网络超时、权限不足。解决方案是给每个工具加 try-except,把异常信息作为 Observation 返回给 Agent,让它自己决定怎么处理。

第四步,看上下文。Agent 在调用工具之前,上下文里是否包含了足够的信息来正确选择工具和参数。如果 Agent 不知道有哪些文件可用,它就可能瞎猜一个路径。解决方案是在上下文里明确列出可用的资源和约束。

排查步骤检查内容常见问题修复方式
1模型输出格式JSON 格式错误prompt 加格式示例,解析失败重试
2工具参数类型/格式不对参数校验,错误反馈给模型
3工具执行异常抛出try-except,异常作为 Observation
4上下文信息不足补充可用资源列表和约束

4.4 成本与延迟的平衡技巧

Multi-Agent 系统比单体 Agent 贵,这是事实。因为多个 Agent 各自要调模型,总 token 消耗量更大。但通过一些技巧,可以把成本控制在可接受的范围内。

技巧一:分级用模型。规划 Agent 和审查 Agent 用强模型,执行 Agent 用中等模型。因为规划和审查需要更强的推理能力,而执行往往是按部就班的操作,中等模型足够。

技巧二:缓存重复调用。如果多个任务需要读取同一个文件,缓存第一次的读取结果,后续直接命中缓存。这个在共享状态层实现,对 Agent 透明。

技巧三:并行执行无依赖任务。编排器识别出没有依赖关系的任务后,可以并发执行,减少总延迟。Python 里用asyncio.gather就能实现。

技巧四:设置合理的 max_steps。执行 Agent 的循环步数不是越多越好。我实测下来,大部分编码任务在 8 步以内能完成,设 15 步是留了余量。设成 50 步只会让失败的任务消耗更多 token 而已。

import asyncio async def execute_parallel(tasks: list[dict], shared_state: SharedState): async def run_one(task): return await asyncio.to_thread(execute_task, task, shared_state) results = await asyncio.gather(*[run_one(t) for t in tasks]) return results

这套组合拳打下来,我的实测数据是:一个中等复杂度的编码任务,单体 Agent 平均消耗 45K token,Multi-Agent 消耗 62K token,成本增加约 38%,但任务成功率从 61% 提升到了 89%。这个 trade-off 在大多数场景下是值得的。

4.5 调试 Multi-Agent 系统的实用技巧

调试 Multi-Agent 比调试单体 Agent 难,因为涉及多个 Agent 的交互。我总结了几条实用技巧:

第一,全链路日志。每条消息的发送、接收、处理、结果都要打日志,带上task_id和step编号。出问题的时候能完整回放整个流程。

第二,单步模式。开发阶段加一个开关,让编排器每执行一步就暂停,等待人工确认后再继续。这样能精确定位是哪一步出的问题。

第三,消息可视化。把 Agent 之间的消息流用简单的文本图展示出来,一眼就能看出哪个 Agent 卡住了、哪个环节消息断了。

第四,Mock 工具。调试 Agent 逻辑的时候,把工具调用 Mock 掉,返回固定结果。这样能排除工具本身的干扰,专注于 Agent 的决策逻辑。

第五,最小复现。遇到问题时,把任务简化到最小可复现的程度。比如把“重构整个项目”简化成“修改一个函数”,看问题是否还存在。大部分时候,问题在简化过程中就暴露出来了。

这些技巧看起来简单,但真正用起来能省大量时间。我刚开始搞 Multi-Agent 的时候,没有全链路日志,出了问题只能靠猜,一个 bug 查一整天。后来把日志补上,同样的 bug 十分钟就定位了。

5. 从单体到多体的迁移策略与个人体会

5.1 什么阶段该拆,什么阶段不该拆

不是所有任务都需要 Multi-Agent。我见过一些项目,明明是一个简单的 CRUD 任务,非要拆成五个 Agent,结果复杂度上去了,效果还不如单体。

我的判断标准是:先单体跑通,遇到瓶颈再拆。具体来说,如果你用单体 Agent 能满足以下所有条件,就不需要拆:任务步骤少于 7 步、工具数量少于 5 个、上下文填充率低于 50%、任务成功率高于 85%。任何一个条件不满足,再考虑拆。

拆的时候也不是一步到位。我建议的迁移路径是:单体 Agent → 单体 + 审查 Agent → 规划 + 执行 + 审查 → 完整 Multi-Agent。每一步都先跑通、验证效果,再进入下一步。这样风险可控,出问题也容易回退。

5.2 我踩过的三个大坑

第一个坑:过度设计。刚开始搞 Multi-Agent 的时候,我设计了一个五层架构,有全局规划、模块规划、任务分配、执行、审查五层。结果跑起来之后,光是 Agent 之间的通信开销就占了总时间的一半,而且调试极其困难。后来砍到三层,效果反而更好。

第二个坑:忽视状态一致性。多个 Agent 并发读写共享状态的时候,出现了数据竞争。一个 Agent 刚写入的结果,被另一个 Agent 覆盖了。解决方案是给共享状态加锁,或者用不可变数据结构,每次更新产生新版本。

第三个坑:审查 Agent 太严格。审查 Agent 的 prompt 写得太苛刻,导致执行 Agent 的产出物反复被打回,一个简单任务返工了七次。后来调整了审查标准,区分“必须修复”和“建议优化”,返工率大幅下降。

5.3 后续可以怎么扩展

这套骨架跑通之后,有几个方向可以继续扩展。

方向一:加入记忆机制。让 Agent 能够记住之前处理过的类似任务,下次遇到的时候直接复用经验。这个可以用向量数据库来实现,把历史任务的解决方案存进去,新任务来了先检索相似案例。

方向二:支持人工介入。在关键决策点设置人工确认,比如规划完成后让人类审核一下任务拆解是否合理,审查不通过时让人类决定是返工还是接受。

方向三:多模型混用。不同 Agent 用不同的模型,规划用推理强的,执行用速度快的,审查用准确性高的。这样能在成本和质量之间找到更好的平衡点。

方向四:加入监控和告警。生产环境里,Agent 系统需要监控任务成功率、平均耗时、token 消耗等指标,异常时自动告警。

我个人在实际操作中的体会是:Multi-Agent 不是银弹,它解决的是单体 Agent 在复杂任务上的结构性瓶颈,但同时也引入了新的复杂度。拆分的收益必须大于协调的成本,这个账要算清楚。我的经验是,当任务复杂度达到单体 Agent 明显吃力的程度时,Multi-Agent 的收益通常是显著的;但如果任务本身简单,强行拆分只会得不偿失。先把手头的单体 Agent 跑到极限,再考虑拆,这个顺序不能反。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 15:58:05

经典系统辨识法全流程:数据采集、参数估计与验证实战

模型结构摆在那儿&#xff0c;参数全是未知数——这种局面我在建模里碰到的次数&#xff0c;比想象中多得多。写方程容易&#xff0c;把方程里那堆系数定下来才是真正见功力的地方。经典辨识法就是干这件事的一套老办法&#xff1a;模型形式已经给定&#xff0c;输入输出数据也…

作者头像 李华
网站建设 2026/10/2 15:57:03

AI Agent接管Android真机测试:Google ARTEMIS实战解析

ARTEMIS 这个名字刚火起来的时候&#xff0c;我第一反应是&#xff1a;又是个玩具 Demo 吧。等真的把它接到一台 Android 真机上&#xff0c;看着 AI Agent 自己把设置页翻了三层、点开开发者选项、还把亮度拉到顶&#xff0c;我才意识到&#xff0c;这次可能真的不一样了。 G…

作者头像 李华
网站建设 2026/10/2 15:57:01

可实施技术方案怎么写?六段式结构让方案真正落地

1. 为什么多数技术方案写完就“废”了我接触过大量技术方案&#xff0c;有团队内部的、跨部门的&#xff0c;也有面向客户交付的。一个很残酷的现状是&#xff1a;大部分方案在评审会结束那一刻就完成了它的历史使命&#xff0c;后续开发根本不按方案走&#xff0c;或者走到一半…

作者头像 李华
网站建设 2026/10/2 15:57:00

掉线重连排查全攻略:从网卡、驱动到DNS的定位思路

接手“回购协议”业务系统的排障任务&#xff0c;是在凌晨两点半的告警刷屏之后。那天的场面其实不复杂&#xff1a;整个业务模块的连接状态&#xff0c;每隔三五分钟就从“正常”跳成“断开”&#xff0c;过几秒又自动重连&#xff0c;白天还能勉强撑住&#xff0c;一到业务高…

作者头像 李华
网站建设 2026/10/2 15:56:16

Firefox 编译:源码拉取与 bootstrap 环境引导全流程详解

Firefox 的编译架构在大型开源项目里算是最能折腾人的那一档。前几篇我们把构建系统、工具链、mozconfig 拆得七七八八&#xff0c;但真正动手的时候&#xff0c;你会发现绝大多数人的第一道坎根本不是写配置&#xff0c;而是两个看起来特别不起眼的步骤&#xff1a;源码拉取和…

作者头像 李华
网站建设 2026/10/2 15:55:46

AI日报写作方法论:从信息筛选到内容编排的完整指南

1. 一份“AI 日报”到底该写什么&#xff1a;从标题倒推内容骨架“AI 日报&#xff08;2026年9月23日&#xff09;”这个标题看起来简单&#xff0c;但它其实暴露了一个很典型的日常内容生产场景&#xff1a;每天都有大量新模型、新工具、新论文、新融资、新政策讨论冒出来&…

作者头像 李华