1. 从单兵作战到集群协同:为什么需要多智能体全流程实战
过去大半年,我一直在折腾各种智能体框架,从最早的单一 Agent 跑通一个任务,到后来发现单个 Agent 在处理复杂业务时总是力不从心——上下文窗口不够用、工具调用容易串味、一个环节出错整个链路就崩了。直到我把 DeepAgents、MCP、A2A 和 Skills 这四个东西串起来用,才真正体会到什么叫“多智能体集群”的威力。这套组合解决的核心问题很明确:让多个各有所长的智能体像一支训练有素的团队一样协同工作,而不是让一个全能选手硬扛所有活。
这篇文章适合谁看?如果你已经玩过基础的 Agent 调用,想进一步了解怎么把多个智能体组织成生产可用的集群架构,那接下来的内容应该对你有用。我会从架构设计思路讲到具体落地步骤,包括 MCP 协议怎么接、A2A 通信怎么配、Skills 怎么封装复用,以及我在实际搭建过程中踩过的那些坑。整套方案不是纸上谈兵,是我自己跑通并持续迭代了三个多月的实战总结。
先把这个组合的逻辑理清楚。DeepAgents在这里扮演的是“智能体运行时容器”的角色,它提供了智能体的生命周期管理、任务调度和状态维护能力。MCP解决的是智能体与外部工具、数据源之间的标准化连接问题,你可以把它理解成智能体世界的 USB-C 接口——不管对面是数据库、文件系统还是第三方 API,只要实现了 MCP 协议,智能体就能即插即用。A2A则是智能体之间的通信协议,让一个智能体能把自己的能力“暴露”给另一个智能体调用,形成能力网络。Skills是封装好的可复用能力单元,一个 Skill 可以是一段提示词模板、一组工具调用逻辑,或者一个完整的子任务处理流程。
这四个东西凑在一起,就形成了一个完整的多智能体集群架构:DeepAgents 管调度,MCP 管工具接入,A2A 管智能体间通信,Skills 管能力复用。下面我按实际搭建的顺序,把每个环节拆开讲透。
2. 集群架构设计:角色划分与通信拓扑
2.1 智能体角色怎么分才合理
多智能体系统最容易犯的错误就是角色划分太细或太粗。我一开始按“每个工具一个智能体”的思路去分,结果搞出来二十多个智能体,光通信开销就把性能拖垮了。后来调整成按业务能力域划分,才找到平衡点。
我的划分原则是这样的:一个智能体负责一个完整的业务闭环,而不是一个单一操作。比如在代码开发场景里,我分了四个核心角色:
- 需求解析智能体:负责理解用户意图,把模糊需求拆解成结构化任务描述
- 代码生成智能体:根据任务描述生成具体代码实现
- 代码审查智能体:对生成的代码做质量检查、安全扫描和规范校验
- 集成测试智能体:负责把代码片段组装起来跑测试,验证功能完整性
这四个角色之间形成流水线协作,每个智能体内部可以调用多个 MCP 工具和 Skills。这样划分的好处是:每个智能体的职责边界清晰,上下文不会互相污染,而且单个智能体出问题不会导致整个链路崩溃——审查智能体发现代码有问题,可以打回给生成智能体重新处理,而不是整个流程重来。
注意:角色数量控制在 3 到 7 个之间比较合适。少于 3 个说明拆分不够,多于 7 个通信复杂度会指数级上升。我实测下来,5 个左右的角色在大多数场景下是甜点区。
2.2 A2A 通信拓扑怎么选
A2A 协议支持多种通信模式,我主要用的是请求-响应模式和发布-订阅模式两种。请求-响应适合有明确调用关系的场景,比如代码生成智能体需要调用代码审查智能体的能力;发布-订阅适合事件驱动的场景,比如某个智能体完成阶段性任务后广播状态变更。
拓扑结构上,我试过三种:
| 拓扑类型 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 星型拓扑 | 中心调度器统一分发任务 | 控制简单,状态好追踪 | 中心节点容易成为瓶颈 |
| 网状拓扑 | 智能体之间自由通信 | 灵活度高,无单点故障 | 通信链路复杂,调试困难 |
| 分层拓扑 | 按业务域分组,组内网状、组间星型 | 兼顾灵活性和可控性 | 架构设计复杂度较高 |
最终我选了分层拓扑。顶层是一个调度智能体,负责接收用户请求并分发给对应的业务域;每个业务域内部用网状拓扑,智能体之间可以直接通信。这样既保证了跨域调度的可控性,又保留了域内协作的灵活性。
2.3 状态管理与上下文隔离
多智能体系统里,状态管理是个容易被忽视但极其关键的问题。我的做法是:每个智能体维护自己的私有状态,共享状态通过 A2A 消息传递。私有状态包括当前任务的中间结果、工具调用历史、局部上下文;共享状态包括全局任务进度、跨智能体的依赖关系、最终输出结果。
上下文隔离这块,我用的是 DeepAgents 提供的上下文沙箱机制。每个智能体在独立的上下文空间中运行,只能看到自己需要的信息。比如代码审查智能体不需要知道需求解析的完整对话历史,它只需要拿到待审查的代码和相关的规范要求就行。这样做的好处是:上下文窗口利用率高,不会因为无关信息挤占 token;同时避免了信息泄露和上下文污染。
3. MCP 协议接入:让智能体真正“长出手脚”
3.1 MCP 到底是什么,为什么需要它
MCP 全称 Model Context Protocol,直译过来是“模型上下文协议”。你可以把它理解成智能体和外部世界之间的标准化接口层。在没有 MCP 之前,每接一个工具就要写一套适配代码,数据库一个写法、文件系统一个写法、第三方 API 又是另一个写法,维护成本极高。MCP 把这些统一了:只要工具实现了 MCP 协议,智能体就能用同一套调用方式去使用它。
MCP 的核心概念有三个:
- Resources:智能体可以读取的数据源,比如文件内容、数据库记录、API 返回结果
- Tools:智能体可以调用的操作,比如执行命令、写入文件、发送请求
- Prompts:预定义的提示词模板,智能体可以直接引用
我实际用下来,MCP 最大的价值在于解耦。工具的实现和智能体的逻辑完全分离,工具升级不影响智能体,智能体调整也不影响工具。而且 MCP 支持流式输出,对于需要处理大文件或长文本的场景特别友好。
3.2 MCP 服务端搭建实操
搭建 MCP 服务端有两种方式:用官方 SDK 自己写,或者用现成的 MCP 服务器实现。我两种都试过,自己写灵活度高但工作量大,用现成的省事但定制性差。下面以自己写一个文件系统 MCP 服务端为例,把关键步骤说清楚。
首先安装 MCP SDK:
pip install mcp然后创建一个基础的 MCP 服务端:
from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent app = Server("file-system-mcp") @app.list_tools() async def list_tools(): return [ Tool( name="read_file", description="读取指定路径的文件内容", inputSchema={ "type": "object", "properties": { "path": {"type": "string", "description": "文件路径"} }, "required": ["path"] } ), Tool( name="write_file", description="写入内容到指定文件", inputSchema={ "type": "object", "properties": { "path": {"type": "string"}, "content": {"type": "string"} }, "required": ["path", "content"] } ) ] @app.call_tool() async def call_tool(name: str, arguments: dict): if name == "read_file": with open(arguments["path"], "r") as f: content = f.read() return [TextContent(type="text", text=content)] elif name == "write_file": with open(arguments["path"], "w") as f: f.write(arguments["content"]) return [TextContent(type="text", text="写入成功")] async def main(): async with stdio_server() as (read_stream, write_stream): await app.run(read_stream, write_stream, app.create_initialization_options()) if __name__ == "__main__": import asyncio asyncio.run(main())这个服务端实现了两个基础工具:读文件和写文件。实际项目中我会根据业务需要扩展更多工具,比如列出目录、搜索文件内容、批量操作等。
提示:MCP 服务端的工具描述(description)字段非常重要,智能体就是靠这个描述来判断什么时候该调用哪个工具的。描述要写得具体、准确,最好包含使用场景和参数说明。我踩过的坑是描述写得太模糊,导致智能体该调工具的时候不调,不该调的时候乱调。
3.3 智能体侧 MCP 客户端接入
服务端搭好后,智能体这边需要配置 MCP 客户端来连接。DeepAgents 内置了 MCP 客户端支持,配置方式如下:
from deepagents import Agent from deepagents.mcp import MCPClient mcp_client = MCPClient( server_command=["python", "file_system_mcp.py"], transport="stdio" ) agent = Agent( name="code-generator", model="claude-sonnet-4-20250514", mcp_clients=[mcp_client], system_prompt="你是一个代码生成智能体,可以使用文件系统工具读写代码文件。" )这里的关键是transport参数,支持stdio和sse两种模式。stdio适合本地进程间通信,sse适合远程服务调用。我本地开发用stdio,部署到服务器上用sse。
接入完成后,智能体就能在推理过程中自动发现并调用 MCP 工具了。我实测下来,一个配置了 5 个 MCP 工具的智能体,在代码生成任务中的工具调用准确率能达到 90% 以上,前提是工具描述写得够清楚。
3.4 多个 MCP 服务端的编排
实际项目中往往需要同时接入多个 MCP 服务端,比如文件系统一个、数据库一个、第三方 API 一个。DeepAgents 支持同时挂载多个 MCP 客户端:
mcp_clients = [ MCPClient(server_command=["python", "file_system_mcp.py"], transport="stdio"), MCPClient(server_command=["python", "database_mcp.py"], transport="stdio"), MCPClient(server_command=["python", "api_mcp.py"], transport="sse", url="http://localhost:8080") ] agent = Agent( name="full-stack-agent", model="claude-sonnet-4-20250514", mcp_clients=mcp_clients )多个 MCP 服务端的工具会合并到一个工具池里,智能体根据任务需要自行选择。这里有个经验:工具数量控制在 15 个以内,超过这个数量智能体的选择准确率会明显下降。如果确实需要更多工具,建议按业务域拆分到不同的智能体上。
4. A2A 协议实战:智能体之间的“对话规则”
4.1 A2A 的核心机制
A2A 协议解决的是智能体之间怎么互相调用的问题。它的核心机制是能力卡片(Agent Card)和任务协商。每个智能体通过能力卡片声明自己会什么、接受什么输入、返回什么输出;调用方智能体根据能力卡片来决定是否把任务委托给对方。
能力卡片是一个 JSON 结构,包含以下关键字段:
{ "name": "code-reviewer", "description": "代码审查智能体,负责检查代码质量和安全问题", "capabilities": [ { "name": "review_code", "description": "审查代码并返回问题列表", "input_schema": { "type": "object", "properties": { "code": {"type": "string"}, "language": {"type": "string"}, "rules": {"type": "array", "items": {"type": "string"}} } }, "output_schema": { "type": "object", "properties": { "issues": {"type": "array"}, "score": {"type": "number"} } } } ], "endpoint": "http://localhost:8001/a2a" }调用方智能体拿到这张卡片后,就知道可以把代码审查任务委托给这个智能体,并且知道需要传什么参数、会得到什么结果。
4.2 智能体能力暴露实操
把一个智能体暴露成 A2A 服务,需要做三件事:定义能力卡片、启动 A2A 服务端、注册到服务发现中心。
from deepagents.a2a import A2AServer, AgentCard card = AgentCard( name="code-reviewer", description="代码审查智能体", capabilities=[...], endpoint="http://localhost:8001/a2a" ) server = A2AServer( agent=review_agent, card=card, port=8001 ) server.start()服务启动后,其他智能体就可以通过 A2A 协议来调用它了。调用方式如下:
from deepagents.a2a import A2AClient client = A2AClient(endpoint="http://localhost:8001/a2a") result = await client.call( capability="review_code", arguments={ "code": "def foo():\n pass", "language": "python", "rules": ["no-unused-vars", "no-security-issues"] } )4.3 服务发现与动态路由
当智能体数量多了之后,手动配置每个智能体的地址就不现实了。我用了一个简单的服务注册中心来解决这个问题:每个智能体启动时把自己的能力卡片注册到中心,调用方通过能力名称来查找可用的智能体。
from deepagents.a2a import ServiceRegistry registry = ServiceRegistry() # 注册 registry.register(card) # 查找 agents = registry.find_by_capability("review_code") # 返回所有支持 review_code 能力的智能体列表服务发现带来的好处是动态路由:如果某个智能体挂了,调用方可以自动切换到另一个具有相同能力的智能体。我在代码审查环节部署了两个审查智能体,一个侧重安全、一个侧重性能,调用方根据代码特征自动选择路由到哪个智能体。
注意:服务注册中心本身要做高可用,否则它挂了整个集群就瘫痪了。我的做法是注册中心用主从模式部署,主节点负责写,从节点负责读,主从之间实时同步。
4.4 A2A 通信的安全与限流
智能体之间的通信也要考虑安全和限流。安全方面,我在 A2A 调用中加了能力令牌机制:调用方需要持有目标智能体签发的令牌才能调用对应能力。令牌有有效期和调用次数限制,防止滥用。
限流方面,每个智能体都配置了并发调用上限和队列长度。超过并发上限的请求进入队列等待,队列满了直接拒绝并返回重试建议。我实测下来,单个智能体的并发调用数控制在 5 到 10 之间比较合适,太高会导致上下文切换开销过大,太低则吞吐量不足。
5. Skills 封装:把能力变成可复用的“积木”
5.1 Skill 的粒度怎么把握
Skills 是多智能体系统里的可复用能力单元。一个 Skill 可以简单到只是一段提示词模板,也可以复杂到包含多个工具调用和条件分支的完整流程。关键问题是:粒度怎么定。
我的经验是:一个 Skill 对应一个原子化的业务能力。比如“生成 REST API 接口代码”是一个 Skill,“生成数据库迁移脚本”是另一个 Skill。不要把“生成整个后端服务”做成一个 Skill,那样太粗,复用性差;也不要把“调用某个具体函数”做成一个 Skill,那样太细,组合成本高。
我目前维护的 Skills 库大概有三十多个,按业务域分类:
| 业务域 | Skill 数量 | 典型 Skill |
|---|---|---|
| 代码生成 | 8 | REST API 生成、数据模型生成、单元测试生成 |
| 代码审查 | 5 | 安全扫描、性能分析、规范校验 |
| 文档处理 | 6 | API 文档生成、注释补全、README 生成 |
| 数据处理 | 7 | 数据清洗、格式转换、统计分析 |
| 集成部署 | 4 | 容器配置生成、CI 配置生成、环境变量管理 |
5.2 Skill 的定义与注册
一个 Skill 的定义包含元数据和执行逻辑两部分。元数据描述这个 Skill 是干什么的、需要什么输入、产生什么输出;执行逻辑可以是提示词模板、工具调用序列,或者两者的组合。
from deepagents.skills import Skill, SkillRegistry rest_api_skill = Skill( name="generate_rest_api", description="根据数据模型生成 REST API 接口代码", input_schema={ "type": "object", "properties": { "model_name": {"type": "string"}, "fields": {"type": "array"}, "framework": {"type": "string", "enum": ["fastapi", "flask", "django"]} } }, output_schema={ "type": "object", "properties": { "code": {"type": "string"}, "file_path": {"type": "string"} } }, prompt_template=""" 你是一个后端代码生成专家。请根据以下数据模型生成 REST API 接口代码: 模型名称:{model_name} 字段列表:{fields} 使用框架:{framework} 要求: 1. 包含完整的 CRUD 接口 2. 包含请求参数校验 3. 包含错误处理 4. 代码风格符合 PEP8 规范 """, tools=["write_file"] ) registry = SkillRegistry() registry.register(rest_api_skill)5.3 Skill 的组合与编排
单个 Skill 能力有限,真正的威力在于组合。DeepAgents 支持把多个 Skill 编排成一个工作流:
from deepagents.skills import SkillPipeline pipeline = SkillPipeline([ "parse_requirement", "generate_data_model", "generate_rest_api", "generate_unit_test", "review_code" ]) result = await pipeline.run(input_data)这个流水线把需求解析、数据模型生成、API 生成、单元测试生成、代码审查五个 Skill 串起来,形成一个完整的开发流程。每个 Skill 的输出自动成为下一个 Skill 的输入,中间不需要人工干预。
我实际用下来,Skill 组合的关键在于输入输出格式的对齐。上游 Skill 的输出 schema 必须和下游 Skill 的输入 schema 匹配,否则流水线会断。我的做法是定义一套通用的数据交换格式,所有 Skill 都遵循这个格式,这样任意两个 Skill 都能自由组合。
5.4 Skill 的版本管理与热更新
Skills 是需要持续迭代的,今天好用的提示词明天可能就过时了。所以我给每个 Skill 加了版本管理:
registry.register(rest_api_skill, version="1.0.0") registry.register(rest_api_skill_v2, version="2.0.0") # 指定版本调用 result = registry.get("generate_rest_api", version="2.0.0").run(input_data)热更新方面,我用了文件监听机制:Skill 定义文件发生变化时自动重新加载,不需要重启整个系统。这在调试阶段特别有用,改完提示词立刻就能看到效果。
提示:Skill 的提示词模板建议单独放在文件里,不要硬编码在代码中。这样非技术人员也能参与优化提示词,而且方便做 A/B 测试。我现在的做法是每个 Skill 对应一个目录,目录里放
skill.yaml(元数据)、prompt.txt(提示词)、tools.py(工具定义)。
6. 全流程串联:从用户请求到最终交付
6.1 完整链路拆解
前面分别讲了 DeepAgents、MCP、A2A、Skills 四个组件,现在把它们串起来看一个完整的业务流程。以“用户提交一个后端开发需求”为例:
第一步:请求接入。用户通过 API 网关提交需求描述,网关把请求转发给调度智能体。
第二步:需求解析。调度智能体调用需求解析 Skill,把自然语言需求拆解成结构化的任务列表。这个 Skill 内部会调用 MCP 工具读取需求文档模板,确保解析结果符合规范。
第三步:任务分发。调度智能体根据任务类型,通过 A2A 协议把子任务分发给对应的业务智能体。比如“生成数据模型”分给数据建模智能体,“生成 API 代码”分给代码生成智能体。
第四步:并行执行。多个业务智能体并行工作,各自调用自己的 Skills 和 MCP 工具。数据建模智能体生成模型定义后,通过 A2A 通知代码生成智能体可以开始工作了。
第五步:质量校验。代码生成完成后,调度智能体把结果发给代码审查智能体。审查智能体调用安全扫描 Skill 和规范校验 Skill,发现问题就打回给生成智能体重新处理。
第六步:集成输出。所有子任务完成后,调度智能体调用集成 Skill 把代码片段组装成完整项目,通过 MCP 文件系统工具写入指定目录,最后返回交付结果给用户。
6.2 关键配置参数与调优
这套流程跑通不难,难的是跑稳、跑快。下面是我在实际调优过程中总结的关键参数:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| 智能体最大并发数 | 5-10 | 超过 10 上下文切换开销明显增大 |
| A2A 调用超时 | 30s | 复杂任务可放宽到 60s |
| MCP 工具调用超时 | 10s | 文件操作类可缩短到 5s |
| Skill 流水线最大重试次数 | 3 | 超过 3 次说明上游有问题,应人工介入 |
| 上下文窗口预留比例 | 30% | 留 30% 给工具返回结果和中间状态 |
| 任务队列最大长度 | 100 | 超过后拒绝新请求,返回繁忙提示 |
这些参数不是拍脑袋定的,是我在不同负载下反复测试得出的。比如 A2A 调用超时,设太短会导致正常任务被误判为超时,设太长会让故障恢复变慢。30 秒是我实测下来在大多数场景下的平衡点。
6.3 监控与可观测性
多智能体系统的调试难度比单智能体高一个数量级,因为问题可能出在任何一个环节。我搭了一套监控体系,重点追踪以下指标:
- 每个智能体的调用次数、成功率、平均耗时:快速定位哪个智能体是瓶颈
- A2A 消息的端到端延迟:判断通信层是否有问题
- MCP 工具调用的错误率和重试率:发现工具层面的异常
- Skill 执行的成功率和平均执行时间:评估 Skill 的质量
- 整个流程的完成率和平均完成时间:衡量系统整体健康度
监控数据我用 Prometheus 采集,Grafana 展示。每个智能体在关键节点打点上报,形成完整的调用链路追踪。出问题的时候,通过 trace ID 就能还原整个请求的处理过程,快速定位故障点。
7. 常见问题与排查技巧实录
7.1 智能体“不听话”怎么办
这是最常见的问题:智能体该调工具的时候不调,不该调的时候乱调。我排查下来主要有三个原因:
工具描述不清晰。智能体判断是否调用工具,完全依赖工具描述。如果描述写得太笼统,智能体就不知道什么时候该用。解决办法是把描述写具体,包含使用场景、输入输出示例、注意事项。
系统提示词冲突。如果系统提示词里说“尽量自己处理”,而工具描述里说“遇到 X 情况必须调用”,智能体会困惑。解决办法是保持提示词和工具描述的一致性,不要互相矛盾。
上下文太长导致注意力分散。当上下文超过一定长度后,智能体对工具描述的注意力会下降。解决办法是控制上下文长度,把不必要的历史信息裁剪掉。
7.2 A2A 调用超时怎么排查
A2A 调用超时通常有三种原因:目标智能体处理太慢、网络延迟、消息序列化/反序列化开销大。
排查步骤:先看目标智能体的日志,确认它是否收到了请求、处理了多久;如果目标智能体处理正常,再看网络层,用 ping 和 traceroute 检查延迟;如果网络也正常,那就是消息体太大了,检查传输的数据量,考虑压缩或分片。
我遇到过一次超时是因为消息体里带了一个巨大的 base64 编码文件,序列化花了十几秒。后来改成传文件路径而不是文件内容,问题就解决了。
7.3 Skill 执行结果不稳定的处理
同一个 Skill 在不同时间执行,结果质量波动很大,这是提示词工程的经典问题。我的应对策略是:
固定随机种子。如果模型支持,设置 temperature 为 0 或固定 seed,减少随机性。
增加输出格式约束。在提示词里明确要求输出 JSON 格式,并给出 schema,这样即使内容有波动,格式至少是稳定的。
加校验层。Skill 执行完后加一个校验步骤,检查输出是否符合预期格式和内容要求,不符合就重试。
多版本对比。维护 Skill 的多个版本,定期做 A/B 测试,用数据驱动的方式选择最优版本。
7.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 智能体不调用工具 | 工具描述模糊 | 检查工具 description 字段 | 补充使用场景和示例 |
| A2A 调用失败 | 目标智能体未注册 | 检查服务注册中心 | 重新注册或重启目标智能体 |
| Skill 输出格式错误 | 提示词约束不够 | 查看原始输出 | 增加格式约束和校验层 |
| 流程卡住不推进 | 某个智能体死锁 | 查看各智能体状态 | 加超时机制和死锁检测 |
| 结果质量下降 | 上下文污染 | 检查上下文内容 | 加强上下文隔离 |
| 系统吞吐量低 | 并发配置不合理 | 监控各环节耗时 | 调整并发数和队列长度 |
7.5 我踩过的三个大坑
第一个坑:智能体之间循环调用。A 智能体调用 B,B 又调用 A,形成死循环。后来加了调用深度限制,超过 5 层就强制中断并报错。
第二个坑:MCP 服务端崩溃导致整个集群不可用。一个 MCP 服务端挂了,依赖它的所有智能体都卡住了。后来加了 MCP 客户端的熔断机制,服务端不可用时快速失败并降级到备用方案。
第三个坑:Skill 版本不兼容。升级了一个 Skill 的输出格式,但下游 Skill 还在用旧格式解析,导致流水线断裂。后来引入了 schema 版本号,上下游版本不匹配时给出明确错误提示。
8. 集群扩展与性能优化心得
8.1 水平扩展的策略
当任务量增大时,单靠优化单个智能体的性能是不够的,需要水平扩展。我的扩展策略是按业务域独立扩展:哪个业务域的智能体是瓶颈,就单独增加该域智能体的实例数。
比如代码生成域压力大,就多部署两个代码生成智能体实例,通过负载均衡分发请求。A2A 的服务发现机制天然支持这种扩展:多个实例注册相同的能力卡片,调用方自动轮询选择。
扩展时要注意状态同步问题。如果智能体是有状态的,多个实例之间需要同步状态。我的做法是尽量把智能体设计成无状态的,状态统一存到外部存储(如 Redis),这样扩展时不需要考虑状态同步。
8.2 缓存策略的运用
多智能体系统里有大量重复计算,合理使用缓存能显著提升性能。我在三个层面加了缓存:
Skill 结果缓存。相同的输入直接返回缓存结果,避免重复执行。缓存 key 用输入内容的哈希值,设置合理的过期时间。
MCP 工具结果缓存。对于读操作类工具(如读文件、查数据库),结果缓存一段时间。写操作不缓存。
A2A 响应缓存。对于幂等的 A2A 调用,缓存响应结果。非幂等调用不缓存。
缓存命中率我实测下来能达到 40% 左右,对于重复性高的任务场景,性能提升非常明显。
8.3 成本控制经验
多智能体系统跑起来,token 消耗是单智能体的好几倍。控制成本的关键在于减少不必要的智能体间通信和优化上下文长度。
我的做法是:能本地处理的逻辑不要委托给其他智能体;上下文只保留必要信息,历史对话定期压缩;简单任务用轻量模型,复杂任务才用大模型。这样下来,整体成本能控制在可接受范围内。
9. 后续扩展方向
这套架构目前跑得比较稳了,但还有很多可以优化的地方。我接下来打算尝试的方向包括:引入更细粒度的权限控制,让不同智能体只能访问自己业务域内的 MCP 工具;探索智能体的动态编排,根据任务特征自动生成最优的协作拓扑;以及把 Skills 库做成可视化的管理界面,方便非技术人员参与维护。
另外,A2A 协议目前还在演进中,后续版本可能会支持更丰富的协商机制,比如智能体之间可以就任务分工进行多轮协商,而不是简单的请求-响应。这个方向值得持续关注。
我在实际搭建这套系统的过程中最大的体会是:多智能体系统的复杂度不在于单个组件有多难,而在于组件之间的交互。把每个组件单独拿出来看都不复杂,但组合在一起就会出现各种意想不到的问题。所以我的建议是:不要一上来就搭大集群,先从两个智能体开始,跑通了再加第三个,逐步扩展。每加一个智能体,都要重新审视通信拓扑和状态管理,确保系统整体可控。