T3 Code 非官方中文翻译不是官方产品,也不是 T3 Tools 官方文档。源提交 d2c25950b4d9英文原文GitHub 源文件
Skip to content

编排 V2 ​

这组文档描述下一代编排模型的目标架构。它不是当前实现的修补计划,并且有意不处理迁移和向后兼容。这些事情应当等目标模型自洽之后再处理。

V2 是编排器(orchestrator)的重写,不是整个应用领域平台的重写。现有的非编排领域、持久化与迁移基础设施、WebSocket/RPC 基础设施,以及投影流语义都应当保留,除非 V2 暴露出某个具体的、编排所特有的缺口。

V2 围绕 Codex app-server 探测中观察到的真实提供方(provider)行为来设计,但它并不专属于 Codex。Codex 被当作我们目前最丰富的协议;能力较弱的提供方会被适配进同一模型,使用应用自有的 id 和显式的能力标志。

文档 ​

主要目标 ​

  • 保持提供方(provider)原生生命周期的保真,同时不把提供方 id 泄漏进应用身份。
  • 把根回合(turn)、子智能体(subagent)、工具、审批、计划和检查点(checkpoint)建模为同一张执行图。
  • 让根运行(run)的完成成为唯一能完成用户可见回合的事件(event)。
  • 支持从普通对话线程(thread),以及已完成的子智能体对话线程或提供方对话线程分叉。
  • 支持在各次运行之间更换提供方,并把这作为一等的上下文交接。
  • 通过共享的对话线程谱系和上下文转移原语,为分叉、提供方交接、合并回源(merge-back)和子智能体建立模型。
  • 通过确定性的应用自有 id 分配,支持 id 较弱或缺失的提供方。
  • 让功能行为由能力驱动,而不是由提供方名称驱动。

关键不变量 ​

  1. 应用 id 是主体。提供方(provider)id 是引用。
  2. 提供方事件(event)绝不会被改写成另一个提供方事件的样子。
  3. 子执行的完成绝不会关闭父运行(run)。
  4. 检查点(checkpoint)附着在可以建立检查点的执行范围上。根运行的检查点会推进应用的运行历史;子级与子智能体的检查点是嵌套的,不会推进父运行的计数。
  5. 回滚用应用运行计数来表达,并与提供方的对话状态对齐。
  6. 每条命令(command)都以应用 id 为目标;适配器(adapter)在边界处把它们翻译成提供方引用。
  7. 提供方能力的缺失会被显式表示,并由策略处理。
  8. 切换提供方会创建显式的上下文交接产物;这不是藏起来的提示词技巧。
  9. 分叉首先记录应用级谱系。提供方原生分叉和可移植上下文交接,是一次运行开始时才选定的惰性解析策略。
  10. 测试应当优先采用有回放支撑的集成覆盖,而不是模拟的单元测试。编排测试中,通常唯一的替代是提供方运行时传输。

概念分层 ​

text
Native provider protocol
  -> Rotating raw provider diagnostics
  -> Provider adapter / normalizer
  -> V2 orchestration event store using existing persistence patterns
  -> Runtime execution graph
  -> Conversation projection
  -> UI / API views

原始提供方(provider)诊断保存提供方实际发送或接收的内容,供调试和回放采集使用。持久的应用状态是规范化之后的编排状态:事件(event)与实体、关联引用、投影,以及命令回执。运行时图保存提供方行为的含义。对话投影保存用户看到的内容。

V2 应当接入现有的编排命令(command)、事件与投影基础设施模式,而不是另建一套无关的事件系统。V2 特有的工作是图模型、提供方生命周期语义、适配器(adapter)契约、规范化器、策略和投影。

V2 拥有自己的事件模式。V1 的命令联合类型和事件联合类型已经移除;V1 的行只作为旧版导入器(apps/server/src/orchestration-v2/legacy/)的输入继续存在,项目事件则保留各自的应用事件形态。

最小心智模型 ​

text
AppThread
  Run 1
    root ExecutionNode
      tool ExecutionNode
      approval ExecutionNode
      subagent ExecutionNode
        ProviderThread
          child root ExecutionNode
  Run 2
    root ExecutionNode

应用对话线程(thread)是用户可见的对话。一次运行(run)就是被计数的用户可见回合(turn)。执行节点是该次运行内部的工作树。提供方(provider)对话线程是提供方原生的对话句柄,可以挂到应用对话线程上,也可以挂到嵌套的执行节点上。

切换提供方不会创建新的应用对话线程。它会创建或重新激活提供方对话线程,并把上下文交接摘要附到下一次运行上。

分叉确实会创建新的应用对话线程,但不应在分叉时强制选择提供方。分叉记录对话线程谱系和一个待定的源点。分叉上的第一次运行会解析这个源点:能够使用提供方原生分叉时就使用它,需要时则使用可移植的上下文转移。

由探测得出的要求 ​

Codex app-server 探测表明,有若干协议上的事实是 V2 模型必须保留的:

  • thread/status/changed 可能在完成之前或临近完成时变为空闲,但 turn/completed 才是权威的回合(turn)终结事件(event)。
  • turn/interrupt 会先作为请求完成;被中断的终结状态稍后才通过 turn/completed 到达。
  • 审批请求是提供方(provider)发起的 JSON-RPC 请求,作用域限定在提供方对话线程(thread)、回合和条目上。
  • thread/rollback 在回滚之后返回一份权威的提供方对话线程快照。
  • 子智能体的子级 turn/completed 事件可以发生在父回合或根回合完成之前。
  • 子级提供方回合是真正的提供方回合,不得重新映射到父级提供方回合 id 上。

这些观察正是 V2 把应用运行(run)、提供方回合和执行节点分开的原因。

现有平台边界 ​

只要现有应用基础设施本身不是这一类编排缺陷的来源,V2 就应当复用它:

  • 命令(command)分发应保留现有的串行且幂等的命令处理,以及命令回执模式。
  • 投影流应保留当前 API 所使用的快照加游标语义。
  • 持久化应保留现有的 SQLite、迁移与仓库基础设施,以及投影流语义。
  • 原始提供方(provider)帧应继续作为诊断日志数据,并通过轮转日志做有界保留。
  • 回放测试应当只替换提供方传输与进程这一边界。

V2 可以增加 V2 原生的编排事件(event)、投影表、命令策略和提供方执行服务。除非现有基础设施无法满足某项已经写明的 V2 要求,否则不应增加持久的原始提供方事件数据库,也不应另建一条通用事件总线。

V2 的命令分发会为已接受的命令返回最后一次已提交的已存储事件序列。重复的命令 id 必须返回同一份由回执支撑的结果,并且不得重新执行提供方副作用。这是前端用于重连与恢复游标的 API 边界。

已跟踪的后续事项:持久化效应发件箱 ​

当前应用流水线使用领域事件(event)加上反应器来触发提供方(provider)副作用:

text
command
  -> domain event(s)
  -> projection
  -> reactor observes event
  -> provider side effect
  -> provider output ingestion
  -> domain event(s)

这样做是成立的,但可能变得难以追踪,因为提供方副作用隐含在一条实时订阅里。如果 V2 需要更强的重启、恢复和可调试性,就为编排副作用引入持久的效应发件箱:

text
command
  -> transaction:
       append domain event(s)
       append provider effect request(s)
  -> projection
  -> stream to UI

effect worker
  -> claim effect request
  -> call provider
  -> ingest provider output
  -> append domain event(s)
  -> mark effect request completed/failed

效应请求示例:

ts
type ProviderEffectRequest = {
  id: ProviderEffectRequestId;
  kind:
    | "provider.turn.start"
    | "provider.turn.interrupt"
    | "provider.runtime-request.respond"
    | "provider.thread.rollback"
    | "provider.thread.fork";
  status: "pending" | "running" | "completed" | "failed" | "cancelled";
  threadId: ThreadId;
  runId: RunId | null;
  nodeId: NodeId | null;
  provider: ProviderKind;
  payload: unknown;
  causationEventId: EventId;
  commandId: CommandId | null;
  attempts: number;
  lastError: string | null;
};

这应当视为基础设施上的改进,而不是第一片 V2 的前提。第一版 V2 实现可以使用现有的反应器模式,但应把提供方副作用的边界保持得足够清晰,这样日后迁到发件箱时,不必重新设计执行图或提供方适配器(adapter)。

非官方译文 · 源提交 d2c25950b4d9 · 不代表 T3 Tools