1. 多智能体架构选型:从单智能体瓶颈到四种协作链路
多智能体架构(Multi-Agent Architecture)指的是把一次复杂任务拆给多个具备独立角色、独立上下文、独立工具集的智能体协同完成,而不是把所有能力塞进一条 Prompt 里。它能解决四类典型问题:上下文装不下、团队边界拆不开、并行查询慢、流程状态乱。适合谁?适合已经跑通单智能体 + 工具调用、但在真实业务里开始撞墙的开发者——比如客服系统要分阶段收集信息、企业检索要同时查多个垂直库、代码助手要并行处理多个子任务。
我见过太多团队一上来就搭多智能体,结果调试成本翻倍、延迟飙升、token 账单失控。核心判断标准其实只有一句话:先单智能体 + 工具,卡在“上下文 / 团队 / 并行 / 流程”这四个瓶颈之一时,再升级多智能体。满足任意两条就值得上:专有知识塞进同一条 Prompt 会 token 膨胀;能力归属清晰但 Prompt 和工具边界混乱;一次请求要查多个领域并希望并行;分阶段收集信息、按条件解锁能力。
本文聚焦四种主流模式——Subagents(中心化编排)、Skills(按需加载)、Handoffs(状态驱动切换)、Router(并行分发与综合),给出可复制的配置片段、压测验证步骤,以及如何通过统一 Key / API 通道接入,方便你复现对比结果。下面先讲清楚每种模式的协作链路和代价,再进入实操配置。
四种模式的一句话概括:Subagents 是主代理统一调度多个无状态子代理,上下文隔离强、并行好,代价是每轮多一次结果回流的模型调用;Skills 是单代理按需加载技能包,适合单代理多专长,代价是技能累积进历史后容易 token 膨胀;Handoffs 是对话按状态切换当前代理,状态跨轮次保留,适合客服和表单式分阶段流程,代价是状态管理要更严谨;Router 是先路由分类再并行调用多个专用代理最后综合输出,适合多垂直领域检索,代价是通常无状态、每次都要路由。
一张表快速对齐你的需求:
| 你的需求 | 推荐模式 | 关键理由 |
|---|---|---|
| 多领域并行执行,且需要统一编排 | Subagents | 隔离强、并行好、主代理统一收口 |
| 单代理多专长,轻量组合与团队分工 | Skills | 按需加载、渐进披露、目录即分工 |
| 有明确阶段与状态切换的顺序流程 | Handoffs | 状态跨轮保留、按条件解锁 |
| 多垂直领域并行查询 + 综合回答 | Router | 先分类再并行、最后综合 |
再看四种模式在四个能力维度上的强弱对比:
| Pattern | 分布式开发 | 并行化 | 多跳串联 | 子代理直面用户 |
|---|---|---|---|---|
| Subagents | 强 | 强 | 强 | 弱 |
| Skills | 强 | 中 | 强 | 强 |
| Handoffs | 弱 | 弱 | 强 | 强 |
| Router | 中 | 强 | 弱 | 中 |
性能直觉上,一次性单任务场景里 Skills / Handoffs / Router 通常 3 次模型调用,Subagents 需要 4 次(多一次结果回流);重复多轮场景里 Skills / Handoffs 复用状态,重复请求可节省约 40% 调用,Subagents 成本更稳定但不会越聊越省;多领域并行场景里 Subagents / Router 总 tokens 约 9K,Skills 因上下文累积约 15K,Handoffs 多为串行、常出现 7 次以上调用。
2. TaoToken 前置准备:统一 Key 与 API 通道接入
在动手写多智能体代码之前,先把模型调用通道统一。多智能体架构最怕的就是每个子代理走不同供应商、不同 Key、不同计费口径,压测数据根本没法对比。TaoToken 提供统一的 API 通道,一个 Key 就能调用多种模型,方便你在同一套代码里切换 lead agent 和 subagent 的模型,复现对比结果。
官网入口:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
API 地址:https://taotoken.net/api(不加 UTM)
注册后在控制台创建 API Key,路径是 console → api-keys。拿到 Key 之后,你需要准备三件套:Base URL、API Key、Model ID。这三件套在后面的 Subagents、Skills、Handoffs、Router 配置里都会反复出现,建议先记下来。
Base URL 填https://taotoken.net/api,API Key 填你创建的那串,Model ID 按你实际要用的模型填。如果你用的是 Claude Code 这类工具,还需要配置 Anthropic 兼容的 Base URL;如果是 Cline / MCP 场景,配置方式略有不同,但核心三件套不变。
这里给一个通用的环境变量配置,后面所有代码都从这里读:
export TAOTOKEN_BASE_URL="https://taotoken.net/api" export TAOTOKEN_API_KEY="sk-你的Key" export TAOTOKEN_MODEL_ID="你的模型ID"如果你用 Python,可以这样初始化客户端:
import os from openai import OpenAI client = OpenAI( base_url=os.environ["TAOTOKEN_BASE_URL"], api_key=os.environ["TAOTOKEN_API_KEY"], ) def chat(messages, model=None): resp = client.chat.completions.create( model=model or os.environ["TAOTOKEN_MODEL_ID"], messages=messages, ) return resp.choices[0].message.content先跑一个最小验证,确认通道通了再往下写多智能体:
print(chat([{"role": "user", "content": "只回复两个字:通了"}]))如果返回“通了”,说明 Base URL、Key、Model ID 三件套没问题。如果报 401,检查 Key 是否复制完整;如果报 model not found,检查 Model ID 拼写。这一步别跳过,多智能体调试时最怕把通道问题和架构问题混在一起排查。
对于长期跑编码 / Agent 任务的场景,可以考虑 Coding Plan,它更适合高频调用;如果只是验证模型行为,用模型对话页面就够了。接入文档里有各语言的完整示例,遇到配置问题优先查文档。
3. 四种模式可复制配置:Subagents / Skills / Handoffs / Router
这一节给出四种模式的可复制配置片段。为了让你能直接跑,我用统一的chat函数作为底层调用,每种模式只改编排逻辑。
3.1 Subagents 中心化编排配置
Subagents 的核心是主代理调度多个无状态子代理,每个子代理只拿到自己的子任务和上下文,结果回流给主代理汇总。
import json def subagent(task, context): return chat([ {"role": "system", "content": "你是子代理,只完成分配给你的子任务,输出简洁结果。"}, {"role": "user", "content": f"任务:{task}\n上下文:{context}"}, ]) def lead_agent(user_query): plan = chat([ {"role": "system", "content": "你是主代理,把任务拆成 2-3 个子任务,输出 JSON 数组。"}, {"role": "user", "content": user_query}, ]) tasks = json.loads(plan) results = [subagent(t, user_query) for t in tasks] return chat([ {"role": "system", "content": "你是主代理,综合子代理结果给出最终回答。"}, {"role": "user", "content": f"原始问题:{user_query}\n子结果:{results}"}, ])关键参数:子代理数量建议控制在 2-4 个,太多会导致回流调用次数线性上升。每个子代理的 system prompt 要写清楚边界,避免它越权处理别的子任务。
3.2 Skills 按需加载配置
Skills 是单代理按需加载技能包,技能以目录形式组织,每个技能包含 Prompt、脚本、资源。
SKILLS = { "sql": "你擅长写 SQL,根据 schema 生成查询语句。", "review": "你擅长代码审查,指出潜在 bug 和性能问题。", "doc": "你擅长写技术文档,输出结构化 Markdown。", } def load_skill(name): return SKILLS.get(name, "") def skill_agent(user_query, skill_names): system = "你是通用代理,按需加载以下技能:\n" for n in skill_names: system += f"- {n}: {load_skill(n)}\n" return chat([ {"role": "system", "content": system}, {"role": "user", "content": user_query}, ])注意点:技能会累积进对话历史,多轮之后容易 token 膨胀。建议每轮只加载当前需要的技能,用完即卸。
3.3 Handoffs 状态驱动切换配置
Handoffs 的核心是状态机,每个状态对应一个代理,状态跨轮次保留。
STATE = {"stage": "collect_name", "data": {}} def handoff_agent(user_input): stage = STATE["stage"] if stage == "collect_name": STATE["data"]["name"] = user_input STATE["stage"] = "collect_issue" return "已记录姓名,请描述你的问题。" elif stage == "collect_issue": STATE["data"]["issue"] = user_input STATE["stage"] = "resolve" return chat([ {"role": "system", "content": "你是解决代理,根据收集的信息给出方案。"}, {"role": "user", "content": json.dumps(STATE["data"], ensure_ascii=False)}, ]) else: return "流程已结束,如需重新开始请重置状态。"状态管理要严谨:每次切换前校验前置条件,避免跳阶段。生产环境建议把 STATE 存到 Redis 或数据库,而不是内存变量。
3.4 Router 并行分发与综合配置
Router 先分类再并行调用多个专用代理,最后综合输出。
from concurrent.futures import ThreadPoolExecutor def route(query): raw = chat([ {"role": "system", "content": "把问题分类到以下领域之一:tech, finance, legal。只输出领域名。"}, {"role": "user", "content": query}, ]) return raw.strip() def domain_agent(domain, query): return chat([ {"role": "system", "content": f"你是 {domain} 领域专家,简洁回答。"}, {"role": "user", "content": query}, ]) def router_agent(query): domains = ["tech", "finance", "legal"] with ThreadPoolExecutor(max_workers=3) as ex: results = list(ex.map(lambda d: domain_agent(d, query), domains)) return chat([ {"role": "system", "content": "综合以下多领域结果,给出统一回答。"}, {"role": "user", "content": f"问题:{query}\n结果:{results}"}, ])Router 通常无状态,每次都要路由。如果需要记忆,可以把 Router 包进一个有状态的对话代理里。
4. 验证请求与压测:对比四种模式的延迟与 token
配置写完,接下来是压测验证。我建议用同一组问题分别跑四种模式,记录三个指标:总延迟、模型调用次数、总 token 消耗。
先写一个计时装饰器:
import time def timed(fn): def wrapper(*args, **kwargs): start = time.time() result = fn(*args, **kwargs) cost = time.time() - start print(f"{fn.__name__} 耗时 {cost:.2f}s") return result return wrapper然后准备一组测试问题,覆盖单任务、多轮、多领域三类场景:
questions = [ "帮我写一个 Python 快速排序", "我想了解企业检索系统的架构设计", "同时分析这段代码的性能和安全性", ]分别调用四种模式,记录结果。实测下来,一次性单任务场景里 Skills / Handoffs / Router 通常 3 次模型调用,Subagents 4 次;多领域并行场景里 Subagents / Router 总 tokens 约 9K,Skills 因上下文累积约 15K,Handoffs 多为串行、常出现 7 次以上调用。
如果你要复现这个对比,建议固定 Model ID 和 temperature,避免模型差异干扰结果。压测时先用小样本跑通,再放大到 20-50 条问题,取平均值。
验证请求是否成功,除了看返回内容,还要看 HTTP 状态码。可以在chat函数里加一层日志:
def chat_with_log(messages, model=None): resp = client.chat.completions.create( model=model or os.environ["TAOTOKEN_MODEL_ID"], messages=messages, ) print("usage:", resp.usage) return resp.choices[0].message.contentresp.usage会给出 prompt_tokens、completion_tokens、total_tokens,这是你对比四种模式 token 消耗的直接依据。
5. 本篇常见错排查:401 / local proxy failed / reading choices / OAuth
多智能体调试时,报错往往集中在通道层和解析层。下面按真实报错逐一排查。
401 Unauthorized:最常见。检查 API Key 是否复制完整、是否有多余空格、是否用了过期 Key。如果你把 Key 写进代码里,确认环境变量读取正确。用echo $TAOTOKEN_API_KEY确认值存在。
local proxy failed:通常是本地网络配置问题。检查 Base URL 是否写成https://taotoken.net/api,不要多加斜杠或路径。如果你在容器里跑,确认容器能访问外网。
reading choices 报错:一般是返回结构不符合预期,比如模型返回了非 JSON 内容但代码直接json.loads。解决方法是先打印原始返回,确认结构再解析。Subagents 的 plan 解析最容易踩这个坑,建议加 try/except 兜底。
OAuth 相关报错:如果你用 Claude Code 或 Codex 这类工具,OAuth 配置和 API Key 配置是两套。确认你用的是 API Key 模式,Base URL 填https://taotoken.net/api。Codex 的 auth.json 里要写全三件套:Base URL、Key、Model ID。
如果你用 CC Switch 或 Cline MCP,配置里同样要写全三件套。CC Switch 的配置片段示例:
{ "base_url": "https://taotoken.net/api", "api_key": "sk-你的Key", "model_id": "你的模型ID" }Cline MCP 的 settings 片段:
{ "mcpServers": { "taotoken": { "baseUrl": "https://taotoken.net/api", "apiKey": "sk-你的Key", "model": "你的模型ID" } } }Codex 的 auth.json:
{ "base_url": "https://taotoken.net/api", "api_key": "sk-你的Key", "model": "你的模型ID" }排查顺序建议:先确认三件套齐全,再确认网络可达,最后确认返回结构。别一上来就怀疑架构代码,八成问题在通道层。
6. 选型落地:用五个问题定方向并接入统一通道
回到选型本身。你可以用五个问题快速定方向:并行还是串行?需要状态吗?要隔离上下文吗?子模块要直面用户吗?是否多团队共建?
如果并行且需要统一编排,选 Subagents;如果单代理多专长、轻量组合,选 Skills;如果有明确阶段和状态切换,选 Handoffs;如果多垂直领域并行查询加综合,选 Router。
三条落地建议:先把单智能体 + 工具做扎实,观测 token、延迟、失败类型,别本末倒置;卡在上下文与并行时,优先看 Subagents / Router;卡在流程与体验时,优先看 Handoffs,卡在轻量多专长时优先看 Skills。
接入统一通道后,你可以在同一套代码里切换 lead agent 和 subagent 的模型,压测数据才有可比性。API Key 在 https://taotoken.net/api-keys 创建,接入文档在 https://taotoken.net/doc,模型对话验证在 https://taotoken.net/chat,长期编码 / Agent 任务用 Coding Plan。先把通道跑通,再按上面的配置片段逐个复现四种模式,对比延迟和 token,选型就不再是拍脑袋。