尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

从零实现自己的agent第五期:用TaoToken统一Key打通子代理dispatch_subagent

发布时间:2026/9/29 9:08:46

资讯中心
01
ARTICLE

从零实现自己的agent第五期:用TaoToken统一Key打通子代理dispatch_subagent

从零实现自己的agent第五期:用TaoToken统一Key打通子代理dispatch_subagent
1. 为什么主 Agent 越跑越慢上下文污染的真实场景做自研 Agent 到第五期很多人会卡在同一个坎上任务规划已经能跑通主 Agent 知道先做什么后做什么但执行几轮之后响应明显变慢回答质量也开始飘。我试过抓三个网页做观点对比主 history 里塞进了上万字的网页正文、grep 输出、报错堆栈最后模型在总结时反而抓不住重点。这不是模型能力问题而是上下文结构问题。主 Agent 的 history 应该只保留「决策」和「结论」而不是「过程」。网页正文、命令输出、文件搜索结果、报错日志这些都属于局部探索的中间材料对最终回答的价值密度很低却会持续稀释注意力。dispatch_subagent要解决的就是这件事把脏活放进独立上下文执行主 history 只接收一条高密度的 tool_result。子代理不是和主 Agent 平级的另一个角色而是主 Agent 可以调用的一个特殊工具和run_command、read_file同级区别在于它内部会启动一套独立的 AgentRunner。这一期要交付的是可复制的配置骨架settings.json与config.toml示例、子代理注册流程、上下文隔离的验证动作以及用 TaoToken 统一 Key 作为子代理调用外部模型的接入层。适合已经在写自研 Agent、准备引入多子代理架构的开发者。2. TaoToken 前置统一 Key 作为子代理的模型接入层多子代理架构里有一个容易被忽略的工程问题每个子代理都需要调用外部模型如果每个子代理各自维护一套 Key 和 base_url配置会迅速失控。更麻烦的是子代理的上下文是隔离的但模型接入层不应该隔离——它应该是共享的、统一的、可审计的。TaoToken 在这里扮演的角色就是统一接入层。你只需要在环境变量里配置一次 Key 和 API 地址主 Agent 和所有子代理都通过同一个通道调用模型。子代理的隔离发生在 history、system prompt、工具白名单层面而不是发生在网络接入层面。先拿到 Key。访问控制台创建 API Keyhttps://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite创建完成后Key 只在创建时完整显示一次复制保存到本地环境变量。接入文档在这里包含各语言 SDK 的调用示例https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewriteAPI 基础地址统一为https://taotoken.net/api注意这个地址不加 UTM 参数它是纯粹的接口端点。环境变量建议这样设置export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/api这样主 Agent 和子代理在初始化模型客户端时都读取同一组环境变量。子代理的 runner 工厂在创建时不需要额外传 Key直接复用父级的模型客户端配置即可。这是统一 Key 的核心价值接入层收敛到一处隔离层专注在上下文和权限。3. 可复制配置settings.json 与 config.toml 骨架配置分两层一层是模型接入配置一层是子代理注册配置。先看模型接入层的settings.json{ model: { provider: taotoken, base_url: https://taotoken.net/api, api_key_env: TAOTOKEN_API_KEY, default_model: claude-sonnet-4-20250514, max_tokens: 8192, timeout_seconds: 120 }, agent: { max_turns: 30, enable_subagent: true, subagent_max_turns: 12, concurrency_safe_tools: [dispatch_subagent, read_file, grep, glob] } }关键字段说明api_key_env指向环境变量名而不是硬编码 Key避免密钥进仓库subagent_max_turns限制子代理的最大回合数防止子代理陷入死循环消耗 tokenconcurrency_safe_tools列出可以并行执行的工具dispatch_subagent必须在其中否则并发派遣不会生效。再看子代理注册层的config.toml[subagents.readonly_explorer] display_name 只读探索者 description 适合阅读代码、搜索项目结构、整理提纲不可写文件 tools [load_skill, read_file, glob, grep] max_turns 8 system_prompt_file templates/subagents/readonly_explorer.md [subagents.web_researcher] display_name 网页研究员 description 适合网页抓取、资料查访、探索性搜索 tools [web_fetch, load_skill, read_file, grep] max_turns 10 system_prompt_file templates/subagents/web_researcher.md [subagents.builder] display_name 构建者 description 可读写可执行适合真正动手改文件、跑命令 tools [run_command, web_fetch, load_skill, read_file, write_file, edit_file, glob, grep] max_turns 15 system_prompt_file templates/subagents/builder.md三个子代理身份对应三种权限边界。readonly_explorer只有只读工具web_researcher多了网页抓取builder才有写文件和执行命令的权限。注意所有子代理的工具白名单里都不包含dispatch_subagent和update_todos——前者防止无限递归派遣后者避免子代理污染主 Agent 的任务计划。注册逻辑在agent/subagents/registry.py里读取这份 TOML构建子代理注册表import tomllib from pathlib import Path class SubagentRegistry: def __init__(self, config_path: str config.toml): with open(config_path, rb) as f: config tomllib.load(f) self._specs {} for name, spec in config.get(subagents, {}).items(): self._specs[name] SubagentSpec( namename, display_namespec[display_name], descriptionspec[description], tool_namestuple(spec[tools]), max_turnsspec.get(max_turns, 10), system_prompt_filespec[system_prompt_file], ) def names(self, include_aliases: bool False) - list[str]: return list(self._specs.keys()) def get(self, name: str) - SubagentSpec: return self._specs[name]dispatch_subagent工具的参数 schema 保持精简def tool_parameters_schema(self): return { agent_type: { type: string, description: 子代理类型必须是可用类型之一, enum: self._subagent_registry.names(include_aliasesTrue), }, task: { type: string, description: 交代给子代理的任务写清目标、范围、输出格式、边界, }, purpose: { type: string, description: 一句话用途标签仅用于终端打印, }, }agent_type决定用哪种子代理身份task是真正交给子代理的工单purpose只是日志标签。这里最容易被忽视的是task——因为子代理有独立上下文它看不到主 Agent 脑内的隐含信息所以 task 必须写成独立工单。4. 验证请求跑通一次完整的子代理调度链路配置就绪后用一次真实请求验证整条链路。先启动主 Agent输入一个适合子代理的任务分别阅读 docs/architecture.md、docs/tools.md、docs/subagent.md 三个文档 提炼每个文档的核心观点最后只回传一个对比表不要把原文全文带回主线。预期行为是主 Agent 在同一轮里发出三个dispatch_subagent调用每个调用指定agent_typereadonly_explorertask 分别指向一个文档。运行时并发等待三个子代理完成主 history 只收到三条总结。验证成功的三个信号第一主线打印了派遣日志能看到agent_type和purpose[dispatch] agent_typereadonly_explorer purpose阅读架构文档 [dispatch] agent_typereadonly_explorer purpose阅读工具文档 [dispatch] agent_typereadonly_explorer purpose阅读子代理文档第二子上下文里出现了自己的工具调用日志比如read_file读取了对应文档这些日志不会进入主 history[subagent:readonly_explorer] tool_call read_file pathdocs/architecture.md [subagent:readonly_explorer] tool_call read_file pathdocs/tools.md第三主线只收到最终回禀而不是所有中间输出[subagent] 子代理仅向主 history 追加 312 字 [subagent] 子代理仅向主 history 追加 287 字 [subagent] 子代理仅向主 history 追加 356 字如果看不到这些信号说明模型可能选择了普通工具路径直接用read_file读了三个文档。这不一定错可能只是普通工具更直接。想稳定触发子代理task 描述里要明确说「分别派三个子代理各自处理一个文档再汇总结果」。再验证一次上下文隔离。在子代理执行前后分别打印主 history 的长度before len(json.dumps(main_history)) result dispatch_subagent(agent_typereadonly_explorer, task阅读 docs/architecture.md 并总结) after len(json.dumps(main_history)) print(f主 history 增长: {after - before} 字符)如果隔离生效增长量应该只有子代理总结的长度而不是文档全文的长度。这是判断子代理是否真正隔离上下文的最直接指标。5. 本篇常见错排查5.1 子代理没有触发主 Agent 直接调用了普通工具最常见的原因不是配置错误而是 task 描述不够明确。模型会根据任务成本选择路径如果「读三个文档」用一条grep或三次read_file就能完成它不会主动派子代理。解决办法是在 task 里显式要求派遣比如「分别派三个子代理各自阅读一个文档最后汇总对比表」。另一个原因是enable_subagent没有设为true或者dispatch_subagent没有注册进主 Agent 的工具表。检查settings.json里的agent.enable_subagent字段以及主 ToolRegistry 是否注册了DispatchSubagentTool。5.2 子代理报错「agent_type not found」agent_type的枚举值来自SubagentRegistry.names()如果config.toml里的子代理名称和调用时传的不一致就会报这个错。注意 TOML 里的 section 名就是agent_type的值比如[subagents.readonly_explorer]对应的agent_type是readonly_explorer不是display_name。排查方法是在启动时打印注册表registry SubagentRegistry(config.toml) print(可用子代理:, registry.names())5.3 子代理能读文件但无法写文件这是权限白名单在起作用不是 bug。readonly_explorer的工具白名单里没有write_file和edit_file所以它无法写文件。如果任务确实需要写操作把agent_type换成builder。这也是安全设计的核心能用代码约束的地方就用代码约束不要只靠 prompt 承诺。5.4 并发派遣没有生效三个子代理串行执行检查dispatch_subagent是否在concurrency_safe_tools列表里。AgentRunner 只会对同一轮模型返回的、且标记为 concurrency_safe 的 tool blocks 做并行执行。如果dispatch_subagent不在列表里即使模型在同一轮发出三个调用也会串行执行。另外并发触发权在模型路由手里。如果模型把三个派遣分散在三轮里发出运行时也无法并行。想稳定演示并发task 里要明确说「在同一轮里分别派三个子代理」。5.5 子代理返回内容过长主 history 仍然被撑爆这说明子代理的 system prompt 没有约束回禀格式。子代理不应该把所有中间过程原样倒回主线那样只是把污染换了一个入口。在子代理的 system prompt 文件里明确要求回禀包含四类信息结论、证据、不确定性、建议动作。并在dispatch_subagent的 task 里指定输出格式比如「回传三条关键结论每条不超过 50 字」。5.6 子代理调用模型时报 401 或鉴权失败检查环境变量TAOTOKEN_API_KEY是否在当前 shell 会话里生效。子代理的 runner 工厂在创建时读取的是同一组环境变量如果主 Agent 能调用模型但子代理不能通常是子代理 runner 初始化时没有继承父级的模型客户端配置。确保runner_factory在创建子代理 runner 时复用父级的 model client而不是重新读取一份可能不存在的配置。6. 继续把子代理链路跑稳子代理的核心价值是把局部执行细节放进独立上下文主 Agent 负责目标、计划和最终回答子代理负责局部探索、试错和整理。TaoToken 统一 Key 在这里的作用是让接入层收敛到一处主 Agent 和所有子代理共享同一个 API 通道隔离发生在 history、system prompt 和工具白名单层面。如果你在排障过程中需要确认 Key 和接入配置是否正确可以直接在模型对话里发一条测试请求验证通道https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite如果准备把子代理调度接入到长期编码或 Agent 工作流里Coding Plan 提供了更适合持续调用的配额方案https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite接入文档里有各语言 SDK 的完整调用示例排障时对照检查 base_url 和鉴权头https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite下一步可以做的验证把子代理的max_turns调小到 3观察子代理在回合耗尽时如何回禀或者给builder子代理加一个写文件任务验证权限白名单是否按预期放行。这两个动作能把子代理的边界行为摸清楚。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。