雨天小六

读懂 Codex(5.20):Safety Buffering 防止半截敏感输出泄漏

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

#Codex#Agent Runtime#Responses API#流式协议#软件架构

在当前客户端源码中,Safety Buffering 不是一个扣住 Text Delta 的本地缓冲器。真正的安全缓冲由服务端决定;Codex 解析 wire 上的 buffering metadata,补齐更快替代模型并向 UI 发通知,内容事件仍按原顺序通过。

具体问题与启用条件

本节刻意区分服务端安全策略与客户端可见通知,避免把事件名称误写成 Core 内的内容过滤。具体云端判定算法不在本地源码范围内。

条件来源决定字段或状态对本机制的影响
响应 Headerx-codex-safety-buffering-*提供默认 faster model treatment
线事件safety_buffering object产生 SafetyBuffering ResponseEvent
事件字段retry_model 存在/缺失优先 wire 值,否则 Header fallback

协议、类型与状态所有权

状态或协议所有者生命周期关键不变量
缓冲/审核决策Responses 服务端服务端生成期间本地仓库不实现判定
SafetyBufferingTreatmentSSE/WS parser连接/流只保存 faster_model fallback
SafetyBufferingEventCore Session event stream客户端通知不进入 History
Text Delta普通 ResponseEvent 路径Item 暂态不会被本地 Safety event 阻塞

机制调用链如下:

响应 Header/WS metadata
→ treatment_from_headers
→ 每个 wire event 读取 safety_buffering
→ 解析 use_cases/reasons/retry_model
→ retry_model 缺失时补 header faster_model
→ show_buffering_ui=true
→ ResponseEvent::SafetyBuffering
→ EventMsg::SafetyBuffering
→ UI 展示状态
服务端安全缓冲与客户端通知的职责边界
图 5.20-1:本地 parser 同时转发通知和内容,不复制服务端判定。

机制怎样工作

Header 中是否出现 enabled/faster-model 决定能否构造 treatment,但 enabled=false 并不在客户端充当事件 gate;测试明确说明 faster-model fallback 仍可被读取。wire event 如果自带 retry_model,它优先于 Header;只有字段根本未出现时才采用 Header 的 faster model。解析成功后客户端强制 show_buffering_ui=true

SSE 和 WebSocket 都在映射普通内容事件之前提取 Safety Buffering,并把通知放入同一队列。测试序列表明通知与 Text Delta 可以交替出现而不丢内容。这意味着本地职责是“把服务端正在缓冲/审核的事实告诉前端”,不是“自己缓存敏感 token 再放行”。如果服务端已经把某段 Delta 发到线协议,本地这层不会因 SafetyBufferingEvent 将其撤回。

Python 风格伪代码

这段伪代码保留生产实现中会改变结果的状态、分支和异步边界;认证 SDK、遥测字段和 Rust 所有权样板被折叠为明确的领域对象。

def safety_notification(wire: dict, treatment: SafetyTreatment) -> SafetyEvent | None:
    payload = wire.get("safety_buffering")
    if not isinstance(payload, dict):
        return None
    retry_model_was_present = "retry_model" in payload
    event = parse_safety_payload(payload)
    event.show_buffering_ui = True
    if not retry_model_was_present:
        event.faster_model = treatment.faster_model
    return event

async def process_wire_event(wire: dict):
    if metadata_headers := wire.get("headers"):
        update_treatment_from_headers(metadata_headers)
    if notice := safety_notification(wire, current_treatment):
        await response_queue.put(notice)
    if content_event := map_content_event(wire):
        await response_queue.put(content_event)  # 不由客户端扣留

失败、取消与恢复

Safety Buffering 替代模型字段的优先级与降级
图 5.20-2:字段是否出现决定回退,不以 enabled Header 阻断 wire 通知。
故障或边界已发生的状态对上层的结果能否直接重试恢复动作
safety_buffering 不是对象/无法解析内容事件仍可处理不发通知不适用继续流
wire 无 retry_model已有 Header treatment使用 faster_model fallback不适用向 UI 提供替代提示
Header 也无模型通知仍有 use_cases/reasonsfaster_model=None不适用UI 不展示切换项
客户端误把通知当过滤器可能永不 flush 或重复缓冲架构错误把安全执行留给服务端

设计取舍与验证

通知与执行分离使开源客户端不必复制服务端策略,也能给用户可见反馈;代价是本地无法独立证明服务端怎样缓存、何时放行。把通知放在普通事件流中保持顺序,却要求 UI 能处理同一响应中的重复状态更新。

可验证契约证据方式预期结果
Header faster model 作为 fallbackSSE 集成测试Event 包含 Header 模型
wire retry_model 优先parser 单元测试不被 Header 覆盖
通知不丢相邻 Text Delta交替事件序列测试所有通知和两个 Delta 顺序保留

Mini Codex 对照

Mini Codex 若没有服务端安全缓冲合同,不应只复制这个通知类型就宣称“防泄漏”。可以实现 SafetyNotice 透传;若要本地缓冲,必须另行定义威胁模型、释放终态、最大缓存和断流丢弃策略。

本节边界

本节能证明的只有客户端通知链,不能从本地源码推导云端审核算法。5.21 回到可完全验证的控制流:限流快照和重试等待怎样处理。

评论


← 返回文章列表