Skip to content

Anthropic

默认的透传提供方 —— 转发调用方自己的凭据、注入一个 API 密钥,或用 OAuth 池化 Claude 订阅账号。

Updated View as Markdown

内置的 anthropic 提供方是 shunt 的默认目的地:任何没有匹配路由的模型都会被原样转发到 api.anthropic.com(可通过 server.default_provider 配置)。它是一个 kind = "anthropic" 提供方 —— 上游本身就讲 Anthropic Messages API,因此 shunt 不做转换直接透传请求,只决定哪一份凭据 被发往上游。

只有一个请求字段例外。带有 shunt 为其他提供方生成的 signature 的 thinking 块会在转发请求前被 移除(Responses、Gemini 和 Cursor 三条路径各自会生成一个 —— 因为思考块需要该字段才算格式完整), 随之变为空壳的 assistant 轮次也会一并移除。Anthropic 会解密该字段并拒绝不是自己签发的值,原样 转发会导致请求失败。Anthropic 签发的 signature 对 shunt 而言是不透明的,因此从不触碰。该移除不会 读取会话历史,所以并不仅限于使用过映射提供方的会话 —— 完全没有 signature 的 thinking 块同样会 被移除:shunt 自己的 Responses 路径在上游未返回 signature 时就会生成这种块,而 Anthropic 无论如何 都会拒绝无法验证 signature 的 thinking 块。

第三方 Anthropic 类提供方 —— 即既不在 api.anthropic.com 上、也不是 claude_oauth 池的 kind = "anthropic" 上游(如 MiniMax、Kimi、Zhipu)—— 可能会在 thinking 块上附带并非 Anthropic 签发的 signature。 无论是否流式,shunt 都会在返回客户端的响应中,把该 signature 包进一个携带提供方 base_url(去掉末尾的 /)摘要的自有 envelope。envelope 不含 URL 本身,因此 URL 及其中的凭据不会暴露给客户端。不过该摘要是未加盐的固定值,可以用来验证猜测:只要能猜出完整的 base_url(例如常见的端点,或其中嵌入的弱凭据),就能拿摘要核对猜测是否正确。之后的轮次回到 base_url 相同的提供方时,shunt 会解开 envelope,按字节原样转发原始 signature,因此该提供方的 extended thinking 续写照常工作;发往其他任何地方(包括 Anthropic)时, envelope 被视为 shunt 生成的值,块会按上文所述被移除。因此,在第三方 Anthropic 类提供方与 Anthropic 之间切换时,该提供方的 thinking 块会被移除而不是让请求失败,回到同一提供方时又会恢复。 api.anthropic.com 和 claude_oauth 池的响应从不被包装。

认证模式 凭据 使用场景
passthrough(默认) 调用方自己的 x-api-key / Authorization 透明代理 —— 每个客户端自带密钥或 OAuth token
api_key 来自环境变量的服务端密钥 用一个运维方持有的 API 密钥为每个请求重新加钥
claude_oauth 一个或多个 shunt 托管的 Claude 订阅账号 消耗 Claude 订阅,支持池化与配额感知轮换

快速开始

让编码 agent 为你完成接入 —— shunt add 会打印一份内置的设置蓝图 (离线且只读;配置由 agent 编辑,该命令绝不会修改配置):

shunt add upstream anthropic --print | claude

或者按照下面的手动步骤操作。

配置上游

anthropic 是内置的 —— 完全不做配置时,shunt 已经会把未路由的模型以 passthrough 转发给它。 要显式声明它(用于有序故障转移,或改变认证方式),请使用预设,它提供了 kind = "anthropic"、 base_url = "https://api.anthropic.com" 和 auth = "passthrough":

[[upstreams]]
name = "anthropic"
provider = "anthropic"

显式字段会覆盖预设默认值。旧的 [providers.anthropic] 表形式仍然受支持 —— 但不要在同一个文件中 混用 [[upstreams]] 和 [providers.*]。

透传(默认)

无需再配置什么:shunt 会转发客户端自己的 x-api-key 或 Authorization 头。配置中不应存放任何服务端机密。客户端应当发送哪一份凭据, 在 连接 Claude Code 中有说明。

服务端 API 密钥

注入一个运维方持有的密钥,而不是信任调用方自带:

[[upstreams]]
name = "anthropic"
provider = "anthropic"
auth = { mode = "api_key", env = "ANTHROPIC_API_KEY", header = "x_api_key" }

如果你注入了服务端凭据,请在把网关暴露到回环之外前,先用 [server.auth] 为它加上门禁。

Claude 订阅账号(claude_oauth)

先创建一个可刷新的、由 shunt 托管的账号,然后把上游限定到它:

shunt login claude --name primary --mode oauth
[[upstreams]]
name = "anthropic"
provider = "anthropic"
auth = { mode = "claude_oauth", account = "primary" }

shunt login claude 有三种模式:--mode oauth 运行 shunt 自己的可刷新 OAuth 流程 (推荐),--mode import 复制当前的 Claude Code 登录,--mode setup-token 创建一个为期一年、仅用于推理的 token。省略 account(以及 accounts)会扫描整个 shunt 托管的账号存储 —— 存储中有多个账号时,它就成了一个负载均衡的池, 带会话粘性和配额感知轮换。完整的池化行为 —— 显式账号条目、按账号阈值、[server.pool] 调优, 以及故障转移 —— 都记录在 Anthropic 多账户 中。

模型与路由

作为默认提供方,anthropic 不需要任何路由 —— 所有你没有映射到别处的东西 都会落到这里。要在一条有序故障转移链中显式把某个模型固定到它:

[[models]]
id = "claude-sonnet-5"
display_name = "Claude Sonnet 5"

[models.upstream_model]
anthropic = "claude-sonnet-5"

映射的键是 [[upstreams]].name,而不是预设 id。在 auto_include_builtin_models = true(默认)时,GET /v1/models 会先在 server.default_provider 是 Anthropic 类型的情况下尝试它的实时模型列表;否则回退到 shunt 内置的 Claude 目录快照 —— 见 模型发现。

自动模式分类器

当 Claude Code 运行在自动模式时,每一个离开工作区或触及网络的动作都会经过一次权限分类器 —— 一个带有独立系统提示、向模型索要待执行动作安全判定的 Messages 请求。它在订阅 OAuth 凭据下 缺少 api.anthropic.com 所查找的 Claude Code identity 块,CLAUDE_CODE_ATTRIBUTION_HEADER=0 时 mod 的 $.model.complete 之类的旁路查询也是如此。shunt 会在自动模式分类器以及任何不带可接受标记的 Claude Code 请求(通过 x-claude-code-session-id、claude-cli/ user agent 或 x-app: cli 识别)上恢复 identity。 该恢复仅在发出的 bearer 是订阅 OAuth 令牌时生效,不作用于上游并不检查的 count_tokens,空的 x-claude-code-session-id 也不算 Claude Code。其他客户端以及使用 API 密钥的请求不会触发该恢复;这次修复所避免的故障见 429 故障排查条目。

分类器默认并不使用会话自身的模型。在 Claude Code 2.1.226 上实测:若服务端下发了模型就用它, 否则用 Sonnet 档位别名,再否则才用会话自身的模型 —— 这正是 ANTHROPIC_DEFAULT_SONNET_MODEL 同样会移动分类器的原因,已在链路上验证。该结论读自分发的 客户端,因此它描述的是所测版本,而非一项契约。

设置 classifier_model 可以在网关一侧把分类器请求固定到某个模型,而不改变会话自身各轮所用的 模型:

[providers.anthropic]
kind = "anthropic"
base_url = "https://api.anthropic.com"
classifier_model = "claude-sonnet-5"

该键仅在 kind = "anthropic" 的提供方上被接受,且它是该提供方内部的模型重映射,而不是通往 另一个提供方的路由。分类器请求依赖 stop_sequences。在 shunt 的 Responses 转换还会丢掉这个字段时, 经由 Sonnet 别名指向 Codex 上游后,分类器重试了它的第一阶段,每次权限检查多花 5–7 秒。现在 shunt 会 在 Responses 路径上模拟 stop_sequences,因此该失败不再适用, 但该键仍然是提供方内部的重映射。不设置该键(默认)时,分类器仍使用客户端请求的模型。

在网关登录部署中,你也可以改为通过托管设置把别名下发给客户端:

[server.gateway.policies.cli.env]
ANTHROPIC_DEFAULT_SONNET_MODEL = "claude-sonnet-5"

这同样会移动分类器,但它改变的是整个会话中 sonnet 的解析目标,而不仅仅是分类器 —— 并且当 设置了 availableModels 列表时,该 id 也必须出现在列表中。

校验

shunt check    # -> config ok
shunt run
curl -sS http://127.0.0.1:3001/v1/models

然后发送一个最小请求(使用 passthrough 时带上你自己的凭据;使用 claude_oauth 时省略它):

curl -sS http://127.0.0.1:3001/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H 'anthropic-version: 2023-06-01' \
  -H 'content-type: application/json' \
  -d '{"model":"claude-sonnet-5","max_tokens":16,"messages":[{"role":"user","content":"Reply with OK."}]}'

相关内容

Navigation

Type to search…

↑↓ navigate↵ selectEsc close