做过多智能体项目的人,应该都有过这种体验:单个 Agent 跑得挺顺,一旦想把三五个 Agent 串起来协同干活,立刻就会遇到一堆跟"智能"无关的破事——工具调用格式不统一、上下文传不过去、Agent 之间没有"共同语言"、复用一个能力还得重新写一版逻辑。这些坑我踩了好几个月,直到我自己动手搭了一套基于 DeepAgents + MCP + A2A + Skills 的 Agent 集群,才算真正把"可编排、可互通、可扩展"这几个词落到了实处。
这套体系解决的核心问题,说白了就一句话:让每个 Agent 专注干自己擅长的事,然后通过标准协议把它们的工具、能力和对话串成一个整体。DeepAgents 负责顶层编排调度,MCP 统一了工具接入方式,A2A 打通了 Agent 之间的通信,Skills 把高频能力沉淀成可复用的技能模块。这篇文章我会把这四个模块的分工、关键实现思路、实操步骤和踩坑记录完整拆开来讲。无论你是刚接触 Agent 开发的小白,还是已经把 LangGraph、AutoGen 玩得挺熟的老手,这套方案里都有值得参考的东西。
1. 整体设计思路:为什么是这四个模块组合
1.1 单 Agent 的局限与集群化瓶颈
先说说我为什么走到了这一步。以前我维护的一个 Agent 应用里,塞了翻译、搜索、代码生成、数据库查询十几个能力,提示词越来越长,工具列表越来越臃肿。表面上功能齐全,实际上每次调用都要把一大堆工具定义塞进上下文,Token 消耗大,模型还经常在多个相似工具之间选错。
后来我把能力拆成了多个独立 Agent,问题又出现了:Agent A 处理完的结果,Agent B 拿不到;想让 Agent B 调用 Agent A 的能力,得自己写 HTTP 接口、定义 JSON 格式、处理鉴权。每加一个 Agent 就要多写一套胶水代码。这个阶段我极度需要一套框架和一个协议来统一这些问题。
DeepAgents 给了我一个解决编排问题的起点。它把复杂的任务拆解成一个主 Agent 和多个子 Agent(subagents)的树形结构,主 Agent 负责理解任务、规划路径,子 Agent 负责垂直领域的执行。这样一来,编排逻辑从硬编码的 if-else 变成模型的推理决策,结构上就灵活得多。
1.2 四个模块的职责边界
我最终确定的架构分层是这样的:
- 编排层(DeepAgents):负责任务拆解、调度子 Agent、决定调用哪个技能、什么时候需要向用户提问。
- 能力层(Skills + MCP):Skills 是模型的提示词级技能包,MCP 是外部工具的标准接入协议。技能负责教模型"怎么完成任务",MCP 负责让模型能真正操作外部系统。
- 通信层(A2A):负责 Agent 与 Agent 之间的相互调用。A2A 协议定义了 Agent Card 发现机制、任务状态同步、消息格式,让不同框架、不同语言的 Agent 都能互相发现和协作。
这个划分的核心思路是把"思考"和"工具"彻底解耦。模型负责推理和规划,工具通过 MCP 接入,技能通过 Skills 加载,Agent 之间的协作走 A2A。任何一个 Agent 实例挂了,换一个同样能力的 Agent 来顶替,对整个集群毫无影响。
1.3 这套方案比"一个大 Agent 包揽一切"好在哪
如果你还没体会过多 Agent 集群和单体 Agent 的差别,我举个例子。假设你要做一个"竞品分析报告":需要抓取竞品官网内容、整理用户评论里的关键词、生成一份 Word 文档。
单体 Agent 的做法:写一个提示词,把所有要求全塞进去,指望一个大模型自己完成所有步骤。结果往往是:工具调用步骤一多就漏步骤,上下文被无关的抓取内容污染,最后的报告质量完全不可控。
集群化做法:定义一个 Orchestration Agent 负责拆解任务,生成三个子任务:抓取任务交给 WebScraper Agent,分析任务交给 TextAnalysis Agent,文档生成任务交给 DocumentAgent。每个 Agent 都有自己的 Skills 和 MCP 工具集,上下文只加载跟自己任务相关的信息。Orchestration Agent 只维护高层级的任务状态,不直接处理原始数据。
实测下来,集群方式的准确率和稳定性都要明显高于单体方式,而且每个模块可以独立迭代——文本分析的逻辑改了,不需要动抓取逻辑。
2. 核心细节解析:MCP、A2A、Skills 与 DeepAgents 的关键实现要点
2.1 MCP:把工具接入变成"插拔式"标准
MCP(Model Context Protocol)解决的是"模型怎么使用工具"的标准问题。在没有 MCP 之前,每个 Agent 框架都有自己的工具调用格式:有的用 OpenAI 的 function calling 格式,有的自定义 JSON Schema,接入一个新工具就要适配一次。
MCP 的思路是做一个类似"操作系统驱动"的抽象层。工具开发商只要实现一个 MCP Server,暴露统一的工具接口,任何支持 MCP 的客户端都能直接调用。我当时接入了一个内部的 Wiki 检索系统,用了大概半天时间写完 MCP Server,然后在多个 Agent 里同时复用,这个效率提升是非常直观的。
这里重点说一下 MCP Server 的结构。一个最基础的 MCP Server 用 TypeScript 写的话,大致是这种感觉:
import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; const server = new Server( { name: "wiki-search-server", version: "1.0.0" }, { capabilities: { tools: {} } } ); server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [{ name: "search_wiki", description: "搜索内部 Wiki 系统", inputSchema: { type: "object", properties: { query: { type: "string" }, limit: { type: "number", default: 5 } }, required: ["query"] } }] })); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; // 在这里调用真实的 Wiki 检索接口 const results = await searchWiki(args.query, args.limit); return { content: [{ type: "text", text: JSON.stringify(results) }] }; }); const transport = new StdioServerTransport(); await server.connect(transport);写 MCP Server 时最容易翻车的点,是工具描述的措辞。模型是根据 description 来决定什么时候调用这个工具的,描述里必须包含"什么时候用"和"怎么用"两个信息。比如"搜索内部 Wiki 系统,当用户想要查找项目文档、技术规范、设计文档时使用,query 参数建议提取用户问题的核心名词短语"。这种描述比简单写一句"搜索 Wiki"要可靠得多。
2.2 A2A:Agent 之间怎么发现、怎么对话
如果说 MCP 解决的是"Agent 用工具"的问题,A2A(Agent-to-Agent)解决的就是"Agent 用 Agent"的问题。
A2A 协议有几个关键概念:Agent Card、Task、Message。Agent Card 是一个 JSON 文件,描述了一个 Agent 的能力、端点地址和认证方式,相当于 Agent 的"名片"。客户端拿到这张名片,就知道另一个 Agent 能干什么、怎么调用它。Task 是一次具体请求的单元,包含状态(submitted、working、completed、failed 等)和工件(artifacts,即处理结果)。Message 分管线和用户消息两类,用来传递多轮对话内容。
在实际集群里,我通常在 DeepAgents 的子 Agent 中包装一层 A2A 客户端。比如集群里有一个独立的"翻译 Agent",它内部用的是 LangGraph,对外暴露了 A2A 接口。主 Agent 要翻译内容时,不是直接调用本地函数,而是通过 A2A 协议向这个翻译 Agent 发一个 Task,然后轮询任务状态,拿到翻译结果。
这种跨框架互通的测试我做过一次:同样是"把英文 PDF 转成中文摘要",我分别用了基于 DeepAgents 的 Node 进程和基于 Python 的独立服务,两边只是通过 A2A 消息互调,完全没有共享代码库。整个过程顺利得让我有点意外,这要归功于 A2A 把交互数据格式约束得很清晰。
2.3 Skills:如何把"经验"沉淀成可复用技能
Skills 跟前两者不一样,它更偏向提示词层面的能力封装。一个 Skill 就是一个文件夹,里面通常包含一个 SKILL.md 文件,描述这个技能的使用场景、工作流程和注意事项,还可以附带参考代码片段。
举例来说,我写过很多次"前端页面交付"的工作流,每次都要处理相同的步骤:先分析设计稿,再生成组件结构,接着写样式,最后检查响应式布局。后来我把这套流程整理成了一个名叫做 frontend-delivery 的 Skill,模型加载这个 Skill 后,就会自动按这套流程走,输出质量稳定得多。
一个 Skill 的目录结构大致是这样:
frontend-delivery/ ├── SKILL.md ├── templates/ │ └── component-template.tsx └── references/ └── style-guide.mdSKILL.md 的开头最好直接说明触发条件,比如"当用户要求根据设计稿或线框图实现前端页面时使用本技能"。正文部分写工作流程的五个步骤,每个步骤里写明输入输出和检查项。我建议不要写得像操作手册,而要写得像"老手给新手的叮嘱"——重点是提醒模型在哪个环节容易出错、哪个环节必须检查。
Skills 的真正威力在于组合。我现在把 MCP 工具调用也封装成了 Skill,模型加载这个 Skill 之后就知道"应该先查看有哪些工具、判断参数类型、再发起调用",而不是胡猜参数。数学建模类的需求,我会加载一个包含"线性回归、时间序列分析、蒙特卡洛模拟"提示词的 Skill,效果相当于给模型装了一个"数模经验包"。
2.4 DeepAgents 编排模型:主 Agent 与子 Agent 的任务树
DeepAgents 的核心概念我之前提过,是主 Agent 加若干 subagents 的结构。跟 LangGraph 那种显式定义状态机的思路不同,DeepAgents 更倾向于让模型在运行时动态决定下一步调哪个子 Agent。
在实操中,我通常这样定义一个子 Agent:
from deepagents import Agent def create_web_researcher(): """创建一个专门负责网页抓取与信息汇总的子 Agent""" return Agent( name="web_researcher", model="deepseek-chat", instructions="你负责抓取网页内容并提取关键信息,输出的信息必须结构化,包含来源链接、主要观点、时间戳。", skills=["web_search", "news_extractor"], mcp_servers=["fetch-server", "search-server"] )这个子 Agent 有独立的名字、模型、Skills 和 MCP 工具集。主 Agent 在决策时会把子 Agent 的"能力摘要"作为上下文的一部分,摘要来自 DeepAgents 在注册时自动生成的功能描述,也是由模型生成的。
我踩过的一个坑是:子 Agent 的主模型默认输出返回给主 Agent 时,会带上大量的中间推理过程,导致上下文迅速膨胀。后来我在定义子 Agent 时,明确要求"输出只包含最终结果的结构化 JSON,不要解释过程",上下文消耗立刻降下来的同时,任务完成率反而提高了。
3. 实操过程:从零搭建一个三节点 Agent 集群
3.1 环境准备与架构规划
下面我完整展示一下我是怎么搭建一个最小但完整的多 Agent 集群的。这个集群包含三个节点:一个编排主 Agent,一个负责内容检索的子 Agent,一个负责报告生成的子 Agent。
我的技术选型是:编排层用 DeepAgents 框架,工具接入走 MCP,子 Agent 之间的远程调用走 A2A,报告生成能力用 Skills 封装。整体跑在 Node 和 Python 混合的环境里,用 Docker Compose 管理各服务的生命周期。
首先准备好环境依赖:
# Node 环境,用于 MCP Server 和 A2A 网关 npm install -g @modelcontextprotocol/sdk # Python 环境,用于 DeepAgents 编排层 pip install deepagents langchain-openai # 本地启动一个 A2A 网关服务(这里用 Python 实现) pip install a2a-sdk说实话,第一次搭这种多组件环境时,最烦的就是依赖版本互相打架。我建议用一个全新的 Python 虚拟环境,Node 也尽量不要用系统自带的旧版本。我的做法是在项目根目录建了一个 .python-version 和 .nvmrc,锁死版本,避免半个月后重装时"昨天还能跑,今天全挂了"的窘境。
3.2 第一个实验:编写一个可复用的检索 Skill
这个集群里最先要沉淀的是"内容检索"能力。我写了一个名为 web-research 的 Skill,目录下有两个文件:SKILL.md 和一个回调脚本 research_helper.py。
SKILL.md 的一部分内容我贴出来:
# Web Research Skill 当用户要求查找资料、验证事实、收集行业动态时使用。 ## 工作流程 1. 将用户问题拆解为 2-3 个搜索关键词组合,覆盖不同常识面。 2. 使用 web_search 工具搜索,优先选择权威来源(官网/学术/新闻报道)。 3. 对每一篇结果,提取标题、时间、核心结论、数据点,存入结构化列表。 4. 如果搜索结果互相矛盾,保留冲突信息并标注"信息冲突"。 5. 最终输出 JSON 数组,每个元素包含 source、title、summary、timestamp。 ## 注意事项 - 不要只取第一页的搜索结果,尽量用不同的关键词组合交叉验证。 - 对数据的引用必须附上来源链接,宁缺毋滥。这个 Skill 在 DeepAgents 里注册之后,主 Agent 会在检索类任务上自动加载它。我遇到的一个小问题是,刚开始写的 Skill 描述太宽泛,导致 Agent 在"写文案"这种任务时也调用它。后来我在描述里加了触发条件,问题就消失了。
3.3 实战:配置 MCP Server 并接入检索工具
接下来是最关键的 MCP Server。我写了一个很轻量的 HTTP 抓取服务,用 Node 实现,通过 MCP SDK 暴露成一个工具:
// fetch-server.js import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import fetch from "node-fetch"; import * as cheerio from "cheerio"; const server = new Server( { name: "fetch-server", version: "1.0.0" }, { capabilities: { tools: {} } } ); async function fetchPage(url) { const response = await fetch(url, { headers: { "User-Agent": "Mozilla/5.0 (compatible; MyAgentCluster/1.0)" } }); if (!response.ok) throw new Error(`HTTP ${response.status}`); const html = await response.text(); const $ = cheerio.load(html); $("script, style, noscript").remove(); return $("body").text().replace(/\s+/g, " ").trim().slice(0, 8000); } server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [{ name: "fetch_webpage", description: "抓取指定 URL 的正文文本内容,用于资料收集和信息提取。", inputSchema: { type: "object", properties: { url: { type: "string", description: "完整 URL,必须以 http/https 开头" }, maxLength: { type: "number", default: 8000 } }, required: ["url"] } }] })); server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name !== "fetch_webpage") { throw new Error(`未知工具: ${request.params.name}`); } const { url, maxLength } = request.params.arguments; const text = await fetchPage(url); return { content: [{ type: "text", text: text.slice(0, maxLength) }] }; }); const transport = new StdioServerTransport(); await server.connect(transport);启动 MCP Server 的方式是把它作为独立进程跑,然后在 DeepAgents 配置里指定它的启动命令。配置完成后,主 Agent 就能通过模型自主决策,调用这个"线上抓取"能力了。
这里有一个容易被忽略的配置项:MCP Server 的超时设置。我在测试时发现,某些页面响应很慢,默认的超时时间根本不够用。我把超时调到了 30 秒,并给工具的 description 里加了一句"适合内容型页面,不适合大文件或登录页",模型会据此判断何时使用这个工具。
3.4 核心操作:注册 A2A 网关,串联多 Node Agent
接下来是 A2A 部分。我的做法是给两个子 Agent(retriever 和 reporter)各挂一个 A2A 网关,主编排节点通过网关向它们派发任务。
下面用 Python 实现一个最小的 A2A Agent 服务端:
from a2a_sdk import AgentServer, InProcessAgent from a2a_sdk.types import AgentCard, AgentCapability async def retrieve_agent(task_input: str) -> str: """执行检索任务,这里实际调用本地检索函数""" results = await local_search(task_input) return json.dumps(results, ensure_ascii=False) async def report_agent(task_input: str) -> str: """执行报告生成任务,加载 Skills 中的模板""" prompt = load_skill("report_generator") rendered = await llm_call(prompt, task_input) return rendered retriever_server = AgentServer( agent=InProcessAgent(handler=retrieve_agent), card=AgentCard( name="RetrieverAgent", description="负责全网信息检索与去重汇总", url="http://localhost:8001", capabilities=[AgentCapability(id="retrieve", description="输入检索需求,返回结构化信息")] ) ) reporter_server = AgentServer( agent=InProcessAgent(handler=report_agent), card=AgentCard( name="ReporterAgent", description="负责生成各类结构化报告", url="http://localhost:8002", capabilities=[AgentCapability(id="report", description="输入数据,返回 Markdown 报告")] ) )这两个服务启动之后,编排层通过 A2A 协议发现它们。DeepAgents 里的主 Agent 在收到"帮我写一份关于智能家居行业的调研报告"这个任务时,会先向 retriever 派发检索任务,拿到结构化信息后,再向 reporter 派发报告生成任务。
实测下来,A2A 的最大价值在于,这两个服务可以是完全独立的进程,甚至可以用不同技术栈实现——一个用 Python,一个用 Node,互不影响。这也是"可互通"的核心。
3.5 完整编排流程验证
最后我把整个流程串起来验证了一遍。主 Agent 收到任务后,通过 DeepAgents 的规划能力生成任务树:子任务一,调用 retriever;子任务二,调用 reporter;子任务三,合并输出。
关键的一段编排逻辑大概长这样:
from deepagents import Agent async def orchestrator(): main_agent = Agent( name="orchestrator", instructions=""" 你是一个任务编排 Agent。收到用户请求后,拆解为检索和报告两个子任务。 先调用 RetrieverAgent 获取资料,再调用 ReporterAgent 生成报告,最后汇总给用户。 不要跳过检索步骤直接生成报告。 """, a2a_agents=["retriever", "reporter"] ) result = await main_agent.run( "写一份2025年消费级AI硬件趋势报告,要求包含市场规模、主要玩家、技术路线对比" ) print(result.output)这个流程跑通之后,我就有了一条原则:任何新的业务能力,先试着封装成 Skill 或者 MCP Server,而不是直接写死在编排逻辑里。这一个原则让我后续扩展集群时省了非常多的事。
4. 常见问题与排查技巧实录
4.1 主 Agent 连续调用工具导致上下文爆炸
这是我最先遇到的问题。主 Agent 在编排多个子任务时,会把每个子任务的完整输入输出都保留在上文里,几轮之后上下文就快撑爆了,Token 费用也直线上升。
我的解决办法是:给子 Agent 的返回结果做摘要压缩。在子 Agent 的 instructions 里明确要求"返回内容只包含结论和关键数据,控制在一百字以内"。如果确实需要完整内容,就写入临时文件,返回一个文件路径引用。实测下来,同样任务的上下文消耗减少了约 60%,编排效果没有明显下降。
4.2 工具并行调用时相互污染
MCP 支持多个工具在一次回复内并行调用,但如果不做隔离,会出现一个工具的输入跑到另一个工具的请求里。这个问题的根源是工具名冲突或参数命名模糊。
我的排查思路是:给每个 MCP Server 分配独立命名空间,工具名尽量用带前缀的形式,例如 fetch_server_webpage 而不是 webpage。另外在 MCP Server 内部加上入参强校验,参数类型不对直接报错,不要静默忽略。这样一旦出错,立刻能在日志里定位到是哪一步导致的。
4.3 A2A 任务卡在 working 状态
A2A 的 Task 状态机里有一个常见的"卡死"状态:任务提交后一直处于 working,客户端一直轮询不到 completed。我遇到的真实原因是:子 Agent 处理过程中异常退出了,但没有更新 Task 状态,导致主 Agent 一直傻等。
我在 A2A 网关里加了保底逻辑:服务端超过 120 秒没有返回进度,就把任务标记为 failed,并附上超时原因。客户端则加了重试机制,允许对同一个 Task 重新提交一次。这条保底逻辑上线之后,集群长时间无人值守运行时的成功率上了很多。
4.4 Skills 之间的优先级冲突
多个 Skill 同时加载时会出现指令冲突,比如一个 Skill 说"输出 JSON",另一个说"输出 Markdown",模型就懵了。
我的经验是:Skills 描述里不写具体的输出格式要求,而是统一放到编排层的系统提示词里。SKILL.md 只专注于"任务怎么做"和"注意什么",不碰"最终输出长什么样"。这样冲突面就小了很多,如果系统提示词本身有变更,也只改一处。
4.5 模型在编排时跳过高价值步骤
模型为了"省事",经常在编排时跳过真正有价值的中间步骤,直接生成答案。比如分析类任务,它可能不先取数就直接给结论。
解决方法是给步骤定义成败标准。我在子 Agent 的输出里增加了一个"evidence"字段,要求必须包含实际检索到的数据或外部引用。主 Agent 判断用户请求涉及事实性问题时,如果子 Agent 返回的内容里没有 evidence,就判定该步骤未完成,触发重跑。
5. 集群扩展与性能调优心得
5.1 从三节点扩展到十节点的关键改进
我的集群从三节点扩展到十个节点之后,遇到了几个新问题。第一个是 MCP Server 进程太多,管理成本剧增。后来我统一用 Docker Compose 管理所有 MCP Server,每个 Server 一个容器,启动、重启、日志查看都规范起来。
第二个是子 Agent 的功能描述互相重叠,编排模型分不清该调哪个。我在注册 A2A Agent 时,给每个 Agent 的 Agent Card 加上了明确的 capability 标签,并在描述里写上"不支持什么"。比如检索 Agent 明确写"不负责内容总结",总结 Agent 明确写"不负责原始信息采集"。这种边界约束让编排准确率提高了不少。
5.2 三类任务的性能对比
我把集群上线后实际处理过的一组任务数据整理成了一张对比表,供大家参考:
| 任务类型 | 单体 Agent 耗时 | 集群方式耗时 | 集群方式错误率 | 备注 |
|---|---|---|---|---|
| 竞品信息搜集与汇总 | 约 2.5 分钟 | 约 1.2 分钟 | 明显降低 | 检索 Agent 并行处理效果显著 |
| 长文档翻译与摘要 | 约 4 分钟 | 约 3 分钟 | 略有降低 | 上下文隔离减少混淆 |
| 结构化周报生成 | 约 3 分钟 | 约 2 分钟 | 降低一半以上 | Skills 复用加快格式化速度 |
这套数据不是严谨的基准测试,但趋势很明确:任务越复杂、所需工具越多,集群化的收益就越明显。如果是简单的一问一答,单体 Agent 反而更快,这也是取舍时需要衡量的。
5.3 成本优化:如何削减 Token 消耗
多 Agent 集群最肉疼的就是 Token 成本。每个子 Agent 都要独立消耗上下文,主 Agent 还要带着一堆子任务状态。我做了几项优化,效果很直接。
第一,模型分层:简单的工具调用走便宜的小模型,复杂的规划与报告走能力更强的大模型。第二,上下文瘦身:MCP 工具返回结果最大长度从 8000 字下调到 4000 字,检索类结果做完摘要再写回上下文。第三,A2A 任务结果的保留策略:完成任务的结果只保留最终 artifact,中间状态直接丢弃。这几项加起来,单次复杂任务的 Token 成本降了约四成。
6. 安全边界与集群治理经验
6.1 MCP 工具的权限控制
多 Agent 集群里的每个子 Agent 都开放 MCP 工具,权限如果不控制,风险很大。我在 MCP Server 上加了一层"工具白名单"配置,每个子 Agent 只能调用自己被授权的工具。比如公开页面抓取工具对所有 Agent 开放,但内部系统查询工具只对特定的内部 Agent 开放。
这个白名单机制不用写得很复杂,一个 JSON 配置加中间层校验就够了。关键是要在架构设计阶段预留这个位置,而不是等出问题了再加。
6.2 Skills 的版本管理
Skills 是文本文件,天然适合做版本管理。我建议把所有 Skills 放进 Git 仓库里,每次变更都走 MR 审查流程。原因很简单:一个 SKILL.md 的小改动,可能影响集群里所有加载它的 Agent 的行为。没有版本管理,出问题之后根本无法回溯。
我自己维护了一套 Skills 的目录规范:stable 目录放经过验证的技能,beta 目录放还在迭代的技能,deprecated 目录放废弃技能。主 Agent 默认只加载 stable 目录里的技能,需要试用 beta 技能时手动指定。
6.3 集群可观测性:日志与追踪
多 Agent 集群的调试比单体难很多,一个任务可能横跨多个进程和多个工具。我目前的方案是统一结构化日志:每个请求携带一个 trace_id,所有子 Agent、MCP Server 在日志里附带这个 trace_id。排查问题时一条命令过滤出来,按时间线重新拼装整个任务流转过程。
A2A 网关里我也会记录 Task 的每次状态变化。这让我能精确还原"任务哪一步开始、哪一步卡住、最终返回了什么"。这套可观测基础设施,让集群规模从 5 个节点扩到 10 个节点时,维护成本没有线性上涨。
7. 未来扩展方向:从集群到生态
7.1 把 A2A 升级为跨组织协作
当每个团队甚至每家公司都把自己的 Agent 通过 A2A 标准暴露出来,Agent 就能跨组织协作。现在 A2A 协议已经支持 Agent Card 的公开发现机制,理论上两个独立团队部署的 Agent 可以直接互相调用。你可以想象成:你的 Agent 能直接调用另一个团队的"行业报告生成 Agent",只要双方遵循同一套协议。这就是标题里"可互通"的深层价值。
7.2 从"技能包"到"技能市场"
Skills 做多了之后,我发现完全可以做一个本地的 Skill Registry,每个 Skill 有名称、作者、版本、描述和依赖的 MCP Server 列表。主 Agent 在启动的时候会自动拉取最新技能清单。未来如果能力足够,甚至可以做一个开放技能市场,让开发者提交各自的 Skills 供社区使用。
7.3 编排策略的持续进化
DeepAgents 的编排目前还是"模型自由发挥为主",但它也支持给主 Agent 提供结构化的规划模板。我下一步的计划是把过去成功的编排路径沉淀成模板。比如"调研报告类任务"固定使用 retriever -> reporter -> formatter 的流程,"数据分析类任务"固定使用 dataloader -> analyzer -> visualizer 的流程。有了这些模板,模型做规划的稳定性会更高,新场景下的初始化过程也更可控。
最后再说一个我在整个实践中最有感触的点:这套体系里最值得投入时间打磨的,不是模型本身,而是 Skills 和 MCP Server 的质量。工具定义得清楚、技能描述得准确,集群的表现会有质的飞跃;反过来,工具定义含糊、技能互相矛盾,再强的模型也带不动。我现在的习惯是每写完一个 Skill,就用一组典型任务实测一遍,迭代两三轮之后再放进 stable 目录。这种"以工具质量为抓手"的思路,比在提示词上反复调参要有效得多。