03 回合协调器与 drain 协调 本节摘要:录入与执行分开后,立刻冒出一个新问题——同一会话并发来了多条输入怎么办?执行引擎要不要排队?中途插进来的「纠正」能不能让模型马上看到?这些协调问题,由 V2 的回合协调器(SessionRunCoordinator)解决。本节讲清这个协调器:它如何合并同一会话的多次唤醒、如何区分两种提示提升语义——「插队(steer,在下一个安全边界插队)」与「排队(queue,直到会话即将空闲才提升)」。还要讲清一个关键不变量:协调是进程内的,不是 durable 的——它协调的是「现在该怎么跑」,而不是「永久状态」。理解这一节,你才理解 V2 如何优雅地处理并发。 一、协调问题从哪来 V2 把录入独立后,录入和执行不再同步。
本节摘要:录入与执行分开后,立刻冒出一个新问题——同一会话并发来了多条输入怎么办?执行引擎要不要排队?中途插进来的「纠正」能不能让模型马上看到?这些协调问题,由 V2 的**回合协调器(SessionRunCoordinator)**解决。本节讲清这个协调器:它如何合并同一会话的多次唤醒、如何区分两种提示提升语义——「插队(steer,在下一个安全边界插队)」与「排队(queue,直到会话即将空闲才提升)」。还要讲清一个关键不变量:协调是进程内的,不是 durable 的——它协调的是「现在该怎么跑」,而不是「永久状态」。理解这一节,你才理解 V2 如何优雅地处理并发。
V2 把录入独立后,录入和执行不再同步。这就带来一个现实场景:用户在模型还在思考时,又输入了一条「等等,换个思路」。这条新输入怎么处理?
这就是协调器要解决的事——在「不破坏回合不变量」的前提下,让并发输入按合理语义生效。
V2 给提示定义了两种「提升(promote)」语义,对应不同的紧迫程度:
| 语义 | 含义 | 何时提升 |
|---|---|---|
| steer(插队,默认) | 在下一个安全边界插队,让模型尽快看到 | 当前 drain 需要续跑时,插到下一回合 |
| queue(排队) | 直到会话即将空闲才提升一个 | 等当前 drain 快结束、会话将 idle 时 |
场景:模型正在跑回合,用户又输入了两条 ├─ 输入A(steer,默认)──► 下一安全边界插队 └─ 输入B(queue) ──► 等会话将空闲时再提升
💡 为什么默认 steer:大多数用户输入是想「马上被看到」的(纠正、补充),steer 让它们尽快生效。queue 是给那些「不急、别打断当前思路」的输入准备的,用得少但关键。
steer 的「下一个安全边界」是个关键概念,它和第 8 章 System Context 的「安全回合边界」是同一个东西的 different 视角。简单说,安全边界是「提升输入与工具结算都完成、但下一次模型调用还没开始」的那个时间点。
一个回合内部: 组装 → 调模型(开始)──► 流式返回 ──► 工具结算 ──► [安全边界] ──► 调模型(下一回合) ▲ steer 输入在这里插队
为什么必须等安全边界?因为如果在模型调用中途插队,会破坏「一次 provider turn 恰好一次模型调用」这个不变量(第 8 章详讲)。在边界处插队,既让 steer 输入尽快生效,又不破坏不变量。
这里要讲清一个容易混淆的词——drain。drain 指的是「把收件箱里的待处理输入排干」这个进程内的协调过程。关键不变量是:
drain 是进程内、临时的协调,不是 durable 实体。它协调「现在该怎么跑」,不会被持久化,进程重启后 drain 状态不保留(但录入记录保留,会重新触发新的 drain)。
这和录入的 durable 形成对照:录入是「永久记下了什么」,drain 是「现在怎么处理它们」。这个区分非常重要——它意味着即使 drain 协调崩了,录入还在,重启后能重新 drain。
回合协调器(SessionRunCoordinator)的核心职责之一是合并同一会话的多次唤醒(coalesce)。场景:会话正在跑,用户连续输入三条,每条都「唤醒」一次会话。协调器不会让三次执行并行打架,而是合并它们:
三次唤醒 ──► 协调器合并 ──► 一次执行(带着合并后的输入)
合并规则大致是:
⚠️ 别误解「合并」:合并不是「丢弃」,而是「同一会话同一时刻只跑一个执行,多余的唤醒被合并进那个执行」。输入一条都不会丢,只是不会并行打架。
最后说清协调器不做什么,避免过度期待:
协调是进程级的,但执行用的服务层(模型、工具、权限、文件系统)却不是进程级的——它们按 Location 缓存。这就是下一节的「Location 作用域」。