Pi 接入第三方 API:费用显示 $0 和 500 报错怎么修

Pi 接自定义 provider 后每次会话都显示 $0,Anthropic 系模型一开推理就报 500。两个都是配置问题,不是 bug。基于 pi 0.84.1 实测。

Pi 接入第三方 API:费用显示 $0 和 500 报错怎么修

Pi 出厂就接好了 36 家 API key provider 和六种订阅登录,而你要用的那家大概率仍然不在里面。 把它指到别处只需要一个 JSON 文件、大约五分钟,这是容易的部分。有意思的是配完之后会出问题的那三件事,它们没有一件会自报家门说「我是配置问题」。

下面所有内容都是 2026-08-18 在 macOS 上用 pi 0.84.1 实跑出来的,对面是一个通过 OpenAI 兼容端点提供 131 个模型的网关。

配置文件:        ~/.pi/agent/models.json
provider 字段:   baseUrl、api、apiKey、models[]
协议:            openai-completions、openai-responses、
                  anthropic-messages、google-generative-ai
从环境变量取 key:"$OFOX_API_KEY"
从钥匙串取 key:  "!security find-generic-password -ws ofox"
未登记的模型:    能跑,带一条警告,按 128K 上下文
费用统计:        $0.00,直到你补上 cost 块
推理 + Claude:   500,直到你加上 supportsDeveloperRole: false
覆盖内置 provider:只改 baseUrl,停在 /v1 之前
重新加载:        自动,每次打开 /model 都会重读

配完之后能做什么,不能做什么?

你会得到网关在卖的每一个模型,跑在 Pi 自己的循环里,账都记在一把 key 上。 你不会得到从网关拉回来的模型列表、能用的费用数字,也不会在某个模型悄悄以真实上下文八分之一的窗口运行时收到任何提示。

马上就能用的:

  • 任何说 OpenAI chat completions、OpenAI Responses、Anthropic Messages 或 Google Generative AI 的端点。
  • 会话中途用 /model 切模型,跨 provider 也行,因为每次打开选择器都会重读这个文件。
  • 从环境变量或一条 shell 命令里取 key,于是没有任何密钥需要躺在 JSON 里。
  • 思考档位、图片输入和工具调用,前提是你逐个模型声明清楚。

用不了的:

  • 没有目录发现。 指向一台会记录每个请求的本地服务器后,Pi 每次运行往 /v1/chat/completions 发了四个 POST,对 /v1/models 一个 GET 都没有。你打什么它就知道什么。
  • 没有费用数字。 token 数是准的,钱是零,直到你自己把单价写进去。
  • 不会嗅探协议。 用错误的基址覆盖内置 provider,回来的错误描述的是鉴权,不是协议。

该把 Pi 指向网关,还是直接用它的内置 provider?

已经在直接给 Anthropic、OpenAI 或 Google 付钱的,用内置的。当你要的模型不在这套里,或者「一把 key 打通所有工具」比「各家各自的用量面板」更重要时,再加自定义 provider。

自定义 provider 值这个价的场景:

  • 你要跑的模型没有任何一方官方 provider 提供,实际上就是大部分中国开源权重旗舰,以及所有第三方托管而非官方直供的模型。
  • 你已经把 Claude Code 或 Codex CLI 走网关了,想要一把 key、一张账单,而不是四份。
  • 你想拿一个便宜的默认模型和一个昂贵的升级模型做 A/B,又不想为后者单开一个账号。

不值得为它写这个文件的场景:

  • 你只用一家厂商、一个套餐。/login 直接覆盖六种订阅,包括 ChatGPT Plus 和 Pro、Claude Pro 和 Max、GitHub Copilot、xAI 和 OpenRouter,上面这些一样都不用做。
  • 你跑的是本地运行时。Ollama、vLLM 和 llama.cpp 都是文档里写明的场景,只需要 baseUrl 加一个模型 ID,本文其余内容都用不上。
  • 你只是想改内置 provider 的 key。那是一行覆盖,文章接近末尾的地方讲了。

停止条件: 如果 pi --list-models 已经列出了你打算跑的模型,可以关掉这一页了。本文存在的全部意义,是加上 Pi 不认识的模型。

开始之前需要准备什么?

Node 22 或更新版本、一把 key,以及一个你已经 curl 过一次的基址。

需要什么我们用的备注
Node.js24.14.1包里声明 engines: node >=22.19.0
Pi0.84.1(最新 0.84.2)@earendil-works/pi-coding-agent,MIT
端点https://api.ofox.ai/v1必须响应 /chat/completions,光有 /models 不算
Key一把网关 key放在 $OFOX_API_KEY 里,绝不内联
模型 ID精确字符串网关的 ID,不是厂商的 ID

还没装的话:

npm install -g @earendil-works/pi-coding-agent
pi --version

写文件之前有件事值得先想清楚:你取的 provider 名字会进到之后每一个 --provider 参数和每一条会话记录里。以后改名,旧会话就会指向一个不再存在的 provider。

怎么给 Pi 加一个自定义 provider?

一个文件里的四个字段,再用一条命令证明它真的通了。

第一步:写 provider 块

~/.pi/agent/models.json 装着全部内容。最小可用的一条:

{
  "providers": {
    "ofox": {
      "baseUrl": "https://api.ofox.ai/v1",
      "api": "openai-completions",
      "apiKey": "$OFOX_API_KEY",
      "models": [
        { "id": "deepseek/deepseek-v4-flash", "contextWindow": 1000000, "maxTokens": 384000 }
      ]
    }
  }
}

openai-completions 是第一个该试的。它是实现最广的形状,而在我们这个网关上 openai-responses 对同一个模型也能跑通,这一点换个地方可不能默认成立。

第二步:把 key 放进环境变量,不要放文件里

export OFOX_API_KEY=sk-...

apiKey 有三种解析方式:字面字符串、$VAR${VAR} 插值,以及 !command,即执行一条 shell 命令并把 stdout 当作 key。共用机器上要用的就是第三种:

"apiKey": "!security find-generic-password -ws ofox"

第三步:确认 Pi 看得到这些模型

pi --list-models ofox
provider  model                       context  max-out  thinking  images
ofox      deepseek/deepseek-v4-flash  1M       384K     no        no
ofox      moonshotai/kimi-k3          1M       1M       no        no
ofox      z-ai/glm-5.2                1M       128K     yes       no

这些列来自你的文件,不是来自网关。把某条记录里的 contextWindowmaxTokens 删掉,同一条命令就会给它打印 128K16.4K,也就是 Pi 文档里写的默认值。如果一个会推理的模型在 thinking 列显示 no,那是你的声明漏了,不是端点拒绝了。

第四步:跑一个会碰到磁盘的任务

print 模式是最快的验证方式,因为它跑的是工具循环,而不只是补全端点:

pi --provider ofox --model deepseek/deepseek-v4-flash -p \
  "Read buggy.py, run it, and state the one-line bug. Do not edit files."

在一个临时目录里放一个两行的文件,add 函数里写着 return a - bDeepSeek V4 Flash 读了文件、通过 bash 工具跑了解释器,第一次就答对了。整个集成测试就是这三件事:读文件、执行 shell、给答案。

openai-responses 也能用吗?

在我们这个网关上,同一个模型可以,唯一的改动就是协议名。"api": "openai-completions" 换成 "api": "openai-responses",重跑同一条提示词,答案一样。

但别把这条推广出去。Responses 支持与否是由托管方逐模型决定的,不是逐网关决定的,所以一个端点可以对某个模型响应 /v1/responses,对下一个模型压根没有 Responses 路由。openai-completions 是覆盖面最广的形状,除非某个特定模型非要,否则选别的没有任何好处。真正会逼你面对这个问题的工具是 Codex CLI,因为它只说 Responses。

为什么 key 明明不能用,pi auth check 还说 ready?

因为它检查的是 key 存不存在,不是有没有效。 我们故意把 provider 配上一把无效的 key 然后问它:

pi auth check --provider ofox
# ready

同一份配置,下一个请求:

401: {"message":"Invalid or expired API key","type":"invalid_api_key","code":401}

ready 的意思是 Pi 成功往 apiKey 这个槽位里解析出了点东西。把它当成对环境变量名的拼写检查就好,别的什么也别指望。真正的就绪测试是第四步。

Pi 为什么每次会话都报 $0?

因为自定义 provider 没有价目表,而 Pi 不会去编一份。 token 记账是精确的。下面是那次首跑两轮对话里 Pi 写下的用量记录,直接取自 ~/.pi/agent/sessions/ 下的会话文件:

轮次inputoutputcacheReadreasoningtotalcost
12,8401220122,962$0.00
252562,94403,052$0.00

这张表里有两件事得分开看。cacheRead 那个数是真的:第二次调用时网关返回了 prompt_tokens_details.cached_tokens,Pi 把它记下来了。cost 那一列不是假的,是根本没有。cost 下面的每个字段都停在零,因为这条模型记录从来没声明过单价。

补上之后算术就开始工作了:

{
  "id": "anthropic/claude-sonnet-5",
  "contextWindow": 1000000,
  "maxTokens": 128000,
  "cost": { "input": 2, "output": 10, "cacheRead": 0.2, "cacheWrite": 2.5 }
}

单价按每百万 token 计,取自你实际接入的那家的价格页,而不是模型厂商的价格页。下一次跑 Claude Sonnet 5 记录了 4,111 个输入 token 和 4 个输出 token,定价为 $0.008222$0.00004,合计 $0.008262,正是这两个数分别乘以每百万 $2 和 $10。这套算术是 Pi 在本地做的,所以数字的诚实程度完全取决于你填的单价。填网关的价,不是模型厂商的价,价格页变了要回来重核。

contextWindow 同理。Sonnet 5 在这个网关上是 1M 上下文的模型,记录里就得这么写,否则 128K 的默认值会悄悄接管。

模型为什么会以「unsupported message role: developer」失败?

因为 reasoning: true 会让 Pi 把系统提示词作为 developer 角色的消息发出去,而不是每个上游都收这个角色。 这个失败很响,看着像服务端故障:

500: {"code":null,"message":"Request error: failed to convert messages:
unsupported message role: developer","param":null,"type":"api_error"}

这段字符串里没有任何东西指向你的配置,所以值得好好隔离一次。三次运行,同一个网关、同一个模型,每次只动一个字段:

模型记录结果
reasoning: true500,unsupported message role: developer
reasoning: truecompat: { supportsDeveloperRole: false }正常
reasoning: false正常

所以触发点就是 developer 这个角色,而两种修法的代价不同。把这三份配置打到一台会记录请求体的本地服务器上,变化一目了然:

模型记录messages[].role发出去的 reasoning_effort
reasoning: true["developer", "user"]"medium"
supportsDeveloperRole: false["system", "user"]"medium"
reasoning: false["system", "user"]没有

compat 这个开关把系统提示词挪回 system 消息,同时保留 reasoning_effort,所以思考能力活着。把 reasoning 设成 false 同样能消错,方式是把 reasoning_effort 整个从请求里去掉,这通常是笔亏本买卖。

同一次抓包还顺带回答了一个常被问到的关于 maxTokensField 的问题:在 openai-completions 下 Pi 发的是 max_completion_tokens,不是 max_tokens。如果你的端点只认旧字段,要翻的就是这个开关。

这个角色只在一部分目录上被拒。同一个网关、同样的 reasoning: true,三个模型家族:

模型reasoning: true 的结果
DeepSeek V4 Flash正常
GLM 5.2正常
Claude Sonnet 5500,直到加上 supportsDeveloperRole: false

规律在上游的形状,不在网关的策略。Anthropic 的 API 里没有 developer 角色,所以一个把 OpenAI 形状的请求翻译成 Messages 的网关,压根没有东西可以映射过去。OpenAI 形状的上游收下就走。这和 Codex CLI 发出空的工具描述、有些上游校验有些上游忽略是同一类问题,我们在拿九个 harness 打同一个网关的时候撞见过。教训重复出现:客户端和端点吵起来的时候,先读报文,再改配置。

Pi 的文档里还列了同一族的另外两个开关,supportsReasoningEffort 用于拒收推理参数的服务端,maxTokensField 用于只认 max_completion_tokens 而不认 max_tokens 的服务端。如果一个模型在你打开思考的瞬间就 400,接下来该试的就是这两个。

必须把每个模型都列出来吗?

不用,而且在一个有 131 个模型的网关上你也不该试。 Pi 从没见过的 ID 照样能跑:

pi --provider ofox --model z-ai/glm-5.2 -p "say ok"
# Warning: Model "z-ai/glm-5.2" not found for provider "ofox". Using custom model id.
# ok

这个兜底就是五行配置和五百行配置的差别。它也藏着一笔代价。未登记的模型会继承 Pi 的默认值,文档里写的是 128,000 上下文和 16,384 最大输出,而 pi --list-models 对任何省略了这两项的记录打印的正是这两个数。自动压缩会在 contextTokens > contextWindow - reserveTokens 时触发,reserveTokens 默认 16,384,于是一个 1M 上下文的模型会在 111,600 token 附近就开始给自己写摘要,而不是接近一百万的时候。输出里没有任何一句话解释为什么。项目社区里确实有人反映压缩来得比预期早;这至少是能产生该现象的一种机制,而且在怪罪模型之前排除它的成本很低。

实操上的分法:让未登记的 ID 覆盖探索期,然后给你每天真跑的那两三个模型写正式记录,把 contextWindowmaxTokensreasoninginputcost 填全。Pi 关于一个模型显示的一切,包括它收不收图片,都来自这条记录,而不是来自端点。

能把 Pi 内置的 anthropic provider 指向网关吗?

能,而且对 Claude 模型来说这是更好的路线,前提是给它 Anthropic 的基址而不是 OpenAI 的。 覆盖只有一行,也不用自己写模型列表:

{ "providers": { "anthropic": { "baseUrl": "https://api.ofox.ai/anthropic", "apiKey": "$OFOX_API_KEY" } } }
pi --provider anthropic --model claude-sonnet-5 -p "Reply with exactly: ok"
# ok

Pi 保留它内置的整个 Claude 目录,窗口都是对的,Claude Fable 5 是 1M,Opus 和 Haiku 那几条是 200K。什么都不用声明,什么都不用同步,也见不到 developer 角色,因为这条路上 Messages 就是原生形状。厂商的模型 ID 直接可用,不用加网关前缀。

基址写错会产生两种错误,而它们描述的都是别的问题。指到 OpenAI 路径上:

401 {"error":{"message":"You didn't provide an API key. You need to provide your API key
in an Authorization header using Bearer auth ...","type":"invalid_request_error","code":401}}

这里没有任何鉴权 bug。Pi 说的是 Messages,所以它发的是 x-api-key,而 OpenAI 那条路径只收 Authorization: Bearer。通过 provider 的 headers 字段补一个 Bearer 头,诚实的答案就出来了:404 Unsupported OpenAI API endpoint。那个 401 是披着鉴权外衣的协议不匹配。

另一种写错的方式是版本段写重了:

404 {"error":{"message":"Unsupported Anthropic API endpoint. ...","code":404}}

这是配置里写成了 .../anthropic/v1。Pi 自己会追加 /v1/messages,所以基址停在 /anthropic

baseUrl结果
https://api.ofox.ai/anthropic正常,内置 Claude 目录全在
https://api.ofox.ai/anthropic/v1404,Unsupported Anthropic API endpoint
https://api.ofox.ai/v1401,实际是走错协议的 404

所以同一把 key 下 Claude 有两条路:上面那个内置覆盖,或者一条用网关自己 ID 的自定义 openai-completions 记录,也就是前面算费用那节用的写法。覆盖打字少,还完全绕开了 developer 角色。自定义记录适合你想要 Pi 本来不知道的逐模型 costcontextWindow 值的时候。

Claude 模型到底该不该在 Pi 里跑?

能跑,而且 Pi 的作者本人记录过新模型上的一个 schema 问题。 2026-07-04 的文章里,Armin Ronacher 写道,「较新的 Claude 模型有时会带着多余的、编造出来的字段调用 Pi 的 edit 工具,塞在嵌套的 edits[] 数组里」,结果是「模型编出不存在的 key,于是 Pi 拒掉这次工具调用并要求重试」。他对趋势的总结才是难受的部分:「越新的 Anthropic 模型这个问题越严重,Opus 4.8 和 Sonnet 5 都有,而更老的模型一个都没有。」

这是训练与工具之间的错配,不是一个基址能修好的,而且每次触发都要多花一次重试。它不至于让 Claude 在 Pi 里没法用。但它确实意味着,如果你在给一个自带 edit 工具的 harness 挑默认模型,最新的 Claude 不会自动成为最稳的那个选择,值得盯一眼会话日志里同一处编辑是否被反复调用。

怎么看清 Pi 实际发出去的是什么?

用 curl 复现同一个调用,再和 Pi 记下来的东西对一遍。 两个文件加一条命令就覆盖了大部分需求,而且都不需要架代理。

会话日志是第一站。每次运行都会在 ~/.pi/agent/sessions/<project>/ 下写一个 JSON Lines 文件,一条事件一行,其中有一行 model_change 写明 Pi 解析出的 provider 和模型 ID,还有一条带着 usage 块的 assistant 消息。如果那个文件里的模型 ID 不是你打算跑的那个,问题出在你的参数或者兜底逻辑上,再怎么调 provider 也没用。

端点是第二站。自己发一份同样形状的请求:

curl -s https://api.ofox.ai/v1/chat/completions \
  -H "Authorization: Bearer $OFOX_API_KEY" -H 'Content-Type: application/json' \
  -d '{"model":"deepseek/deepseek-v4-flash","messages":[{"role":"user","content":"hi"}],"max_tokens":8}' \
  | python3 -c 'import sys,json; print(json.load(sys.stdin)["usage"])'

usage 里有两处值得仔细读。prompt_tokens_details.cached_tokens 就是 Pi 映射到 cacheRead 那一列的东西,所以这个 key 要是从来不出现,无论你的前缀多稳定,会话日志里的缓存数字都会一直是零。而 completion_tokens_details.reasoning_tokens 告诉你思考到底有没有在跑,这比读输出然后猜要快得多。

改配置之前先做这一步,是我们写过的每一篇 harness 接入文章共同的结论。错误字符串是谁抛的谁写的,而抛错的那个组件经常不是出问题的那个。

配置过程中会坏在哪,怎么修?

六种失败,其中五种在 0.84.1 上对着真实端点复现过,还有一种用 Pi 自己的模型表演示过。

症状原因修法
401: {"message":"Invalid or expired API key"...}key 解析出来了但不对,或者变量在当前 shell 里是空的怪文件之前先 echo $OFOX_API_KEYpi auth check 抓不到这种
404 404 page not foundbaseUrl 少了版本段https://host/v1,不是 https://host
500 ... unsupported message role: developer在一个上游没有 developer 角色的模型上设了 reasoning: truecompat: { supportsDeveloperRole: false }
anthropic provider 上报 401 ... provide your API key ... using Bearer auth内置覆盖指到了 OpenAI 路径,于是 Pi 发 x-api-key,而那边只收 Bearer用网关的 Anthropic 基址,结尾停在 /anthropic
404: {"message":"Model 'openai/gpt-5.6' not found","type":"model_not_found"}ID 格式没错,但不在这个网关的目录里从网关自己的模型页上抄 ID;厂商发布了一个模型,不等于每个目录里都有它
会话远早于模型真实窗口就开始压缩未登记的模型退回了 128K 默认值给那个模型声明 contextWindowmaxTokens

第四行和第五行是最费时间的两个,因为这两条错误信息描述的都不是真正的问题。

团队之间怎么共享 Pi 的 provider 配置?

共享文件,永远不共享 key。 当每个 apiKey 都写成 $VAR!command 时,models.json 里不含任何密钥,于是可以安心提交进 dotfiles 仓库或者引导脚本。

一种能扛住多人协作的切分:

  • 提交 provider 块:基址、协议,以及带 contextWindowmaxTokensreasoningcost 的完整模型记录。这些是关于端点的客观事实,对每个人都一样,写错了就会产生悄悄的提前压缩和假的 $0 账单。
  • 绝不提交 key。共享文件里写 "apiKey": "$OFOX_API_KEY",真值放在每位开发者自己的 shell 配置或钥匙串里。
  • 锁住你验证过的版本。 Pi 大约每周发一版,不到一个月就从 0.82.1 走到了 0.84.2。记下你的配置是在哪个版本上测过的。
  • 给所有人同一个基址。 一个端点意味着一份模型目录、一个限流池、一个能看到花销的地方,而不是每位开发者各猜各的。

最后这条是团队最容易跳过的,也正是它能把「你现在用的是哪个模型?」从一个问题变成一次查表。

怎么让每个 harness 都指向同一把 key?

每个 harness 都用自己的方言存放模型访问方式。Claude Code 读 ANTHROPIC_BASE_URLANTHROPIC_AUTH_TOKEN。Codex CLI 要 config.toml 里的 model_providers 块,而且只认 Responses API。Cline 有个设置面板。DeepSeek Harness 要一个自定义 provider 表单或者 DEEPSEEK_BASE_URL。Pi 要上面那个 JSON 文件。五个工具、五个轮换 key 的地方、五份逐渐跑偏的模型列表。

它们对着一个 OpenAI 兼容或 Anthropic 兼容的端点说的都是 HTTP,所以修法在每个工具里是同一个:一个基址、一把 key,唯一变化的是模型字符串。这就是为什么这些工具的自定义 provider 表单长着同样的四个字段。

ofox 上,这个端点是 https://api.ofox.ai/v1,配 openai-completions,2026-08-18 这天一把 key 够到了 131 个模型,除了上文用到的 DeepSeek、GLM 和 Claude 记录,还包括 Kimi K3MiniMax M3。其他工具的等价配置在我们的 Codex CLI 自定义 provider 指南OpenCode 配置详解Cursor、Claude Code 与 Cline 配置 里。

Pi 和你现在用的 harness 比怎么样?

活是同一份活,表面积小得多,配置文件也诚实得多,它对自己假设了多少一清二楚。 Pi 给模型四个工具和一套扩展 API,而 Claude Code 开箱就给 hooks、subagent、skills 和 MCP server。抽象地说谁更好没有意义。问题在于你想要的是组装好的,还是需要组装的。

自定义 provider 这条路暴露的,是这种极简主义在哪里要付出代价。不拉目录、不带价目表、不嗅探协议。本文里的三个问题,每一个都是 Pi 拒绝替你猜;每一个修法,都是你把事实写下来一次。

想看 Pi 在整个赛道里的位置,包括各个 harness 内部人们实际在跑哪些模型的 OpenRouter 用量数据,见九个 harness 的横评。只想看终端 agent 的话,Claude Code、Codex CLI 与 Cursor 的对比讲得更细。

References

常见问题

Pi coding agent 是什么?
Armin Ronacher 和 Mario Zechner 做的终端 coding agent,MIT 协议,现在在 Earendil 名下开发,仓库是 github.com/earendil-works/pi。它只给模型四个内置工具(read、write、edit、bash),别的几乎什么都不带,用一套扩展 API 代替 hooks、subagent 和 skills。截至 2026-08-18,仓库有 92,619 star,npm 包每周下载量 137 万。
装 Pi 该用哪个 npm 包?
@earendil-works/pi-coding-agent,当前版本 0.84.2,engines 要求 node >=22.19.0。旧的 @mariozechner/pi 包停在 0.70.6,每周只有几百次下载,那是收购前的发布通道,装它拿到的是迁到 Earendil 之前的版本。
Pi 支持接第三方 API 端点吗?
支持,走 ~/.pi/agent/models.json。一条 provider 记录包含 baseUrl、api、apiKey 和一个 models 数组,其中 api 取值为 openai-completions、openai-responses、anthropic-messages 或 google-generative-ai 之一。不用改代码也不用 fork,而且每次打开 /model 选择器时这个文件都会重新加载。
Pi 能从环境变量读 API key 吗?
能。apiKey 支持 $VAR 和 ${VAR} 两种插值写法,还支持 !command,即执行一条 shell 命令并把 stdout 当作 key。第二种写法就是用来从系统钥匙串取密钥、避免把明文留在 JSON 里的。想写字面上的美元符号用 $$。
models.json 里必须把每个模型都列出来吗?
不必。传一个不在列表里的模型 ID,Pi 会打印 Warning: Model not found for provider,然后当自定义模型 ID 跑起来。代价是未登记的模型会继承默认值,也就是 128,000 上下文和 16,384 最大输出,于是一个 1M 上下文的模型会远早于必要的时机就开始压缩。
Pi 为什么每次请求都显示 $0 费用?
因为自定义 provider 不带任何价格元数据。token 数在会话文件里记得完全正确,但 cost 下面的每个字段都会一直是零,除非你在模型记录里补一个 cost 块,写上 input、output、cacheRead 和 cacheWrite 的每百万 token 单价。
Pi 内置的 anthropic provider 能指向网关吗?
能,前提是给它网关的 Anthropic Messages 基址而不是 OpenAI 那条路径。覆盖内置 anthropic provider 的 baseUrl 就能保留 Pi 完整的 Claude 目录和正确的上下文窗口,而且不需要写 models 数组。URL 要停在版本段之前,因为 Pi 自己会追加 /v1/messages;指到 OpenAI 路径上会返回一个看着像鉴权、其实是协议不匹配的 401。
Pi 里报 unsupported message role: developer 是什么意思?
意思是这条模型记录里 reasoning 设成了 true,于是 Pi 把系统提示词当成 developer 角色的消息发出去,而上游拒收这个角色。加上 compat 里的 supportsDeveloperRole: false 可以保住思考能力;把 reasoning 设成 false 也能消错,代价是丢掉思考。在 OpenAI 形状的网关上,这个问题只会出现在 Anthropic 系模型上。