具体问题与边界
怎样既验证真实 Responses 边界,又让默认 CI 可重复、离线、可诊断?
RecordingModelClient 透明包裹任意 ModelClient;ReplayModelClient 按 request fingerprint 回放完整 ModelEvent;live test 只有两个显式环境变量同时存在才运行。 下面同时给出状态所有者、顺序、伪代码、故障残留和不可外推边界。
状态所有权
| 对象 | 所有者 | 生命周期 | 持久化 |
|---|---|---|---|
| inner ModelClient | RecordingModelClient | 测试/运行配置 | 否 |
| request fingerprint | record/replay adapter | 一条 trace | JSONL |
| recorded ModelEvents | trace file | 跨测试运行 | 是 |
| replay cursor | ReplayModelClient | 一个实例 | 否 |
| live credentials/model | 环境变量/Host | 单测试 | 不写 trace |
正常路径
- fingerprint 对 instructions、完整 history、tool wire specs、step_sequence 和 tool_generation 做 canonical JSON+SHA-256;不包含随机 step_id/turn_id。
- Recording client 把 inner 事件原样 yield 给 Runtime,同时收集;stream 正常结束后追加 fingerprint+events JSONL。
- 事件以明确 type 编码:text_delta、message、tool_call、completed。Replay load 严格解码未知类型。
- Replay 每次调用先比较实际 fingerprint;不匹配立即失败,而不是把旧答案喂给不同 Prompt。
- live test 用真实 HttpxResponsesTransport,只有 OPENAI_API_KEY 与 MINI_CODEX_LIVE_MODEL 都设置时才发请求;默认 pytest 明确显示 skip。
Python 风格伪代码
def fingerprint(request):
canonical = json.dumps({
"instructions": request.instructions,
"history": encode_items(request.history),
"tools": encode_specs(request.tools),
"step_sequence": request.step_sequence,
"tool_generation": request.tool_generation,
}, sort_keys=True, separators=(",", ":"))
return sha256(canonical)
async def recording_stream(request, token):
events = []
async for event in inner.stream(request, token):
events.append(event)
yield event
append_jsonl({"fingerprint": fingerprint(request),
"events": encode_events(events)})
async def replay_stream(request, token):
trace = traces[next_index()]
require(fingerprint(request) == trace.fingerprint)
for event in trace.events:
token.raise_if_cancelled()
yield event
live_enabled = bool(OPENAI_API_KEY and MINI_CODEX_LIVE_MODEL)
伪代码没有复制 Rust 语法;它保留了状态修改、await、取消、外部副作用和结果反馈的实际顺序。
失败、取消与恢复
| 故障点 | 残留/风险 | 处理 |
|---|---|---|
| 不同 Prompt 误用 trace | 测试产生虚假确定性 | fingerprint mismatch fail fast |
| trace 耗尽 | 没有合法模型响应 | RuntimeError |
| 录制中途断流 | 事件可能已展示 | 不写成 completed trace |
| 缺 credentials/model | 不应偷偷联网 | 测试 explicit skip |
| live Provider 行为变化 | 结果文本不稳定 | 只断言协议达到 ModelCompleted,不断言具体措辞 |
不变量
回放只能服务同一规范请求;真实网络测试必须显式 opt-in,默认测试不能依赖密钥、网络或模型文本稳定性。
设计取舍与不能外推的结论
完整事件 trace 比 mock 一个最终字符串更接近 Runtime 边界,但会增加 fixture 维护和敏感数据治理成本。Mini 尚未做 trace 脱敏、版本迁移、压缩或 partial-event 录制。
测试与复现
cd examples/mini-codex
uv run pytest -q -k 'test_model_recording_replays_only_matching_request or test_live_responses_stream_reaches_completed'
uv run mypy src
uv run python benchmarks/runtime_baseline.py
test_model_recording_replays_only_matching_requesttest_live_responses_stream_reaches_completed
官方源码导航
- codex-rs/core/src/client_tests.rs:模型流、取消与 Rollout trace 测试
- codex-rs/core/src/stream_events_utils_tests.rs:typed event fixture 与组装断言
- codex-rs/core/tests/suite:HTTP fixture 驱动的端到端行为测试
- codex-rs/rollout-trace/src:原始/规范事件 trace 与 reducer
Mini Codex 对照
src/mini_codex/models/recording.py:fingerprint、录制和回放src/mini_codex/models/responses.py:真实 HTTP/SSE Clienttests/test_model_recording.py:离线 deterministic replaytests/test_live_responses.py:opt-in live smoke test
本节结论
回放只能服务同一规范请求;真实网络测试必须显式 opt-in,默认测试不能依赖密钥、网络或模型文本稳定性。
评论
登录后即可评论