多智能体协作(Multi Agent)现在最值得先搞清楚的一件事,不是模型又多强,而是 Agent、Skills、Tools、MCP、Harness 这些概念在一条真实链路里到底怎么配合。我最近把一个以 Multi Agent 为主线、配合 Harness 调度、Skills 技能封装、Tools 工具调用和 MCP 协议接入的完整项目跑了一遍,最后落到 AI 职业规划这个示例场景上。结论先说:单纯把 MCP 接上、再在界面里堆几个 Agent 名字,不叫多智能体协作;真正让系统跑顺的,是职责拆解、Skill 封装、任务交接和日志回读这几件事。这篇文章按实际落地顺序来拆,不做概念堆砌,尽量让读者照着能把最小流程复现出来。
1. 多智能体协作到底在解决什么问题
1.1 从单 Agent 到多 Agent,变的不是模型而是分工
很多人误以为“多 Agent”就是把同一个模型复制好几份,然后让它们对话。实际碰过之后会发现,这种设计很快会出问题:几个 Agent 都在抢同一段上下文,输出互相覆盖,最后根本分不清哪个结论是哪个 Agent 给的。
单 Agent 的真正瓶颈不是能力,而是职责边界。当一个提示词里既要分析用户背景、又要查行业数据、还要生成规划建议、最后还要检查输出质量,上下文会变得很长,模型容易丢掉早期指令,工具调用也会越来越混乱。多 Agent 解决的就是这个问题:把一个大任务拆成几个边界清晰的小任务,每个 Agent 只负责一段,做完之后把结构化结果交给下一个。
我建议先理解一句话:多 Agent 协作本质上是一个“工程问题”,不是“模型问题”。模型可以不变,变化的是任务怎么拆、结果怎么传、错误怎么处理。这也是为什么后面必须先搭 Harness,而不是直接堆 Agent。
1.2 Skills、Tools、MCP、Harness 在一条链路里各管什么
这四个词经常被混着说,实际上分工差别很大。简单理解:
- Harness是执行框架和调度层,负责启动 Agent、控制任务循环、调用工具、收集日志、处理重试。它解决的是“系统怎么跑起来”。
- Agent是带角色的推理单元,负责判断当前任务该怎么完成、该调用哪个工具、该输出什么。
- Skill是可复用的技能包,本质是一套指令、模板和参考数据,让 Agent 知道“这类任务按什么规范做”。
- Tool是原子操作,比如查数据库、算薪资区间、调搜索接口。Tool 是实际执行动作的地方。
- MCP是工具和 Agent 之间的标准协议,解决“工具怎么被 Agent 发现和调用”的问题。
五个角色的关系可以类比成一支施工队:Harness 是项目经理和流程制度,Agent 是各工种工人,Skill 是作业指导书,Tool 是电钻和测量仪,MCP 是统一的电源和接口标准。只看单个工人能力没用,真正决定项目进度的是流程、分工和接口。
我建项目时会把它们分成两层:底层是 Harness 和 MCP,提供运行环境和工具通路;上层是 Agent 和 Skill,提供业务逻辑。Tools 则横跨两层,既是底层能力,又被上层按需调用。
2. 先搭建一个最小可运行的 Harness 环境
2.1 环境准备:模型接口、依赖与目录结构
跑一个最小 Harness,不需要一开始就上重型框架。先用 Python 3.10 以上版本,配一个兼容 OpenAI 接口的大模型服务,再准备一个清晰的目录结构。这里说的是通用做法,如果你的环境版本不一致,先确认依赖版本再继续。
我一般这样建目录:
project/ agents/ # 各 Agent 的定义和提示词 skills/ # 技能包,每个技能一个目录 tools/ # 工具函数 mcp/ # MCP Server 代码 tasks/ # 输入任务文件 outputs/ # 输出结果 logs/ # 运行日志为什么目录要先建好?因为多 Agent 系统跑起来之后,最怕的不是逻辑写错,而是文件乱放。日志找不到、输出不知道写哪、Skill 路径写错,这类问题占排查时间很大比例。
Harness 这个词在不同项目里指的东西略有差别,社区里常提到的 DeepSeek Harness、Codex Harness 之类,本质都是给模型套了一层任务循环外壳。名称不重要,重要的是它承担的职责:读配置、调模型、执行工具、维护对话状态、收集日志。自己写一个最小 Harness 也不难,后面会给示例结构。
2.2 用 Skill 封装一个职业画像分析能力
Skill 的目录约定在社区里比较常见的是SKILL.md加资源目录。一个技能包里至少包含:技能名称、适用场景、执行步骤、输出规范、可选参考文件。
以“职业画像分析”这个技能为例:
skills/career-profile/ SKILL.md examples/ input_sample.json output_sample.jsonSKILL.md可以写成这样:
--- name: career-profile-analysis description: 解析用户职业背景,输出结构化画像字段 when_to_use: 收到用户背景描述或简历信息时 --- 1. 从文本中提取字段:current_role、years、skills、industry、goal。 2. 缺失字段标记为 unknown,不要猜测。 3. 技能列表按熟练度排序。 4. 只输出 JSON,不要额外解释。为什么要用 Skill 而不是把这段直接写进 Agent 提示词?因为同一个技能可能被多个 Agent 复用,比如用户画像分析技能,规划 Agent 要用,评估 Agent 也要用。写成 Skill 之后,改一处就全局生效,这就是技能封装的核心价值。
社区里能见到的 superpower skills、Claude Code 那类 SKILL.md 约定,思路都差不多。至于某个具体技能包质量如何,要看它的步骤是否可执行、输出是否结构化、是否包含边界条件。照搬之前先在小样本上试一次。
2.3 最小用例:让一个规划 Agent 先跑通
最小 Harness 只需要做三件事:读 Skill、调模型、返回结果。先用单条输入验证,不要一上来就搞并行。
下面是一个演示性质的最小代码结构:
# harness_demo.py(演示结构,实际参数以你的环境为准) def load_skill(skill_path): # 读取 SKILL.md 和示例文件 return {"prompt": read_file(skill_path), "examples": read_examples(skill_path)} def run_with_skill(task, skill_path, client): skill = load_skill(skill_path) messages = [ {"role": "system", "content": skill["prompt"]}, {"role": "user", "content": task}, ] resp = client.chat.completions.create( model="your-model", messages=messages, temperature=0.3, ) return resp.choices[0].message.content跑通之后,用三条标准判断是否正常:
- 进程能启动,不报依赖错误;
- 单条任务能返回符合 Skill 要求的 JSON;
- 日志里能看到完整调用链路,也就是“读了哪个 Skill、调了哪个模型、返回了什么”。
我自己会先用一段两三百字的用户描述做测试,比如“我有五年前端开发经验,熟悉 React 和 Node.js,想转 AI 应用开发”,看模型能不能正确提取字段。这一步的目的是把输入输出链路钉死,后面再加 Tools 和 MCP 时,出问题就知道是新增环节的问题,而不是基础链路的问题。
3. 接入 Tools 和 MCP Server,把 Agent 从“只会说”变成“能办事”
3.1 Tools 的粒度怎么设计
Agent 文本输出得再漂亮,没有真实数据支撑也只是一个“话痨”。Tools 就是让 Agent 能查数据、能计算、能调用外部接口的通道。
Tools 设计第一条原则是:越原子越容易复用。不要写一个叫do_career_planning的大函数,因为一旦 Agent 判断错误,整个调用就失败了。应该拆成:
search_jobs(keyword, city):查招聘岗位;calc_salary_range(role, years, city):按城市和经验估算薪资区间;search_skill_demand(skill):查某个技能的市场需求。
每个 Tool 的入参和出参都要固定。入参用 JSON Schema 描述,出参统一成{ "code": 0, "data": ..., "message": "..." }这种结构。为什么强调这个?因为 Agent 需要靠返回值判断下一步,如果返回格式不稳定,后续的规划生成质量就不可控。
另外,每个 Tool 都要设超时时间。一个查岗位接口如果卡住 30 秒,整个 Harness 都会卡住。实际做的时候,我会把外部接口超时控制在 5 秒到 10 秒,并对超时和异常单独返回错误码,让 Agent 知道“这个工具失败了,而不是返回了空数据”。
3.2 MCP 接入的协议边界和验证
MCP(Model Context Protocol)解决的核心问题是:Agent 怎么“发现”工具、怎么按规范调用工具。如果没有统一协议,每接一个外部系统就要写一套自定义接口,Agent 的提示词也会越来越乱。
MCP 的基本结构是 Server 和 Client。Server 端暴露三类能力:Tools、Resources、Prompts。Client 端是 Harness 或 Agent 运行时,通过标准协议跟 Server 通信。常见的传输方式有 stdio 和 HTTP/SSE。
假设要接一个职业数据库 MCP Server,配置可能长这样:
{ "mcpServers": { "career-db": { "command": "python", "args": ["mcp/career_db_server.py"] } } }接入之后不要直接跑完整流程,先做三步验证:
- 列出工具:确认 Harness 能连上 Server,能看到 server 暴露了哪些 tool;
- 调用一个工具:传一个最小入参,看返回格式是否符合预期;
- 制造一个错误:传错误参数,确认错误信息能正常返回给 Agent,而不是让进程崩溃。
这里容易踩的坑是:工具列表能加载,但实际调用必失败。这时候多数不是协议问题,而是 Server 的启动路径、依赖环境、工作目录不对。用 stdio 连接的 MCP Server,它的当前工作目录往往取决于启动它的父进程,所以先确认日志里 Server 到底有没有正常启动。
现在设计协作、绘图、建模领域的 MCP Server 也多起来了,比如蓝湖、MasterGo、Blender 相关社区项目都有 MCP 实现。这类垂直 MCP 的价值在于,把专业能力封装成 Agent 能调用的工具,但接入前一定要先验证它的返回结构和稳定性。
3.3 常见接入失败:不是协议问题,而是路径和依赖
MCP 接入失败最常见的几个原因,排在前面的几乎都不是协议问题:
- MCP Server 启动失败,原因是 Python 依赖没装全,或者 Node 版本不对;
- 连上了但找不到工具,原因是 Server 工作目录不对,导致相对路径下的工具注册文件没加载;
- 调用时报“参数校验失败”,原因是 Agent 传了 JSON Schema 之外的字段;
- stdio 模式下日志和正常输出混在一起,导致 Client 解析失败;
- HTTP 模式端口被占用,或者超时时间设置得太短。
排查时按顺序来:先看 Server 自己能不能独立启动,再通过 Client 看工具列表,最后才看单次调用。不要一上来就改 Harness 代码,很多问题在 Server 侧就能定位。这个顺序我每次都会遵守,因为跨进程的问题最容易因为“两边看代码都觉得没问题”而浪费时间。
4. Deep Agent 的推理编排:从串行到多轮协作
4.1 普通 Agent 与 Deep Agent 的差别
普通 Agent 拿到任务后直接生成答案,适合“查资料、转格式、写摘要”这类简单场景。Deep Agent 不一样,它会在一次任务里进行多步推理,把任务拆成“规划、执行、观察、反思、修正”这几个阶段。
更直白地说,Deep Agent 会先想清楚要分几步做,每步做完还要看一眼结果是否正确,然后再决定下一步。比如生成职业规划时,Deep Agent 不会直接输出一段建议,而是先问自己:用户目标是什么?当前技能差距在哪里?市场需要什么?然后再决定要不要调用工具补充数据,最后生成规划并进行一次自我检查。
两者差别可以用这个表概括:
| 维度 | 普通 Agent | Deep Agent |
|---|---|---|
| 任务长度 | 单轮完成 | 多轮计划-执行-反思 |
| 工具使用 | 偶尔调用 | 频繁调用并回读结果 |
| 错误处理 | 失败即返回 | 失败后尝试修正策略 |
| 适合场景 | 摘要、格式转换、简单问答 | 规划、决策、复杂分析 |
| 资源消耗 | 低 | 较高 |
所以 Deep Agent 不是“更高级的模型”,而是一种更重的推理流程。代价是耗时更长、Token 消耗更多,需要更完善的日志和中断机制。
4.2 多 Agent 协作时的任务交接和上下文管理
多 Agent 之间最忌讳的是把完整对话历史直接传给下一个 Agent。Agent A 的推理过程、中间错误、重复尝试,对 Agent B 来说基本都是噪音。正确做法是传一份结构化交接信息。
比如职业规划场景里,信息采集 Agent 完成之后,交接给规划 Agent 的消息应该是:
{ "task_id": "task_001", "current_role": "前端开发工程师", "years": 5, "skills": ["React", "Node.js", "TypeScript"], "goal": "转型 AI 应用开发", "market_data": { "ai_engineer_salary_range": "25k-50k" }, "status": "profile_ready" }这样规划 Agent 只需要读这个 JSON 就能接续工作,不需要翻前面的对话。上下文管理的关键是:每个 Agent 只看到自己需要的字段。共享上下文可以放任务描述、用户原始输入、结构化中间结果;模型内部思考过程不要让其他 Agent 看到。
另外要防止多 Agent 之间出现循环调用。比如评估 Agent 和规划 Agent 互相反复修改结果,虽然看起来“协作深入”,实际上可能只是两个 Agent 在互相覆盖对方输出。我会给每个 Agent 设置最大执行轮数,到达上限后强制输出当前结果并记录告警。
4.3 参数调整和判断标准
Deep Agent 和 Multi Agent 跑起来之后,最常调整的是这几个参数:
- max_iterations:单个 Agent 最多执行多少轮。调大能提高任务完成率,但会显著增加耗时和成本。
- temperature:生成规划类内容时我一般用 0.2 到 0.4,避免输出过于发散;简单抽取任务甚至可以更低。
- tool_timeout:外部工具调用的超时时间,按接口真实耗时设置。
- concurrency:并发数。默认先从 1 开始,确认一切正常再逐步提高。
判断系统是否“健康”,不要只看最终输出好不好看,要看几个可量化指标:任务完成率、平均耗时、失败重试率、输出格式合法率。我实际跑的时候会先记录 20 条单任务的四个指标,作为基线。后面调参时,只有指标比基线好,才保留这个参数组合。
5. 实战案例:AI 职业规划助手的 Multi Agent 流程
5.1 场景拆解与 Agent 分工
为了让前面的概念能落地,这里用一个 AI 职业规划助手做示例。输入是一段用户背景描述,输出是一份包含现状评估、技能差距、市场机会、行动计划的规划方案。
拆出来的 Agent 分工如下:
| Agent | 职责 | 使用 Skill | 需要 Tools |
|---|---|---|---|
| 信息采集 Agent | 从用户描述中提取职业画像 | career-profile | 无 |
| 技能评估 Agent | 对比用户技能与目标岗位要求 | skill-gap | search_skill_demand |
| 市场分析 Agent | 查询岗位数据和薪资区间 | market-analysis | search_jobs, calc_salary_range |
| 规划生成 Agent | 综合以上结果生成规划 | career-plan | 无 |
| 评审 Agent | 检查规划是否完整、是否一致 | review | 无 |
这个分工里,评审 Agent 是很多项目容易漏掉的。它不生产内容,只做质量检查,专门发现“规划里提到的目标和评估结论不一致”“技能差距分析没有落到行动步骤”这类问题。加一个评审角色,看似多了一次模型调用,实际上大幅减少后续人工返工。
5.2 输入、Skill 调用和结果输出
整个流程从一条用户输入开始。示例输入:
{ "user_input": "我有五年前端开发经验,熟悉 React 和 Node.js, 做过监控平台,想转 AI 应用开发,希望两年内完成。" }流程执行顺序是:信息采集 Agent 先跑,输出用户画像;然后技能评估 Agent 和市场分析 Agent 可以并行跑;两个结果合并后交给规划生成 Agent;最后评审 Agent 检查。这里不建议一开始就让五个 Agent 全部并行,因为后续步骤依赖前面步骤的输出,并行反而会造成等待和结果不完整。
最终输出规范可以这样定:
{ "task_id": "task_001", "profile": { "current_role": "前端开发工程师", "years": 5 }, "skill_gap": { "missing": ["Python", "LLM 应用架构"], "priority": "high" }, "market": { "ai_engineer_salary_range": "25k-50k", "demand": "high" }, "plan": [ { "month": 1, "action": "完成 Python 基础与 FastAPI 实战" }, { "month": 2, "action": "实现一个基于 LLM 的内部工具 Demo" } ], "review_result": "pass" }这里有个关键点:每一步都要让模型“只输出自己负责的字段”,不要让它顺手把其他 Agent 的活也干了。职责越清晰,输出越稳定。
5.3 验证结果与边界
怎么判断这个职业规划结果好不好?我一般看四件事:
- 一致性:规划里的目标是不是用户原始输入里的目标,技能差距是否对应真实缺失;
- 可执行性:每个行动项是否有时限、有具体动作,而不是“提升技术能力”这种空话;
- 数据引用:市场分析部分是否引用了工具实际返回的数据,而不是模型编造的数字;
- 格式合规:是否按约定的 JSON Schema 输出,评审参数是否明确。
同时要清醒认识边界。这个系统不保证给出“最优职业路径”,它只能基于输入数据和外部信息的时效性做合理推断。市场数据会过时,用户描述可能不完整,模型也会有幻觉。所以生产环境里,职业规划这类严肃场景需要增加人工审核环节,不能让输出直接以“官方建议”形式呈现给终端用户。
低配置环境也能跑这个 demo,但要把模型输入控制得短一点、并发数降到 1,并且不要一次处理太长的用户简历。能跑通和能批量跑是两个阶段,demo 阶段先把质控链路做出来。
6. 批量任务、稳定性与排查链路
6.1 从单条任务到批量任务,变化点在哪里
单条任务跑通之后,很多人直接写一个 for 循环批量跑测试文件,结果跑到第三条就卡住,或者输出文件互相覆盖。批量任务和单任务最核心的差别,不是“多跑几次”,而是增加了三类问题:
- 输入管理:批量任务的输入要列表化,每条任务有唯一 ID;
- 输出管理:每条任务的结果要落到独立文件,命名规则要稳定;
- 过程管理:失败的任务要可重试,不能因为一条失败导致整个批次终止。
输入格式可以先统一成一个 JSON Lines 文件,每行一条任务。输出按outputs/{task_id}.json命名,日志按logs/{task_id}.log记录调用链。这样即使一条任务失败,也不影响其他任务,而且事后能定位。
6.2 日志、重试和输出命名
批量跑之前,先把日志格式定下来。我建议每条日志至少包含:任务 ID、Agent 名称、动作类型、耗时、状态。日志比什么监控都重要,因为多 Agent 系统一旦出错,如果日志不全,排查两小时找不到方向。
重试策略要分情况。如果是限流或临时网络错误,可以重试两到三次,间隔递增。如果是工具参数错误或输入格式错误,重试没有意义,应该直接标记失败并记录原因。判断标准很简单:失败原因是否可能通过重试消失。能消失就重试,不能消失就停止。
输出命名也不要小看。用任务 ID 而不是时间戳,因为时间戳在毫秒级并发下可能重复,而且不方便上游系统对账。我见过很多批量任务“跑完了”但结果文件丢了,最后追踪下来都是命名冲突和路径覆盖,这个坑一定要提前堵住。
6.3 排查顺序:先看现象,再逐层定位
多 Agent 系统的排错链路和传统后端不太一样,它多了一层“模型行为不确定性”。同一个任务跑两次,结果可能不完全一样,所以排查时不能只对着代码看。我的习惯顺序是:
- 看现象:是报错、卡住、无输出,还是输出格式不对。现象描述直接决定排查方向;
- 看输入:检查任务 ID、输入内容、文件编码、路径是否存在。这一步能排除大量低级问题;
- 看日志:确认 Harness 是否正常调度、Agent 是否走到了预期步骤、工具调用是否成功;
- 看环境和参数:依赖版本、模型服务连通性、并发数、超时时间、最大轮数;
- 最后才改代码:确认前四步都没有问题时,才怀疑逻辑本身。
尤其要提醒一点:报错不一定是模型问题,很可能是输入格式、工具路径或依赖版本问题。我自己遇到过多次“Agent 不按指令输出”,排查半天发现是传给它的 Skill 文件路径写错了,模型读到的根本是另一个技能的提示词。所以排查时,要先确认数据真的传到了模型手里,再怀疑模型的判断。
7. 生产化落地:哪些能复用,哪些要重写
7.1 学习 Demo 和生产系统的差距
Demo 跑通和上线能用的距离,通常比想象中要大。Demo 阶段可以容忍输入不规范、没有鉴权、失败就重启;生产系统不行,必须处理并发、限流、数据隔离、审计、异常兜底、结果可追溯。
差距最大的是任务调度。Demo 里的循环直接顺序执行,生产环境需要引入任务队列,支持优先级、超时取消、失败隔离、人工介入。第二个差距是数据安全。给外部系统提供数据时,MCP 连接要鉴权,工具调用要记录操作日志。第三个差距是模型成本控制,批量任务跑起来之后,Token 消耗增长很快,需要设置预算上限和告警。
如果你只是学习,默认配置通常够用;如果要长期使用,就要把日志、输出目录和任务队列提前整理好。
7.2 可复用的工程清单
把完整项目跑一遍之后,我整理了一份通用清单,适合大多数 Multi Agent + Harness + Tools + MCP 项目:
- Skill 目录统一:每个技能包含 SKILL.md、示例输入、示例输出,命名清晰;
- Tool 接口稳定:统一入参 Schema 和出参结构,自带超时和错误码;
- MCP Server 可独立启动:不依赖 Harness 的当前工作目录,日志与协议输出分离;
- 任务上下文结构化:Agent 间只传固定字段,不传完整对话;
- 日志带任务 ID:每条日志能关联到具体任务和 Agent;
- 失败可重试:重试只针对临时性错误,永久失败直接标记;
- 输出不覆盖:用任务 ID 命名结果文件,输出目录与日志目录分离;
- 评审与兜底:至少有一个 Agent 只做质检,不参与生成。
这些不是高级功能,而是基础工程习惯。多 Agent 系统比单服务更复杂,一旦基础不牢,后续每加一个 Agent、每接一个 MCP,都会放大混乱。
7.3 建议的下一步
我个人更建议把推进顺序固定下来:单任务先稳,再批量化,最后才做接口化和并发优化。很多项目死在第一步就开最大并发,结果日志全乱,根本不知道谁跑成功了。
下一步值得研究的方向有两个。一个是把 Skill 从“文本提示词”升级为“可执行的技能流程”,让 Skill 内部能嵌套调用多个 Tool,而不是只靠模型按提示词自由发挥。另一个是把 MCP 接入的企业系统做细,比如把真实岗位数据库、内部人才库通过 MCP 接进来,这样职业规划的输出才真正有数据基础。
踩过几次之后我发现,很多问题不是工具能力不够,而是前置环境和输入材料没有处理干净。Multi Agent、Harness、Skills、Tools、MCP 这套东西,真正落到生产环境的门槛不在“会不会喊概念”,而在“能不能稳定复现结果”。先把最小链路跑稳,再把每一步的输入输出和错误处理理清楚,后面接什么都顺。