1. 从一次调用链踩坑说起:skill 和 subagent 到底谁在干活
如果你正在搭多智能体工作流,大概率遇到过这个场景:主 Agent 收到一句「帮我审一下这段代码」,然后你发现日志里既出现了skill: syntax_check,又出现了subagent: code_reviewer,最后你搞不清到底是哪个环节在真正执行任务。更麻烦的是,两者用的 Key 不一样、通道不一样,排查问题时根本对不上号。
这就是 agent-skill 与 subagent 最容易混淆的地方。简单说,agent-skill 是「能力组件」,本质是一个无自主决策的执行单元,比如文本摘要 skill、OCR 识别 skill、代码生成 skill,它必须依附在主智能体的调度逻辑上才能跑起来。而 subagent 是「层级子个体」,是一个具备轻量化自主决策能力的迷你智能体,有自己的感知、规划、执行逻辑,可以独立完成细分任务。一个 subagent 内部可以挂载多个 agent-skill,比如「代码审查 subagent」可能同时集成了语法检测 skill、漏洞扫描 skill、性能分析 skill。
从调用链视角看,区别更清晰:skill 是「被调用者」,subagent 是「调用者兼被调用者」。主 Agent 可以直接挂载 skill 做轻量扩展,也可以拆分出独立 subagent 处理复杂任务。问题在于,当你用不同的 API Key 分别触发这两条链路时,日志会散落在不同通道,验证成本极高。这篇就带你用 TaoToken 统一 Key 和 API 通道,把两种调用链跑通并验证。
2. 前置准备:用 TaoToken 统一 Key 打通两条调用链
在动手写配置之前,先把通道统一。TaoToken 的作用是提供一个统一的 API 入口,让你用同一个 Key 分别触发 skill 调用和 subagent 调用,这样日志和计费都能在一个地方对齐。
你需要先拿到 Key。访问控制台创建 API Key:
https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite创建完成后,你会得到一个形如sk-xxxx的 Key。接下来确认 API 基地址,注意这里不加 UTM:
https://taotoken.net/api模型对话入口可以用来快速验证 Key 是否可用:
https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite如果你后续要做长期编码或 Agent 工作流,可以了解 Coding Plan:
https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite接入文档在这里,配置细节以文档为准:
https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite环境变量先设好,后面所有配置都引用它,避免 Key 硬编码:
export TAOTOKEN_API_KEY="sk-你的Key" export TAOTOKEN_BASE_URL="https://taotoken.net/api"注意:Key 只放在环境变量或本地配置里,不要提交到 Git 仓库。日志排查时如果发现 Key 泄露,第一时间去控制台吊销重建。
3. 可复制配置:settings.json 与 config.toml 骨架
下面给出两份骨架配置。settings.json用于定义 skill 与 subagent 的注册关系,config.toml用于定义 API 通道和调用参数。两者配合,才能让同一个 Key 分别触发两条链路。
3.1 settings.json:定义 skill 与 subagent 的挂载关系
{ "agent": { "name": "main_agent", "api_key_env": "TAOTOKEN_API_KEY", "base_url_env": "TAOTOKEN_BASE_URL", "skills": [ { "id": "syntax_check", "type": "agent-skill", "entry": "skills.syntax_check:run", "description": "语法检测,无自主决策,输入代码返回问题列表" }, { "id": "vuln_scan", "type": "agent-skill", "entry": "skills.vuln_scan:run", "description": "漏洞扫描,依赖规则库" } ], "subagents": [ { "id": "code_reviewer", "type": "subagent", "entry": "subagents.code_reviewer:main", "skills": ["syntax_check", "vuln_scan"], "description": "代码审查子智能体,自主规划审查步骤并调度 skill" } ] } }关键点:skills数组里的是 agent-skill,它们没有自己的调度循环;subagents数组里的是 subagent,它通过skills字段引用 skill,形成「subagent 调度 skill」的层级关系。
3.2 config.toml:统一 API 通道与调用参数
[api] base_url = "https://taotoken.net/api" api_key = "${TAOTOKEN_API_KEY}" timeout = 60 max_retries = 3 [api.headers] Content-Type = "application/json" [skill.default] model = "qwen3-vl-8b" temperature = 0.1 max_tokens = 2048 [subagent.default] model = "qwen3-vl-32b" temperature = 0.3 max_tokens = 4096 planning_depth = 3 [logging] level = "debug" log_file = "./logs/agent_call.log" log_skill_calls = true log_subagent_calls = true这里把 skill 和 subagent 的模型参数分开配置,是因为两者职责不同:skill 是单一执行单元,温度调低保证稳定;subagent 需要规划,温度略高、token 上限更大。log_skill_calls和log_subagent_calls都打开,后面验证时才能区分两条链路。
4. 验证请求:分别触发 skill 与 subagent 并检查日志
配置写好后,不要急着跑完整工作流,先用最小请求分别验证两条链路是否生效。
4.1 直接触发 agent-skill
skill 的调用是「直接执行」,不经过规划层。用一个 Python 脚本模拟:
import os import json import requests base_url = os.environ["TAOTOKEN_BASE_URL"] api_key = os.environ["TAOTOKEN_API_KEY"] payload = { "model": "qwen3-vl-8b", "messages": [ {"role": "system", "content": "你是语法检测 skill,只返回问题列表。"}, {"role": "user", "content": "检查这段代码:def f(x) return x+1"} ], "temperature": 0.1 } resp = requests.post( f"{base_url}/v1/chat/completions", headers={"Authorization": f"Bearer {api_key}"}, json=payload, timeout=60 ) print(resp.status_code) print(json.dumps(resp.json(), ensure_ascii=False, indent=2))预期结果:返回 200,内容里包含「缺少冒号」之类的语法问题。日志中应出现skill_call: syntax_check标记。
4.2 触发 subagent
subagent 的调用会先走规划,再调度 skill。请求体里通过agent_id指定:
payload = { "model": "qwen3-vl-32b", "agent_id": "code_reviewer", "messages": [ {"role": "user", "content": "审查这段代码并给出修复建议:def f(x) return x+1"} ], "temperature": 0.3, "max_tokens": 4096 } resp = requests.post( f"{base_url}/v1/chat/completions", headers={"Authorization": f"Bearer {api_key}"}, json=payload, timeout=60 ) print(resp.status_code) print(json.dumps(resp.json(), ensure_ascii=False, indent=2))预期结果:返回 200,内容里不仅有语法问题,还有修复建议和审查步骤。日志中应出现subagent_call: code_reviewer以及它内部调用的skill_call: syntax_check、skill_call: vuln_scan。
4.3 日志检查动作
打开./logs/agent_call.log,按时间顺序核对:
| 日志标记 | 含义 | 出现时机 |
|---|---|---|
skill_call: syntax_check | skill 被直接调用 | 4.1 请求后 |
subagent_call: code_reviewer | subagent 启动 | 4.2 请求后 |
subagent_plan: step=1 | subagent 规划步骤 | 4.2 请求中 |
skill_call: vuln_scan | subagent 内部调度 skill | 4.2 请求中 |
如果 4.1 只出现 skill 标记、4.2 同时出现 subagent 和 skill 标记,说明两条调用链已经区分开且都生效。
5. 本篇常见错排查
5.1 两条链路日志混在一起分不清
最常见的原因是log_skill_calls和log_subagent_calls没同时打开,或者用了两个不同的 Key 导致日志写到不同文件。统一用 TaoToken 的同一个 Key,并在 config.toml 里把两个开关都设为 true。
5.2 subagent 没有调度 skill
检查 settings.json 里 subagent 的skills字段是否引用了正确的 skill id。如果 id 拼写不一致,subagent 会启动但找不到可调度的 skill,日志里只有subagent_call没有skill_call。
5.3 skill 调用返回 401
说明 Key 没被正确读取。确认环境变量TAOTOKEN_API_KEY已 export,且 config.toml 里写的是${TAOTOKEN_API_KEY}而不是硬编码的空值。如果用的是模型对话入口测试,确认请求头是Authorization: Bearer sk-xxx。
5.4 subagent 规划深度过大导致超时
planning_depth = 3是保守值。如果你的任务复杂,可以调到 5,但要注意 timeout 也要相应放大。实测下来,depth 超过 5 后收益递减,反而容易触发超时。
5.5 模型名写错导致 404
skill 和 subagent 用的模型名必须和 TaoToken 支持的模型列表一致。去模型对话入口确认可用模型名,不要凭记忆写。
6. 把统一 Key 用顺:后续接入与长期编码
两条链路跑通后,你会发现统一 Key 的最大价值是「可观测」:skill 和 subagent 的调用都走同一个通道,日志、计费、限流都在一处,排查问题时不用来回切换。如果你要长期跑编码类 Agent,建议把 Coding Plan 也接进来,让 skill 和 subagent 共享同一套配额策略:
https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite接入细节以文档为准,配置项和本篇的 config.toml 可以复用:
https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite最后留一个实用技巧:在 settings.json 里给每个 skill 和 subagent 加一个tag字段,日志里会带上这个 tag,排查时用grep tag=code_review就能一次性捞出整条调用链,比按时间翻日志快得多。