
你发现没有最近技术圈讨论 Agent 的文章越来越多但真正能把多智能体系统落地的人并不多。很多人以为多 Agent 就是把几个 Prompt 拼在一起让几个模型各说各话然后合并结果。实际跑一遍就会遇到几个非常具体的问题任务到底由谁接手工具调用结果如何回到上下文模型在反复兜圈子时怎么终止多个 Agent 的日志如何串联。这些问题不解决所谓多智能体只是一个概念。这篇文章我会把一个最小可运行的多智能体系统拆开讲名字就叫 Hermes Agent。它是一个由 1 个主控调度器和 3 个 Worker Agent 组成的轻量框架模型底座使用 Qwen 通义千问。核心的技术动作有三个按意图拆解任务、把子任务路由给正确 Agent、把各 Agent 的结果汇总成最终答案。读完这篇文章你不仅能跑通一个多智能体示例还能理解主控调度、工具调用、防死循环、上下文管理等工程问题。更重要的是你能从“会调接口”往上走一层开始用工程思维评估多智能体系统它好在哪里、坏在哪里、优化该从哪里入手。1. 这篇文章真正要解决的问题1.1 单 Agent 能做的事为什么要拆成多 Agent先想一个问题你在企业里做一个“智能客服”系统用户问一句“我们的销售额同比下降了 8%帮我查一下原因并写一封给领导的汇报邮件”。如果只用单 Agent模型要同时完成三件事从数据库或知识库查出销售数据分析可能的原因生成一封语气得体的邮件。模型当然可以一次生成但效果往往不稳定。数据没查准原因分析就会编造上下文太长前面查到的数据会被后面冲淡你也没办法单独优化“检索”“分析”“写作”任何一个环节。换成多智能体之后可以拆成三个角色检索 Agent 负责查数据分析 Agent 负责人肉理解写作 Agent 负责输出邮件。每个 Agent 的提示词和工具都围绕单一职责设计输出质量更容易控制。类比来说单 Agent 像是一个人又做项目经理、又写代码、又做测试、又写文档听起来全能但关键环节出错的概率很高。多智能体更像一个正规团队有人拆任务有人做执行有人负责最终交付。这个类比不完美但方向是对的多智能体的本质不是把模型变多而是把复杂任务拆成可以独立验证、独立优化的小任务。1.2 多智能体真正的难点不在模型而在编排很多开发者第一个多智能体 Demo 是这么写的定义三个角色给他们不同的 system prompt然后并行调用模型最后把三个结果拼接。这种写法当然也算多智能体但只能处理“各写一段”这种简单并行任务。真正需要多智能体的场景往往是任务之间有依赖关系或者执行过程中需要根据中间结果决定下一步。比如“先检索资料再根据资料生成代码最后运行代码验证结论”。这种情况下你必须有一个人来负责编排谁先执行、谁后执行、中间结果交给谁、出现问题让谁重试。这个负责人就是主控调度器也叫 Orchestrator。在 Hermes Agent 架构里它不直接干活而是负责意图识别和任务分发。如果把 Worker Agent 比作执行工程师主控就是项目经理。项目经理不一定是最懂技术的人但一定是最清楚任务流程的人。1.3 什么样的读者最适合读这篇文章如果你是下面几类人这篇文章会比较有用已经在用 Qwen 或者其他大模型 API想从单轮调用进阶到多智能体架构看了很多 Agent 概念但还不知道一个 Agent 系统应该怎么组织代码正在做一个知识库问答或者自动化办公项目需要多个角色配合完成复杂任务想了解 MCP、Skill、工具调用这些概念在实际代码里到底怎么落地。如果你只是想找一个开箱即用的商业客户端这篇文章不会教你怎么安装某个现成软件。但要提醒一句现成客户端能帮你节省搭建时间但理解多智能体的核心架构会让你在排错和二次开发时不再依赖别人。2. 多智能体核心概念Agent、交互模式、Skill 与 MCP2.1 Agent 与 Multi-Agent 的概念边界在继续之前先把几个容易混淆的术语说清楚。Agent智能体不是一个严格统一的定义。在这篇文章里我们可以把它理解成一个“有角色、有工具、能执行任务”的模块它比单纯的 Prompt 多了一步可以根据模型输出决定是否调用工具也可以根据工具返回结果继续推理。Multi-Agent多智能体是指多个这样的模块协同工作。协同的关键在于它们之间如何通信、如何分工、如何避免冲突。很多人以为多个模型同时在跑就是多智能体其实那只是并行调用。多智能体真正的特征是角色化和交互每个 Agent 只负责自己领域内的内容并且会与其他 Agent 或主控交换信息。主控调度Orchestrator是负责分配任务的角色。它不一定是单独一个模型也可能是一个路由函数。在我们后面的代码里主控本身也调用 Qwen因为它需要理解用户意图并决定任务应该交给谁。Worker Agent执行智能体是真正干活的角色。它接收主控下发的子任务可能调用工具也可能直接生成文本然后把结果返回给主控。2.2 多智能体的四种交互模式不同多智能体系统最大的差别体现在 Agent 之间如何交互。社区里总结比较多的是四种模式交互模式典型结构适用场景优点缺点星型 / 主从模式一个中心调度器管理多个 Worker任务类型多、需要统一分配职责清晰、容易控制流程中心节点可能成为瓶颈流水线模式上一个 Agent 的输出作为下一个输入有固定处理顺序的任务流程明确、依赖关系清晰链路长时延迟高网状 / 对等模式Agent 之间可以直接对话协商高度动态的复杂问题灵活、适合探索容易失控、上下文混乱分层模式高层 Agent 管理下层 Agent 组大型系统例如一个小组长管多个组可扩展性好设计复杂、调试成本高这篇文章选择的星型主从模式是四种模式里最容易落地、也最容易排查问题的。你只需要盯住一个主控的日志就可以看到所有子任务的分发和返回。2.3 Skill 和 MCP 到底有什么区别很多人在搭建 Agent 时会遇到两个词Skill 和 MCPModel Context Protocol模型上下文协议。它们不是同一个层面的东西。MCP 是一种标准化连接协议。它解决的是“模型如何统一地调用外部工具、数据源和资源”。比如你的系统里有一个数据库有一个文件系统有一个搜索 API。如果不做标准化每个工具都要写一套自己的调用方式模型无法泛化。MCP 相当于把工具接口统一成了模型熟悉的格式让不同的外部能力可以被同一套机制接入。Skill 更像是对 Agent 能力的一种封装。它可能包含一组提示词、一组工具调用模板、以及触发条件。比如“代码审查 Skill”可能包含读取代码文件、执行静态检查、调用模型做 review、输出结构化报告。Skill 解决的是“Agent 在什么场景下应该怎么工作”的问题。我们可以用厨房类比MCP 是标准化的水龙头和燃气灶接口任何厨具都能接上去Skill 则是菜谱它告诉你先放油还是先放菜以及在什么情况下要用哪个设备。两者不是互斥的生产环境通常两者都要用 MCP 接入工具用 Skill 定义流程。2.4 为什么本文选择“主控调度”模式我看过很多 Agent 项目最后维护成本最低的往往不是对话能力最强的而是流程最清晰的。主控调度模式有一个天然优点所有决策都流经主控问题定位只需要看主控日志。如果用户说“你给的结果不对”你可以先看主控把任务拆给了谁再看对应 Agent 的工具调用记录很快就能找到是拆解问题、检索问题还是生成问题。这也决定了这个系统的代码组织方式主控是一个独立模块Worker 是三个独立模块模块之间通过函数调用通信而不是通过自然语言自由对话。这种结构虽然看起来不酷但工程上非常稳定。3. 系统架构设计1 个主控 3 个 Agent3.1 整体架构Hermes Agent 的整体结构是这样的用户输入任务主控调度器接收任务调用 Qwen 模型进行意图识别主控根据意图选择调用一个或多个 Worker AgentWorker Agent 执行子任务必要时调用外部工具Worker 返回结果给主控主控汇总结果生成最终回复。在这个架构里主控和 Worker 都用 Qwen 一个模型。这不是偷懒而是为了控制变量先把流程跑通再考虑不同模型分别承担不同角色。实际上生产环境完全可以主控用更强模型、Worker 用更便宜模型但那是后话。3.2 三个 Worker Agent 的职责划分为了让示例有区分度我们把三个 Agent 设计成不同职能资料检索 Agentresearcher负责查询知识库、搜索外部信息。它只做和查找相关的事不负责总结也不负责生成最终报告。工具方面我们给它一个 web_search 工具用于查询信息。计算与代码 Agentcoder负责数学计算、数据计算和代码任务。它可以使用 calculator 工具进行安全的表达式计算也可以使用 now_time 工具获取当前时间。它不负责写作文案只负责处理确定性的逻辑任务。文案与报告 Agentwriter负责把其他 Agent 的结果整理成结构化、可读的中文内容。它一般没有外部工具因为它的核心工作是生成而不是检索。这样划分之后每个 Agent 的 system prompt 都很短职责边界非常清楚。实际开发中你完全可以按同样的思路换成“SQL Agent”“邮件 Agent”“图表 Agent”。3.3 为什么模型底座选择 Qwen选择 Qwen 通义千问主要原因是它在中文场景表现稳定而且通过阿里云 DashScope 提供了 OpenAI 兼容接口接入成本很低。对于多智能体系统来说模型需要具备两个能力一是遵循 system prompt 的角色设定二是能理解工具调用的 schema 并返回结构化参数。Qwen 系列模型在这两个方面都比较成熟。这里需要说明本文示例使用 DashScope 的 OpenAI 兼容模式模型名用 qwen-plus 作为占位。你在实际使用时请以官方文档提供的可调用模型名为准。如果你的公司有私有化部署的 Qwen 模型只要该部署提供 OpenAI 兼容端点代码逻辑可以完全复用。3.4 一次任务是怎么被拆解和分发的用一个具体例子来看流程。用户输入请你搜索一下 Qwen 最近发布的模型信息计算一下 128 乘 32 的结果然后把两个结果整理成一段文字。主控收到任务后会调用 Qwen并传入一个 route_to_agent 工具。模型会生成这样的意图拆解第一个调用route_to_agent(agent_nameresearcher, agent_task搜索 Qwen 最近发布的模型信息)第二个调用route_to_agent(agent_namecoder, agent_task计算 128 * 32);第三个调用route_to_agent(agent_namewriter, agent_task综合两个结果写成一段中文总结)。主控依次执行这三个 Agent把结果收集回来再让 Qwen 整理成最终回答。这就是主控调度的核心流程。4. 环境准备与前置条件4.1 基础环境要求本文代码使用 Python 3 开发推荐 3.10 及以上版本。需要安装两个 Python 包pip install openai python-dotenvopenai 是 OpenAI 官方 Python SDK使用 DashScope 的 OpenAI 兼容模式时可以直接使用python-dotenv 用于加载项目根目录下的 .env 环境变量文件。项目本身不依赖重量级框架核心代码都使用标准库方便你迁移到 FastAPI、Spring Boot 或其他工程体系。4.2 获取 Qwen API Key调通整个系统需要一个可用的 Qwen API Key。流程一般是登录阿里云百炼平台或其他 Qwen 模型服务控制台开通模型服务并创建 API Key确认你的服务端点是 OpenAI 兼容模式地址通常是 https://dashscope.aliyuncs.com/compatible-mode/v1复制 API Key 到本地 .env 文件。关于“某些 Agent 客户端安装时需要登录网站”的问题这通常是为了账号鉴权和配额管理。如果遇到类似情况请以对应产品的官方文档为准不要在来源不明的页面输入 API Key。4.3 项目目录结构建议按下面的目录组织项目hermes-agent/ ├── .env.example ├── main.py └── hermes/ ├── __init__.py ├── config.py ├── tools.py ├── agent.py ├── workers.py └── orchestrator.pyconfig.py 负责读取环境变量tools.py 定义工具函数和工具 Schemaagent.py 定义 BaseAgent 基类workers.py 定义三个 Worker Agentorchestrator.py 定义主控调度器main.py 是入口文件。5. 完整代码实现5.1 配置文件 .env.example文件路径.env.exampleDASHSCOPE_API_KEYsk-xxxx QWEN_MODELqwen-plus QWEN_BASE_URLhttps://dashscope.aliyuncs.com/compatible-mode/v1 MAX_ITERATIONS8使用时复制为 .env并填入真实 Key。不要提交 .env 到 Git 仓库。5.2 配置模块文件路径hermes/config.pyimport os from dotenv import load_dotenv load_dotenv() DASHSCOPE_API_KEY os.getenv(DASHSCOPE_API_KEY, ) QWEN_MODEL os.getenv(QWEN_MODEL, qwen-plus) QWEN_BASE_URL os.getenv( QWEN_BASE_URL, https://dashscope.aliyuncs.com/compatible-mode/v1, ) MAX_ITERATIONS int(os.getenv(MAX_ITERATIONS, 8))这个模块要做的事很简单把环境变量加载成常量避免在业务代码里散落 os.getenv。注意 API Key 默认值是空字符串在入口程序里要做一次校验避免无意义的请求。5.3 工具模块文件路径hermes/tools.py工具函数和工具 Schema 放在一起方便维护。这里实现三个工具模拟搜索、安全计算器、当前时间。import ast import operator as op from datetime import datetime from typing import Any, Dict # ---------- 工具函数 ---------- def web_search(query: str) - Dict[str, Any]: 模拟搜索引擎。真实项目里可以替换为搜索引擎 API 或向量知识库查询。 fake_db { qwen: Qwen 是阿里云推出的通义系列大模型具备较强的中文理解能力。, 多智能体: 多智能体系统通过多个角色协作完成复杂任务。, } for key in fake_db: if key in query: return {status: ok, query: query, result: fake_db[key]} return {status: ok, query: query, result: 未找到精确结果请尝试更换关键词。} def calculator(expression: str) - Dict[str, Any]: 仅支持四则运算和乘方不使用 eval避免任意代码执行风险。 try: node ast.parse(expression, modeeval).body result _eval_expr(node) return {status: ok, result: float(result)} except Exception as e: return {status: error, message: str(e)} def now_time() - Dict[str, Any]: 返回当前时间供 Agent 做带时间信息的回答。 return {status: ok, result: datetime.now().strftime(%Y-%m-%d %H:%M:%S)} def _eval_expr(node): operators { ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul, ast.Div: op.truediv, ast.Pow: op.pow, ast.Mod: op.mod, } if isinstance(node, ast.Constant): if isinstance(node.value, (int, float)): return node.value raise ValueError(f不支持的常量类型: {type(node.value)}) if isinstance(node, ast.BinOp): left _eval_expr(node.left) right _eval_expr(node.right) op_func operators.get(type(node.op)) if op_func is None: raise ValueError(f不支持的操作符: {type(node.op).__name__}) return op_func(left, right) raise ValueError(f不支持的表达式节点: {type(node).__name__}) # ---------- 工具 Schema ---------- WEB_SEARCH_SCHEMA { type: function, function: { name: web_search, description: 搜索互联网或知识库中的信息返回文本片段。, parameters: { type: object, properties: { query: { type: string, description: 搜索关键词尽量具体。, } }, required: [query], }, }, } CALCULATOR_SCHEMA { type: function, function: { name: calculator, description: 计算四则运算表达式例如 128 * 32。, parameters: { type: object, properties: { expression: { type: string, description: 数学表达式只允许数字和 - * / % **。, } }, required: [expression], }, }, } NOW_TIME_SCHEMA { type: function, function: { name: now_time, description: 获取当前日期和时间。, parameters: { type: object, properties: {}, }, }, }这里的核心设计是工具函数返回的是 dict而不是纯字符串。这样在写入上下文时可以通过 json.dumps 统一序列化也方便在工具返回里携带 status 字段供模型判断工具是否执行成功。5.4 Agent 基类 BaseAgent文件路径hermes/agent.pyBaseAgent 是所有 Worker