跳转到内容

Sidecar:Web Search 与 Vision

不同路由模型对托管 Web Search 和原生图像输入的支持并不相同。opencodex 通过两个 sidecar 补齐这些能力;它们可以使用 ChatGPT 登录(forward)provider,也可以使用已存储的 Anthropic OAuth provider;web search 还可通过显式 xai 后端使用已存储的 Grok OAuth。Sidecar 错误会转换成长度受限的工具结果或图像提示,不会让整个 turn 失败。

当 Codex 为非透传的路由模型请求托管 web_search 时,opencodex 会:

  1. 移除托管的 web_search 工具,改为向路由模型提供一个合成的 web_search(query) function 工具。原托管工具的选项会保留并用于 sidecar 调用。
  2. 让路由模型在一个小型 agentic 循环中运行。模型调用 web_search 时,opencodex 使用所选 后端:OpenAI 默认以 gpt-5.6-luna 运行托管 web_search;Anthropic 默认以 claude-sonnet-5 运行 web_search_20250305。xAI 默认以 grok-4.6 运行托管 web_search, 并在 xSearch.enabled 为 true 时把 x_search 加入同一请求。Streaming 答案及引用会解析为工具结果。
  3. 循环直到模型回答,或真实查询总数达到 maxSearchesPerTurn(默认 3)。达到上限后会移除 search 工具并强制生成最终答案。如果模型调用 apply_patch 或 shell 等真实客户端工具,当前 turn 会结束,以便这些调用到达 Codex。

路由模型的每次迭代都会向上游请求 stream: true,但 opencodex 会在决定搜索还是返回最终答案前, 在内部完整缓冲所有语义 event。只有第一次迭代的最终 header/status 和 429 key rotation 会被提前 取得。因此,合成搜索调用和中间输出不会作为模型输出暴露给客户端。

注入结果会包裹在不可信数据边界中,限制长度,并按来源 URL 去重。在结构化输出 turn (json_schema / json_object)中,结果会以紧凑 JSON 而不是普通文本传入。若路由模型是纯文本 模型,search 模型还会收到指令,用文字描述相关图像并附上来源 URL。

{
"webSearchSidecar": {
"enabled": true,
"backend": "anthropic",
"model": "claude-sonnet-5",
"reasoning": "low",
"maxSearchesPerTurn": 3,
"routedModelStallTimeoutMs": 200000,
"timeoutMs": 200000
}
}

托管后端不允许在 minimal reasoning 下使用工具,因此默认值为 low。搜索失败时,路由模型会 收到长度受限的错误结果,仍可依据已有上下文继续回答。

此路径采用四个相互独立的时钟。stallTimeoutSec 是基础 bridge event-stall 预算。 connectTimeoutMs(默认 200000)只限制 DNS/TCP/TLS 和最终响应 header。仅可在配置文件中 设置的 webSearchSidecar.routedModelStallTimeoutMs(默认 200000,整数 1..2147483647)限制每次路由模型迭代中原始响应 byte 连续无活动的时间,并在收到每个非空 byte 时重置。webSearchSidecar.timeoutMs 独立限制单次托管搜索请求。实际 bridge watchdog 为 max(基础 stall, connect timeout, 路由模型 stall, sidecar timeout) + 30 秒。路由模型 stall 不是总生成 timeout。SSE 开始前的失败会返回非 2xx JSON;响应 header 开始后发生的生成失败则以 response.failed SSE 传递。

当路由模型列在其 provider 的 noVisionModels 中,或该模型在 modelInputModalities 中被声明为仅文本, 并且请求包含图像时,只要有可用的 vision sidecar plan,opencodex 就会在主调用之前描述每张图像并用文字替换图像。 如果没有可用 plan,原始图像会被移除,而不会继续转发给纯文本后端。模型目录会为每个由 sidecar 覆盖的模型声明图像输入。 只有当每个 combo 成员都能原生或通过 sidecar 接受图像、且 combo 的 imageInput 设置未禁用时,combo 才会声明图像输入; 这样 Codex 应用等客户端会允许附件,而不会在 sidecar 运行前阻止它们。当 visionSidecar.model 缺失或为空时,OpenAI 执行路径、 Dashboard 和管理 API 都使用 gpt-5.4-mini 作为回退。启动时仍会把明确保存的旧 gpt-5.4-mini 值迁移到 gpt-5.6-luna;该迁移只作用于已保存值,不适用于缺失的 model 字段。

  • 图像可以来自 user、developer 和 tool-result message,也包括 Codex 的 view_image 结果。
  • OpenAI 路径(ChatGPT 登录透传)会通过 Responses 端点把每张图像发送给配置的视觉模型,并携带所选 的 reasoning.effort(默认为 low),描述结果就地替换图像部分。Anthropic 路径走 Messages 端点并使用自己的思考预算映射,会忽略这个 OpenAI 专用设置。
  • 对于具有可靠能力元数据的原生模型,不支持的推理等级会归一化到不高于请求值的最高支持档位;如果 不存在这样的档位,则使用最低支持档位。对于缺少可靠能力元数据的未知模型或自定义模型,保持宽松处理。
  • 描述任务最多同时处理 3 张图像,并保持输入顺序。发送给描述模型的用户上下文最多 800 个字符, 每张图像注入的描述最多 2,000 个字符。请求不会发送 ChatGPT 后端不支持的 max_output_tokens
  • 图像 URL 会在转发前校验。data URL 必须是 png / jpeg / jpg / webp / gif,base64 数据限制在约 20 MB;只接受 data:https: scheme。远程 https 图像由 OpenAI 后端获取, 而不是代理。
  • noVisionModels 匹配会忽略 Ollama 风格的 :size 后缀,因此一个 gpt-oss 条目也能覆盖 gpt-oss:120b
  • 如果描述失败,模型会收到简短的处理错误提示。(如果没有可用的 sidecar plan,则不会尝试描述, 原始图像会按上文所述被移除。)
  • maxDescriptionsPerTurn(默认 8)限制每个主模型 turn 的新增描述次数。缓存命中和同一 turn 的重复请求不会消耗配额。成功的 data: 图像描述会按后端、模型、detail、图像字节和消息上下文 缓存;OpenAI 的缓存键还会额外包含推理强度(Anthropic 键不含,因为该字段在那里被忽略)。 内容可变的 https: 图像不会缓存。
{
"visionSidecar": {
"enabled": true,
"backend": "openai",
"model": "gpt-5.6-luna",
"reasoning": "medium",
"maxDescriptionsPerTurn": 8,
"timeoutMs": 45000
}
}

纯文本模型按 provider 标记:

{
"providers": {
"ollama-cloud": {
"baseUrl": "https://ollama.com/v1",
"noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"]
}
}
}

仪表盘的视觉附属服务卡片可以启用或停用 sidecar,并设置 maxDescriptionsPerTurntimeoutMs,同时保留已有的模型、后端和推理强度控件。停用不会删除这些设置;重新启用后仍会保留原来的模型、后端、推理强度、超时和次数上限。

PUT /api/sidecar-settings 接受相同字段。部分更新会保留未提交的键。timeoutMs 使用运行时整数边界(1–2147483647 毫秒)。

如果更想直接改文件,仍可在 config.json 中把 enabled 设为 false。Anthropic OAuth 搜索和图像描述沿用现有 Claude Code OAuth fingerprint 先例,但仍应使用目标账户和实际负载充分 soak test。所有字段见 配置参考