1. 从一次 Skill 调用失败说起
Eino 的 Skill 机制,简单说就是让 Agent 按需加载「技能包」的一套架构。它最核心的设计是渐进式加载:先用List()拿到所有技能的元数据(name、description、context、agent、model),等模型真正决定要用某个技能时,再通过Get()把完整指令拉进来。这样做的好处很直接——不用把几十个技能的全文塞进上下文,token 消耗能压下来一大截。
这套机制适合谁?如果你正在用 Eino 搭 ChatModelAgent,或者准备把 Skill 接到自己的 AI 工具链里,又或者你需要在本地复现一条完整的 Skill 调用链路来排查问题,那这篇就是写给你的。我试过在本地把 Skill 从配置到调用跑通,中间踩了几个坑,下面把可复制的骨架和验证动作都摊开讲。
Skill 的执行模式有三种:inline(默认,在当前 agent 上下文里返回技能内容)、fork(开一个干净的子 agent)、fork_with_context(子 agent 带上历史消息)。选哪种取决于你的任务需不需要历史上下文。而 Skill 本身是框架自带的工具,不用你单独实现,只要配好 Backend 和 middleware 就能用。
2. TaoToken 前置:统一 Key/API 通道
在跑通 Skill 调用链路之前,得先解决模型通道的问题。Eino 的 ChatModelAgent 底层是 ReAct 实现,模型调用是整条链路的关键一环。如果你本地要同时接多个模型做对比验证,或者团队里几个人共用一套 Key,直接散着配很容易乱。
TaoToken 在这里的角色是统一 Key/API 通道:一个 Key 走通模型对话、编码计划、控制台管理这几块。对 Skill 机制验证来说,最实际的价值是你不用为每个模型单独维护一套凭证,切换模型时只改配置里的模型名就行。
入口分几个:
- 官网总入口:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
- API 地址(配置里填这个):https://taotoken.net/api
- 模型对话(验证模型是否通):https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite
- Coding Plan(长期编码/Agent 场景):https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite
- 控制台:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite
- API Keys 管理: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
先把 Key 拿到手,后面配置骨架里要用到。这一步别跳过,否则验证请求会直接 401。
3. 可复制配置骨架
3.1 settings.json 骨架
如果你用的是 Cline 这类支持 settings.json 的工具,配置大概长这样。注意 baseUrl 填 TaoToken 的 API 地址,不要带 UTM 参数:
{ "models": { "provider": "openai-compatible", "baseUrl": "https://taotoken.net/api", "apiKey": "sk-你的Key", "model": "claude-sonnet-4-20250514", "temperature": 0.7, "maxTokens": 8192 }, "agent": { "name": "skill-test-agent", "handlers": ["skill"] } }这里model字段换成你在模型对话页确认可用的模型名。handlers里加skill表示启用 Skill middleware。
3.2 config.toml 骨架
如果你走的是 config.toml 路线(比如 CC Switch 场景),结构类似:
[provider] name = "taotoken" base_url = "https://taotoken.net/api" api_key = "sk-你的Key" [model] name = "claude-sonnet-4-20250514" max_tokens = 8192 temperature = 0.7 [skill] enabled = true backend = "filesystem" base_dir = "/path/to/skills" tool_name = "skill" use_chinese = truebase_dir指向你本地存放 SKILL.md 的目录。每个技能一个子目录,里面放 SKILL.md,FrontMatter 里写 name、description、context、agent、model。
3.3 CC Switch 接入步骤
CC Switch 的作用是快速切换不同的模型配置。接入 TaoToken 的步骤:
第一步,在 CC Switch 里新建一个 provider,base_url 填https://taotoken.net/api,api_key 填你的 Key。
第二步,把上面 config.toml 的[model]段填进去,模型名从模型对话页复制。
第三步,在[skill]段确认enabled = true,base_dir指向你的技能目录。
第四步,保存后切换到这个 provider,重启一下 agent 进程让配置生效。
3.4 Cline 接入步骤
Cline 的接入更直接。打开设置,找到 API Provider,选 OpenAI Compatible,Base URL 填https://taotoken.net/api,API Key 填你的 Key,Model ID 填模型名。保存后新建一个对话,发一条测试消息确认通道通了。
Skill 部分需要在 Cline 的 agent 配置里启用 skill handler,具体字段参考上面的 settings.json。如果你用的是 Eino 原生集成,直接在adk.NewChatModelAgent的Handlers里加skillHandler就行。
4. 验证请求与成功结果
配置好之后,怎么确认 Skill 机制真的跑通了?分两步验证。
4.1 先验证模型通道
用 curl 直接打 TaoToken 的 API,确认 Key 和模型名都对:
curl -X POST https://taotoken.net/api/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-你的Key" \ -d '{ "model": "claude-sonnet-4-20250514", "messages": [{"role": "user", "content": "回复 OK"}], "max_tokens": 10 }'返回里如果有choices[0].message.content且内容是 OK 相关,说明通道没问题。如果返回 401,检查 Key;返回 404,检查模型名;返回超时,检查网络。
4.2 再验证 Skill 调用链路
模型通道通了之后,发一条会触发 Skill 的请求。比如你有一个叫pdf的技能,发:
处理这个 PDF 文件预期行为是:模型先看到技能列表(只有元数据),思考后发起 function call{"name": "skill", "arguments": "{\"skill\": \"pdf\"}"},然后InvokableRun执行,返回 pdf 技能的完整指令,模型按指令继续执行。
成功的结果长这样:
Launching skill: pdf Base directory for this skill: /skills/pdf # PDF Processing Skill ...看到Launching skill这行,说明渐进式加载走通了——List 拿到了元数据,Get 按需加载了完整内容。
4.3 三种执行模式的验证差异
inline 模式下,技能内容作为 tool result 返回,当前 agent 的历史消息保留。fork 模式下,会开一个新的子 agent,历史消息不保留,只有技能内容。fork_with_context 模式下,子 agent 拿到完整历史加技能内容。
验证 fork 模式时,你可以在 SKILL.md 的 FrontMatter 里写context: fork,然后观察日志里是不是起了新的 agent 实例。如果日志里看到子 agent 的 system message 只有技能内容,没有之前的对话历史,说明 fork 生效了。
5. 本篇常见错排查
5.1 Skill 工具没被注入
现象是模型根本不知道有 skill 这个工具,不会发起 function call。原因通常是 middleware 没加到 Handlers 里。检查adk.NewChatModelAgent的Handlers字段,确认skillHandler在里面。另外确认skill.NewMiddleware返回的 error 被处理了,如果 Backend 创建失败,middleware 可能是 nil。
5.2 List 返回空列表
现象是模型看到的技能列表是空的。检查base_dir路径对不对,以及每个技能目录下有没有 SKILL.md。FrontMatter 的格式也要注意,name 和 description 是必填的,缺了会导致解析失败被跳过。
5.3 Get 加载不到完整内容
现象是 List 能看到技能,但调用时 Get 报错。常见原因是 SKILL.md 的 Content 部分为空,或者文件编码有问题。另外如果你用的是云端 Backend,确认 BaseDirectory 的路径转换逻辑对——本地文件系统是绝对路径,内存存储是虚拟路径,云端是路径标识符,三者含义不同。
5.4 模型切换不生效
现象是 SKILL.md 里指定了 model,但实际调用还是用的默认模型。检查setActiveModel有没有被调用,以及WrapModel有没有正确读取 run local value。这个机制依赖adk.SetRunLocalValue和adk.GetRunLocalValue的配对使用,如果中间有地方覆盖了 context,值可能丢失。
5.5 fork 模式子 agent 拿不到技能内容
检查BuildForkMessages的实现。默认情况下 fork 模式的消息是[]adk.Message{schema.UserMessage(skillContent)},如果你自定义了这个函数,确认 skillContent 被正确传进去了。fork_with_context 模式则是append(history, schema.ToolMessage(skillContent, toolCallID)),注意 toolCallID 要对上。
5.6 扩展点没生效
CustomToolParams、BuildContent、BuildForkMessages、FormatForkResult 这四个扩展点,如果配了没效果,检查是不是在skill.Config里正确赋值了。这些是函数类型的字段,传 nil 就用默认实现,传了自定义函数就会覆盖。注意 BuildContent 的签名里 rawArgs 是原始 JSON 字符串,需要自己 unmarshal。
6. 把链路固定下来
Skill 机制跑通之后,建议把配置骨架和验证脚本一起放进版本控制。每次改 Backend 或 middleware 配置,先跑一遍 curl 验证模型通道,再发一条触发 Skill 的请求确认链路完整。这样出问题能快速定位是通道层还是 Skill 层。
如果你需要长期跑编码或 Agent 任务,Coding Plan 那条线可以看看:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite
Key 管理和接入文档在这里,配置过程中随时对照:
- API Keys: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
最后提一个实际踩过的坑:SKILL.md 的 FrontMatter 里context字段的值必须是inline、fork、fork_with_context三者之一,写错了不会报错,会静默走 default 分支也就是 inline。如果你发现 fork 模式没生效,先检查这个字段的拼写。