会话与并发:sessionKey / lane / queue

这一章关注“排队系统”:如果会话隔离做不好,最直接的症状就是串线、乱序、上下文污染。

你需要掌握的要点

  • sessionKey 如何设计才能在群组/频道/私聊之间保持稳定隔离
  • 为什么同一 sessionKey 必须串行(避免并发写会话导致历史竞争)
  • 为什么还需要全局并发上限(保护模型/工具/IO 资源)

双层排队模型:session 保序 + global 限流

你可以把它理解成“局部有序 + 全局门控”:

  • 外层:按 sessionKey 进入 session lane(同会话严格串行)
  • 内层:按全局 lane 进入 global queue(限制同时运行的任务数)

这能同时解决两件事:

  1. 同会话不乱序(避免并发写会话历史)
  2. 全局并发可控(避免模型/工具/IO 资源被打爆)

Lane 状态机:队列、并发、告警与恢复

建议你在实现里显式保留这些机制:

  • maxConcurrent:lane 并发上限(默认 1)
  • warnAfterMs/onWait:排队太久只告警不取消,让上层可观测
  • generation:热重启/异常恢复时递增 generation,防止旧任务 finally 回写污染新状态

clear 与 reset 的边界

  • clear:只取消“未开始”的队列任务,不影响已在执行中的任务
  • resetAllLanes:用于进程内热重启等场景,强制清空 active 并通过 generation 让旧回调失效,然后继续 drain 积压队列

Probe lane:探针失败应静默

认证探针等任务本质是“试错”,失败是预期行为。 对 probe lane 的失败建议静默处理,避免产生误导性错误日志。

与运行注册表的配合:steer/abort 必须可预测

如果你支持在 run 进行中插入 follow-up(steer),建议做到:

  • 只在 streaming 且非 compacting 时接受 queueMessage
  • 清理 active run 时做 handle 匹配,避免竞态误删

相关入口:

入口:

最小可验证行为

  1. 同一 sessionKey 两条消息不会乱序。
  2. 不同会话可以并行执行,且不会互相污染上下文。
  3. 你能从 runId 反查到这次执行对应的会话归属(用于排障与审计)。

失败场景与排障入口

  • 乱序/串线/上下文污染:优先确认 sessionKey 是否稳定且隔离(群聊/频道/私聊不要共用同一个 key),再确认 session lane 是否串行(maxConcurrent=1)。入口:会话管理 / 队列与 Lane
  • 吞吐突然下降:检查 global lane 并发上限是否过低,或某类任务(例如 Cron/Subagent)是否占满并发;用 日志 结合 runId 看排队与执行耗时。
  • 重启后队列“僵死”:确认 reset/generation 机制是否生效,避免旧任务 finally 回写污染新状态(参见 生命周期与运维)。

验收清单(建议做并发压测)

  1. 同一 session 连续发送多条消息,回复顺序稳定。
  2. 两个 session 可以并发执行,且不会互相阻塞到“完全串行”。
  3. reset 后不会出现队列僵死(积压任务能继续 drain)。
  4. 旧 finally 不会污染新状态(generation 机制生效)。

读源码入口(可选)

  • src/agents/pi-embedded-runner/lanes.ts
  • src/process/command-queue.ts
  • src/gateway/server-lanes.ts
  • src/gateway/server-session-key.ts
  • src/config/sessions/session-key.ts