Provider 切换与上下文交接¶
Provider 切换是 V2 的一等公民功能。应用线程可以包含来自多个 Provider 的运行,同时保留每个 Provider 的原生对话句柄以及应用的规范对话历史。
本文档描述更广泛的 线程谱系与上下文转移 模型中 Provider 切换的特化。Provider 切换是一种上下文转移,其源应用线程和目标应用线程通常是同一个线程。Fork、merge-back 和子代理使用相同的源点/解析概念,但具有不同的产品生命周期。
推荐方案¶
当切换回之前使用过的 Provider 时,默认恢复该 Provider 之前的 Provider 线程,并注入一份增量摘要,覆盖该 Provider 非活动期间发生的运行。
仅在以下情况下创建带有完整应用线程摘要的全新 Provider 线程:恢复之前的 Provider 线程失败、设置不兼容,或增量交接可能产生较差的上下文。
这提供了最佳默认权衡:
- 保留 Provider 特定先前工作的原生连续性,
- 避免反复对所有内容进行摘要,
- 让每个 Provider 保持自己的工具/历史能力,
- 使跨 Provider 的上下文显式且可审计,
- 在旧的 Provider 上下文过期时保留清晰的回退方案。
示例¶
runs 1-5: Codex, ProviderThread C1
switch to Claude for run 6
-> ContextHandoff H1 covers runs 1-5
-> create ProviderThread L1
-> run 6 uses L1 with H1
runs 6-8: Claude, ProviderThread L1
switch back to Codex for run 9
-> ContextHandoff H2 covers runs 6-8
-> resume ProviderThread C1
-> run 9 uses C1 with H2
ProviderThread C1 并不期望原生包含 Claude 的运行。它包含 Codex 的原生对话,以及总结外部进展的显式交接产物。
为什么不一律创建全新的 Provider 线程?¶
始终创建全新的 Provider 线程更简单,但会丢失有用的 Provider 原生连续性:
- 仅存在于 Provider 线程中的先前隐藏推理/上下文,
- Provider 端的线程元数据,
- 可用的原生工具状态,
- Provider 特定行为的连续性。
它还迫使每次切换都依赖于完整摘要的质量。完整摘要是有用的回退产物,但当之前的 Provider 线程可以安全恢复时,不应将其作为默认方案。
为什么不能在没有摘要的情况下恢复?¶
因为应用线程在 Provider 非活动期间已经继续推进。恢复的 Provider 不会知道其他 Provider 做了什么。这会产生错误的答案、过时的计划和不安全的文件假设。
除非自该 Provider 上次参与以来没有发生任何运行,否则切换回 Provider 需要一份交接摘要。
Provider 线程覆盖¶
Provider 线程具有原生覆盖和交接覆盖。
type ProviderThreadCoverage = {
providerThreadId: ProviderThreadId;
nativeRunOrdinals: number[];
handoffIds: ContextHandoffId[];
};
对于上面的示例:
C1 native runs: 1,2,3,4,5,9
C1 handoffs: H2 covering 6-8
L1 native runs: 6,7,8
L1 handoffs: H1 covering 1-5
应用线程仍然是完整历史的事实来源。
交接产物¶
交接是显式的图数据。在更广泛的模型中,ContextTransfer 记录源、目标、关系类型和解析状态。ContextHandoff 是物化的可移植上下文产物,当转移无法通过 Provider 原生连续性满足时使用。
type ContextHandoff = {
id: ContextHandoffId;
transferId: ContextTransferId;
threadId: ThreadId;
targetRunId: RunId;
fromProviderThreadIds: ProviderThreadId[];
toProviderThreadId: ProviderThreadId;
coveredRunOrdinals: { from: number; to: number };
strategy:
| "delta_since_target_last_seen"
| "full_thread_summary"
| "checkpoint_summary"
| "manual_context";
summaryText: string;
status: "pending" | "ready" | "failed" | "superseded";
createdByProvider: ProviderKind | null;
createdAt: string;
updatedAt: string;
};
交接应链接到消费它的运行。如果重新生成了相同的摘要,旧的交接应被取代,而不是被不可见地修改。
切换流程¶
start run with target provider P
-> find current app thread history
-> find best ProviderThread for P
-> decide handoff strategy
-> generate ContextHandoff if needed
-> ensure ProviderSession for P
-> ensure/resume/create target ProviderThread
-> send provider turn with handoff context + user message
用户消息仍然是实际的运行输入。交接上下文是一个独立的 preamble/system/developer/synthetic-user 上下文,具体取决于 Provider 的能力。
策略选择¶
推荐的策略顺序:
- 无交接:目标 Provider 线程已覆盖当前应用运行。
- 增量交接至现有 Provider 线程:目标 Provider 线程可以恢复,且仅遗漏了一段运行范围。
- 完整线程交接至现有 Provider 线程:目标 Provider 可以恢复,但增量链过于复杂。
- 完整线程交接至新的 Provider 线程:目标 Provider 恢复失败、设置不兼容,或旧上下文已过期。
- 不支持:Provider 无法接受交接上下文,且不存在安全的重建路径。
过期策略¶
应用应能够判断切换回旧的 Provider 线程是否已不再合适。
可能的过期信号:
- 自上次使用以来,非本 Provider 的运行过多,
- 链式交接过多,
- 目标 Provider 模型/运行时模式发生了不兼容的更改,
- Provider 线程恢复失败,
- 之前的 Provider 线程以冲突的方式被回滚或 fork,
- 用户选择了“干净上下文(clean context)”,
- 交接摘要将超出 Provider 的上下文策略。
当判断为过期时,使用 full_thread_summary 创建新的 Provider 线程。
摘要生成来源¶
摘要可以由以下方式生成:
- 正在离开的 Provider,
- 正在进入的 Provider,
- 已配置的摘要 Provider,
- 用于结构化产物的本地确定性摘要器。
摘要生成器不一定是目标提供方。交接会记录 createdByProvider,以便日后检查其质量和来源。
摘要应包含的内容¶
摘要应以应用线程为规范,而不是只记录提供方转录。
包含:
- 用户目标和约束,
- 已做出的决策,
- 已更改的文件,
- 运行过的命令/测试,
- 计划/待办状态,
- 未解决的审批/问题,
- 当前检查点/diff 摘要,
- 已知失败,
- 相关的子代理结果。
不要包含:
- 无关的提供方协议噪音,
- 隐藏的思维链,
- 已被后续运行取代的过时工具输出,
- 当文件摘要足够时的大段 diff。
检查点交互¶
提供方切换不会改变检查点的归属。检查点仍然附加到应用运行。
但交接生成应引用最新的检查点和 diff 摘要,以便下一个提供方获得准确的工作区状态图景。
run N completed
-> checkpoint N captured
switch provider for run N+1
-> handoff includes summary through checkpoint N
回滚交互¶
回滚会使覆盖已回滚运行的交接失效。
rollback to run 5
-> runs 6+ marked rolled_back
-> handoffs covering 6+ marked superseded
-> provider threads whose native/handoff coverage includes 6+ marked divergent or rolled back by capability
如果当前提供方支持提供方回滚,请调用它。否则,在恢复工作时,从保留的应用线程摘要创建新的提供方线程。
Fork 交互¶
从混合提供方的应用线程进行 Fork 时,应记录应用线程的谱系和源点,而无须在 Fork 时选择提供方。提供方上下文会在 fork 上首次运行开始时被延迟解析。
示例:
- 从 Codex 上的运行 5 进行 Fork,fork 的首次运行使用 Codex:如果源引用足够强,则优先使用 Codex 原生 fork。
- 从 Codex 上的运行 5 进行 Fork,fork 的首次运行使用 Claude:在首次调度时延迟构建可移植上下文。
- 从没有提供方原生线程的应用检查点进行 Fork:使用检查点/完整线程上下文创建目标提供方线程。
- 从子代理提供方线程进行 Fork:如果稳定,则直接使用子代理的提供方线程;否则回退到可移植上下文。
数据模型变更¶
提供方切换需要:
Run.providerRun.providerThreadIdRun.contextHandoffIdContextTransferProviderThread.firstRunOrdinalProviderThread.lastRunOrdinalProviderThread.handoffIdsContextHandoff- 用于交接支持的提供方能力
应用不需要为提供方切换单独维护会话线程。它需要将多个提供方线程附加到同一个应用线程上。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 t3/t3code