雨天小六

读懂 Codex(9.6):ModelClient Protocol 与真实 Responses Adapter

· 更新于 2026-08-03 · 专栏:读懂 Codex

#Codex#Agent Runtime#Python#软件架构

具体问题与边界

怎样把领域 ModelRequest 编译成真实 Responses API 请求,同时让 Runtime 完全不知道 HTTP 与认证细节?

Runtime 依赖 ModelClient.stream();ResponsesModelClient 负责编译 wire payload;HttpxResponsesTransport 负责认证、HTTP status、SSE 连接与取消。 本节以官方 Codex 锁定提交为事实基线,以 Mini Codex 的可执行断言检验 Python 设计;二者不是逐行翻译关系。

OpenAI 官方迁移文档明确要求 Responses 流消费者按 typed SSE event 的 type 分支;文本常见事件包括 response.output_text.deltaresponse.completederror,函数调用还会出现 arguments delta/done。见 官方 Responses streaming 迁移说明

协议、类型与状态所有权

对象创建/所有者生命周期与作用域是否持久化
ModelRequestPromptCompiler/Session一个 Step
Responses payloadResponsesModelClient一次 HTTP 请求
HTTP/SSE connectionHttpxResponsesTransport一个 stream 调用
API keyHost 配置Transport 生命周期绝不进入 History/Rollout

正常路径

ModelClient Protocol 与真实 Responses Adapter正常路径图
图 9.6-1:怎样把领域 ModelRequest 编译成真实 Responses API 请求,同时让 Runtime 完全不知道 HTTP 与认证细节?
  1. ModelClient 是只有 stream(request, cancellation) 的 Protocol;Session 不导入 httpx 或 OpenAI SDK 类型。
  2. Adapter 把 Message 编成 role/content,把 ToolCall 编成 function_call,把 ToolResult 编成 function_call_output,并序列化 arguments。
  3. ToolSpec 通过 to_responses_api() 生成 type=function、name、description、strict 和 parameters;内部 output_schema 不发给普通 Responses tools。
  4. 请求显式携带 model、instructions、input、tools、stream=true 与 store 配置。教学实现默认 store=false,避免把本地 Rollout 与远端存储含混。
  5. Transport 使用 Authorization header 打开 POST /v1/responses;HTTP 只产出 JSON event,领域 Adapter 再映射为 ModelEvent。

调用链与等待点

1. `ModelClient` 是只有 `stream(request, cancellation)` 的 Protocol;Session 不导入 httpx 或 OpenAI SDK 类型
→ 2. Adapter 把 Message 编成 role/content,把 ToolCall 编成 function_call,把 ToolResult 编成 function_call_output,并序列化 arguments
→ 3. ToolSpec 通过 `to_responses_api()` 生成 `type=function`、name、description、strict 和 parameters;内部 output_schema 不发给普通 Responses tools
→ 4. 请求显式携带 model、instructions、input、tools、stream=true 与 store 配置
→ 5. Transport 使用 Authorization header 打开 `POST /v1/responses`;HTTP 只产出 JSON event,领域 Adapter 再映射为 ModelEvent

每个 await 都是状态可被取消、外部副作用可能已经发生或其他任务能够推进的边界。正文因此同时写清输入形状、所有者、成功产物和失败残留,而不是只列方法名。

Python 风格伪代码

class ModelClient(Protocol):
    def stream(request, cancellation) -> AsyncIterator[ModelEvent]: ...

class ResponsesModelClient:
    def build_payload(request):
        return {
            "model": configured_model,
            "instructions": request.base_instructions,
            "input": [to_wire(item) for item in request.history],
            "tools": [spec.to_responses_api() for spec in request.tool_specs],
            "stream": True,
            "store": False,
        }

    async def stream(request, cancellation):
        payload = build_payload(request)
        async for wire_event in transport.stream_json(payload, cancellation):
            cancellation.raise_if_cancelled()
            if model_event := map_typed_event(wire_event):
                yield model_event

伪代码表达顺序和责任。真实可运行实现没有把万能调用当作未解释黑箱;对应模块见 Mini 导航。

失败、取消与恢复

ModelClient Protocol 与真实 Responses Adapter失败路径图
图 9.6-2:失败分支按实际状态所有者收口,模型可见失败与 Runtime fatal 分开。
故障点已发生/残留状态处理与模型可见结果
ToolSpec 不是预期类型无法可靠生成线格式Adapter 在发请求前 TypeError
HTTP 4xx/5xx没有领域 ModelCompletedTransport raise_for_status,Turn owner Aborted
API key 写进 ModelRequest可能进入 trace/日志认证只属于 Transport header
断线前没有 completedAccumulator 拒绝 finish半响应不进入规范历史

必须保持的不变量

Session 只能看见领域 ModelEvent;认证、HTTP status 和 SSE 资源清理必须留在 Transport;只有真实 complete 事件能结束一次成功采样。

设计取舍

Adapter 分层增加一次 wire/domain 转换,却允许离线 Scripted、录制回放和真实 API 复用同一个 Turn Loop。Mini 不实现官方 Provider 能力协商、WebSocket、重试和 previous_response 优化。

“为什么”的表述是从源码状态机、调用顺序和测试反推的工程解释;源码未声明的动机不写成官方承诺。

测试与复现实验

cd examples/mini-codex
uv run pytest -q -k 'test_responses_adapter_builds_wire_payload_and_maps_typed_events or test_function_tool_spec_serializes_responses_wire_shape or test_function_tool_wire_omits_internal_output_schema'
uv run mypy src

关键断言:

  • test_responses_adapter_builds_wire_payload_and_maps_typed_events
  • test_function_tool_spec_serializes_responses_wire_shape
  • test_function_tool_wire_omits_internal_output_schema

官方源码导航

Mini Codex 对照

  • src/mini_codex/models/client.py:Provider 中立 Protocol
  • src/mini_codex/models/responses.py:真实 payload 和 HTTP/SSE Adapter
  • src/mini_codex/tools/specs.py:Function Tool 线格式

本节边界

已经证明:Session 只能看见领域 ModelEvent;认证、HTTP status 和 SSE 资源清理必须留在 Transport;只有真实 complete 事件能结束一次成功采样。

阅读导航

上一节:9.5 · 下一节:9.7

评论


← 返回文章列表