前台语音与后台任务为什么要做双循环:从委派到结果新鲜度

前台语音与后台任务为什么要做双循环:从委派到结果新鲜度

一个迟到结果如何变成事故

用户对语音 Agent 说:"帮我找上海周末适合孩子的活动。"系统把搜索交给后台,前台没有卡住,还能自然回应:"好,我查一下,你可以继续补充要求。"

两秒后,用户补充:"不要室外的,最好周日下午。"又过几秒,用户接到电话,改口说:"算了,先帮我记一下,晚点看。"

十秒后,第一轮搜索成功完成。结果本身没有任何事实错误:它确实找到了上海周末亲子活动。但语音 Agent 突然打断电话,把室内和室外活动混在一起念了一遍。

从后台任务看,这是一次成功;从用户体验看,这是三次失败:结果对应旧条件,交付时机错误,用户已经撤回了立即播报的意图。

这类问题不能靠"把请求放进线程池""换成消息队列"或"用更强模型"解决。异步执行只是让前台不必等待,真正困难的是后台完成以后,谁有权决定这个结果是否仍属于当前会话、是否仍满足当前目标、是否应该现在出现,以及已经发生的外部副作用怎样处理。

OpenAI 对 GPT-Live 的公开描述确认了一条重要方向:连续交互由前台语音模型维持,搜索、深度推理和更复杂的 Agent 工作可以委派给后台模型;后台运行时,对话仍可以继续。S1 但官方没有公开内部任务字段、状态机或消息协议。本文讨论的是实现类似体验时需要显式解决的工程合同,不是对 GPT-Live 私有架构的猜测。

全文只沿一条判断链展开:后台完成只是一个事件;版本化任务信封必须重新证明结果仍属于当前目标,前台才决定是否以及怎样交付。 后面的双状态机、五道准入门、取消、重试、指标和失败注入,都是这一个任务信封在不同阶段的读取与验收方式,不是六套并列框架。

一、异步只搬走等待,没有解决结果归属

传统串行语音链路通常是:听完用户输入,调用工具,等待工具完成,再生成和播放回答。它的问题很直接------后台任务耗时越长,前台越像断线。

最自然的改造是异步化:前台提交一个任务,立即恢复收音;后台继续搜索、推理或执行工具,完成后再发回结果。OpenAI Responses API 的 Background mode 就提供了这种公开能力:任务可以异步运行,调用方通过 Response 对象轮询状态,也可以取消进行中的后台 Response。S2

但"异步"通常只改变执行位置,没有自动建立两个循环之间的语义关系。很多系统仍保留一个危险假设:

text 复制代码
task completed -> append result to current conversation -> speak

这个箭头同时偷掉了五个判断:

  1. 完成的是否还是当前任务;
  2. 任务目标是否已经被用户修改;
  3. 结果依赖的数据是否仍有效;
  4. 当前是否适合打断用户;
  5. 结果是否已经交付过,或者是否带有不可重复的副作用。

因此,真正的双循环不是"前台一个线程、后台一个线程",而是两个拥有不同目标、时间尺度和完成条件的控制循环。

循环 首要目标 典型时间尺度 主要状态 成功条件
前台交互循环 让当前交流可理解、可中断、可继续 毫秒到秒 当前话题、话权、用户最新意图、播放状态 用户在当下获得合适反馈
后台任务循环 把搜索、推理或业务工作可靠完成 秒到分钟甚至更久 任务身份、输入快照、执行进度、工具副作用、结果 工作按任务合同完成或明确失败

二者必须解耦,否则长任务会冻结前台;二者又必须重新连接,否则后台会带着旧目标自由返回,前台只能被动接收。

二、事故根因:执行时钟与意图时钟持续分叉

后台任务启动时拿到的是某个时刻的目标快照。前台会话却没有停在这个快照上,用户可能继续补充条件、修正实体、取消操作、开启第二个任务,甚至离开当前话题。

所以系统里至少存在两个时钟。

第一个是执行时钟。它描述任务已经排队、运行、调用了哪些工具、生成了哪些中间结果、是否完成。

第二个是意图时钟。它描述用户当前想解决什么问题、哪些约束已经改变、旧任务是否仍有意义、结果应该怎样交付。

如果只看执行时钟,任何 COMPLETED 都会被当成成功。如果只看意图时钟,后台可能因为每次用户插话都被盲目重启,造成浪费、重复副作用和永远无法完成。

可靠系统要做的是把二者显式比较,而不是假设它们自动一致。最小判断可以写成:

text 复制代码
deliverable = completed
           && task_identity_matches
           && task_version_matches
           && freshness_policy_passes
           && side_effect_state_is_known
           && delivery_policy_allows_now

这里的 task_idtask_version 等名称是本文建议的工程字段,不是 OpenAI 内部 Schema。它们的价值在于让"为什么不播报"成为可解释决策,而不是让一个迟到回调直接支配当前会话。

三、全文主模型:版本化任务信封

很多实现只给后台队列传一个 conversation_id,完成后再读取这段会话的最新消息。这个设计看似灵活,实际上把任务目标变成了一个不断移动的指针:后台无法证明自己究竟为哪一版需求工作,前台也无法判断结果对应哪一版输入。

更稳妥的做法是为每次可独立完成的工作建立稳定任务身份,并保存启动时的目标快照。一个最小任务信封可以是:

yaml 复制代码
task_id: task_8f21
task_version: 3
conversation_id: conv_42
parent_event_id: evt_user_190

goal:
  type: family_activity_search
  summary: 上海周日下午室内亲子活动
  snapshot_hash: sha256:...

execution:
  status: running
  created_at: 2026-08-09T10:00:00+08:00
  deadline_at: 2026-08-09T10:00:30+08:00
  cancel_requested_at: null
  side_effect_class: read_only

freshness_policy:
  require_current_task_version: true
  max_result_age_seconds: 60
  dependency_versions:
    location: shanghai
    date_window: 2026-08-09/2026-08-10

delivery_policy:
  mode: wait_for_floor
  interrupt_priority: low
  allow_notification: true

这些字段分成四类职责。

1. 身份字段回答"这是谁的结果"

task_id 在任务整个生命周期中稳定;task_version 在用户修改目标时递增;parent_event_id 把任务和发起它的用户事件连接起来。后台返回时必须携带这些身份,不能只说"搜索完成"。

2. 目标快照回答"它做的到底是哪件事"

摘要方便人读,结构化条件和 hash 用于机器比较。这里不要求把完整对话复制到每个任务,而是固定与任务正确性有关的条件。如果用户把"周末"改成"今天",系统应能指出旧任务哪一项依赖已经失效。

3. 执行字段回答"取消和副作用进行到哪里"

读操作和写操作不能共用同一取消语义。搜索任务取消后,最多浪费计算;付款、发邮件或创建工单可能已经产生外部效果。side_effect_class 至少要区分只读、可幂等写入、可补偿写入和不可逆写入。

4. 新鲜度与交付字段回答"现在还能不能说"

TTL 只是其中一个条件。一个两秒前完成的结果,如果对应旧城市,也已经过期;一个十分钟前完成的航班延误查询,如果数据版本仍有效,可能仍可展示。新鲜度必须由业务依赖和当前目标共同决定。

四、任务信封怎样吸收用户的新输入

后台任务运行时,前台收到的新输入至少要分成五类。分类错误,比后台模型答错更常见。

用户输入 例子 对后台任务的默认动作 是否递增任务版本
状态查询 "查到哪了?" 保持任务,读取进度或给出诚实等待说明
目标修订 "不要室外的" 修改目标;能增量更新则派生新版本,否则取消旧版并重启
明确取消 "不用查了" 发出取消请求,前台立即停止承诺交付 通常是
并行新任务 "顺便查一下停车" 新建独立任务并建立依赖或并行关系 新任务独立计数
话题切换 "先聊别的" 任务可继续、暂停或取消,由交付策略决定 不一定

关键是"先分类,再动作",不能把所有用户补充都追加到同一个 Prompt。

例如"不要室外的"不是新的闲聊消息,而是旧任务的约束修订。如果后台支持增量搜索,可以创建 task_version=2 并复用仍有效的中间结果;如果不支持,就应让版本 1 进入 SUPERSEDED,启动版本 2。无论采用哪条路线,版本 1 的完成事件都不能直接进入前台。

"先聊别的"也不自动等于取消。用户可能仍希望稍后收到结果,只是不想让搜索阻塞当前话题。这时任务继续运行,但交付方式从 speak_when_ready 改为 notify_or_save,比盲目取消更符合意图。

五、完成事件为什么必须进入交付状态机

最容易出错的状态机只有四个状态:PENDING -> RUNNING -> SUCCESS/FAILED。它适合描述执行器,不足以描述对话系统。

双循环至少需要两组状态。

执行状态

text 复制代码
PROPOSED -> ACCEPTED -> RUNNING -> COMPLETED
                      |       |
                      |       +-> FAILED
                      +-> CANCEL_REQUESTED -> CANCELLED
                                      |
                                      +-> CANCEL_UNCONFIRMED

交付状态

text 复制代码
RESULT_RECEIVED -> ELIGIBILITY_CHECK
                     |-> READY
                     |-> STALE
                     |-> SUPERSEDED
                     |-> DUPLICATE
                     |-> REQUIRES_CONFIRMATION

READY -> DELIVERED | DEFERRED | SAVED | DISCARDED

这两个状态机拆开后,一个任务可以执行成功但交付为 STALE;也可以执行失败但前台给出有价值的恢复建议;还可以取消请求未及时传播,但前台已经停止承诺结果,并在后台继续追踪未知副作用。

"完成不等于交付"是整篇文章最重要的边界。完成事件只能触发准入检查,不能直接触发 TTS。

六、怎样读取任务信封:五道准入检查

第一关:身份是否匹配

结果必须绑定稳定 task_id,事件必须绑定唯一 event_id 或等价身份。OpenAI Webhooks 官方文档明确说明:失败会触发指数退避重试,少数情况下可能重复投递同一事件,可以使用 webhook-id 去重。S3

因此,Webhook、消息队列或轮询观察到同一完成状态时,消费端必须幂等。去重键应该防止重复改变业务状态,而不是只防止日志重复打印。

第二关:目标版本是否仍是当前版本

如果用户已经把上海改成杭州,旧搜索即使完成也不能播报。这里不能用"最后完成者获胜",因为耗时更长的旧任务往往最后返回。应以目标版本或单调序号决定谁有资格推动前台。

第三关:依赖和时间是否仍新鲜

新鲜度不是一个统一的 expires_at。天气、库存、价格、路线、权限、用户位置和会话目标都有不同失效规则。任务应该记录它依赖哪些版本;返回时只重查会改变结论的依赖,而不是无条件相信缓存或无条件全部重算。

第四关:副作用状态是否已知

如果后台只是搜索,过期结果可以丢弃。如果后台已经发送邮件、提交订单或控制设备,丢弃回答并不能撤销事实。结果准入前必须知道外部动作是未开始、已提交、已确认、未知还是已补偿。

第五关:现在是否适合交付

内容正确不代表此刻应该打断。用户可能正在说话、接电话、执行高风险确认或已离开会话。前台要根据优先级和当前话权选择:立即打断、等待自然停顿、先给一句摘要、发送通知、存入任务中心,或者静默丢弃。

这五关共同决定结果"可交付",其中任何一关失败,都不应让后台完成事件直接进入播放器。

七、副作用边界之一:取消是一条传播链

OpenAI Background mode 提供对进行中 Response 的取消,并说明重复取消是幂等的。S2 这对模型执行层很有价值,但应用系统不能把它解释成"所有工作都已经撤销"。

一个后台任务可能已经经过:

text 复制代码
前台意图 -> 任务调度 -> 模型推理 -> 工具调用 -> 外部系统 -> 回调 -> 结果交付

用户说"取消"时,至少要分别记录:

  1. 前台是否已经接受取消意图;
  2. 调度器是否停止派发新步骤;
  3. 模型或 Workflow 是否收到取消;
  4. 正在运行的工具是否支持协作停止;
  5. 已产生的副作用是否需要补偿;
  6. 迟到结果是否被隔离;
  7. 用户是否得到诚实确认。

Temporal 的官方文档提供了一个清晰参照:Workflow 取消是可处理、可清理的协作式请求;Activity 只有发送 heartbeat 并设置 heartbeat timeout,才有机会收到取消。Terminate 则是立即停止、不给 Workflow 清理机会的另一种语义。S5

这说明"发出 cancel"与"执行已停止"必须分开。对用户的表达也要分级:

  • 已收到取消请求:可以说"我正在停止";
  • 后台已确认停止且无副作用:可以说"已经取消";
  • 外部动作状态未知:必须说"任务已停止继续处理,但刚才的提交状态还在确认";
  • 副作用已发生:不能说取消成功,只能进入撤销或补偿流程。

对不可逆动作,最可靠的设计往往不是更强取消,而是在提交前增加确认门槛,把后台任务限制在"准备方案",由前台在用户确认后才执行。

八、副作用边界之二:重试必须服从业务幂等

连接断开、Webhook 超时或工具返回 5xx 时,系统很容易选择自动重试。但"没收到成功响应"不等于"对方没有执行"。

RFC 9110 的边界很明确:客户端不应自动重试非幂等请求,除非它知道请求语义本身是幂等的,或者能够确认原请求没有被应用。S6

这对 Agent 工具尤其重要。以下三种调用不能混在同一重试策略中:

操作 示例 建议
只读 搜索、查询天气 可在限次与截止时间内重试
幂等写入 以稳定业务键更新草稿 携带幂等键,服务端返回同一操作结果
非幂等或不可逆 转账、发消息、控制设备 未确认原操作状态前不得盲目重试

一个常见事故是:前台因超时重新提交任务,后台两个版本都成功发送邮件;系统只保留第二个结果,于是界面看起来正常,收件人却收到两封。正确做法不是只在队列层去重,而是把稳定 operation_id 传到真正产生副作用的服务端,并记录外部系统返回的业务标识。

九、准入通过后,前台仍要选择交付方式

语音 Agent 最容易滥用的动作是:后台一完成,就抢到话权读结果。实际上,交付至少有六种方式。

  1. 立即打断:只适合高优先级、强时效、用户明确等待的结果,例如安全告警。
  2. 等待自然停顿:适合普通搜索和推荐,不破坏当前发言。
  3. 先给摘要:结果很长时先说结论,再询问是否展开。
  4. 请求确认:结果会触发高风险动作或目标可能变化时,先确认再执行或播报。
  5. 通知或任务中心:用户已切换话题、离开语音会话或不适合打断时,保存并提醒。
  6. 静默丢弃:任务已取消、被新版本替代、结果重复且没有审计价值时,不进入用户界面。

交付策略应在任务启动或用户修改意图时确定,并允许前台更新。它不能由后台模型根据"我已经做完了"自行决定。

十、怎样观察这条判断链是否真的有效

后台任务成功率很高,仍可能对应糟糕体验。至少需要四组指标。

1. 目标一致性

  • 过期结果注入率:已被修改、取消或替代的结果仍进入前台的比例;
  • 版本命中率:交付结果与当前 task_version 一致的比例;
  • 依赖失效发现率:价格、位置、权限等变化是否在交付前被识别。

2. 取消可靠性

  • 取消请求接受延迟;
  • 调度停止延迟;
  • 工具停止确认延迟;
  • CANCEL_UNCONFIRMED 占比;
  • 取消后副作用发生率。

3. 幂等与重复

  • 重复完成事件率;
  • 去重命中率;
  • 重复副作用率;
  • 同一任务多版本并发完成率。

4. 交互质量

  • 后台完成后到合适交付时机的延迟;
  • 不必要打断率;
  • 用户主动追问进度率;
  • 长结果二次展开率;
  • 用户切换话题后仍被旧结果打断的比例。

这些指标必须能按 conversation_id -> task_id -> task_version -> event_id -> operation_id -> delivery_id 串起来。否则事故复盘只能看到"任务成功"和"用户不满意"两个互不相连的事实。

十一、再用八个失败注入验证状态边界

场景 注入方式 通过条件
用户修订目标 任务运行中修改地点或时间 旧版本结果不得播报,新版本可解释地接管
用户取消 完成前与完成瞬间分别取消 取消与完成竞态有确定决策,不按到达顺序碰运气
重复回调 同一完成事件投递两次 业务状态与副作用只推进一次
回调乱序 版本 2 先完成,版本 1 后完成 版本 1 进入 superseded,不覆盖当前结果
工具超时但已执行 模拟写操作响应丢失 不自动重复副作用,先查询 operation 状态
前台断线 后台运行时关闭语音连接 任务按策略继续或取消,恢复后不会重复交付
结果过期 修改依赖版本或推进时钟 准入门拒绝旧结果,必要时重算或请求确认
用户正在说话 完成事件在长发言中到达 普通结果等待话权,高优先级结果按策略处理

如果系统只测"后台能否完成",它验证的是执行器;只有覆盖目标变化、取消竞态、重复投递、乱序和副作用,才是在验证双循环。

十二、按风险分四步上线,而不是一次造完所有组件

完整 Workflow、事件溯源和补偿事务并不适合所有团队。可以按风险逐层落地。

第一阶段:稳定身份与交付隔离

为每个后台任务分配 task_id 和版本;后台结果只能进入准入检查,不能直接触发 TTS。先把旧版本结果污染率降到零。

第二阶段:取消确认与幂等消费

区分取消请求和取消完成;Webhook、消息队列和轮询统一以事件身份去重。所有重试先按只读、幂等写入和非幂等副作用分类。

第三阶段:依赖感知的新鲜度

记录目标依赖和截止时间。高变化数据在交付前轻量复核,避免只用统一 TTL。

第四阶段:交付策略与多任务

再加入立即打断、等待话权、摘要、通知和任务中心。只有单任务版本化稳定后,才开放多个后台任务并发,否则复杂度会成倍放大。

十三、什么时候应当退回简单串行链路

双循环会增加任务存储、状态机、去重、取消、补偿、观测和交互策略成本。以下场景更适合简单串行链路:

  • 任务通常在一两秒内完成,等待不会破坏体验;
  • 用户必须逐步确认字段,不能在后台自由推进;
  • 操作高风险且不可逆,任何并行都会增加误执行概率;
  • 业务不需要在会话外保存或恢复任务;
  • 团队尚不能可靠处理幂等键、任务版本和副作用审计。

对于这些场景,清晰地说"我需要几秒钟,请稍等",可能比伪装成自然连续交互更可靠。双循环的价值不是让架构更先进,而是在实时交流和长任务确实同时存在时,避免两者互相阻塞或互相污染。

结论:完成属于后台,交付属于当前前台

GPT-Live 的后台委派方向让语音 Agent 可以同时追求自然交流和复杂任务能力。但一旦前台会话继续向前,后台结果就不再天然属于"当前"。

真正可靠的系统不会把 COMPLETED 直接翻译成"现在说出来"。它会先问:这是哪个任务、哪一版目标、依赖是否仍有效、取消是否收敛、副作用是否明确、现在怎样交付最合适。

前台循环负责维护当下的交流与意图,后台循环负责可靠完成工作。二者通过版本化任务身份、协作式取消、结果新鲜度、幂等事件和交付策略连接。只有做到这一步,"后台执行时仍可继续对话"才不是演示层的并发,而是生产级的连续交互。

参考资料

事实边界

  • 已确认:GPT-Live 官方发布页确认连续交互与后台委派方向;OpenAI 公共 API 文档确认 Background mode、取消、轮询/恢复流、Webhook 重试和去重边界;Temporal 与 RFC 提供协作式取消和重试语义的公开参照。
  • 作者工程设计:任务信封、目标版本、双状态机、五道准入门、交付策略、指标和失败注入均为实现类似系统的建议,不代表 OpenAI 私有协议。
  • 未知项:GPT-Live 内部任务消息格式、调度器、取消传播、存储、模型拓扑、结果新鲜度字段和前后台模型间通信机制未公开。
相关推荐
呆呆敲代码的小Y1 小时前
awesome-llm-apps 开源项目详解:100+ AI Agent 与 RAG 模板一键复用
人工智能·ai·开源·ai编程·ai agent·awesome·llm应用
Bzc11456231 小时前
中医辨证调护:慢病视角下的血糖健康养护思路
大数据·人工智能·生活
短视频矩阵源码定制1 小时前
个性化学习机构四大模式深度评测与选型指南
人工智能·学习
我是大卫2 小时前
《窄门》:真正毁掉爱情的,不是不爱,而是“太爱”
人工智能
doubt。2 小时前
大模型prompt工程Zero-Shot与Few-Shot以及json格式
人工智能·深度学习·机器学习·语言模型·json·prompt
Fluxart.ai2 小时前
2026电商AI出图质量验收标准与质检SOP完整教程
大数据·人工智能
全栈技术负责人2 小时前
Ollama + Open WebUI 搭建本地大模型
人工智能·ai·ai编程
海兰2 小时前
【记忆】openclaw之Honcho 记忆系统
人工智能·agent·openclaw
泛普软件2 小时前
工程施工项目管理办公系统能解决哪些现场管理难题与超支问题
大数据·人工智能·软件需求