一台 Windows 上装着 Codex、Claude Code、OpenCode、Hermes、Kimi Code、Cline、ZCode……每个客户端一套模型配置,格式还都不一样。magpie 干的事就一件:在你机器上起一个 127.0.0.1:3425 的网关,所有客户端都连它,模型在一处选。听起来很轻,实测下来有三个真香点,也有一个会把配置撑大一倍、逼我先备份再回滚的坑。
TL;DR
· 适合:客户端多、模型换得勤、想把“本机 AI 资产”盘清楚的人。
· 很香:ls 盘点 24 个 agent 的当前模型;三窗口配额面板;三种协议实测都通。
· 有坑:magpie codex
· 结论:装来做盘点和配额;接管前备份整个 ~/.codex,跑完立刻 diff。配置回滚 ≠ 状态回滚。
· 最大收获:不是网关本身,是它生成的 magpie-models.json——那份约 660KB 的能力声明,帮我定位了一个卡了很多天的“看不见图”问题。
测试时间:2026 年 11 月,Windows。以下模型名、版本号仅代表测试时状态。
一、它解决的其实是矩阵爆炸
先说清楚痛点,不然会觉得这东西多余。
我机器上装了二十来个 AI 客户端。不是因为花心,是因为每个都有独占能力:Codex 的本地 app-server 能被我自己的飞书插件遥控,Claude Code 的 skills 生态最全,OpenCode 轻,Cline 在 VS Code 里,Hermes 有个常驻 gateway。它们各自要填模型、base_url、key、wire_api、思考档位,配置文件格式从 config.toml 到 settings.json 到 opencode.json 到 cordis.patch.yml,没有两个是一样的。
结果是:换一次模型,要改二十个地方。 模型一多,这件事从“麻烦”升级成“不可能维护”——你永远不知道哪个客户端还在偷偷用上个月的模型。
magpie 的思路是:不在客户端里配模型,而是在中间加一层。客户端只认一个 provider(就是它),所有真实厂商的 key 都收在网关里。
```
Codex ──┐
Claude ─┤
OpenCode├──→ 127.0.0.1:3425 (magpie) ──→ <订阅 provider> / 各家订阅 / 各家 API
Cline ──┤
Hermes ─┘
```
它把这件事叫“一个地方给每个 agent 选模型”。README 截至测试时有 34.6k 星,MIT,Go 写的,发布物是单个 exe。我下的是 0.1.3809,82MB 一个 exe,没有任何安装程序。
---
二、十分钟能走到哪一步
我实测的完整路径,全是命令行,没有一次点 GUI:
```powershell
.\magpie.exe --version → magpie 0.1.3809
.\magpie.exe serve → ● serves the gateway on 127.0.0.1:3425 · pid
.\magpie.exe ls → 24 个 agent,逐个列出当前模型 / 配置路径
```
ls 的输出比我预期狠。它不只是“知道你装了 Codex”,而是能读出每个客户端的当前生效模型:
```
Codex
OpenCode
DeepSeek Harness
Hermes Agent
Kimi Code
MiniMax Code custom_provider:provider-go/
OpenClaw
...
```
二十四条,覆盖了 Codex / Claude Code / Claude Desktop / OpenCode / OpenChamber / Pi / Prime Agent / VS Code / DeepSeek Harness / omp / Hermes Agent / Kimi Code / QwenPaw / OpenClaw / jcode / MiniMax Code / Cline / Qoder / ZCode / WorkBuddy / Paseo / AionUi / Cherry Studio / Claude Code·mem。
这一步的信息价值已经超过很多同类工具了:它等于给你一张“本机 AI 客户端资产盘点表”。
配额面板也很实:
```
provider-go plan 5h 0% 7d 11% ↻4d2h Mon 08:00 Month 32% ↻2d10h Sat 16:15
```
三个窗口同时给:五小时、七天、整月。↻ 后面是重置时间和时刻。这个设计明显是给订阅制用户做的——你不需要自己算“我这个月还剩多少”,它把三个口径一起摊开。
三、加 provider 很克制,接管 agent 是加法
这是我最想看的部分:一个工具替我更配置文件,到底动了什么。
先说结论:加 provider 很克制,只有真正“接管某个 agent”时才动客户端配置;但接管不是“只改一个键”,是加法。
我加自己的上游是这样:
```powershell
.\magpie.exe provider add provider-go url=https://
key=
→ ✓ added <订阅 provider> (provider-go) / ✓ 30 models from
```
这条只写进 magpie 自己的库。之后我 Compare-Object 比对了 ~/.codex/config.toml,120 行一字未变。
真正接管 Codex 是另一条命令,magpie codex
```toml
model = "provider-go/
# magpie: Codex on magpie's local gateway; Disconnect in magpie or `magpie uninstall` undoes it
model_provider = "magpie"
model_catalog_json = "magpie-models.json"
openai_base_url = "http://127.0.0.1:3425/backend-api/codex"
[model_providers.magpie]
name = "magpie"
base_url = "http://127.0.0.1:3425/v1"
wire_api = "responses"
experimental_bearer_token = "magpie"
```
四条键 + 一个 provider 段落,每行都带 # magpie: 注释说明怎么撤销。这是好习惯——它的注释本身就是回滚文档。
注意 model_catalog_json = "magpie-models.json" 这一条。它没有改你的 models.json,而是另写一份目录文件然后把 Codex 指过去。这个细节后来成了我定位看图问题的入口。
四、坑:它把我的配置撑大了一倍
这是全文最该看的一段,也是我差点写错的一段。
magpie codex
```
✓ Codex model provider-go/
↻ Codex builds its model list at start-up — restart the Codex app / app-server
```
但 diff 完新老配置,行数从 120 行变成 228 行,provider 从 1 个变成 15 个。
多出来的 14 个段是这样构成的:magpie 自己 1 个,加上 13 个来自其他客户端配置的占位 provider:
```
[model_providers.magpie] ← 它自己的网关
[model_providers.provider-a] ← 我没写过
[model_providers.provider-b] ← 我没写过
[model_providers.provider-c] ← 我没写过
[model_providers.provider-d] ← 我没写过
[model_providers.provider-e] ← 我没写过
[model_providers.provider-f] ← 我没写过
[model_providers.provider-g] ← 我没写过
[model_providers.provider-h] ← 我没写过
[model_providers.provider-i] ← 我没写过
[model_providers.provider-j] ← 我没写过
[model_providers.provider-k] ← 我没写过
[model_providers.provider-l] ← 我没写过
[model_providers.provider-m] ← 我没写过
```
这十三个名字,我一个都没在 ~/.codex/config.toml 里写过。它们来自我机器上别的客户端的配置——OpenCode、Cline、Pi、omp 都用过这些 provider 名。magpie 把它们通通汇总过来,在 Codex 的配置里各建一个占位段,全部指向自己的网关:
```toml
[model_providers.provider-m]
name = "provider-m"
# magpie: points at magpie's local gateway; ... or remove this entry and pick another model
base_url = "http://127.0.0.1:3425/v1"
wire_api = "responses"
experimental_bearer_token = "magpie"
```
原有的那个 provider-go 也被改了指向:
```toml
# 改之前
base_url = "https://
experimental_bearer_token = "
# 改之后
base_url = "http://127.0.0.1:3425/v1" wire_api = "responses"
experimental_bearer_token = "
```
所以准确的说法不是“它删了你的配置”,而是“它给你的配置做了加法”。key 没丢,header 没丢,只把 base_url 指走了。这一点要说清楚,不能冤枉它。
但加法本身也有代价:一份 120 行、你自己看得懂的配置,变成 228 行、混着十三个你永远不会在 Codex 里用的 provider。Codex 启动时会把它们全读进去,模型列表里就可能混进不相干的条目。而且那十三个段落的注释只说“删掉这行再挑个模型”,没告诉你它们是从哪儿冒出来的——你只能靠回忆和 diff 去还原。
我更希望它这么做:接管时只写 [model_providers.magpie],别顺手把别的客户端的 provider 名搬进来。或者至少加一行注释说明来源。
另一条提示也值得记:
```
codex: 数百条 thread(s) started on provider-go moved to magpie, where Codex resumes them
```
它会改写历史线程的归属记录,我这次是数百条。理论上是好事(老会话还能接着用),但这确实是对 Codex 状态的写操作,跟“只动一个键”的宣传有距离。配置回滚不等于状态回滚——这是这次实测里我最想强调的一句。
我给自己的规矩,写在这里:任何声称“只改一个键”的工具,第一次跑之前先备份整个 agent 状态目录,跑完立刻 Get-FileHash + Compare-Object。
```powershell
$ts = Get-Date -Format yyyyMMdd-HHmmss
Copy-Item "$HOME\.codex" "$HOME\.codex.bak-$ts" -Recurse
.\magpie.exe codex
Compare-Object (Get-Content "$HOME\.codex.bak-$ts\config.toml") `
(Get-Content "$HOME\.codex\config.toml") | Format-Table -Wrap
```
这次就是靠这一步看到 228 行和 15 个 provider 的,然后原样恢复:
```powershell
Copy-Item "$HOME\.codex.bak-<时间戳>\config.toml" "$HOME\.codex\config.toml" -Force
→ hash 回到
```
五、三种协议,我逐个打了
网关起来之后,我没有动客户端配置,直接用 HTTP 打 3425,这样零风险。
第一种,OpenAI chat/completions。 一句话,返回正常,而且响应体里多了一段别处看不到的账:
```json
"provider-go": {
"billing_source": "<订阅>",
"list_cost": 6.86e-06,
"allowance": 6.86e-06,
"tokens": { "input": 13, "output": 18, "reasoning": 15 }
}
```
billing_source 说明这次走的是订阅额度而不是按量付费,allowance 是这次实际扣了多少额度。它把“这笔调用花的是订阅还是现金”标出来了,这是多 provider 混用场景里最容易算不清的一笔账。
第二种,Responses + 工具调用。 Codex 用的就是这套。我给它一个 calc 函数定义,让它算 37*89:
```
ITEM function_call "calc" "{\"expr\": \"37*89\"}"
usage: input 299 / output 39 / total 338
```
参数完全正确,是 {"expr": "37*89"} 而不是自然语言。这一步很关键——很多本地网关能把 chat 协议转过去,但 tools 的 schema 一转就坏,模型开始“用文字描述它想调用什么”。这里没有。
第三种,SSE 流式。 事件序列打出来是这样的:
```
response.created → response.in_progress → response.output_item.added
→ response.reasoning_summary_part.added → response.reasoning_summary_text.delta ×14
→ response.content_part.added → response.output_text.delta ×5
→ response.output_text.done → response.completed 总耗时 2.04s
```
五行的输出,五条 output_text.delta,连思考摘要的流都给(reasoning_summary_text.delta 14 条)。这个细节决定了 Codex 里“思考中”那个灰字会不会卡住不动。实测不卡。
六、最值钱的发现:你的 agent 不是不能看图,是被元数据拦了
这一段跟 magpie 关系不大,但它让我把一件一直没解决的事解决了。
我这套链路上,手机飞书发图过来,电脑这边一直“看不见”,只能靠 tesseract OCR 兜底。同一张飞书截图,OCR 出来是这样:
```
团 永 | 099 $al @DI68% 《 仰meBO辜 智 能 体 @ … 自 消 息 " 国 文 件 "
```
基本没法用。
但我用网关把这张图直接 base64 塞给模型,问“这张图里第一行标题是什么”:
```
provider-go/deepseek-v4.1-flash → '这张图里第一行标题是"<产品名>"。'
provider-go/mimo-v2.6-flash → '这张图里第一行标题是:**<产品名>**。'
```
两个模型都答对了。至少说明模型侧能理解图,问题更像出在客户端入口的能力声明,而不是模型能力。
那 Codex 为什么拦?我去翻 ~/.codex/magpie-models.json(magpie 生成的那份约 660KB 目录),里面这些模型标的是:
```
provider-go/deepseek-v4.1-flash text,image
provider-go/mimo-v2.6-flash text,image
```
再翻我自己的 ~/.codex/models.json——38 个模型,input_modalities 全是 ["text"]。
这里补一句时间线,不然容易看岔:接管时 Codex 被指向 magpie-models.json,那是 magpie 的目录;我回滚配置后,Codex 用的是自己的 models.json。所以准确的说法是——magpie 的目录声明了 image,我本地的目录只声明了 text,客户端按本地声明在入口就把图片丢掉了。 一份元数据文件,卡了我一条链路。
这里面还有个副产品:magpie 内置的 magpie-vision 工具,实测是拿 provider-go/mimo-v2.6-flash 去读图的,一张飞书截图约 4K tokens,按当时单价估算成本不到一美分。它输出的不是我上面那种一句话,是结构化全文还原——状态栏时间、电量、标题栏、标签页、每条消息气泡的内容、谁发的、底部输入框占位符,逐项列出来。跟 OCR 的差距不是精度问题,是“能理解版面”和“认字形”的差距。
七、它没替你解决的部分
体验完要说短板,不然就是通稿。
搜索要自己配。 内置 web_search 工具定义得很漂亮,但直接调:
```
magpie: magpie couldn't search the web: no provider that can search the web,
nor a search API, is set up in magpie: add a search API in Settings → Models → Web search
```
它认 Tavily、Brave、Exa、Firecrawl、You.com、SearXNG 等一堆,但一个都不预置。想开箱即用是不行的。
会话历史默认不读。
```
magpie reads no agent's session history until you allow it:
magpie sessions read on all (or on
```
态度是对的(隐私默认关闭),但你得知道有这么个开关,不然会以为功能坏了。
模型列表是启动时构建的。 改完模型必须重启 Codex app-server:
```
↻ Codex builds its model list at start-up — restart the Codex app,
open codex sessions and the app-server they share
```
这条最容易被忽略,表现是“我明明切了模型,怎么还是老模型”。
它有 6 个模型默认不暴露给我。 magpie models codex 只列 24 个,另有 6 个模型(记为
八、对我自建遥控链路的启示
我有一套自己写的飞书遥控插件,跑在 Codex 的 app-server 上,手机发一句话电脑就干活。玩完 magpie,我改了三处认知,也顺手定了几条以后要照做的规矩。
第一,网关是新的单点。 以前 Codex 直连上游,上游挂了就一条链路断。现在中间多了一层,3425 不通则所有接管的客户端一起哑。这是必然代价,但必须配探活。我的 daemon guard 之前只查进程和心跳,现在得加一条 GET 127.0.0.1:3425 的检查——最好打 /health 或 /v1/models,带超时,失败就重启,否则网关静默退出时 guard 会以为一切正常。
第二,回滚必须是脚本,不能是记忆。 magpie 每行注释都在教你怎么撤销,这比“文档里写了卸载步骤”强一百倍。我自己的插件改了 config.toml 之后从来没留过这种可执行的撤销痕迹,全靠我记。这次之后,凡是改配置的路径,都要落一份 .bak-<时间戳> 并且在输出里打印恢复命令;要动状态目录的,备份整个目录,不只备份配置文件。
第三,元数据比代码更容易出错。 一个 input_modalities 字段,让一条已经上线的链路“看不见图”了很多天。我一直在查图片解码、查 base64、查网关转发,谁也没想到问题在模型目录的声明里。教训是:当“能力明明有但用不上”时,先查能力声明,再查实现。
如果重来一次,我还会多做三件事:接管前先看 dry-run / diff(它没有就自己 diff),只接管真正需要的那几个 agent 而不是全收;给本地网关加一个随机 token,别让 3425 对本机所有进程裸奔;把 magpie-models.json 定期 diff 成一份“模型能力盘点报告”,这比网关本身更有长期价值。
九、这台机器现在的状态
网关跑着,pid
它的价值我认——盘点和配额这两件事,它做得比任何客户端自带的都好。接管那一步,我暂时不给自己用——我的 config.toml 是手写的,key 和 header 都埋在里面,不值当为一次体验把 228 行的脏配置再收拾一遍。
但我把它的 magpie-models.json 留下了,约 660KB,里面是我这台机器所有能用的模型和它们的真实能力声明。那份文件,比 magpie 本身对我更有用。