1. Pi Agent 调用 opencode 免费模型为什么总报流量耗尽
Pi Agent 是一个跑在本地的命令行 Agent 框架,它的定位很直接:把大模型能力接到你的终端里,让模型帮你读写文件、执行命令、跑测试。opencode 则是一个开源的编码助手项目,它对外暴露了兼容 OpenAI 协议的接口,并且提供了一批免费模型额度。很多人第一反应就是把这两个拼起来用——本地 Agent 负责调度,opencode 负责出 token,看起来是零成本方案。
但真正动手之后,报错会来得比想象中快。最常见的两类现象是:第一,请求直接返回流量耗尽或者额度不足,可你明明刚注册、额度还是满的;第二,模型列表拉出来是空的,/models返回 200 但data数组里什么都没有。这两个问题看起来像鉴权失败,实际上根因往往不在 Key,而在两个特别容易被忽略的地方:models.json里的字段结构,以及请求头里的User-Agent。
我先把结论摆出来,方便你对号入座。opencode 的服务端会校验请求来源,它期望看到的是自家客户端发出的请求特征。如果你的 Agent 用默认的 HTTP 客户端 UA(比如python-requests/2.x、node-fetch、Go-http-client),服务端可能直接判定为非官方流量,于是返回额度耗尽的错误——这个错误信息具有误导性,它不告诉你 UA 不对,只告诉你没流量了。另一个坑是models.json的层级,很多人把headers写在 provider 同级而不是嵌在 provider 内部,配置加载时被静默忽略,UA 根本没生效。
这篇面向的是本地 Agent 开发者,尤其是已经在用 Pi Agent、想接 opencode 免费模型做联调的人。你需要的基础是:会编辑 JSON 配置文件,能用 curl 发请求,看得懂 HTTP 状态码。下面我会给出可直接复制的models.json片段、UA 覆盖写法、curl 验证命令,以及把 endpoint 切到 TaoToken 统一 Key 通道完成联调的完整动作。全程不需要任何额外网络工具,纯本地配置。
2. TaoToken 统一 Key 通道的前置准备与模型清单确认
在讲 opencode 的坑之前,先说一下为什么建议你同时准备一条 TaoToken 的通道。原因很实际:opencode 免费模型有额度限制,而且它的 UA 校验规则可能随版本变化,今天能用的写法下个月未必能用。当你需要稳定联调、或者免费额度用完之后,把 endpoint 切到一个统一的 Key 通道是最省事的做法,不用改 Agent 代码,只改配置里的 base URL 和 Key。
TaoToken 的接入地址是https://taotoken.net/api,它兼容 OpenAI 的/v1/chat/completions和/v1/models协议,所以 Pi Agent 里凡是走 OpenAI 兼容格式的 provider,基本都能直接指过来。你需要先去控制台创建一个 API Key,入口在https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=。创建完之后把 Key 复制出来,格式通常是一串以sk-开头的字符串,先存到环境变量里,别硬编码进配置文件。
模型清单这块,你可以先用模型对话页面确认一下当前可用的模型 ID,地址是https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=。为什么要先确认?因为 Pi Agent 的models.json里要写死模型 ID,如果你写的 ID 在服务端不存在,请求会返回 404 或者model not found,这个错误和 UA 问题混在一起排查会很痛苦。把模型 ID 抄准,能省掉一半的排障时间。
环境变量建议这样设,Linux/macOS 用:
export TAOTOKEN_API_KEY="sk-你的key" export TAOTOKEN_BASE_URL="https://taotoken.net/api"Windows PowerShell 用:
$env:TAOTOKEN_API_KEY="sk-你的key" $env:TAOTOKEN_BASE_URL="https://taotoken.net/api"设完之后用echo $TAOTOKEN_API_KEY确认一下有没有生效。这一步看着简单,但我见过太多人配置文件里写的是$TAOTOKEN_API_KEY,结果 shell 没导出,Agent 读到的字面量就是那个美元符号加变量名,然后报 401。如果你打算长期用编码类 Agent,也可以了解一下 Coding Plan,地址是https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=,它更适合高频调用场景。
3. models.json 字段结构与 User-Agent 覆盖的可复制配置
现在进入正题。Pi Agent 的配置文件默认在~/.pi/agent/models.json,Windows 下是%USERPROFILE%\.pi\agent\models.json。这个文件的结构是providers对象下面挂各个 provider,每个 provider 里有baseUrl、apiKey、models等字段。关键点在于headers的位置:它必须是 provider 的直接子字段,和baseUrl平级,不能写到models数组里面去。
先给你一份接 opencode 免费模型的完整配置,可以直接复制:
{ "providers": { "opencode": { "baseUrl": "https://opencode.ai/api", "apiKey": "opencode-free", "headers": { "User-Agent": "opencode/1.18.18" }, "models": [ { "id": "opencode/grok-code", "name": "Grok Code Fast" } ] } } }这里有几个细节要盯住。第一,User-Agent的值写成opencode/1.18.18这种「客户端名/版本号」的格式,服务端就是靠这个判断你是不是官方客户端。版本号可以跟着 opencode 的 release 走,但不要写成浏览器 UA,那样反而更可疑。第二,apiKey在免费模型场景下通常填一个占位符就行,因为 opencode 免费额度不校验真实 Key,但字段不能缺,缺了 Pi Agent 加载时会报 provider 配置不完整。第三,models数组里的id必须和服务端实际暴露的模型 ID 完全一致,大小写敏感。
如果你要把 endpoint 切到 TaoToken,配置改成这样:
{ "providers": { "taotoken": { "baseUrl": "https://taotoken.net/api", "apiKey": "${TAOTOKEN_API_KEY}", "headers": { "User-Agent": "opencode/1.18.18" }, "models": [ { "id": "claude-sonnet-4-5", "name": "Claude Sonnet 4.5" } ] } } }注意apiKey这里用了${TAOTOKEN_API_KEY}的变量引用写法,Pi Agent 支持从环境变量插值。如果你不确定你的版本支不支持,就直接把 Key 字符串填进去,先跑通再优化。User-Agent我建议保留,因为有些网关对 UA 有白名单策略,保留一个正常的客户端标识比裸奔更稳。
再强调一次headers的层级。下面这种写法是错的,UA 不会生效:
{ "providers": { "opencode": { "baseUrl": "https://opencode.ai/api", "models": [ { "id": "opencode/grok-code", "headers": { "User-Agent": "opencode/1.18.18" } } ] } } }错在哪?headers被塞进了models数组的元素里,而 Pi Agent 读取请求头是从 provider 层级读的,模型层级的headers会被忽略。这个错误不会报错,只会表现为「配置改了但没用」,非常隐蔽。改完配置后,建议用python -m json.tool ~/.pi/agent/models.json校验一下 JSON 合法性,避免逗号或括号问题导致整个文件加载失败。
4. 用 curl 与日志验证模型可用性的具体动作
配置写完别急着在 Agent 里跑,先用 curl 单独验证,把配置问题和网络问题分开。第一步验证模型列表:
curl -s https://opencode.ai/api/models \ -H "User-Agent: opencode/1.18.18" \ -H "Authorization: Bearer opencode-free" | python -m json.tool如果返回的 JSON 里data数组有内容,说明 UA 和鉴权都过了。如果data是空的,先检查 UA 是不是被 shell 转义了,再检查 URL 路径对不对。如果返回 401,把Authorization头去掉再试一次,有些免费端点不认这个头。
第二步验证对话接口:
curl -s https://opencode.ai/api/chat/completions \ -H "Content-Type: application/json" \ -H "User-Agent: opencode/1.18.18" \ -H "Authorization: Bearer opencode-free" \ -d '{ "model": "opencode/grok-code", "messages": [{"role": "user", "content": "say hi"}], "max_tokens": 16 }' | python -m json.tool正常返回里会有choices[0].message.content。如果返回choices字段读取失败,或者报reading 'choices'之类的错误,八成是响应体根本不是 JSON,可能是 HTML 错误页,这时候把-s换成-i看完整响应头,状态码会告诉你真相。
切到 TaoToken 通道后,验证命令换成:
curl -s https://taotoken.net/api/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -d '{ "model": "claude-sonnet-4-5", "messages": [{"role": "user", "content": "say hi"}], "max_tokens": 16 }' | python -m json.toolcurl 通了之后,再回到 Pi Agent 里跑。开启调试日志的方式通常是设环境变量PI_LOG_LEVEL=debug,然后启动 Agent,观察它实际发出的请求头里有没有你配置的 UA。日志里会打印request headers,如果看到User-Agent: python-requests/2.x而不是你配的值,说明配置没被加载,回去检查models.json的路径和层级。实测下来,把 curl 验证和 Agent 日志这两步做扎实,排障时间能从半天压缩到十分钟。
5. 401、local proxy failed、reading choices 等报错逐条排查
这一节把常见报错和根因对应起来,你按现象查就行。
401 Unauthorized:先确认 Key 有没有正确注入。如果你用的是${TAOTOKEN_API_KEY}变量写法,在 shell 里echo一下确认非空。如果 Key 是对的还报 401,检查Authorization头的格式,必须是Bearer加空格加 Key,少个空格也会 401。还有一种情况是 Key 前后带了换行或引号,从控制台复制时容易带上,用cat -A看一下配置文件里有没有隐藏字符。
local proxy failed:这个错误通常出现在你本地配了 HTTP 代理,但代理进程没起来或者端口不对。Pi Agent 会读取HTTP_PROXY/HTTPS_PROXY环境变量,如果你之前设过,先unset HTTP_PROXY HTTPS_PROXY再跑。注意这里说的是本地代理进程配置问题,不是让你去用什么网络工具,纯粹是环境变量清理。
reading 'choices' / cannot read properties of undefined:这是响应体结构不符合预期。根因一般是服务端返回了错误 JSON,比如{"error": {"message": "..."}},而 Agent 代码直接去读choices就崩了。解决办法是先用上一节的 curl 命令看原始响应,把error.message读出来。常见的有model not found(模型 ID 写错)、insufficient quota(额度真用完了)、invalid user agent(UA 没生效)。
OAuth 相关报错:如果你之前用 opencode 官方客户端登录过,本地可能残留了 OAuth token 文件,Pi Agent 读取时可能和你的 API Key 冲突。检查~/.opencode/或~/.config/opencode/下有没有auth.json之类的文件,临时改名备份再试。如果你用的是 Codex 系的 Agent,auth.json里通常存OPENAI_API_KEY和base_url,确认这两个字段指向的是你要用的通道。
模型列表为空但状态码 200:这是最迷惑的一种。状态码 200 说明请求通了,但data为空说明服务端认为你没有可用模型。除了 UA 问题,还要检查请求的路径是不是/models而不是/v1/models,有些服务两种路径返回不同结果。另外确认你的账号有没有完成必要的初始化步骤,有些平台新账号需要先调一次对话接口才会激活模型列表。
排查顺序建议固定下来:先 curl 看原始响应,再开 Agent debug 日志看实际请求头,最后对照配置文件层级。三步走完,90% 的问题都能定位。
6. 把 endpoint 切到 TaoToken 完成联调的收尾动作
当你用 opencode 免费模型把 Pi Agent 的调用链路跑通之后,最后一步是把 endpoint 切到 TaoToken 统一 Key 通道,做一次完整联调。这么做的好处是:免费额度用完不用改代码,模型 ID 想换就换,而且 UA 校验的不确定性也绕开了。
具体动作就三步。第一步,把models.json里的 provider 从opencode改成taotoken,baseUrl改成https://taotoken.net/api,apiKey换成你的真实 Key。第二步,把models数组里的id换成你在模型对话页面确认过的模型 ID。第三步,重启 Pi Agent,跑一个真实任务,比如让它读一个文件并总结,观察日志里请求是否正常返回。
如果你在切通道过程中遇到鉴权问题,接入文档在https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=,里面有各语言的请求示例和错误码说明。Key 管理在https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=,可以随时创建和吊销。需要快速验证模型是否可用时,直接用模型对话页面发一条消息最直观。
最后留一个我踩过的坑:改完配置后一定要完全退出 Pi Agent 进程再重启,有些版本会缓存models.json,热重载不生效,你会以为配置没写对,其实是进程没重启。确认进程重启后,日志里能看到新的 base URL 和模型 ID,就说明切换成功了。