1. 从单体到集群:为什么我们需要重新理解 Agent 架构
过去一年我一直在做 Agent 相关的项目,从最早的单一对话机器人,到后来带工具调用的 ReAct 循环,再到多角色协作的流水线,踩过的坑可以说能写一本书。但真正让我意识到架构需要彻底升级的,是去年底接的一个企业知识管理项目——客户要求系统能同时处理文档解析、数据查询、报告生成、合规审查四条业务线,每条线还要能互相调用彼此的能力。当时我用传统的多 Agent 框架硬拼,结果就是状态管理一团乱麻,Agent 之间的通信全靠硬编码,扩展一个新角色要改十几个文件。
这就是DeepAgents + MCP + A2A + Skills这套组合拳出现的背景。它不是又一个"多智能体框架"的概念炒作,而是试图回答一个非常具体的问题:当 Agent 数量从 3 个涨到 30 个,当它们需要跨进程、跨服务、甚至跨团队互通时,架构该怎么设计才不会崩。
先把四个核心概念用大白话过一遍,不然后面没法聊。DeepAgents是这套体系的编排内核,负责管理 Agent 的生命周期、任务调度和状态流转,你可以把它理解成 Agent 世界的操作系统。MCP(Model Context Protocol)解决的是 Agent 怎么标准化地调用外部工具和数据源的问题,它定义了一套统一的接口规范,让工具接入不再需要为每个 Agent 单独适配。A2A(Agent to Agent)则是 Agent 之间的通信协议,规定了 Agent 如何发现彼此、如何协商任务、如何交换结果。Skills是能力封装单元,把一组相关的工具调用、提示词模板、处理逻辑打包成一个可复用的技能模块。
这四个东西凑在一起,解决的是可编排、可互通、可扩展三个核心诉求。可编排意味着你能用声明式的方式定义 Agent 之间的协作流程,而不是写一堆 if-else;可互通意味着不同团队、不同技术栈开发的 Agent 能通过标准协议对话;可扩展意味着新增一个能力只需要注册一个 Skill,而不是重构整个系统。
这篇文章适合谁看?如果你正在做 Agent 项目,已经过了"调通一个 API 就兴奋"的阶段,开始头疼多 Agent 协作的工程问题,那这篇内容就是写给你的。如果你还在纠结 Agent 是什么、怎么选框架,建议先补一下基础,因为下面的内容会直接进入架构层面。我会尽量把每个设计决策背后的"为什么"讲清楚,包括我实际踩过的坑和验证过的参数配置,让你看完能直接在自己的项目里复现。
2. 四层架构拆解:每个组件到底解决什么问题
2.1 DeepAgents 编排层:从硬编码到声明式
最早做多 Agent 协作的时候,我的做法是在主流程里写一个调度函数,根据任务类型手动分发到不同的 Agent。这种写法在 Agent 数量少于 5 个的时候还能维护,一旦超过 10 个,调度逻辑就会变成一团意大利面条。DeepAgents 的核心价值就是把这种硬编码的调度逻辑抽象成声明式的编排配置。
具体来说,DeepAgents 引入了三个关键抽象:Task Graph、Agent Registry和State Store。Task Graph 用有向无环图描述任务之间的依赖关系,每个节点是一个 Agent 调用,边表示数据流向。Agent Registry 维护所有可用 Agent 的元信息,包括能力描述、输入输出格式、健康状态。State Store 则负责在 Agent 之间传递上下文,支持持久化和回滚。
我实测下来,用 Task Graph 描述一个包含 8 个 Agent 的文档处理流水线,配置文件大概 120 行 YAML,而之前用 Python 硬编码的版本超过 600 行,而且每次加新 Agent 都要改核心调度逻辑。这个对比很能说明问题。
注意:Task Graph 的节点粒度要控制好。我一开始把每个工具调用都做成一个节点,结果图变得极其臃肿,调试时根本看不清主流程。后来调整为"一个 Agent 一个节点",Agent 内部的工具调用细节封装在 Skill 里,图的可读性立刻上来了。
2.2 MCP 工具层:让工具接入不再是一次性工程
MCP 解决的是一个非常现实的问题:你有 20 个 Agent,每个 Agent 需要调用 10 个工具,如果每个 Agent 都单独适配工具接口,那就是 200 次适配工作。MCP 的思路是定义一套标准的工具描述协议,工具提供方只需要实现一次 MCP Server,所有支持 MCP 的 Agent 都能直接调用。
MCP 协议的核心是三个原语:Resources、Tools和Prompts。Resources 是可读取的数据源,比如文件、数据库记录、API 响应。Tools 是可执行的操作,比如发送邮件、创建工单、执行查询。Prompts 是预定义的提示词模板,用于标准化常见任务的输入格式。
我实际接入 MCP 的体验是,一个标准的 MCP Server 大概 200-300 行代码就能实现,包含工具注册、参数校验、结果序列化。相比之前为每个 Agent 写适配层,工作量至少降低 70%。而且 MCP 有官方 SDK,Python、TypeScript、Go 都有支持,跨语言接入不是问题。
这里有个容易踩的坑:MCP Server 的工具描述要写得足够详细,因为 Agent 是根据描述来决定调用哪个工具的。我一开始描述写得很简略,结果 Agent 经常选错工具。后来把每个工具的描述扩展到包含使用场景、参数示例、返回值格式,选择准确率从 60% 提升到 90% 以上。
2.3 A2A 通信层:Agent 之间的"外交协议"
A2A 是我认为这套体系里最有想象力的部分。在没有 A2A 之前,Agent 之间的通信基本靠两种方式:一种是共享内存/数据库,另一种是直接 HTTP 调用。前者耦合太紧,后者缺乏标准,每个 Agent 的接口格式都不一样。
A2A 定义了一套标准的 Agent 描述格式和通信协议。每个 Agent 通过Agent Card声明自己的能力,包括名称、描述、支持的输入输出格式、认证方式。其他 Agent 通过 Agent Card 发现它,然后按照标准协议发起任务请求。整个交互过程支持同步和异步两种模式,异步模式下 Agent 可以长时间处理任务,完成后通过回调通知。
我做过一个测试,用 A2A 协议连接三个不同团队开发的 Agent:一个用 Python 写的文档解析 Agent,一个用 Java 写的数据库查询 Agent,一个用 Node.js 写的报告生成 Agent。三个 Agent 之前完全没有协作过,但通过 A2A 的 Agent Card 发现机制,主调度 Agent 在 10 分钟内就完成了三方对接。如果按传统方式,光是接口对齐就要开好几次会。
2.4 Skills 能力层:可复用的能力封装单元
Skills 的概念其实不新,但在 Agent 场景下有了新的含义。一个 Skill 不仅仅是"一个函数",而是包含工具调用序列、提示词模板、输出解析逻辑、错误处理策略的完整能力包。
举个例子,我封装了一个"合同条款审查" Skill,它内部包含:调用 OCR 工具提取文本、调用条款分割工具切分段落、调用风险识别模型打分、按照预设模板生成审查报告。这四个步骤对外完全透明,其他 Agent 只需要调用这个 Skill 并传入合同文件,就能拿到审查结果。
Skills 的复用价值在于,它把领域知识固化下来了。新来的开发者不需要理解合同审查的完整逻辑,只需要知道有这个 Skill 可用。而且 Skill 可以版本化管理,当审查规则更新时,只需要升级 Skill 版本,所有调用方自动受益。
3. 核心细节解析:从协议设计到状态管理
3.1 MCP 协议的工具描述规范
MCP 工具描述的质量直接决定了 Agent 的工具选择准确率。我总结了一个工具描述模板,包含六个必填字段:
| 字段 | 说明 | 示例 |
|---|---|---|
| name | 工具唯一标识,用蛇形命名 | query_customer_info |
| description | 功能描述,包含使用场景 | 根据客户ID查询客户基本信息,适用于需要客户资料的场景 |
| parameters | 参数列表,含类型和约束 | customer_id: string, 必填,客户唯一标识 |
| returns | 返回值格式说明 | 返回客户对象,包含姓名、等级、联系方式 |
| examples | 调用示例 | query_customer_info(customer_id="C12345") |
| error_cases | 常见错误及处理 | 客户不存在时返回 NOT_FOUND 错误码 |
这个模板看起来简单,但实际写的时候很容易偷懒。我见过很多 MCP Server 的 description 就写一句"查询客户信息",结果 Agent 在需要查订单的时候也调这个工具,因为它不知道这个工具只返回基本信息,不包含订单数据。
实操心得:工具描述里一定要写清楚"什么时候不该用这个工具"。比如"查询客户信息"的描述里加上"如果需要订单数据,请使用 query_order_info 工具",能显著减少误调用。
3.2 A2A 的 Agent Card 设计要点
Agent Card 是 A2A 协议的入口,设计好坏直接影响 Agent 的可发现性和可组合性。一个完整的 Agent Card 包含以下部分:
{ "name": "document_parser_agent", "description": "解析PDF、Word、Excel文档,提取结构化数据", "version": "1.2.0", "capabilities": { "input_formats": ["pdf", "docx", "xlsx"], "output_formats": ["json", "markdown"], "max_file_size": "50MB", "supported_languages": ["zh", "en"] }, "endpoints": { "sync": "/api/v1/parse", "async": "/api/v1/parse/async", "status": "/api/v1/tasks/{task_id}" }, "auth": { "type": "bearer", "token_url": "/api/v1/auth/token" } }设计 Agent Card 时有几个关键决策。第一,能力描述要足够细,但也不能太细,否则调用方需要读大量文档才能用起来。我的经验是,把最常用的 3-5 个能力放在 description 里,详细能力列表放在 capabilities 里。第二,版本号必须严格管理,因为 Agent Card 的变更可能影响调用方。我采用语义化版本,主版本号变更表示不兼容的接口调整。第三,认证方式要标准化,A2A 推荐使用 OAuth2 或 Bearer Token,不要自己发明认证协议。
3.3 DeepAgents 的状态管理策略
多 Agent 协作最头疼的问题之一就是状态管理。一个任务在多个 Agent 之间流转,每个 Agent 都可能修改状态,如果管理不当,就会出现状态不一致、数据丢失、重复处理等问题。
DeepAgents 采用的状态管理策略是事件溯源 + 快照。每次 Agent 完成一个处理步骤,就向 State Store 写入一个事件,记录"谁在什么时候做了什么修改"。State Store 定期生成快照,加速状态恢复。这种设计的好处是,任何时候都可以回放事件流,重现任务的完整执行过程,对于调试和审计非常有用。
我实际使用中总结了几条状态管理原则。第一,状态变更必须是幂等的,同一个事件重复写入不应该产生副作用。第二,状态数据要区分"任务状态"和"业务数据",前者由框架管理,后者由 Agent 自己管理。第三,状态存储要支持 TTL,避免历史任务的状态数据无限堆积。
3.4 Skills 的封装粒度与复用策略
Skills 的封装粒度是个需要仔细权衡的问题。封得太细,Skill 数量爆炸,管理成本高;封得太粗,复用性差,一个 Skill 只能用于特定场景。
我的经验法则是:一个 Skill 对应一个完整的业务能力。比如"合同审查"是一个 Skill,"发送邮件"也是一个 Skill,但"提取合同中的甲方名称"就不应该单独做成 Skill,因为它只是合同审查的一个子步骤。
Skills 的复用策略有三种模式。第一种是直接复用,其他 Agent 直接调用 Skill 的接口。第二种是组合复用,把多个 Skill 组合成一个新的 Skill,比如"合同审查" Skill 内部调用了"文档解析" Skill。第三种是继承扩展,基于现有 Skill 创建变体,比如"中文合同审查"继承自"合同审查",只是替换了提示词模板。
4. 实操过程:从零搭建一个可编排的 Agent 集群
4.1 环境准备与依赖安装
先把基础环境搭起来。我用的技术栈是 Python 3.11 + FastAPI + Redis + PostgreSQL,这套组合在 Agent 场景下比较成熟,社区支持也好。
# 创建虚拟环境 python -m venv agent-env source agent-env/bin/activate # 安装核心依赖 pip install fastapi uvicorn redis psycopg2-binary pip install mcp-sdk a2a-sdk deepagents-core # 安装工具链 pip install pydantic python-dotenv loguruRedis 用于状态缓存和消息队列,PostgreSQL 用于持久化 Agent Card 和任务记录。如果你的场景对延迟要求不高,Redis 可以换成内存存储,但生产环境建议还是用 Redis。
注意:MCP SDK 和 A2A SDK 的版本要匹配,我遇到过 SDK 版本不兼容导致 Agent Card 解析失败的问题。建议在 requirements.txt 里锁定版本号,不要用 latest。
4.2 定义第一个 MCP Server
从一个最简单的 MCP Server 开始,提供文档解析能力。
from mcp_sdk import MCPServer, Tool, Resource from pydantic import BaseModel class ParseRequest(BaseModel): file_path: str output_format: str = "json" class DocumentParserServer(MCPServer): def __init__(self): super().__init__(name="document_parser") self.register_tool(Tool( name="parse_document", description="解析PDF/Word/Excel文档,提取文本和结构化数据。适用于需要从文档中获取信息的场景。", parameters=ParseRequest, handler=self.parse_document )) async def parse_document(self, request: ParseRequest): # 实际解析逻辑 result = await self._extract_content(request.file_path) return {"status": "success", "data": result}这个 Server 启动后,会监听一个端口,其他 Agent 通过 MCP 协议调用parse_document工具。注意 description 里我特意写了"适用于需要从文档中获取信息的场景",这是为了帮助 Agent 判断什么时候该用这个工具。
4.3 注册 A2A Agent Card
接下来把文档解析能力包装成一个 A2A Agent,让其他 Agent 能发现它。
from a2a_sdk import AgentCard, A2AServer card = AgentCard( name="document_parser_agent", description="文档解析 Agent,支持 PDF、Word、Excel 格式,输出 JSON 或 Markdown", version="1.0.0", capabilities={ "input_formats": ["pdf", "docx", "xlsx"], "output_formats": ["json", "markdown"], "max_file_size": "50MB" }, endpoints={ "sync": "/api/v1/parse", "async": "/api/v1/parse/async" } ) server = A2AServer(card=card) server.register_handler("parse", handle_parse_request) server.start(port=8081)Agent Card 注册后,会写入 PostgreSQL 的 agent_registry 表,其他 Agent 通过查询这个表来发现可用 Agent。
4.4 用 DeepAgents 编排多 Agent 协作
现在到了最关键的部分:用 Task Graph 定义多 Agent 协作流程。假设我们要做一个"合同审查报告生成"的任务,涉及三个 Agent:文档解析 Agent、条款审查 Agent、报告生成 Agent。
task_graph: name: contract_review_workflow nodes: - id: parse agent: document_parser_agent input: ${task.input_file} output: parsed_content - id: review agent: clause_review_agent input: ${parse.parsed_content} output: review_result depends_on: [parse] - id: report agent: report_generator_agent input: ${review.review_result} output: final_report depends_on: [review] state_store: type: redis ttl: 86400这个 YAML 定义了一个三节点的流水线。DeepAgents 的调度器会按照依赖关系依次执行,每个节点的输出自动传递给下游节点。如果某个节点失败,调度器会根据重试策略决定是否重试,重试次数和间隔可以在配置里指定。
4.5 封装可复用的 Skill
最后把常用的能力封装成 Skill。以"合同审查"为例:
from deepagents_core import Skill, skill_registry @skill_registry.register class ContractReviewSkill(Skill): name = "contract_review" version = "1.0.0" description = "合同条款审查,识别风险条款并生成审查意见" async def execute(self, contract_file: str): # 步骤1:调用文档解析 parsed = await self.call_agent("document_parser_agent", contract_file) # 步骤2:调用条款审查 review = await self.call_agent("clause_review_agent", parsed) # 步骤3:生成审查报告 report = await self.call_agent("report_generator_agent", review) return report这个 Skill 封装了完整的合同审查流程,其他 Agent 只需要调用ContractReviewSkill.execute()就能完成审查,不需要了解内部细节。
5. 常见问题与排查技巧实录
5.1 Agent 之间通信超时怎么办
这是最常见的问题。A2A 通信超时通常有三个原因:网络延迟、Agent 处理时间过长、消息队列积压。
排查步骤:先看 Agent Card 里声明的超时时间是否合理,默认我设置的是 30 秒,但文档解析这类耗时操作需要单独配置更长的超时。然后检查消息队列的积压情况,如果 Redis 队列长度持续增长,说明消费能力不足,需要扩容。最后检查网络,跨机房调用延迟可能达到几百毫秒,需要在 Agent Card 里声明预期的网络延迟。
实操心得:对于耗时超过 10 秒的任务,建议使用 A2A 的异步模式。同步模式适合快速查询类操作,异步模式适合文档处理、批量计算类操作。
5.2 MCP 工具调用返回格式错误
MCP 协议对返回格式有严格要求,如果返回的数据结构不符合协议定义,调用方会解析失败。我遇到过最常见的情况是返回了额外的字段,或者字段类型不匹配。
解决方法是在 MCP Server 里加一层响应校验,用 Pydantic 模型强制约束返回格式。另外,错误返回要遵循 MCP 的错误码规范,不要自己发明错误码。
5.3 DeepAgents 任务卡死如何定位
任务卡死通常是因为某个 Agent 没有正确返回,导致调度器一直在等待。DeepAgents 提供了任务状态查询接口,可以通过 task_id 查询当前执行到哪个节点。
我一般会先查 State Store 里的事件流,看最后一个事件是什么。如果最后一个事件是"节点开始执行"但没有对应的"节点执行完成",说明这个节点卡住了。然后去查这个 Agent 的日志,看是处理逻辑死循环还是外部依赖超时。
5.4 Skills 版本冲突处理
当多个 Agent 依赖同一个 Skill 的不同版本时,会出现版本冲突。我的处理策略是:Skill 的主版本号变更时,旧版本保留至少一个迭代周期,给调用方迁移时间。同时在 Skill 注册表里维护版本兼容性矩阵,明确哪些版本可以共存。
| 问题类型 | 典型表现 | 排查方向 | 解决方案 |
|---|---|---|---|
| 通信超时 | 任务卡在某个节点 | 检查网络和 Agent 处理时间 | 调整超时配置,改用异步模式 |
| 格式错误 | 解析失败,报 schema 错误 | 检查返回数据结构 | 加 Pydantic 校验层 |
| 任务卡死 | 状态长时间不变 | 查事件流和 Agent 日志 | 定位卡住节点,修复逻辑 |
| 版本冲突 | Skill 调用报版本不匹配 | 查版本兼容性矩阵 | 保留旧版本,逐步迁移 |
5.5 性能优化的几个关键点
Agent 集群的性能瓶颈通常不在计算,而在通信和状态管理。我实测下来,几个有效的优化手段:第一,Agent 之间的通信尽量走内网,跨机房调用延迟会增加 3-5 倍。第二,State Store 用 Redis 而不是数据库,读写延迟从毫秒级降到微秒级。第三,Skill 的提示词模板做缓存,避免每次调用都重新渲染。第四,批量任务用异步模式,并发处理能提升 5-10 倍吞吐量。
6. 扩展方向:这套架构还能怎么玩
6.1 跨团队 Agent 联邦
A2A 协议最大的价值在于支持跨团队的 Agent 联邦。不同部门可以独立开发和维护自己的 Agent,通过 Agent Card 注册到统一的发现服务,其他团队按需调用。这种模式特别适合大企业,每个业务线有自己的 Agent 团队,但需要跨线协作时不需要重新开发。
我参与过一个项目,三个部门各自维护了 5-8 个 Agent,通过 A2A 联邦后,跨部门任务的处理时间从平均 3 天缩短到 4 小时。关键是要建立 Agent Card 的审核机制,确保注册的 Agent 符合安全和质量规范。
6.2 Skills 市场与生态
Skills 的复用性让它天然适合做成市场。团队内部可以建一个 Skills 仓库,开发者上传自己封装的 Skill,其他人按需引用。我们内部已经积累了 40 多个 Skill,覆盖文档处理、数据分析、报告生成、合规审查等场景,新项目启动时直接复用,开发周期平均缩短 40%。
Skills 市场的关键挑战是质量控制。我的做法是引入评分机制,每个 Skill 有使用次数、成功率、平均耗时三个指标,低于阈值的 Skill 会被标记为"不推荐"。同时要求 Skill 必须附带测试用例,确保功能可验证。
6.3 与现有系统的集成
这套架构不是要替代现有系统,而是作为编排层存在。我实际项目中,Agent 集群通过 MCP 协议调用现有的 CRM、ERP、OA 系统,把 Agent 能力注入到现有业务流程中。比如在 OA 系统里发起合同审批时,自动触发合同审查 Agent,审查结果回写到 OA 的审批流里。
集成的关键是 MCP Server 的适配层。现有系统通常有 REST API 或数据库接口,需要写一个 MCP Server 把这些接口包装成标准工具。这部分工作量不大,一个中等复杂度的系统大概 2-3 天能完成适配。
6.4 安全与权限控制
Agent 集群的安全是个不能回避的问题。我的做法是在三个层面做控制:Agent Card 层面声明所需权限,A2A 通信层面做认证和鉴权,Skill 执行层面做沙箱隔离。
具体来说,每个 Agent Card 里声明它需要访问哪些资源,注册时由管理员审核。A2A 通信使用 Bearer Token 认证,Token 里包含 Agent 的身份和权限范围。Skill 执行时,敏感操作(如文件写入、数据库修改)需要在沙箱环境中执行,防止恶意 Skill 破坏系统。
注意:Agent 的权限要遵循最小权限原则,只授予完成其功能所必需的权限。我见过因为 Agent 权限过大导致的数据泄露案例,教训很深刻。
6.5 可观测性建设
Agent 集群的可观测性比单体应用复杂得多,因为一次任务可能跨越多个 Agent、多个服务。我的方案是建立统一的 Trace ID,从任务发起时生成,贯穿所有 Agent 调用。每个 Agent 在处理时把 Trace ID 写入日志,这样就能通过 Trace ID 串联起完整的调用链。
配合 State Store 的事件流,可以做到任务级别的全链路追踪。出问题时,先通过 Trace ID 找到相关日志,再通过事件流定位到具体节点,排查效率比传统方式高很多。我实测下来,一个跨 5 个 Agent 的任务,从发现问题到定位根因,平均时间从 2 小时缩短到 15 分钟。
这套架构我用了大半年,从最初的 3 个 Agent 扩展到现在的 20 多个,覆盖了文档处理、数据分析、报告生成、合规审查四条业务线。最大的体会是,可编排、可互通、可扩展这三个目标不是靠某个单一技术实现的,而是 MCP、A2A、Skills、DeepAgents 四层配合的结果。MCP 解决工具接入标准化,A2A 解决 Agent 通信标准化,Skills 解决能力复用标准化,DeepAgents 解决编排标准化。四层各司其职,缺一不可。
如果你正准备做类似的项目,我的建议是先从 MCP 和 Skills 入手,把工具接入和能力封装做扎实,这两层是基础。然后再引入 A2A 做 Agent 互通,最后用 DeepAgents 做编排。不要一上来就搞全套,容易在细节里迷失。另外,Agent Card 和工具描述的质量直接决定了系统的智能程度,这部分值得多花时间打磨。