[控场AI]
· 10 分钟阅读· 5,064 字

Qwen Code 接入自定义 OpenAI 兼容 API 实战指南

Qwen Code 接入自定义 OpenAI 兼容 API 实战指南

实测 Qwen Code 接入 OpenAI 兼容端点的配置陷阱与请求成本。

作者用可模拟 401、429、503 的本地 mock 服务器,逐请求实测了 Qwen Code 0.25.2 接入 OpenAI 兼容端点的行为。结论包括:base URL 必须保留 `/v1`,否则 SDK 追加路径后多数网关 404;默认 `max_tokens` 为 32000,易超模型上限或占用配额;provider 条目的 `generationConfig` 整体替换顶层配置,导致顶层设置静默失效;一次 `qwen -p` 调用会发出两个请求,系统提示词约 41500 字符,托管自动记忆默认开启;SDK 与 CLI 两层重试相乘,宕机端点可在数分钟内产生 40 多个请求。文章给出配置清单,并说明循环检测是常开保护。

Qwen Code 是通义团队推出的终端编码智能体,最初从 Gemini CLI 分叉而来,但改用 OpenAI Chat Completions 协议,因此可以对接 vLLM、Ollama 的 /v1 端点、LiteLLM 或任意托管网关。这给了开发者很大的自由度——但默认配置里有不少不那么显眼的细节。一位开发者用本地 mock 服务器(可模拟 401、429、503)实测了 Qwen Code 0.25.2 的每个网络请求,以下是实测结果与值得注意的配置陷阱。

Qwen Code 自定义 API 接入实测

快速配置:三个环境变量

最轻量的接法是三个环境变量:OPENAI_API_KEY、OPENAI_BASE_URL、OPENAI_MODEL,然后直接 qwen -p "..." 运行。无需任何配置文件,Qwen Code 就会选择 OpenAI 兼容路径,发送带 Bearer 鉴权、stream: true 和工具列表的请求。

实测中有两个容易踩的坑。

base URL 必须保留 /v1。 Qwen Code 使用官方 OpenAI Node SDK,它会在你给的地址后追加 /chat/completions。如果 OPENAI_BASE_URL=https://host(不带 /v1),请求会发到 /chat/completions,多数网关直接回 404。

max_tokens 默认是 32000。 每次仅用环境变量的请求都携带 "max_tokens": 32000。部分服务器会拒绝超过模型输出上限的值,另一些则会按这个额度预留配额。如果你的模型输出上限更低,需要手动设置。

密钥错误时的行为倒是很干净:一次请求,报 [API Error: 401 Invalid API key],退出码 1,不重试。

OpenAI Chat Completions 协议 是目前事实上的 LLM 推理接入标准,主流自托管框架都实现了它:vLLM 提供高性能生产推理,Ollama 面向本地轻量部署,LiteLLM 则做多供应商统一代理与密钥管理。它们的共同点是暴露 /v1/chat/completions 端点并接受 Bearer 鉴权,因此任何按此协议实现的客户端都能无缝切换后端。Qwen Code 之所以从 Gemini CLI 分叉后改写网络层,正是为了放弃 Gemini 私有协议而获得这种可替换性。理解这一点很关键:下文所有路径、鉴权、重试行为都来自官方 OpenAI Node SDK 的默认实现,而非 Qwen Code 自己发明的约定。

环境变量优先级 也值得留意。Qwen Code 同时支持 OPENAI_API_KEY 这类通用变量和 OPENAI_BASE_URL 这类网关地址,它们构成一个临时性的“运行时模型”。这种方式适合快速验证,但不支持多 provider 并存、超时、重试次数等细粒度控制,所以官方文档把 settings.json 作为日常方案。两者同时存在时,需要明确哪一方最终决定请求参数,否则容易出现改了配置却不见生效的情况。

日常推荐:settings.json 中的 modelProviders

环境变量创建的是文档所称的“运行时模型”。日常使用更推荐在 ~/.qwen/settings.json 里配置 provider 条目,结构更清晰、也更容易管理。关键字段包括 id、name、envKey(环境变量的名称,不是密钥本身)、baseUrl,以及嵌套的 generationConfig(含 timeout、maxRetries、contextWindowSize 和 samplingParams)。另需在 security.auth.selectedType 指定 openai,并在顶层 model.name 指向同一模型 ID。

modelProviders 下的键必须是真实的协议名(openai、anthropic、gemini、vertex-ai)。自造一个 "apiclaw" 这样的键会被跳过并给出警告,除非你在顶层 providerProtocol 块里做映射。

envKey 指向的密钥有三个可放位置:项目目录的 .env 文件(有效)、~/.qwen/.env(有效)、或都没有(Qwen Code 在发送前即停止,提示缺少 API 密钥)。如果用项目 .env,务必加入 .gitignore——编码代理就在你的仓库里运行,密钥被误提交的风险真实存在。

配置生效后,请求体变为 temperature: 0.2、max_tokens: 4096,32000 的默认值消失。

会静默失效的设置

实测还发现一个隐蔽行为:同一文件里顶层的 model.generationConfig.samplingParams.top_p 从未被发送。这符合文档规则——一旦选中某个 modelProviders 条目,它的 generationConfig 会整体替换,顶层同名配置对该模型被忽略。samplingParams、customHeaders、extra_body 都是整对象替换而非合并。如果某项设置似乎没生效,先检查它是不是放错了层级。

generationConfig 的整对象替换 是 Qwen Code 配置模型的核心语义。它把采样参数(temperature、top_p、max_tokens)、超时、重试、上下文窗口等打包成一个单元,选中某个 provider 条目时整块生效,不与顶层或其他 provider 的同名字段合并。这与多数工具“就近覆盖单个键”的做法相反,因此从顶层的某个参数迁移到 provider 内部时,容易误以为设了却没生效。排查方法是把最终请求体打印出来核对,而不是反复修改配置文件。

envKey 与密钥托管 的设计意图是避免把密钥写进 JSON。envKey 只存变量名,真实值由 .env 文件或 shell 环境注入。项目级 .env 会被编码代理读取并在仓库内工作,一旦误提交就等于把密钥连同代码一起泄露;放到 ~/.qwen/.env 则可跨项目复用且不入库。多环境场景下,还可以用 shell 变量按会话覆盖,避免长期凭证散落在磁盘上。

一句“Say hi”的真实成本

非交互式运行 qwen -p "Say hi" 会发送两个请求,而不是一个。

第一个是回答请求,系统提示词达 30,553 字符,另有一个 10,963 字符的块列出可用技能和记忆路径,再加六个工具定义(glob、grep_search、read_file、skill、tool_call、tool_search)。这意味着在你输入提示词之前,已有约 41,500 字符,按每 token 四字符粗算约 1 万输入 token。

第二个请求在回答打印之后发出,系统提示词以“You are now acting as the managed memory extraction subagent”开头,重放对话并提供 write_file 和 edit 工具,用于更新 ~/.qwen/ 下的记忆文件。这是默认开启的托管自动记忆功能。

按 token 计费的计划,这个基线对短提示词影响明显;按请求数或每分钟限流计费的计划,记忆轮次会让一次性运行翻倍。可以通过 "memory": {"enableManagedAutoMemory": false, "enableManagedAutoDream": false} 关闭,之后 qwen -p "Say hi" 只发一个请求。

重试:两层机制相乘

这是 CI 或慢速端点部署前最该配置的部分。

快速恢复的 429 处理得当。mock 两次返回 Retry-After: 2 的 429 时,Qwen Code 每次等待约 2 秒,第三次请求成功。

持续宕机的服务器则是另一回事。默认设置下,mock 始终返回 503 时:OpenAI SDK 每次尝试重试三次,退避约 0.4 秒、0.9 秒、1.8 秒,请求四个一组到达;Qwen Code 自身的重试循环以逐渐增大的间隔(约 2、3、7、14、30、30 秒)重复这些组,七组共 28 个请求、约 107 秒;随后 CLI 打印 Retrying in 60s (attempt 1/10) 开始新一轮。实测在 200 秒、44 个请求时手动停止,仍在重试。

把 generationConfig 里的 maxRetries 设为 0 可去掉 SDK 层。同样宕机的端点随后只收到 7 个请求(约 74 秒),CLI 以 [API Error: 503 upstream busy] 和退出码 1 结束。脚本和 CI 任务建议将 maxRetries 设为 0 或 1,并在外层包自己的 timeout,让供应商故障在一分钟内失败而非挂起。

指数退避与 Retry-After 是理解重试放大的基础。OpenAI SDK 默认在遇到 429、5xx 和网络错误时重试,间隔按指数增长并带抖动,同时尊重服务器返回的 Retry-After 头。Qwen Code 在 SDK 之外又包了一层自己的重试循环,用于处理会话级失败。两层独立计数意味着实际请求数约为两者乘积:SDK 的 3 次尝试乘以 CLI 的 7 轮,再乘以每轮可能的多请求,就能在几分钟内累积到数十个请求。对按请求计费或严格限流的端点,这既浪费配额也可能触发封禁。把 maxRetries 调低只关闭 SDK 层,外层循环仍会继续,所以 CI 中务必同时设置进程级超时作为兜底。

一个令人欣慰的保护机制

mock 设有持续请求同一工具调用、参数不变的重复模式。Qwen Code 在三次请求后停止:Loop detection halted the run (global_tool_call_duplicate...)。这是常开保护,对有时会卡在重复步骤上的小模型或廉价模型尤其有用。

循环检测(loop detection) 针对的是智能体常见的失败模式:模型反复调用同一工具、参数完全相同,既消耗 token 又无法推进任务。Qwen Code 在连续三次相同调用后以 global_tool_call_duplicate 中止,属于无需配置的常开保护。它对能力较弱或量化程度较高的模型尤其有价值,因为这些模型更容易陷入重复步骤。需要注意的是,合法的批处理式重复调用也可能被误判,此时应调整提示词或拆分任务,而不是依赖关闭检测来绕过。

配置清单

  • OPENAI_BASE_URL(或 baseUrl)以 /v1 结尾,不要带 /chat/completions
  • 使用 modelProviders 配 envKey,密钥放 ~/.qwen/.env 或被 git 忽略的项目 .env
  • 将 samplingParams.max_tokens 设为模型真实输出上限,而非 32000 默认值
  • provider 模型的所有设置放在它自己的 generationConfig 里,顶层配置对该模型无效
  • 按请求计费或跑一次性 -p 任务时关闭托管自动记忆
  • CI 中把 maxRetries 设低并加外层超时

需要说明的是,原文作者是 OpenAI 兼容网关 APIClaw 的开发者,示例指向其服务,观点需相应权衡;但上述行为对任何兼容端点均适用。

#Qwen Code#OpenAI 兼容 API#modelProviders#终端编码智能体#API 重试配置#maxRetries#OpenAI Chat Completions#自动记忆
分享:

相关推荐