cli-one-liner-model-switch · ZH · 2026-10-24

用 curl 在一把聚合站 key 上中途换模型:一份可复制的速查表

面向 shell 与 CI 用户,讲解如何用一个 API key 通过 curl 直接调用聚合站的多个模型,并给出切换模型时的最小请求写法与字段速查要点,无需安装任何 SDK。

为什么 shell 用户关心“换模型”这件事

在 CI、运维脚本或临时排查里,装 SDK、配虚拟环境、管依赖版本往往比请求本身还费时间。聚合站的接口是 OpenAI 兼容的 HTTP 接口,这意味着只要机器上有 curl,就能直接调模型;一个 API key 覆盖多个后端,换模型时改的就是请求体里的一个字段。

最小请求骨架

所有调用都是向同一个 chat completions 端点 POST 一段 JSON。把密钥放在环境变量里,避免写进脚本或历史记录:

export AGG_KEY="sk-..."
curl -sS https://<你的站点的接口域名>/v1/chat/completions \
  -H "Authorization: Bearer $AGG_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<模型名>",
    "messages": [{"role": "user", "content": "说一句你好"}]
  }'

响应是 JSON,脚本里通常只需要把正文取出来。装了 jq 的话:

... | jq -r '.choices[0].message.content'

没有 jq 也能跑,但解析 JSON 用专门的工具更稳,不建议用 grep/sed 硬切。

换模型:只改 model 字段

聚合站把不同厂商的模型统一到同一套请求/响应结构,所以从 Claude 换到 GPT、DeepSeek、Qwen、GLM 或 Kimi,端点、鉴权头、消息结构都不变,唯一必须改的是 model。

实践上建议把模型名做成变量,脚本里一处切换:

MODEL="${MODEL:-<默认模型名>}"
curl -sS "$BASE/v1/chat/completions" \
  -H "Authorization: Bearer $AGG_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"model\": \"$MODEL\", \"messages\": [{\"role\": \"user\", \"content\": \"hi\"}]}"

这样在 CI 里就可以用环境变量控制用哪个后端跑同一段任务,比如让同一份评测脚本在不同模型上重跑。

注意模型名必须用聚合站文档里列出的标识符,不要凭记忆拼厂商自己的命名。具体可用的名称以站点模型列表为准。

三个常见字段的写法

max_tokens

限制回复长度。放在请求体顶层,和 model 同级:

{"model": "<模型名>", "max_tokens": 256, "messages": [...]}

不同模型对上限的约束不一样,写太大可能被拒或直接被截断到模型上限。脚本里给一个保守值通常比不写更可控,尤其是批量跑的时候能防止单次请求把预算吃掉。

tools(函数调用)

需要结构化输出时用 tools。它同样是顶层字段,结构是 OpenAI 风格的数组:

{
  "model": "<模型名>",
  "messages": [{"role": "user", "content": "查一下订单 123"}],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_order",
        "description": "按订单号查询订单状态",
        "parameters": {
          "type": "object",
          "properties": {"id": {"type": "string"}},
          "required": ["id"]
        }
      }
    }
  ]
}

返回里会出现 tool_calls,你需要自己把结果作为一条 tool 角色消息回填,再发下一轮。这块各模型的遵循程度不一致,切换后端时最好先用一个最小样例验证一下工具调用是否按预期返回,而不是假定行为完全一致。

messages

messages 是数组,role 用 system / user / assistant(工具回填用 tool)。多轮对话就是把历史消息按顺序堆进数组,接口本身不保存会话状态,所以上下文长度要自己控制。

在 bash 里安全拼 JSON

手写长 JSON 容易在引号和换行上出错。两种更省事的做法:

  • 用 jq -n 构造请求体,把变量作为参数传进去,由 jq 负责转义:
BODY=$(jq -n --arg m "$MODEL" --arg p "$PROMPT" \
  '{model: $m, messages: [{role: "user", content: $p}]}')
curl -sS "$BASE/v1/chat/completions" \
  -H "Authorization: Bearer $AGG_KEY" \
  -H "Content-Type: application/json" \
  -d "$BODY"
  • 或者把请求体写进 .json 模板文件,用 envsubst 之类替换占位符。

涉及用户输入或多行文本时,不要直接字符串拼接进 -d,转义问题几乎必然出现。

切换后端时的检查清单

同一段脚本换模型,建议按这个顺序确认:

  1. 模型名是否在站点当前列表里,且拼写一致。
  2. max_tokens 是否在该模型可接受范围内。
  3. 如果用了 tools,先用单条请求验证返回的是 tool_calls 还是普通文本。
  4. 响应解析是否只依赖 choices[0].message.content,有没有假设固定字段一定存在。
  5. 是否把 HTTP 状态码和错误体打进日志,方便在 CI 里定位是鉴权、限流还是参数问题。

关于计费的背景

聚合站按官方价 ×1.3 扣费,用 USDC 充值、不需要 KYC;如果你贡献自己的 key,则按官方价 ×1.1(优质 key ×1.2)返 USDC。写脚本做成本控制时,关心的是请求侧的 max_tokens 与调用次数,而不是在客户端做价格计算——计费在服务端完成。

什么时候不该用裸 curl

简单脚本、健康检查、CI 里的单次调用,curl 足够。但如果你需要流式解析、重试退避、并发控制、结构化日志,自己用 bash 实现会迅速变复杂。这种场景换成任意支持自定义 base URL 的 OpenAI 兼容 SDK 更划算,聚合站的意义就在于你可以继续用熟悉的客户端,只把 base URL 和 key 指向它。