Skip to main content
提示词排队让你在 agent 还在跑的时候把后续要求写进去。 排队条上只有四个操作,没有更多。这一页讲它们各自做什么,以及背后的一个设计决定: 队列状态用全量快照而不是增量。它正好能说明扣瓦怎么处理「前端状态」这类问题。

按线程隔离

队列按线程隔离:每个 threadId 一条 FIFO,串行链也是每线程一条。 不同线程的 turn 并行执行、互不阻塞。
这条隔离解决的是早期最难缠的一类问题:上一轮没结束时到达的新 prompt 如果直接 打到 agent.prompt(),会撞上 Agent is already processing 守卫。现在它进入该线程的 FIFO,由该线程的串行链依次执行。 所以「排队」不是全局的一个盒子,而是每个会话各自的一条队—— 你在会话 A 排队不会挡住会话 B。

四个操作

编辑、暂停/恢复、失败熔断在重构中被有意移除,不是遗漏:
  • 没有暂停:派发中的项已从快照消失,无锁定态可言;上一轮流收尾后链节自动取队首开跑
  • 没有熔断:失败项随流终结回填成气泡,要不要重发由你决定,而不是让一个自动闸 替你决定后面几条也一起停

早期的问题在哪

队列状态没有持久化、事实源分散、前端对消息数组做多路手术。 刷新重启后排队消息失踪变成 ghost;消息数组要摘除/回填/恢复/抑制四条路径, 还有恢复竞态、对账重复追加、顺序错乱;任意旁路流结束把会话状态打回 ready, 按钮闪烁。
队列重写为纯状态机 QueueEngine——队列、自增 id、派发收口在一个无 I/O 的类里。 每次变更向会话转录追加一行全量快照,重启、切线程、树导航之后从最后一条恢复; 同时向线程广播全量快照,前端「最后快照胜出」。
选全量快照而不是增量的理由很朴素:队列通常不超过 5 条,全量最简单、没有对账逻辑, 广播频率也低。

快照怎么走

  • 持久化:每次变更向 session JSONL 追加一行 queue_state 全量快照
  • 恢复:sidecar 重启后经 get_queue_state 回放恢复,不再自动暂停
  • 广播:每次变更经 sendEventChunk 向该线程发 data-queue-state
  • 冲突:前端「最后快照胜出」。线程无活跃请求时快照静默丢弃——空闲态的变更都由 前端自身的 invoke 发起,前端从回复里自更新
「同 id 原地更新多 phase」那种增量 chunk 形态已整体废弃。 入队、位置、派发全部由全量快照承载,没有 per-item 生命周期 chunk。

前端三条同步规则

前端对消息数组的所有操作收敛为三条,每条幂等: 被删掉的旧路径:restore 竞态补偿、cancelled 标记、对账方向分支——全部由 「快照状态 + 三条规则」替代。

稳定 id

条目有持久化的自增 id,跨重启不重复,派发与取消都以 id 寻址。 这不是细节:早期用数组下标寻址,重启后下标错位就会取消错东西。

附件不丢

早期快照只持久化文本,刷新后重建的气泡不带附件。后来修掉了——快照条目现在携带 图片附件(协议形状 { name, mimeType, data | path },与 prompt 帧同形), 刷新/重启恢复、接力泵出队重发与队列条缩略图都不丢图。
这里有个值得记的坑:sidecar 原本按一个前端从不发送的 type: "image" 字段判形状, 实际把图片全部丢弃——所以历史 queue_state 行无一携带附件,而且不报错。 教训是:协议形状要对齐已经在用的那一套,而不是发明一个新字段。

已知边界

「发送后、确认前」刷新:注册表在前端内存里,刷新即清空。此时那一项按快照重建 (现在含图片附件),可接受。

下一步

模式总览

一个应用里五个「模式」,别混。

会话工作区

排队条在对话流里的位置。