编排 V2¶
本文档集描述了下一代编排模型的目标架构。它不是针对当前实现的补丁计划,并且有意忽略迁移/向后兼容问题。这些问题应在目标模型形成一致后处理。
V2 是编排器的重写,而不是整个应用领域平台的重写。现有的非编排领域、持久化/迁移基础设施、WebSocket/RPC 基础设施以及投影流式语义都应保留,除非 V2 暴露了具体的、与编排相关的缺口。
V2 围绕在 Codex 应用服务器探针中观察到的真实 provider 行为设计,但它并不是 Codex 专属的。Codex 被视为我们当前拥有的最丰富协议;能力较弱的 provider 通过应用自有 ID 和显式能力标志被适配到同一模型中。
文档¶
主要目标¶
- 保持 provider 原生的生命周期保真度,同时不将 provider ID 泄漏到应用身份中。
- 将根 turn、子代理、工具、审批、计划和检查点建模为一个执行图。
- 让根 run 的完成成为唯一结束用户可见回合的事件。
- 支持从普通线程以及已完成的子代理/provider 线程进行 fork。
- 支持在 run 之间切换 provider,并将其作为一等公民的上下文交接。
- 通过共享的线程血缘和上下文交接原语,对 fork、provider 交接、合并回主线程和子代理进行建模。
- 通过确定性的应用自有 ID 分配,支持 ID 较弱或缺失的 provider。
- 让特性行为由能力驱动,而不是由 provider 名称驱动。
关键不变式¶
- 应用 ID 是主身份。Provider ID 是引用。
- Provider 事件永远不会被重写为看起来像另一个 provider 事件。
- 子执行完成永远不会关闭父 run。
- 检查点挂接到可检查点的执行作用域。根 run 检查点推进应用 run 历史;子级/子代理检查点是嵌套的,不会推进父 run 计数。
- 回滚以应用 run 计数表示,并与 provider 会话状态对账。
- 每条命令都以应用 ID 为目标;适配器在边缘将其转换为 provider 引用。
- 缺失的 provider 能力被显式表示,并由策略处理。
- Provider 切换会创建显式的上下文交接产物;它们不是隐藏的提示词 hack。
- Fork 首先记录应用级血缘。Provider 原生 fork 和可移植的上下文交接是 run 启动时选择的惰性解析策略。
- 测试应优先采用基于回放的集成覆盖,而不是 mock 单元测试。在编排测试中,唯一正常的替代物是 provider 运行时传输。
概念分层¶
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 诊断信息保存 provider 实际发送或接收的内容,用于调试和回放捕获。持久的应用状态是标准化后的编排状态:事件/实体、关联引用、投影和命令回执。运行时执行图存储 provider 行为所代表的意义。会话投影存储用户看到的内容。
V2 应当与现有的编排 command/event/projection 基础设施模式集成,而不是创建一个无关的事件系统。V2 的特定工作在于图模型、provider 生命周期语义、适配器契约、normalizer、策略和投影。
V2 拥有自己的事件 schema。V1 的 command/event 联合类型不再存在;V1 行记录仅作为旧版导入器(apps/server/src/orchestration-v2/legacy/)的输入保留,项目事件则保持自己的应用事件形态。
最小心智模型¶
AppThread
Run 1
root ExecutionNode
tool ExecutionNode
approval ExecutionNode
subagent ExecutionNode
ProviderThread
child root ExecutionNode
Run 2
root ExecutionNode
一个 app 线程(AppThread)是用户可见的会话。一个 run 是被计数的用户可见回合。执行节点(ExecutionNode)是 run 内部的工作树。Provider 线程(ProviderThread)是 provider 原生的会话句柄,可以挂接到 app 线程或嵌套的执行节点上。
Provider 切换不会创建新的 app 线程。它们会创建或重新激活 provider 线程,并将上下文交接摘要附加到下一个 run。
Fork 确实会创建新的 app 线程,但不应在 fork 时强制选择 provider。Fork 会记录线程血缘和待定的源点。fork 上的第一个 run 会在可能时通过 provider 原生 fork 解析该源点,在需要时通过可移植的上下文交接解析。
探针推导出的需求¶
Codex 应用服务器探针显示了 V2 模型必须保留的若干协议现实情况:
thread/status/changed可能在完成之前或接近完成时进入 idle,但turn/completed是权威的 turn 终止事件。turn/interrupt先作为请求完成;被打断的终止状态稍后通过turn/completed到达。- 审批请求是 provider 发起的 JSON-RPC 请求,其作用域为 provider 线程、turn 和 item。
thread/rollback在回滚后返回权威的 provider 线程快照。- 子代理子级的
turn/completed事件可能先于父级/根 turn 完成而发生。 - 子级 provider turn 是真实的 provider turn,绝不能重映射到父级 provider turn ID 上。
这些观察结果正是 V2 将应用运行、提供方回合和执行节点分离的原因。
现有平台边界¶
V2 应复用现有的应用基础设施,前提是这些基础设施并非编排缺陷类问题的根源:
- 命令分发应保留现有的序列化/幂等命令处理与命令回执模式。
- 投影流应保留当前 API 所使用的快照加游标(snapshot-plus-cursor)语义。
- 持久化应保留现有的 SQLite/迁移/仓库基础设施以及投影流语义。
- 原始提供方帧应继续作为诊断日志数据,并通过轮转日志实现有界保留。
- 回放测试应仅替换提供方的传输/进程边界。
V2 可以新增 V2 原生的编排事件、投影表、命令策略和提供方执行服务。除非现有基础设施无法满足已记录的 V2 需求,否则不应新增持久化的原始提供方事件数据库或独立的通用事件总线。
V2 命令分发会为已接受的命令返回最后提交的已存储事件序列号。重复的命令 ID 必须返回相同的、由回执支撑的结果,且不得重新运行提供方副作用。这是前端用于重连/恢复游标的 API 级边界。
已跟踪的后续事项:持久化效果发件箱¶
当前应用流水线使用领域事件加反应器来触发提供方副作用:
command
-> domain event(s)
-> projection
-> reactor observes event
-> provider side effect
-> provider output ingestion
-> domain event(s)
这种方式是有效的,但由于提供方副作用隐含在实时订阅中,可能变得难以追踪。如果 V2 需要更强的重启/恢复/可调试能力,应引入用于编排副作用的持久化效果发件箱(durable effect outbox):
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
示例效果请求:
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 分片(slice)的先决条件。首个 V2 实现可以使用现有的反应器模式,但应保持提供方副作用边界足够清晰,以便日后迁移到发件箱时无需重新设计执行图或提供方适配器。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 t3/t3code