Claude国内使用和中转站系列第 22 / 22 篇

通过中转站用 Claude API:哪些功能能用,哪些不能,附一个自测脚本

自己写程序调 Claude 的接口,走中转站时哪些功能和官方一样、哪些用不了、哪些看着成功其实没生效。本站把这个系列在一家中转站上实测到的出入汇成一张表,分成四类讲怎么应对,并给出一个 25 个请求的自测脚本,换成你的地址和 key 就能照着测一遍。

发布 · 约 19 分钟读完 · 经人工审核()· 本站的内容怎么来的

本站已实测,测试日期:2026-10-13。

本页目录(12 节)
  1. 先说结论
  2. 一、总表
  3. 二、直接报错的:好发现,找替代
  4. 三、悄悄忽略的:最要小心
  5. 四、校验比官方松的:在这里能过,换个地方会 400
  6. 五、数字对不上的:缓存和计数
  7. 六、报错长得不一样
  8. 七、自测脚本
  9. 八、通过中转站写程序的几条习惯
  10. 和 Claude Code 的关系
  11. 花费
  12. 这次实测没有覆盖到的

这篇解决一个问题:在国内自己写程序调 Claude 的接口,走的是中转站,官方文档里的功能哪些能照着用,哪些不能?

官方文档不会告诉你这个,中转站自己的文档一般也只写怎么接入。本站这个系列的前 17 篇,每一篇都在同一家中转站上把文档里的功能实际跑了一遍,测到的出入分散在各篇里。这一篇把它们汇到一处,再给一个自测脚本,你可以拿它测自己在用的那一家。

用 Claude Code 而不是自己调接口的读者,看的应该是《通过中转站用 Claude Code》,那一篇测的是 Claude Code 的功能。

实测环境
日期 2026-10-13
中转站 酷站AI,一家,一把有额度上限的 key
模型 主要是 Sonnet 5.5,部分项目加测了 Opus 5.5
方式 Python anthropic SDK 1.13.0 和直接发 HTTP 请求;文末的自测脚本只用标准库

先把边界说清楚。 本站没有官方的 key,官方接口本身没有测。下面“官方文档”一栏是文档的说法,“实测”一栏是通过这家中转站看到的。这是一家、一天的表现:别家可能不同,这一家以后也可能改。所以这张表更适合当一份检查清单,照着它测你自己的那一家。

先说结论

  • 日常写程序够用。 发消息、流式、多轮、工具调用、结构化输出、看图、读 PDF、引用、联网搜索、思考和 effort,都和文档一致。
  • 有十来项用不了。 文件接口、批量、上下文压缩、代码执行、抓取网页、MCP 连接器、Agent Skills、快速模式、托管智能体、管理接口。大多数有替代办法,见第二节。
  • 最要小心的是“看着成功,其实没生效”。 代码执行、按需压缩、停止序列、旧的结构化输出参数,返回的都是 200,只是功能没有发生。见第三节。
  • 提示缓存要多做一步。 不带固定的 metadata.user_id,缓存基本不命中,反而比不开缓存更贵。见第五节。
  • 在这里能跑通,不等于写对了。 好几种官方会拒绝的写法,这家中转站放过去了。见第四节。

一、总表

“详见”一栏是测这一项的那篇文章,里面有请求的写法和原始输出。

基本的请求和响应

功能 官方文档 实测 详见
发消息、多轮、system 可用 一致 第一个请求
流式 可用 一致,事件顺序和文档相同 同上
两种带 key 的请求头 都认 都认 同上
官方的各语言 SDK、ant 命令行 可用 Python、TypeScript 和 ant 跑通了,只改地址和 key SDK 和命令行
模型列表 每个模型带上限和能力说明 能列出 16 个,每条只有 ID 等四个字段;查单个模型是 404 模型和价格
模型别名 可用 404,要写完整的 ID;列表里有的也不一定调得通 同上
不带 max_tokens 它是必填参数 503,带“60 秒后重试”,重试也一样 报错和限流
stop_sequences 命中就停 不生效,照常输出完 同上
报错的类型名 not_found_error 这一套 状态码是对的,类型名是中转站自己起的,消息是中文 同上
request-id 响应头 每个响应都有 没有;有一个中转站自己的 x-request-id 同上
限流的响应头 每个响应都有一组 一个也没有 同上
数 token 的接口 给的是估算,和实际只差一点 能调,数字对不上,偏差最大到三分之一 模型和价格

思考、缓存和上下文

功能 官方文档 实测 详见
effort 的五档 可用 一致,看得出差别 思考和 effort
思考的显示方式、任务预算 可用 请求被接受 同上
快速模式 研究预览,要申请 400,没有开 同上
提示缓存 标了就缓存,前缀相同就命中 能用,但不带固定的 user_id 基本不命中 提示缓存
不标 cache_control 不缓存 照样在缓存,而且是 1 小时的那种 同上
缓存诊断 返回没命中的原因 始终是 null 同上
上下文编辑(清工具结果) 可用 一致 压缩和上下文编辑
到阈值自动压缩 可用 400 同上
按需压缩 返回压缩块 参数被忽略,返回一段普通回答 同上
对话中途的 system 消息 可用 能用,缓存保得住 中途的系统消息
只管一轮的提醒(clear_at) 可用 在末尾时能用;留在历史里再发是 400 同上
中途按名字增减工具 可用 400;在消息里直接定义新工具能用 同上

工具

功能 官方文档 实测 详见
自己定义工具、并行调用、strict、Tool Runner 可用 一致 工具调用入门
bash、文本编辑器、记忆、电脑操作 可用 一致,定义占的 token 数也对得上 内置工具
联网搜索 可用 一致,带引用 服务器端工具
顾问 可用 能用 同上
工具搜索 可用 单次请求能用;带着历史继续对话是 400 同上
抓取网页 可用 400 同上
代码执行 可用 被忽略:返回 200,模型说自己没有这个工具 同上
程序化调用工具 可用 400 同上
MCP 连接器 可用 400 同上
Agent Skills 可用 列 skill 的接口 404;请求里带 skill 被忽略 Agent Skills

输入和输出

功能 官方文档 实测 详见
结构化输出(output_config.format) 可用 一致 结构化输出和引用
旧参数 output_format 400 被忽略,返回普通文字 同上
文档引用、搜索结果块 可用 一致 同上
图片和 PDF,用 base64 可用 一致,图片占的 token 数和官方的算法对得上 图片、PDF、文件和批量
图片,用网址 可用 400 同上
Files API 和 file_id 可用 接口 404,引用 400 同上
批量接口 可用 404 同上

消息接口以外的

功能 实测 详见
组织、用量、花费、限流、工作区的管理接口 全部 404 key、工作区和用量
托管智能体的六个接口 全部 404 托管智能体入门
OpenAI 兼容接口 能用,但走的是中转站自己的兼容层,行为和官方那一个不同 SDK 和命令行

管理接口和托管智能体在中转站上用不了是意料之中的:中转站发给你的 key、你看到的用量和账单,都是它自己的系统。

二、直接报错的:好发现,找替代

返回 400 或 404 的那些,第一次跑就知道了。麻烦的是找替代办法:

用不了的 替代办法
文件接口、用网址给图片 图片和 PDF 用 base64 放进请求。多轮对话里每轮都要重发,注意请求的大小
批量接口 自己并发发普通请求。拿不到批量的半价
抓取网页 自己写一个抓网页的工具给模型调,或者用联网搜索
MCP 连接器 自己在程序里当 MCP 客户端,把服务器的工具转成普通的工具定义;或者直接用 Claude Code、Agent SDK
上下文压缩 用上下文编辑清掉旧的工具结果;或者自己发一个请求让模型写摘要,再用摘要替换历史
工具搜索带历史继续、按名字增减工具 工具不多就全量给;要中途加工具,在消息里直接给完整定义
快速模式 没有替代
托管智能体 用 Agent SDK,循环和工具跑在你自己的机器上

三、悄悄忽略的:最要小心

这一类返回的是 200,没有任何报错,功能却没有发生。代码不检查的话,会带着错的结果往下走,还照样花了钱。

请求里带了什么 实际发生的 怎么发现
代码执行工具 模型回答说自己没有这个工具,或者给一段让你自己运行的脚本 看响应里有没有代码执行的内容块
Agent Skills 同上,文件没有生成 看有没有代码执行的结果块和 file_id
按需压缩 模型把对话当成普通请求接着回答,一两千 token 检查 stop_reason 是不是 compaction
stop_sequences 输出没有在序列处停下 检查 stop_reason 是不是 stop_sequence
旧参数 output_format 返回一段普通文字,不是 JSON 解析失败时别只当成偶发,查一下参数名
缓存诊断 diagnostics 始终是 null 用 usage 里的读写数字判断
一个不存在的工作区 ID 被忽略 没有影响,知道就行

对应到代码里就是一条习惯:不要只看状态码,要看响应里有没有你期望的那个东西。期望工具被调用,就找那个内容块;期望在某处停下,就看 stop_reason;期望 JSON,就解析并校验。这在官方接口上也是该做的,在中转站上是必须做的。

四、校验比官方松的:在这里能过,换个地方会 400

下面这些写法,官方文档说会被拒绝,通过这家中转站都返回了 200:

写法 官方文档 实测
最后一条是 assistant(预填),5.5 这一代的模型 400 200,模型接着往下写
Sonnet 5.5 把 thinking 设成 disabled 400 200
采样参数用非默认值 新一代的模型是 400 200,看不出有没有生效
删掉或改动 thinking 块再传回去 400 200
工具结果前面放一段文字 400 200
六个工具调用只回了五个结果 400 200,模型把缺的那个重新调了一次
工具名带点号、用中文、超过 128 个字符 不符合命名规则 被接受;超长的那个,返回的工具名和请求里的不一样
system 消息放在第一条,或者后面直接跟 user 消息 400 200
改掉搜索结果里的加密内容再带回 400 200,搜索结果被丢掉,模型否认搜过
结构化输出加预填 不兼容 200

单看每一条,宽松好像是好事。问题在于你的代码会不知不觉依赖它:在中转站上测试全过,哪天换一家、或者换成直连官方,同一份代码直接报 400。升级模型的时候尤其容易踩,因为文档列的那些“这一代开始拒绝”的写法,在这里测不出来,见《升级到 5.5 的清单》。

所以写代码时按官方的规则来:每个工具调用都回结果、结果放在消息最前面、工具名只用字母数字下划线和连字符、不用预填、不改 thinking 块。能不能跑通不是标准。

反过来也有一处是中转站更严:thinking 的 between_tools 写法,文档说 Sonnet 5.5 支持,这里返回 400。

五、数字对不上的:缓存和计数

提示缓存:带上固定的 user_id

这是花钱最多的一处。同一段系统提示连发几次,按文档第二次起都该读到缓存。通过这家中转站,不做处理的话基本读不到,而没读到的每一次都按写缓存的价付钱。

在请求里加一个固定的 metadata.user_id 就好了:

"metadata": {"user_id": "my-app-session-0001"}

提示缓存那一篇里每组发八次,看后七次:不带的两组命中 2 次和 0 次;带固定值的两组命中 6 次和 7 次。今天用文末的脚本又各发了四次、跑了两遍,结果相同:不带的后三次命中 0 次,带的命中 2 次。

为什么会这样,本站只能推测,中转站的文档里没有写。做法很简单:一场对话里的所有请求带同一个 user_id,不同的对话用不同的值,填一个不含个人信息的字符串。Claude Code 自己会带这个字段,所以用 Claude Code 时不用操心。

其他几个数字

  • 数 token 的接口别拿来估花费。 今天的脚本里,同一句话接口说 21 个 token,真实请求计了 51 个。估算花费看真实响应里的 usage。
  • 每个请求计的输入比内容本身多一小截。 提示缓存那一篇里量到的是二十几个 token,来源本站分不清。
  • 没有限流的响应头。 文档里“读响应头,快到上限时主动放慢”的做法用不上,只能等 429 来了再退避。本站没有触发过 429。
  • 实际扣多少钱,以中转站的账单为准。 本文和各篇里的花费都是按官方标价估算的,没有和账单核对。

六、报错长得不一样

状态码是对的,所以 SDK 抛的异常类没有分错。不一样的是细节:

  • 类型名是中转站自己的:模型名写错是 model_not_found,key 错了是 auth_failed。
  • 消息是中文的,有的响应体少了顶层的 type 和 request_id。
  • 路径写错时返回的是纯文本,不是 JSON。

判断出了什么错,看 HTTP 状态码或者 SDK 的异常类,不要匹配 error.type 的字符串。 要找中转站查某一次请求,记下响应头里的 x-request-id。

七、自测脚本

下面这个脚本把上面最要紧的十几项各试一次。只用 Python 标准库,不用装任何东西。它不打印 key 和请求头;一共 25 个请求,都很短,按官方标价估算跑一遍约 0.1 美元,大头是缓存那两组和一次联网搜索。

"""中转站自测:对着你的接口地址,把 Claude API 的常用功能各试一次。只用标准库。

用法:
  export ANTHROPIC_BASE_URL="中转站给的接口地址"
  export ANTHROPIC_API_KEY="中转站给的 key"
  python3 relay_check.py

不打印 key 和请求头。一共 25 个请求,都很短。
"""
import base64
import json
import os
import struct
import time
import urllib.error
import urllib.request
import zlib

BASE = os.environ["ANTHROPIC_BASE_URL"].rstrip("/")
KEY = os.environ.get("ANTHROPIC_API_KEY") or os.environ["ANTHROPIC_AUTH_TOKEN"]
MODEL = "claude-sonnet-5-5"  # 换成你的中转站模型列表里有的


def call(path, body=None, beta=None):
    """发一个请求,返回(状态码,响应体,响应头的名字)。有 body 时是 POST,没有时是 GET。"""
    headers = {"x-api-key": KEY, "anthropic-version": "2023-06-01",
               "content-type": "application/json", "user-agent": "relay-check/1"}
    if beta:
        headers["anthropic-beta"] = beta
    data = json.dumps(body).encode() if body is not None else None
    req = urllib.request.Request(BASE + path, data=data, headers=headers)
    try:
        with urllib.request.urlopen(req, timeout=180) as r:
            code, text, names = r.status, r.read().decode("utf-8", "replace"), list(r.headers.keys())
    except urllib.error.HTTPError as e:
        code, text, names = e.code, e.read().decode("utf-8", "replace"), list(e.headers.keys())
    try:
        parsed = json.loads(text)
    except ValueError:
        parsed = {}
    return code, parsed if isinstance(parsed, dict) else {}, [n.lower() for n in names]


def msg(content, beta=None, **fields):
    """发一条消息。content 是用户消息的内容,其他字段原样并进请求体。"""
    body = {"model": MODEL, "max_tokens": 200, "messages": [{"role": "user", "content": content}], **fields}
    return call("/v1/messages", body, beta)


def brief(code, body):
    if code != 200:
        return f"HTTP {code}({(body.get('error') or {}).get('type', '响应体不是官方的报错格式')})"
    return f"HTTP 200,stop_reason={body.get('stop_reason')}"


def text_of(body):
    return "".join(b.get("text", "") for b in body.get("content", []) if b.get("type") == "text")


def row(name, result):
    print(f"{name}:{result}", flush=True)


def png(size=64):
    """现做一张纯红色的小图,省得带文件。"""
    def chunk(tag, data):
        return struct.pack(">I", len(data)) + tag + data + struct.pack(">I", zlib.crc32(tag + data))
    pixels = b"".join(b"\x00" + b"\xff\x00\x00" * size for _ in range(size))
    return (b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", struct.pack(">IIBBBBB", size, size, 8, 2, 0, 0, 0))
            + chunk(b"IDAT", zlib.compress(pixels)) + chunk(b"IEND", b""))


# —— 一、基本的 ——
code, body, names = msg("只回答两个字:你好", max_tokens=30)
row("基本请求", brief(code, body))
row("响应头", f"request-id {'有' if 'request-id' in names else '没有'};"
    f"anthropic-ratelimit-* {'有' if any(n.startswith('anthropic-ratelimit-') for n in names) else '没有'}")

code, body, _ = call("/v1/models")
row("模型列表", f"HTTP {code},{len(body.get('data') or [])} 个")

req = urllib.request.Request(BASE + "/v1/messages", data=json.dumps(
    {"model": MODEL, "max_tokens": 30, "stream": True, "messages": [{"role": "user", "content": "只回答两个字:你好"}]}).encode(),
    headers={"x-api-key": KEY, "anthropic-version": "2023-06-01", "content-type": "application/json", "user-agent": "relay-check/1"})
try:
    with urllib.request.urlopen(req, timeout=180) as r:
        events = [line.decode().split(":", 1)[1].strip() for line in r if line.startswith(b"event:")]
    row("流式", "正常,以 message_stop 结束" if events[-1:] == ["message_stop"] else f"事件不完整,最后几个是 {events[-3:]}")
except urllib.error.HTTPError as e:
    row("流式", f"HTTP {e.code}")

# —— 二、官方会拒绝或有明确行为的几种写法 ——
code, body, _ = call("/v1/messages", {"model": MODEL, "messages": [{"role": "user", "content": "你好"}]})
row("不带 max_tokens", brief(code, body))

code, body, _ = msg("从 1 数到 5,用逗号隔开,只输出数字。", max_tokens=40, stop_sequences=["3"])
row("停止序列", "生效" if body.get("stop_reason") == "stop_sequence" else f"没有生效({brief(code, body)})")

code, body, _ = call("/v1/messages", {"model": MODEL, "max_tokens": 30, "messages": [
    {"role": "user", "content": "你好"}, {"role": "assistant", "content": "你好,"}]})
row("预填(最后一条是 assistant)", brief(code, body))

question = "用一句话解释什么是提示缓存。"
c1, counted, _ = call("/v1/messages/count_tokens", {"model": MODEL, "messages": [{"role": "user", "content": question}]})
c2, real, _ = msg(question, max_tokens=60)
usage = real.get("usage") or {}
billed = sum(usage.get(k) or 0 for k in ("input_tokens", "cache_read_input_tokens", "cache_creation_input_tokens"))
row("数 token 的接口", f"接口说 {counted.get('input_tokens')},真实请求计了 {billed}" if (c1, c2) == (200, 200) else f"HTTP {c1}")

# —— 三、常用功能 ——
weather = {"name": "get_weather", "description": "查某个城市现在的天气",
           "input_schema": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}
row("工具调用", brief(*msg("上海现在天气怎么样?", tools=[weather])[:2]))

schema = {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"], "additionalProperties": False}
code, body, _ = msg("中国的首都是哪里?", output_config={"format": {"type": "json_schema", "schema": schema}})
try:
    got_json = "city" in json.loads(text_of(body))
except ValueError:
    got_json = False
row("结构化输出", "拿到了合规的 JSON" if got_json else f"{brief(code, body)},没有拿到要的 JSON")


def image(source):
    return [{"type": "image", "source": source}, {"type": "text", "text": "这张图主要是什么颜色?只答颜色。"}]


row("图片,base64", brief(*msg(image({"type": "base64", "media_type": "image/png", "data": base64.b64encode(png()).decode()}))[:2]))
row("图片,网址", brief(*msg(image({"type": "url", "url": "https://www.python.org/static/img/python-logo.png"}))[:2]))

# —— 四、提示缓存:同一段系统提示连发 4 次,后 3 次应当都读到缓存 ——
RULES = "".join(f"第 {i} 条:回答 {1000 + i} 号仓库的问题时,先确认仓库编号和日期,再给库存数字,数字后面写单位,不确定的要说明来源。\n" for i in range(1, 41))


def cache_hits(extra):
    tag = os.urandom(4).hex()  # 每一组的开头不同,互不干扰
    system = [{"type": "text", "text": f"会话 {tag}。你是仓库助手,遵守下面的守则:\n{RULES}", "cache_control": {"type": "ephemeral"}}]
    reads = []
    for i in range(4):
        _, body, _ = msg(f"{1001 + i} 号仓库有多少库存?一句话。", max_tokens=40, system=system, **extra)
        reads.append((body.get("usage") or {}).get("cache_read_input_tokens") or 0)
        time.sleep(2)
    return f"后 3 次里读到缓存 {sum(1 for n in reads[1:] if n > 0)} 次"


row("提示缓存,不带 metadata", cache_hits({}))
row("提示缓存,带固定的 user_id", cache_hits({"metadata": {"user_id": "relay-check-" + os.urandom(4).hex()}}))


# —— 五、在 Anthropic 那边执行的工具:光看 200 不够,要看响应里有没有工具的内容块 ——
def server_tool(tool, question):
    code, body, _ = msg(question, max_tokens=600, tools=[tool])
    if code != 200:
        return brief(code, body)
    blocks = [b["type"] for b in body.get("content", []) if b.get("type") not in ("text", "thinking")]
    return f"HTTP 200,响应里有 {blocks[0]}" if blocks else "HTTP 200,但响应里没有工具的内容块:这个工具被忽略了"


row("联网搜索", server_tool({"type": "web_search_20250305", "name": "web_search", "max_uses": 1}, "搜索 Python 最新的稳定版本号,一句话回答。"))
row("抓取网页", server_tool({"type": "web_fetch_20250910", "name": "web_fetch", "max_uses": 1}, "抓取 https://example.com 并用一句话说它是什么。"))
row("代码执行", server_tool({"type": "code_execution_20250825", "name": "code_execution"}, "用代码执行工具算 2 的 100 次方的各位数字之和。"))

# —— 六、消息接口以外的 ——
row("文件接口", f"HTTP {call('/v1/files', beta='files-api-2025-04-14')[0]}")
row("批量接口", f"HTTP {call('/v1/messages/batches')[0]}")

本站在服务器上通过酷站AI 跑了两遍,两遍的输出一字不差:

基本请求:HTTP 200,stop_reason=end_turn
响应头:request-id 没有;anthropic-ratelimit-* 没有
模型列表:HTTP 200,16 个
流式:正常,以 message_stop 结束
不带 max_tokens:HTTP 503(billing_admission_failed)
停止序列:没有生效(HTTP 200,stop_reason=end_turn)
预填(最后一条是 assistant):HTTP 200,stop_reason=max_tokens
数 token 的接口:接口说 21,真实请求计了 51
工具调用:HTTP 200,stop_reason=tool_use
结构化输出:拿到了合规的 JSON
图片,base64:HTTP 200,stop_reason=end_turn
图片,网址:HTTP 400(invalid_request_error)
提示缓存,不带 metadata:后 3 次里读到缓存 0 次
提示缓存,带固定的 user_id:后 3 次里读到缓存 2 次
联网搜索:HTTP 200,响应里有 server_tool_use
抓取网页:HTTP 400(invalid_request_error)
代码执行:HTTP 200,但响应里没有工具的内容块:这个工具被忽略了
文件接口:HTTP 404
批量接口:HTTP 404

怎么读结果

按官方文档,每一行应当是这样的。和它不一样的行,就是你的中转站和官方不一样的地方:

这一行 按官方文档应当是
基本请求、流式、工具调用、结构化输出、图片(两种) 都成功
响应头 两样都有
不带 max_tokens 被拒绝,它是必填参数
停止序列 生效
预填 5.5 这一代的模型是 400
数 token 的接口 两个数接近。文档说它给的是估算,和实际只差一点
提示缓存 带不带 metadata,后 3 次都读到缓存
联网搜索、抓取网页、代码执行 都是 200,响应里有工具的内容块
文件接口、批量接口 200

几点说明:

  • 第一行就失败的话,先查地址、key 和模型名。MODEL 要换成你那一家的模型列表里有的。
  • 缓存那两行各发 4 次,中间隔 2 秒。带 user_id 的那组本站是 3 次里命中 2 次:前两次都在写,第三次起才读到。它测的是“带和不带差多少”,次数少,别当成精确的命中率。
  • 脚本只测“请求能不能过、功能有没有发生”,不测模型是不是它声称的那个。那是另一件事,见《thinking signature 是什么》。

八、通过中转站写程序的几条习惯

  1. 地址和 key 从环境变量读,代码里不写死。换一家、换成直连,都不用改代码。
  2. 每个请求都带 max_tokens,模型名写完整的 ID。
  3. 一场对话带同一个 metadata.user_id,否则缓存白开。
  4. 看结果,不只看状态码。 工具有没有被调用、有没有在该停的地方停、JSON 能不能解析,都检查。
  5. 按官方的规则写请求,不依赖中转站放过去的那些写法。
  6. 判断报错看状态码或异常类,不匹配类型名的字符串。
  7. 估花费看 usage,实际扣费看中转站的账单。
  8. 文件用 base64,长对话自己管上下文,不指望文件接口和压缩。
  9. 换中转站或者升级模型之前,把自测脚本跑一遍。

和 Claude Code 的关系

上面这些,用 Claude Code 的时候大多碰不到:它自己带 user_id,自己管上下文,联网、读文件、跑命令用的是它在你机器上的工具,不经过这里用不了的那几个接口。Claude Code 通过中转站有它自己的一组注意事项,见《通过中转站用 Claude Code》。

所以如果你要的只是“让 Claude 帮我干活”,而不是把它做进自己的程序里,直接用 Claude Code 更省事。要做进程序里、又想少操心这些,Agent SDK 是中间的一条路。

花费

自测脚本跑一遍 25 个请求,按官方标价估算约 0.1 美元,两遍合计约 0.2 美元。总表里其余各项的花费记在各自的文章里。这些都是估算,不是中转站实际扣的金额。

这次实测没有覆盖到的

  • 只测了一家中转站、一天。 别家的表现要自己测;这一家以后也可能改。
  • 官方接口没有测。 “官方文档”一栏和“按官方文档应当是”一栏都是文档的说法。
  • 总表里的多数项目只在 Sonnet 5.5 上测过,各项跑了几次、用的什么请求,以“详见”的那一篇为准。今天重新跑的只有自测脚本里的那些。
  • 限流没有触发过,429 时这家中转站返回什么不知道。
  • 中转站怎么计费没有核对,包括缓存、联网搜索和顾问调用。
  • 自测脚本没有覆盖的:上下文压缩和编辑、中途的系统消息、工具搜索、顾问、MCP 连接器、PDF、引用。这些要测的话,照“详见”那几篇里的请求写法。

来源

通过 API 中转站使用这些模型?到 Veridrop 免费检测中转站给你的是不是它声称的模型,或者看看中转站排行榜前 100 名。

相关文章

  • Claude Code 的网络和数据:代理怎么配,它实际连了哪些地方,你的代码和对话去了哪教程 · Claude Code · 约 10 分钟

    本站在代理后面跑 Claude Code,记下它实际连的主机:即使模型请求走中转站,一句话的工夫它还连了五次 api.anthropic.com;设一个环境变量后只剩中转站。附代理配错的三种表现(非交互模式下会一声不吭地卡两分钟以上)、证书和防火墙要放行的地址,以及对话和代码存在哪、存多久、哪些会被发出去。

  • Claude Code 常见报错怎么查:连不上、401、模型不可用、配置不生效教程 · Claude Code · 约 15 分钟

    报错分三类:装不上、请求报错、配置不生效。本站用假地址和假 key 把常见的请求报错逐个复现,记下原文和原因。两个容易误判的:接口地址多写一级 /v1,报的是“模型有问题”;settings.json 写坏了,claude -p 照常运行、没有提示。附 claude doctor、--debug 的用法。

  • 通过中转站用 Claude Code:哪些功能和直连一样,哪些不一样,出了问题怎么判断是谁的教程 · Claude Code · 约 12 分钟

    Claude Code 官方有一份写给网关的兼容性说明:哪些请求头和字段必须原样转发,漏了会坏什么。本站对照这份说明,在一家中转站上把十几项功能实测了一遍:大部分正常,遇到过三处不一样的地方,其中子代理换模型后主会话报 400 的一处,中转站已经修好。附判断问题出在哪一方的办法。

  • Claude 桌面应用接入中转站:在第三方推理设置里填网关地址和 key教程 · Claude Code · 约 6 分钟

    按 Claude 官方文档整理:桌面应用不读环境变量和 settings.json,要打开开发者模式,在 Configure Third-Party Inference 里选 Gateway 并填中转站的地址和 key。讲清中转站要满足什么条件、认证方式怎么选、模型列表为什么是空的,以及接入后哪些功能不可用。