ribincao
← ARTICLES
2026.08.26 · 15 MIN · 中文 · ENGINEERING

网关能代理协议,代理不了身份:把账单迁出 Anthropic 的一次失败验证

目标很简单:把账单从 Anthropic 迁走,改用 Cloudflare AI Gateway 或 OpenRouter 统一出账。结果两条路都跑不动这个文档加工 agent,卡点还完全不同:一个卡在组织身份隔离,一个根本不支持 agent 平台能力。这是那次验证的完整记录,连同那句真正的教训。总成本不到 5 毛钱。

测试环境:2026-08-26,模型 claude-sonnet-5。被测系统是一个依赖 Anthropic 托管文档技能(pptx / docx / xlsx / pdf)加代码执行容器的文档加工 agent。

结论速查

能跑本产品 卡点 单价 充值手续费
Anthropic 直连 $2 / $10 每 MTok
Cloudflare AI Gateway(Unified Billing) 组织身份隔离 同价,不加价 5%
OpenRouter 不支持 agent 平台能力 同价,不加价 5.5%(USDC 5%)

两个中转都是原价透传、只收充值手续费,所以直连是最便宜的一条。中转的卖点是统一账单和可观测性,不是价格。


一、能力对比矩阵

✅ 实测通过 ❌ 实测失败 ⚠️ 返回成功但功能未生效 — 未测

能力 Anthropic 直连 CF AI Gateway OpenRouter
/v1/messages 基本调用
客户端工具(function calling)
anthropic-beta 头透传(4 个)
container + 托管技能 ✅ 返回 container id ⚠️ 200 但 container=None
code_execution 服务端工具 ✅ 真正执行 400
context_management
prompt caching ✅ usage 字段完整
Files API 上传 404 ⚠️ 有自己的 /files,非同一物
跨端引用 file_id 404 File not found
容器出网(egress)

的项目没测,因为上游已经断了,再测没有意义。OpenRouter 的 code_execution 是 400,容器这条路已经不存在,所以没有继续测它的 caching 和 beta 头透传。


二、缺失能力详解

2.1 Cloudflare AI Gateway:Files API 不可用,根因是组织身份

表现:Unified Billing 模式下(不带任何 Anthropic key),文件端点返回 404。

POST {gateway}/anthropic/v1/files   →  404 not_found_error

第一层排查:错误体是 Anthropic 的格式{"type":"error","error":{...},"request_id":"req_..."}),不是 Cloudflare 的。说明请求已经转发到上游,是 Anthropic 返回的 404,而不是网关拒绝了这个路径。

第二层排查:三组对照。

调用方式 /v1/files
网关 + Unified Billing(不带 key) 404
直连 Anthropic + 自己的 key 200
网关 + 自己的 key 200

结论:路径是透传的,网关本身没有白名单。差异在凭证。

第三层排查(决定性):用自己的 key 上传文件拿到 file_id,再让走 Unified Billing 的推理去引用它。

File `file_01S3eG9Xkm9gJwkjXCn2QYL6` not found.

同一个 file_id:带自己的 key 引用是 200,走 Unified Billing 是 404。

根因:Unified Billing 的推理运行在 Cloudflare 自己的 Anthropic 组织下,你的文件存在你的组织下,两者互不可见。

这不是「Files API 没被代理」——它被代理得很好。这是身份归属:网关能转发请求形状,转发不了「你是谁」。任何依赖上游平台状态的东西(存储的文件、容器实例、组织级配额)都过不了这一跳。

影响范围:输入文档要靠 Files API 进容器,产出文件要靠 Files API 取回来。两个方向同时断。

2.2 OpenRouter:不支持 Anthropic agent 平台

OpenRouter 提供 Anthropic Messages 兼容端点,基本调用正常。逐字段测下来:

请求 结果
只加 code_execution 工具 400 Invalid Anthropic Messages API request
只加 container(带技能) 200,但响应 container=None
只加 container(空) 200,同样 container=None
普通客户端工具(对照组) 200 正常,返回 thinking + tool_use

结论:它把 Anthropic 当作「文本 + 视觉 + 函数调用」模型代理,不支持容器、托管技能、服务端代码执行。

⚠️ 注意 container 那一行:不报错,静默忽略。返回 200、结构正常、container 字段是 None——技能根本没加载,但没有任何信号。只测这一条会得到一个「能跑但产出质量莫名变差」的系统,且极难归因。

关于 OpenRouter 的 /api/v1/files:它确实有,返回 or_file_... 形式的 id。但那是给模型上下文喂文件用的,和 Anthropic 的容器文件系统是两回事。由于容器本身已不可用,未继续验证。

2.3 共同的硬约束:代码执行容器完全无 egress

尝试绕开 Files API:让容器直接从 R2 拉文件、产物再 PUT 回 R2。

https://pypi.org/simple/     -> 000 (exit 28)
https://example.com          -> 000 (exit 28)
https://pub-0.r2.dev         -> 000 (exit 28)
https://api.anthropic.com/v1/models -> 000 (exit 28)
https://1.1.1.1              -> 000 (exit 28)
getent hosts pypi.org        -> dns fail
env | grep -i proxy          -> no proxy vars

五个目标全部超时(curl exit 28 = 操作超时),DNS 解析本身就失败。不是域名白名单,是没有网络。API 层面也没有任何网络相关参数可配(我们能控制的只有 container: { id, skills })。

文件只能走 Files API 进出。

2.4 唯一可行的绕法:base64,以及它的成本

输入把文档 base64 内联进 prompt,让模型解码写进容器;输出让模型 base64 -w0 打到 stdout,从 tool result 里拼回。

实测可行:不带任何 Anthropic key,8221 字节的 docx 完整往返,解码后是合法 docx(word/document.xml 存在,内容正确)。

成本实测

8221 字节  →  base64 10964 字符  →  10,393 token
≈ 每字节 1.26 token

base64 是高熵串,BPE 压缩率极差。(原本按「3 字节 1 token」估算,实测差约 4 倍。)

文件大小 单程 token 单程成本 可行性
16KB 20,713 $0.062 勉强
100KB 129,454 $0.388 不划算
1MB 1,325,611 $3.98 超出 1M 上下文窗口
5MB 6,628,056 $19.88 不可能

对照:本产品一次完整任务的正常成本约 $0.15。给最小文件加一次 base64 往返即 +80% 成本,PPT 场景直接撞窗口。

结论:技术可行,经济不可行,且恰好在主力场景(PPT)上失效。


三、验证方法(可复现)

3.1 环境准备

中转 需要的凭证 端点
CF AI Gateway 网关 auth token(权限 AI Gateway: Run)+ 账户 ID + 网关名 https://gateway.ai.cloudflare.com/v1/{acct}/{gw}/anthropic/v1/...
OpenRouter OpenRouter API key https://openrouter.ai/api/v1/messages

CF 侧 Unified Billing 需先充值 credits(最低 $10)。关键:使用 Unified Billing 时不能发送 x-api-key,发了会被转给 Anthropic,账单回到 Anthropic 且请求可能失败。

3.2 探路请求:先区分 401 和 404

在拿到凭证之前,用空请求探路:

curl -X POST "$GW/v1/messages" -H 'content-type: application/json' -d '{}'
curl -X POST "$GW/v1/files"    -H 'content-type: application/json' -d '{}'

两者都返回 401 而不是 404,说明网关对 /anthropic/ 下的路径是透传的、没有白名单。这一步就排除了「路径不支持」这个假设,把排查方向直接指向凭证——不需要任何凭证就能完成。

3.3 能力逐项验证

基本调用 + 计费字段

curl "$GW/v1/messages" \
  -H "cf-aig-authorization: Bearer $CF_AIG_TOKEN" \
  -H 'anthropic-version: 2023-06-01' -H 'content-type: application/json' \
  -d '{"model":"claude-sonnet-5","max_tokens":16,
       "messages":[{"role":"user","content":"说一个字"}]}'

判读:响应 usage 里必须有 cache_read_input_tokenscache_creation。中转吞掉这些字段的话缓存成本就不可见了。

容器 + 技能 + 服务端工具(用真实请求形状,不要简化):

BETAS='files-api-2025-04-14,skills-2025-10-02,code-execution-2025-08-25,context-management-2025-06-27'
curl "$GW/v1/messages" -H "cf-aig-authorization: Bearer $CF_AIG_TOKEN" \
  -H "anthropic-beta: $BETAS" -H 'anthropic-version: 2023-06-01' \
  -H 'content-type: application/json' -d '{
    "model":"claude-sonnet-5","max_tokens":2000,
    "thinking":{"type":"adaptive"},
    "system":[{"type":"text","text":"…","cache_control":{"type":"ephemeral"}}],
    "context_management":{"edits":[{"type":"clear_tool_uses_20250919"}]},
    "container":{"skills":[{"type":"anthropic","skill_id":"docx","version":"latest"}]},
    "tools":[{"type":"code_execution_20260521","name":"code_execution",
              "cache_control":{"type":"ephemeral"}}],
    "messages":[{"role":"user","content":"用 python 打印 1+1"}]}'

判读要点(这是最容易漏的一步):不能只看 HTTP 200,要看功能痕迹:

OpenRouter 正是在这一步暴露的:container 返回 200 但 container=None

逐字段二分:一次只加一个可疑字段,定位是哪一个被拒。

# 只加 code_execution 工具   -> 400
# 只加 container(带技能)   -> 200 但 container=None
# 只加 container(空)       -> 200 但 container=None
# 普通客户端工具(对照组)   -> 200 正常

Files API 与跨端身份

# 1. 网关 + Unified Billing 上传
curl -X POST "$GW/v1/files" -H "cf-aig-authorization: Bearer $TOKEN" \
  -H 'anthropic-beta: files-api-2025-04-14' -F 'file=@t.txt'          # 404

# 2. 直连 Anthropic 上传(对照)
curl -X POST https://api.anthropic.com/v1/files -H "x-api-key: $KEY" \
  -H 'anthropic-beta: files-api-2025-04-14' -F 'file=@t.txt'          # 200

# 3. 网关 + 自己的 key(判断是路径问题还是凭证问题)
curl -X POST "$GW/v1/files" -H "cf-aig-authorization: Bearer $TOKEN" \
  -H "x-api-key: $KEY" -H 'anthropic-beta: files-api-2025-04-14' \
  -F 'file=@t.txt'                                                     # 200

# 4. 决定性:用 2 拿到的 file_id,走 Unified Billing 引用
#    messages 里放 {"type":"container_upload","file_id":"file_xxx"}     # 404 not found

容器 egress

# 让模型执行:
for u in https://pypi.org/simple/ https://example.com https://1.1.1.1 ; do
  echo -n "$u -> "; curl -s -o /dev/null -m 6 -w '%{http_code}' "$u"; echo " (exit $?)"
done
getent hosts pypi.org || echo 'dns fail'
env | grep -i proxy || echo 'no proxy vars'

判读:只测一个域名不够,要覆盖 PyPI(常见白名单)、纯 IP(排除 DNS 问题)、以及 DNS 本身。

base64 成本测量

# 两次 max_tokens=1 的调用,取 input_tokens 差值
#   A: content = "x"
#   B: content = <base64 字符串>
# 差值 / 文件字节数 = 每字节 token 数

3.4 排查方法要点

  1. 先找承重依赖。这个系统里换掉就塌的是「文件必须和推理同身份」。找到它,两个中转各五分钟就能判死刑,不必跑完整个能力矩阵。
  2. 探路请求要能区分假设,不是能跑通就行。401 vs 404 这一个信号排除了整条「路径不支持」的假设。
  3. 看错误体的格式判断谁在回答。同样是 404,body 是上游格式(带 request_id)还是中转格式,结论完全相反。
  4. 200 不等于生效。要检查它声称做的事有没有痕迹(container id、tool 调用块、usage 字段)。静默忽略比明确报错难查得多。

四、价格对比

4.1 Token 单价(claude-sonnet-5,官方价)

单价
输入 $2 / MTok
输出 $10 / MTok
5 分钟缓存写 $2.50 / MTok
缓存读 $0.20 / MTok

两个中转均为原价透传,不加价。

4.2 手续费

手续费 举例
Anthropic 直连
Cloudflare AI Gateway 充值 5% 充 $20 实扣 $21
OpenRouter 信用卡 5.5%、USDC 5% 充 $20 实扣 $21.1

直连最便宜,中转比直连贵 5%。

4.3 代码执行容器

小团队用量下等于免费,容器不是隐藏成本,开销全在 token 上

4.4 实际成本参考

本产品一次完整任务(上传 docx → 审核 → 改写 → 导出 → 渲染预览图)约 $0.15,主要来自输出 token。

缓存的影响:未加 prompt caching 时,一份两页文档一次任务烧掉 270 万 input token(约 $8);加上 caching + clear_tool_uses 后降到 18 个未命中 + 23 万缓存读(约 $0.15),差约 50 倍。所以中转是否完整透传 cache_* 字段是必须验证的项目。


五、结论

目标 结果
账单统一到 Cloudflare ❌ 与「使用 Anthropic 托管文档技能」互斥
账单统一到 OpenRouter ❌ 不支持 agent 平台能力
省钱 ❌ 中转比直连贵 5%
免除自有 Anthropic key ❌ 除非放弃托管技能,或接受 base64 的成本与体积上限

可行方案:Anthropic 直连(最便宜),或 AI Gateway + BYOK(把自己的 key 存进网关)。

后者账单仍归 Anthropic,但能换来网关的请求日志、预算上限和限流——这些可以替代自建配额逻辑。若需要可观测性,这是有价值的取舍。


附:未验证项

诚实标注,避免后续误用本文档: