1. OpenCode Agent 里 todowrite 工具提示词到底解决什么问题
如果你最近在折腾 OpenCode 这类终端 Agent,大概率会遇到一个很具体的困惑:明明模型能力不差,但一让它做多步任务,它就开始东一榔头西一棒子,改完 A 文件忘了 B 文件,重构到一半突然跑去写测试,最后你只能自己拿小本本记它到底改到哪了。todowrite 这个工具提示词,本质上就是给 Agent 装一个「任务看板」,让它把脑子里那团乱麻先拆成一条条能打勾的清单,再按顺序执行。
我在实际项目里试过让 Agent 做一次跨 8 个文件的重命名,没有 todowrite 的时候它改到第 5 个文件就「失忆」了,前面改过的引用关系全乱套;加上 todowrite 提示词之后,它会先搜索、再列清单、逐项执行、逐项回传结果,整个过程你能在终端里看到它一步步推进。这就是 todowrite 的核心价值:把「多步、复杂、高风险」的任务从模型脑内推理变成外部可见的结构化状态。
它适合谁?三类人最该关注。第一类是正在用 OpenCode 做日常编码的开发者,尤其是需要 Agent 帮忙做重构、批量修改、新功能拆解的场景;第二类是在调 Agent 工具链的人,想搞清楚工具提示词怎么写才能让模型稳定触发 todowrite;第三类是准备把模型请求统一走一个 API 通道的团队,因为 todowrite 这类工具调用对请求稳定性、模型 ID 一致性要求比较高,通道换来换去很容易出现工具调用格式对不上的问题。
这篇要做的三件事很明确:把 OpenCode 的 settings 配置改到 TaoToken 统一通道,给出可复制的 todowrite 工具提示词模板,然后跑一次完整的 Agent 任务验证工具调用和结果回传是否正常。全程都是可跟做的步骤,配置片段直接抄就行。
2. 把 OpenCode settings 接到 TaoToken 的前置准备
在动 settings 之前,先把几个概念对齐,不然后面配置报错你会不知道错在哪。OpenCode 的模型请求最终是发到一个兼容 OpenAI 协议风格的 Base URL 上,工具调用(tool call)依赖模型返回结构化的 function call 字段。todowrite 作为一个工具,它的提示词会作为 system 或工具描述注入到请求里,模型根据提示词决定什么时候调用、传什么参数。所以整条链路是:OpenCode 读 settings → 拼请求(含工具提示词)→ 发到 Base URL → 模型返回 tool call → OpenCode 执行并回传结果。
TaoToken 在这里扮演的角色就是那个统一的 Base URL 和 Key 通道。你不需要在 OpenCode 里为每个模型单独配一套地址,只要把 Base URL 指向https://taotoken.net/api,Key 用你在控制台生成的,Model ID 填你实际要用的模型标识,工具调用链路就能跑通。这样做的好处是:todowrite 提示词里如果写死了模型行为假设,换模型时不用改提示词,只改 Model ID 就行。
前置准备清单如下。先去控制台生成一个 API Key,地址是https://taotoken.net/console,生成后复制保存,后面 settings 里要用。然后确认你要用的 Model ID,这个在模型列表或文档里能查到,地址https://taotoken.net/doc。如果你还没决定用哪个模型,可以先在模型对话页面试一下工具调用是否正常,地址https://taotoken.net/models,输入一段需要多步拆解的任务,看它会不会主动列清单。
这里有个容易踩的坑:很多人以为 Base URL 填官网首页就行,结果请求 404。记住 API 地址是https://taotoken.net/api,不带任何多余路径,OpenCode 会自己在后面拼/v1/chat/completions这类端点。另外 Key 不要带空格,复制的时候容易多一个换行,导致 401。
还有一点关于 todowrite 提示词的准备。你要在 OpenCode 的工具配置里定义 todowrite 这个工具的 schema,包括它接受哪些参数(通常是任务列表数组,每项含 id、content、status),以及工具描述文本。这个描述文本就是「工具提示词」,它决定了模型什么时候触发 todowrite。描述里要写清楚触发条件,比如「当任务涉及 3 个以上步骤、或涉及多个文件修改、或属于高风险重构时,必须先调用 todowrite 建立清单」。这段文本后面我会给模板。
3. 可复制的 settings 配置与 todowrite 提示词模板
这一节是核心,直接给可复制的片段。OpenCode 的 settings 文件通常是 JSON 或 TOML 格式,路径一般在项目根目录的.opencode/settings.json或用户目录下的配置里。下面给一份 JSON 版本,字段名按 OpenCode 常见约定来,你对照自己的版本微调。
{ "provider": { "taotoken": { "type": "openai-compatible", "baseURL": "https://taotoken.net/api", "apiKey": "sk-你的TaoTokenKey", "models": { "default": { "id": "你的ModelID", "name": "taotoken-default" } } } }, "agent": { "tools": { "todowrite": { "enabled": true, "description": "当任务涉及3个以上步骤、多个文件修改、或高风险重构时,必须先调用本工具建立任务清单。清单每项包含 id、content、status,status 取值 pending/in_progress/completed。执行过程中每完成一项必须更新状态。", "parameters": { "type": "object", "properties": { "todos": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "content": { "type": "string" }, "status": { "type": "string", "enum": ["pending", "in_progress", "completed"] } }, "required": ["id", "content", "status"] } } }, "required": ["todos"] } } } } }如果你用的是 TOML 版本,等价写法如下,注意[provider.taotoken]这种表头结构。
[provider.taotoken] type = "openai-compatible" baseURL = "https://taotoken.net/api" apiKey = "sk-你的TaoTokenKey" [provider.taotoken.models.default] id = "你的ModelID" name = "taotoken-default" [agent.tools.todowrite] enabled = true description = "当任务涉及3个以上步骤、多个文件修改、或高风险重构时,必须先调用本工具建立任务清单。清单每项包含 id、content、status,status 取值 pending/in_progress/completed。执行过程中每完成一项必须更新状态。"三件套对照一下:Base URL 是https://taotoken.net/api,Key 是控制台生成的sk-开头字符串,Model ID 是你选的模型标识。这三个必须同时正确,缺一个都会在验证阶段报错。
关于 todowrite 提示词模板,我建议在 description 里再补一段「反例约束」,防止模型滥用。比如加上「如果任务只是单文件单步骤修改,不要调用本工具,直接执行」。这样能避免模型为了「显得严谨」而给一个改错别字的任务也列五步清单。实测下来,加了反例约束后,简单任务的响应速度明显变快,工具调用次数也降下来了。
另外,如果你的 OpenCode 版本支持在工具描述里引用变量,可以把当前工作目录、项目类型这些上下文注入进去,让模型判断复杂度时更准。比如描述里写「当前项目为 {projectType},涉及 {fileCount} 个文件」,模型看到文件数多就会更倾向触发 todowrite。这个不是必须的,但能提升触发准确率。
配置改完之后,别急着跑复杂任务,先做一次最小验证:让 Agent 做一个明确需要三步以上的任务,看它是否先返回 todowrite 的 tool call,参数里 todos 数组是否结构正确。如果它直接开始改文件而不列清单,说明 description 的触发条件写得不够强,回去把「必须先调用」改成「必须首先调用,否则视为违规」。
4. 验证请求与成功结果:跑一次完整 Agent 任务
配置就位后,来跑一次真实任务。我选一个典型场景:把项目里一个函数名oldHandler全局重命名为newHandler,涉及多个文件。这个任务复杂度够,能触发 todowrite,也能验证工具调用链。
启动 OpenCode,输入任务描述:「把项目中所有 oldHandler 重命名为 newHandler,注意不要改到注释和字符串里的同名内容,改完跑一次构建确认没有引用错误。」
预期行为分四步。第一步,Agent 先调用搜索工具,摸清 oldHandler 出现在哪些文件、哪些位置。第二步,它调用 todowrite,返回一个 tool call,参数类似下面这样。
{ "todos": [ { "id": "1", "content": "搜索 oldHandler 所有引用位置", "status": "completed" }, { "id": "2", "content": "修改 src/handler.js 中的函数定义", "status": "in_progress" }, { "id": "3", "content": "修改 src/router.js 中的调用点", "status": "pending" }, { "id": "4", "content": "修改 src/middleware.js 中的引用", "status": "pending" }, { "id": "5", "content": "运行构建并处理报错", "status": "pending" } ] }第三步,OpenCode 执行这个 tool call,把清单渲染到终端,然后逐项推进,每完成一项就发一次 todowrite 更新状态。第四步,全部完成后,Agent 返回最终结果,包含修改的文件列表和构建输出。
成功结果的判断标准有三个。一是 todowrite 的 tool call 在请求日志里能看到,说明工具提示词生效了;二是清单状态从 pending 逐步变成 completed,说明结果回传正常;三是最终构建通过,说明任务真的执行到位了。如果构建报错,Agent 应该把报错信息作为新任务追加到清单里,而不是直接结束。
这里贴一段验证时终端输出的简化示意,帮你对照。
[tool call] todowrite todos: 5 items [execute] 搜索完成,找到 8 个文件 15 处引用 [tool call] todowrite todos: item2 -> in_progress [execute] src/handler.js 修改完成 ... [result] 构建通过,0 errors如果你在模型对话页面单独测工具调用,地址用https://taotoken.net/models,输入同样的任务,看模型是否返回结构化的 function call。这一步能帮你排除是 OpenCode 配置问题还是模型本身不支持工具调用。
验证通过后,建议把这次成功的 settings 和提示词存一份到项目里,团队其他人直接复用。因为 todowrite 的触发效果和模型强相关,换模型时最好重新跑一次这个验证任务,确认新模型的 tool call 格式没变。
5. 本篇常见错误排查:401、local proxy failed 与 choices 解析失败
配置和验证过程中,报错基本集中在几个地方。下面按真实报错对照排查。
401 Unauthorized。这个最常见,原因就三个:Key 错了、Key 过期了、Key 带了多余字符。先检查 settings 里的 apiKey 字段,确认是sk-开头且没有换行和空格。然后去控制台https://taotoken.net/api-keys确认这个 Key 还在有效期内。如果都没问题,用 curl 直接测一下,排除是 OpenCode 的问题。
curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer sk-你的Key" \ -H "Content-Type: application/json" \ -d '{"model":"你的ModelID","messages":[{"role":"user","content":"hi"}]}'如果 curl 返回 200,说明 Key 和 Base URL 没问题,问题在 OpenCode 配置读取上,检查 settings 文件路径是否被正确加载。
local proxy failed。这个报错通常出现在你本地有代理设置,或者 OpenCode 尝试走本地代理端口但没起来。先检查环境变量里有没有HTTP_PROXY、HTTPS_PROXY这类设置,有的话临时清掉再试。另外确认 Base URL 没有写成localhost或127.0.0.1开头的地址,必须是https://taotoken.net/api。如果报错信息里提到端口,检查那个端口是不是被别的进程占了。
reading choices 解析失败。这个报错说明请求发出去了,模型也返回了,但返回结构里没有choices字段,或者choices是空的。原因可能是 Model ID 填错了,请求被路由到了一个不返回标准结构的端点。去https://taotoken.net/doc核对 Model ID 拼写,注意大小写。还有一种可能是请求体里stream参数和 OpenCode 的解析逻辑不匹配,试试在 settings 里显式关掉流式。
OAuth 相关报错。如果你在配置里看到了 OAuth 字样,说明 OpenCode 尝试走 OAuth 认证而不是 API Key。检查 settings 里 provider 的 type 是不是openai-compatible,如果是oauth或类似值,改成兼容模式。OAuth 那套是给特定平台用的,走 TaoToken 统一通道不需要。
工具调用不触发。这个不算报错,但很常见。表现是 Agent 直接开始改文件,不调 todowrite。排查顺序:先看 description 里触发条件是否够强,把「当任务涉及」改成「必须首先调用」;再看模型是否支持 function call,有些轻量模型不支持工具调用,换一个支持 tool use 的 Model ID;最后看 OpenCode 版本,老版本可能工具注册方式不同,升级到最新版。
排查时有个通用技巧:把 OpenCode 的日志级别调到 debug,看完整请求和响应。请求里能看到工具提示词有没有被注入,响应里能看到模型返回的原始结构。对照这两端,问题基本能定位。
6. 把 todowrite 用顺之后的下一步
todowrite 跑通之后,你会发现 Agent 做多步任务的可控性上了一个台阶。但工具提示词不是一劳永逸的,不同模型对同一段 description 的响应差异挺大。我的做法是维护一个提示词版本表,每换一次 Model ID 就跑一次第 4 节那个重命名验证任务,记录触发率和清单质量,选触发稳定、清单粒度合理的那个组合。
如果你打算长期用 Agent 做编码,建议把 todowrite 和 Coding Plan 配合起来,地址https://taotoken.net/coding-plan,这样工具调用链路的请求量有保障,不会因为额度问题中断任务。接入文档在https://taotoken.net/doc,里面有各语言的调用示例,对照着调 settings 更快。
最后留一个实用技巧:todowrite 的清单项 content 写得越具体,Agent 执行时越不容易跑偏。比如「修改 src/router.js 第 42 行的调用点」就比「修改路由文件」好得多。你可以在工具提示词里加一句「每项 content 必须包含具体文件路径和修改位置」,模型列清单时就会自动细化。这个改动很小,但实测对执行准确率提升明显。