网关能代理协议,代理不了身份:把账单迁出 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_tokens 和
cache_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,要看功能痕迹:
- 响应顶层有没有
container.id——没有就是被静默忽略了 content里有没有server_tool_use和bash_code_execution_tool_result——没有就是代码没真执行usage里 cache 字段在不在
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 排查方法要点
- 先找承重依赖。这个系统里换掉就塌的是「文件必须和推理同身份」。找到它,两个中转各五分钟就能判死刑,不必跑完整个能力矩阵。
- 探路请求要能区分假设,不是能跑通就行。401 vs 404 这一个信号排除了整条「路径不支持」的假设。
- 看错误体的格式判断谁在回答。同样是 404,body
是上游格式(带
request_id)还是中转格式,结论完全相反。 - 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 代码执行容器
- 每个组织每月赠送 1,550 容器小时
- 超出部分 $0.05 / 小时 / 容器
- 请求中带文件时,即使未调用工具也计执行时间(文件要预载进容器)
小团队用量下等于免费,容器不是隐藏成本,开销全在 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,但能换来网关的请求日志、预算上限和限流——这些可以替代自建配额逻辑。若需要可观测性,这是有价值的取舍。
附:未验证项
诚实标注,避免后续误用本文档:
- CF Gateway 的**流式(SSE)**未单独验证。基本 messages 调用通过,但端到端流式跑通前就在文件那步失败了。
- OpenRouter 的 prompt caching、beta
头透传、流式均未测——
code_execution已 400,容器路径不存在,继续测无意义。 - BYOK 模式(把 Anthropic key 存进网关)未实际配置验证,仅从「网关 + 自带 x-api-key 返回 200」推断可行。
- Anthropic 是否对 Files API
调用计费未确认。观察到上传/下载响应不返回任何
usage字段,费用似乎都体现在 messages 的 token 上,但这是观察不是官方口径。 - 价格数据为 2026-08-26 当日,会变,引用前请复核。