LLM Agent Harness 的协议适配问题:从一次 GLM-5.1 接入失败说起
更新时间:2026/05/02
文章定位:一次真实 agent harness 接入排障的技术复盘。
关键词:Agent harness, OpenAI Responses API, Chat Completions, SiliconFlow, Wecode, protocol adapter
这篇文章记录一个很小但很有代表性的工程问题:我尝试把硅基流动上的 Pro/zai-org/GLM-5.1 接到自己的类 Codex/Wecode agent harness,最初以为只是 config.toml 和 auth.json 没写对,最后发现根因并不在配置,而在模型服务和 agent harness 之间的 wire protocol 不兼容。
这个问题值得单独写下来,是因为它揭示了一个容易被忽略的事实:模型 API 能聊,不等于它能被 agent harness 使用。Coding agent 不是普通聊天客户端。它依赖流式事件、工具调用、状态标识、错误恢复和上下文压缩策略。只要这些协议层不匹配,模型本身再强也接不进去。
1. 症状:一个看起来像配置错误的 404
最初的目标很直接:在远程机器的 ~/.wecode/config.toml 里增加一个硅基流动 provider,让 Wecode 使用 GLM-5.1:
model_provider = "siliconflow" model = "Pro/zai-org/GLM-5.1" disable_response_storage = true [model_providers.siliconflow] name = "SiliconFlow CN" base_url = "https://api.siliconflow.cn/v1" wire_api = "responses" requires_openai_auth = true
auth.json 的思路也很自然:把硅基流动 API key 放到 OpenAI-compatible harness 常见的 OPENAI_API_KEY 字段中。
但启动后直接报错:
Unexpected status 404 Not Found: Not Found, url: https://api.siliconflow.cn/v1/responses
第一眼看上去,这很像下面几类常见问题:
- API key 没有写对,或者
auth.json的字段名不对。 - 模型名写错,例如
Pro/zai-org/GLM-5.1和zai-org/GLM-5.1混淆。 - 域名选错,例如
api.siliconflow.cn和api.siliconflow.com。 - provider 的
base_url多写或少写了/v1。
这些都值得检查,但这次都不是根因。真正有用的线索在错误 URL 本身:程序请求的是 /v1/responses。
2. 快速定位:服务端没有这个 endpoint
硅基流动的 OpenAI-compatible 接口主要是 Chat Completions 风格,也就是:
POST https://api.siliconflow.cn/v1/chat/completions
可以用一个最小 curl 验证服务本身是否可用:
curl https://api.siliconflow.cn/v1/chat/completions \
-H "Authorization: Bearer $SILICONFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "Pro/zai-org/GLM-5.1",
"messages": [{"role": "user", "content": "hello"}],
"stream": false
}'
这一步的意义是把问题分层。如果 /chat/completions 成功,而 harness 请求 /responses 失败,那么问题就不是 key,也不是模型不可用,而是客户端协议选错了。
3. 第二个错误:wire_api = "chat" 已经被移除
直觉上的修复是把配置改成:
wire_api = "chat"
但新版 Codex/Wecode 类 harness 会直接拒绝这个配置:
Error loading config.toml: /home/fcy/.wecode/config.toml:11:12: `wire_api = "chat"` is no longer supported. How to fix: set `wire_api = "responses"` in your provider config. More info: https://github.com/openai/codex/discussions/7782 | 11 | wire_api = "chat"
这条错误把问题彻底钉住了:
| 组件 | 支持的协议 | 实际 endpoint | 结论 |
|---|---|---|---|
| 新版 Codex/Wecode harness | Responses API | /v1/responses | 只会按 Responses 协议发请求 |
| SiliconFlow GLM-5.1 | Chat Completions API | /v1/chat/completions | 不提供 /v1/responses |
所以这是一个协议不兼容问题。config.toml 可以选择 provider,但不能让服务商凭空支持一个不存在的 HTTP endpoint。
4. 为什么这不是“改个 URL”就能解决
很多模型 API 都自称 OpenAI-compatible。但在 agent harness 里,OpenAI-compatible 这句话必须拆开问:兼容的是哪一个 API?
Chat Completions
经典聊天接口。核心输入是 messages,输出是 assistant message 或 streaming delta。很多第三方模型服务兼容的是这一层。
Responses API
更适合 agent 的统一接口。除了文本,还要承载 response id、工具调用、推理内容、结构化 output item 和更细粒度的事件流。
普通聊天客户端可能只需要:
messages -> assistant text
但 coding agent harness 通常需要:
conversation state -> model request -> streaming output events -> tool call events -> shell/apply_patch execution -> tool result injection -> model continuation -> final answer -> trace and persistence
这意味着协议层至少包含:
- 消息结构:Responses 的 input item 与 Chat Completions 的 role messages 不完全同构。
- 流式事件:Chat 的 delta chunk 需要翻译成 harness 期待的 response events。
- 工具调用:tool call 的 id、name、arguments、增量拼接和完成事件都要保持一致。
- 状态管理:Responses API 常带有 response id、previous response、output item 等状态语义。
- 错误归一化:供应商错误码、限流、模型不存在、鉴权失败都要转成 harness 能理解的形态。
- 审计与压缩:长程任务依赖 trace、rollout、上下文压缩摘要,协议事件如果丢失,后续排障会很困难。
因此,简单把 /responses 字符串替换成 /chat/completions 往往只能让第一跳 HTTP 成功,不能保证 agent 真的能完成任务。
5. 一个更准确的心理模型:模型不是后端,协议适配器才是后端
在 agent 系统里,模型服务最好不要被当成“直接可替换后端”。更稳的抽象是:
/v1/responsesschema + stream + tools
/v1/chat/completions这层 adapter 不是网关转发器,而是语义翻译器。它需要知道 harness 的事件模型,也要知道供应商的能力边界。
如果没有 adapter,那么 base_url 的配置只能覆盖网络地址,不能覆盖协议语义。今天这个 404 就是最直接的例子:网络到了,协议没到。
6. 三种解决方案
这次问题的实际可选方案并不多:
| 方案 | 工程成本 | 稳定性 | 适合场景 |
|---|---|---|---|
| 换支持 Responses API 的供应商或网关 | 低 | 高 | 想快速让新版 Codex/Wecode 跑起来 |
| 换支持 Chat Completions 的旧版 harness | 中 | 中 | 明确想继续用硅基流动模型,且可以接受旧协议限制 |
| 写 Responses-to-Chat adapter | 高 | 取决于实现 | 正在构建自己的 agent platform,需要统一接多家模型服务 |
如果只是个人临时使用,最省事的是前两种。如果目标是长期维护自己的 agent harness,第三种才是正确的工程方向。
7. 适配层应该怎么设计
一个最小可用 adapter 可以从非流式文本开始,但真正用于 coding agent,至少要逐步覆盖下面这些模块:
Request Translator
把 Responses request 中的 input、instructions、tools 和模型参数转换成 Chat Completions 的 messages、tools、tool_choice 和 stream 参数。
Stream Translator
把 Chat Completions 的 SSE chunks 转成 harness 期待的 response events,尤其是文本增量、工具参数增量和 completed 事件。
Tool Semantics
确保 tool call id 稳定,arguments 可以增量拼接,工具结果能回灌给下一轮模型请求,而不是只在文本里描述。
Failure Contract
把 401、404、429、5xx、模型不存在、上下文过长等错误归一成 harness 可诊断的错误,避免只暴露低信息量 HTTP 异常。
更完整的 adapter 还要处理模型能力声明:
- 是否支持 tool calling。
- 是否支持 parallel tool calls。
- 是否支持 reasoning content。
- 最大上下文长度和输出长度。
- streaming 下 tool arguments 是否稳定。
- 供应商对 system/developer/user 角色的支持差异。
这些能力不应该散落在 prompt 里,而应该成为 provider capability metadata。否则 agent 在某个模型上能跑,在另一个模型上随机失败,最后很难定位。
8. 配置文件应该更早失败
这次排障里最浪费时间的地方,是错误看起来像鉴权或模型名问题。一个更好的 harness 应该在启动时做 provider preflight:
1. 读取 model_provider 和 wire_api 2. 检查 binary 是否支持这个 wire_api 3. 检查 provider 是否声明支持目标 endpoint 4. 用最小请求验证 endpoint 和模型名 5. 输出协议层诊断,而不是等到任务中途 404
理想错误信息应该像这样:
Provider protocol mismatch: harness wire_api: responses requested endpoint: /v1/responses provider appears to support: /v1/chat/completions Config-only fix is not available. Use a Chat-compatible harness, a Responses-compatible provider, or a protocol adapter.
这比裸露的 Unexpected status 404 Not Found 更能帮助用户做正确决策。
9. 从这次问题里得到的结论
这次真正的修复不是某一行配置,而是把问题从“怎么写 config.toml”重新表述成“agent harness 与模型服务之间的协议契约是否一致”。
我的几个判断:
- 模型兼容性不是 API key 兼容性。API key 能通过鉴权,只说明网络和账户层没问题,不说明 agent 协议可用。
- OpenAI-compatible 不是一个单一标准。很多服务兼容的是 Chat Completions,不是 Responses API。
- Agent harness 应该把 protocol adapter 作为一等组件。模型路由不只是选 base URL,还要选择 schema、stream、tool 和 failure contract。
- 配置文件不能弥补协议缺失。当 binary 移除了
wire_api = "chat",继续调 config 只会绕圈。 - 长程 agent 任务更需要协议清晰。一次聊天可以容忍轻微格式差异,但 coding agent 的工具调用和上下文状态会把协议差异放大成系统性失败。
10. 后续如果要继续接 GLM-5.1
如果我后面继续把 GLM-5.1 接到自己的 agent harness,我会走 adapter 路线,而不是继续试图用新版 Codex 的 responses provider 直连硅基流动。
优先级大概是:
Phase 1: non-streaming text completion Phase 2: streaming text delta Phase 3: single tool call Phase 4: incremental tool arguments Phase 5: multi-turn tool result injection Phase 6: failure normalization and trace persistence Phase 7: benchmark against existing Responses-native provider
只有 Phase 5 之后,它才算是一个真正能跑 coding agent 的适配层。Phase 1 的“能回复 hello”只能证明模型 API 可用,不能证明 agent harness 可用。
参考
- SiliconFlow Chat Completions API:https://docs.siliconflow.cn/cn/api-reference/chat-completions/chat-completions
- OpenAI Codex discussion:
wire_api = "chat"no longer supported:https://github.com/openai/codex/discussions/7782 - 本文基于 2026/05/02 一次真实 Wecode/Codex-like harness 排障记录整理。具体供应商接口和 Codex 行为可能随版本变化,需要以当时文档和 binary 行为为准。