1. 从单 Agent 到 SubAgent:为什么需要 config.toml 骨架
如果你已经用 Microsoft Agent Framework 跑通过一个单 Agent,大概率会经历这样一个阶段:一个 Agent 什么都能干,写代码、查资料、跑命令全塞在一个循环里。任务一复杂,上下文就开始互相污染,调试时根本分不清是哪一步出的问题。SubAgent(Multi-Agent)要解决的就是这件事——把一个大目标拆给多个职责单一的 Agent,每个 Agent 只关心自己那一小段逻辑。
Microsoft Agent Framework 里,SubAgent 的编排不是靠代码里硬编码 if-else,而是靠一份声明式的config.toml。这份文件定义了有哪些 Agent、每个 Agent 用什么模型、能调用哪些工具、以及主 Agent 可以把任务委派给谁。你可以把它理解成一张“组织架构图”:主 Agent 是项目经理,SubAgent 是各个专项工程师,config.toml就是他们的岗位说明书和汇报关系。
这篇内容面向已经了解 Agent 基本概念、准备在本地跑通一个最小 Multi-Agent 协作示例的开发者。我会从config.toml骨架出发,给出可复制的配置片段,然后一步步验证 Agent 注册、SubAgent 编排和调用链是否真的生效。过程中涉及模型调用的部分,我会用统一的 Key/API 通道接入,避免在多个供应商之间来回切换配置。
2. 前置准备:统一 Key/API 通道与运行环境
在写config.toml之前,先把两件事定下来:模型调用的通道,以及本地运行环境。
模型通道这块,我建议用一个统一的入口来管理 Key,而不是每个 Agent 单独配一套。TaoToken 提供的就是这种统一通道,官网是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api 。你可以在控制台创建一个 Key,后面所有 Agent 的模型调用都走这个 Key 和 API 地址。这样做的好处是:SubAgent 数量增加时,不用为每个 Agent 单独申请和轮换凭证。
具体操作上,先到控制台生成 API Key:
# 控制台地址(创建和管理 Key) https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite生成后把 Key 存到环境变量里,不要写进config.toml明文:
export TAOTOKEN_API_KEY="sk-你的key"运行环境方面,你需要:
- Python 3.10 以上(Microsoft Agent Framework 的 Python 包对版本有要求)
- 安装框架本体:
pip install microsoft-agent-framework - 一个空的本地项目目录,用来放
config.toml和测试脚本
如果你还没装框架,可以先确认版本:
python -c "import agent_framework; print(agent_framework.__version__)"能打印出版本号,说明环境就绪。接下来所有配置都围绕这个环境展开。
3. config.toml 骨架:Agent 注册与 SubAgent 编排
config.toml的结构可以分成三块:全局模型通道、Agent 注册表、SubAgent 委派关系。下面这份骨架可以直接复制到项目根目录,改掉模型名和工具路径就能用。
# ============ 全局模型通道 ============ [provider] api_base = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" default_model = "gpt-4o-mini" # ============ Agent 注册表 ============ [agents.planner] name = "planner" role = "主控 Agent,负责拆解任务并委派给 SubAgent" model = "gpt-4o-mini" tools = ["delegate", "finish"] subagents = ["coder", "reviewer"] [agents.coder] name = "coder" role = "代码生成 SubAgent,只负责根据指令写代码" model = "gpt-4o-mini" tools = ["write_file", "read_file"] [agents.reviewer] name = "reviewer" role = "代码审查 SubAgent,检查代码问题并给出修改建议" model = "gpt-4o-mini" tools = ["read_file"] # ============ SubAgent 委派关系 ============ [delegation] max_depth = 2 allow_parallel = false timeout_seconds = 120几个关键点解释一下。
[provider]里的api_base指向统一通道,api_key_env告诉框架从哪个环境变量读 Key。这样config.toml本身可以提交到版本库,不会泄露凭证。
[agents.planner]里的subagents = ["coder", "reviewer"]是 SubAgent 编排的核心。它声明了 planner 有权把任务委派给 coder 和 reviewer。没有出现在这个列表里的 Agent,planner 调不到。
[delegation]控制调用链的边界。max_depth = 2表示委派最多嵌套两层,防止 Agent 之间无限互相调用。allow_parallel = false表示 SubAgent 串行执行,调试阶段建议关掉并行,方便看日志。
工具部分先声明名字,具体实现后面在代码里注册。delegate和finish是框架内置的委派与结束工具,write_file、read_file需要你自己实现。
4. 可复制配置:把 SubAgent 接进调用链
有了骨架,接下来把配置加载进代码,并注册工具。下面这段脚本可以直接运行,它会读取config.toml,构建 Agent 图,然后跑一个最小任务。
import os import tomllib from agent_framework import AgentRuntime, tool # 读取配置 with open("config.toml", "rb") as f: config = tomllib.load(f) # 注册工具 @tool def write_file(path: str, content: str) -> str: with open(path, "w", encoding="utf-8") as f: f.write(content) return f"已写入 {path}" @tool def read_file(path: str) -> str: with open(path, "r", encoding="utf-8") as f: return f.read() # 构建运行时 runtime = AgentRuntime( api_base=config["provider"]["api_base"], api_key=os.environ[config["provider"]["api_key_env"]], default_model=config["provider"]["default_model"], ) # 注册所有 Agent for agent_id, spec in config["agents"].items(): runtime.register_agent( name=spec["name"], role=spec["role"], model=spec["model"], tools=spec["tools"], subagents=spec.get("subagents", []), ) # 绑定工具实现 runtime.bind_tool("write_file", write_file) runtime.bind_tool("read_file", read_file) # 设置委派边界 runtime.set_delegation( max_depth=config["delegation"]["max_depth"], allow_parallel=config["delegation"]["allow_parallel"], timeout=config["delegation"]["timeout_seconds"], ) print("Agent 注册完成:", runtime.list_agents())运行后如果打印出['planner', 'coder', 'reviewer'],说明 Agent 注册和 SubAgent 关系都加载成功了。
这里有个容易踩的坑:tools列表里的名字必须和bind_tool注册的名字完全一致,大小写敏感。如果config.toml写的是write_file,代码里绑成writeFile,运行时会报工具找不到。
5. 验证请求:跑通一个最小 Multi-Agent 协作
配置加载只是第一步,真正要验证的是调用链有没有按预期走。下面这段代码发起一个任务,让 planner 拆解后委派给 coder 和 reviewer。
task = "写一个 Python 函数,计算斐波那契数列第 n 项,并让 reviewer 检查边界条件" result = runtime.run( agent="planner", input=task, trace=True, # 打开调用链追踪 ) print("最终输出:", result.output) print("调用链:") for step in result.trace: print(f" [{step.agent}] {step.action} -> {step.detail}")预期看到的调用链大致是这样:
[planner] delegate -> coder [coder] write_file -> fib.py [planner] delegate -> reviewer [reviewer] read_file -> fib.py [reviewer] finish -> 边界条件建议:n<=0 时应返回 0 或抛异常 [planner] finish -> 任务完成如果你看到delegate后面跟着具体的 SubAgent 名字,说明 SubAgent 编排生效了。如果 planner 直接finish而没有委派,通常是两个原因:一是任务描述太简单,planner 判断自己能搞定;二是subagents列表没配对。
想单独验证某个 SubAgent 是否可用,可以绕过 planner 直接调用:
direct = runtime.run(agent="coder", input="写一个冒泡排序函数") print(direct.output)这一步能跑通,说明 SubAgent 本身注册没问题,问题就出在委派关系或 planner 的决策上。
6. 本篇常见错排查
报错一:KeyError: 'TAOTOKEN_API_KEY'
环境变量没导出,或者导出后没重新加载 shell。检查方式:
echo $TAOTOKEN_API_KEY如果为空,重新执行export,或者把它写进.bashrc/.zshrc。
报错二:Agent 'coder' not found in delegation scope
planner 的subagents列表里没有 coder,或者名字拼写不一致。检查config.toml里[agents.planner]的subagents字段,确保和[agents.coder]的name完全一致。
报错三:Tool 'write_file' is not bound
config.toml里声明了工具,但代码里没有bind_tool。每个在tools列表里出现的名字,都必须有对应的绑定。
报错四:调用链深度超限
如果 SubAgent 又去委派别的 Agent,可能触发max_depth。调试阶段先把max_depth设成 2,确认链路正常后再按需调整。不要一上来就设很大,否则出问题时日志会非常长。
报错五:请求超时
timeout_seconds默认 120 秒,复杂任务可能不够。但先别急着调大,优先看是不是某个 SubAgent 陷入了循环。打开trace=True,看调用链里有没有重复的delegate动作。
7. 继续深入:从最小示例到可用系统
跑通最小示例后,下一步通常是两件事:一是把 SubAgent 的职责拆得更细,比如加一个专门查文档的 Agent;二是把模型调用统一到稳定通道上,避免多 Key 管理带来的混乱。
如果你准备长期做编码类 Agent,可以了解 Coding Plan 的接入方式,它更适合需要持续调用和额度管理的场景:
https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite需要管理多个 Key 或查看调用量时,控制台在这里:
https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewriteAPI Key 的创建和轮换入口:
https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite接入文档里有完整的参数说明和示例:
https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite想先直观感受模型对话效果,可以直接在模型对话页测试:
https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite我自己的习惯是:每次改完config.toml,先跑一遍runtime.list_agents()确认注册表,再跑一个最小任务看调用链,最后才上真实任务。这样出问题时,能快速定位是配置层、注册层还是委派层的问题。