雨天小六

读懂 Codex(3.6):Submission ID、Turn ID、Item ID 与 Call ID 的作用域

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

#Codex#Agent Runtime#生命周期#软件架构

Codex 的 ID 不是一条不断细分的树,也没有统一生成器。Submission、Turn、Item 和 Call 分别解决投递、工作边界、界面对象和请求相关性;某些路径复用同一个字符串,是协议映射,不是类型等价。

作用域矩阵

ID谁通常生成作用域主要相关对象
Submission IDSessionIo / App Server一次提交Submission 与即时 Event
Turn ID创建 Turn 的提交当前 Thread 中一次工作TurnStarted/Complete/Aborted
Item IDRuntime、模型或投影层一个 TurnItem/ResponseItemstarted/delta/completed
Call ID多数由模型工具调用给出一次调用及其 output/waiterTool call、approval、result
Process IDUnifiedExec 分配或调用方提供进程管理器write_stdin、poll、terminate
Submission、Turn、Item、Call 和 Process ID 的相关关系
图 3.6-1:Turn 可以复用创建它的 Submission ID;Item 和 Call 不是 Turn ID 的字符串子路径;后台进程又有独立 process id。

为什么公开 Turn ID 常等于 Submission ID

SessionIo 默认用 UUIDv7 创建 Submission id。对会创建 Turn 的 UserInput,new_turn_with_sub_id 把这同一个 id 存进 TurnContext 的 sub_id;所有带 TurnContext 发出的 Event 随后使用它。App Server 因而可以把“创建 Turn 的请求 id”稳定暴露为 public turn id。

async def submit_user_input(input):
    submission_id = uuid_v7()
    await tx.send(Submission(id=submission_id, op=UserInput(input)))

async def handle_user_input(submission):
    turn = await new_turn_with_sub_id(submission.id)
    assert turn.sub_id == submission.id
    await start_task(turn)

但这不是所有 Submission 都是 Turn。审批答复、刷新、Realtime audio 和 Shutdown 也有 submission id,却不创建普通 Turn。反过来,Mailbox 唤醒等内部 Turn 可以用 UUIDv4 或内部生成 id,不一定来自公开 UserInput。

Item ID 与 ResponseItemId

协议投影的 TurnItem 在缺少上游 id 时用 UUIDv7 生成 item id。模型的 ResponseItem 可能携带 provider 给出的 id;Runtime 还提供 ResponseItemId 强类型来校验、保留或生成特定格式的响应项标识。

Item ID 负责把 started、delta、completed 投影成同一个 UI 对象。它不一定等于工具 Call ID。某些命令项为了方便会直接用 call id 作为 item id,但消费者不能把这个实现习惯推广到所有 Item 类型。

Call ID 是相关键,不是全局主键

函数调用、custom tool、MCP 和 hosted tool 的 ResponseItem 带 call_id。Runtime 用它生成对应 output,并索引审批、动态工具或权限请求的 oneshot waiter。

async def dispatch_tool(item, turn_state):
    call_id = item.call_id       # 通常来自模型响应
    waiter = oneshot()
    turn_state.approvals[call_id] = waiter.sender
    decision = await waiter.receiver
    output = await execute_if_allowed(item, decision)
    return FunctionCallOutput(call_id=call_id, output=output)

Call ID 的正确要求是在它的相关上下文中不冲突并保持 call/output 配对。代码没有宣称它跨所有 Thread、所有 provider 永久全局唯一,因此存储键至少应包含 Thread/Turn 或明确的调用域。

Call ID 贯穿工具调用、审批、执行与输出配对
图 3.6-2:Call ID 的价值是相关性:同一个键贯穿模型请求、审批等待和模型可见 output;它不替代 Turn 或 Process 身份。

常见错误

  1. 用任意 Submission id 查询 Turn:该提交可能只是审批答复。
  2. 假定 Item id 等于 Call id:普通消息和计划项没有这种关系。
  3. 用 Call id 直接终止后台进程:UnifiedExec 续写和终止使用 process id。
  4. 只按 Call id 建全局数据库唯一索引:不同响应域可能产生相同字符串。
  5. 把内部 synthetic Turn 当成客户端提交:它可能没有对应 public request。

评论


← 返回文章列表