☰
慢任务快回答:VisionClaw异步结果交付的心跳、延迟降级与关键词验证设计
2026/10/2 14:05:27 网站建设 项目流程

慢任务快回答:VisionClaw异步结果交付的心跳、延迟降级与关键词验证设计

【免费下载链接】VisionClawReal-time AI assistant for Meta Ray-Ban smart glasses -- voice + vision + agentic actions via Gemini Live and OpenClaw项目地址: https://gitcode.com/gh_mirrors/vi/VisionClaw

VisionClaw 是一个专为 Meta Ray-Ban 智能眼镜打造的实时 AI 助手:戴上眼镜,用语音下达任务——查天气、发消息、网购比价,它都能看着你看到的世界直接回答。但真正的难点在于"慢任务":让 AI 帮你浏览购物网站、操作日历,往往要几十秒甚至几分钟。VisionClaw 用一套心跳保活 + 延迟降级 + 关键词验证的异步交付设计,做到"慢任务快回答"——语音永远不被卡住,结果落地也绝不打丢。

痛点:眼镜上的 AI,等不起

在手机 App 里,转圈等待尚可接受;但在眼镜上,用户看不见屏幕,唯一的感知渠道是声音。一次实测观察写在源码注释里:任务超过十几秒没动静,用户就会以为"AI 死机了"直接挂断——而挂断会连带杀掉会话、工具调用和还没到的答案。

所以 VisionClaw 的核心设计目标很明确:语音通道永远不能阻塞在慢任务上,且结果最终必须可靠送达。

整体思路:三条防线

整套机制集中在语音工作进程 agent/main.py 与任务网关 gateway/src/server.ts 中,可以概括为:

防线触发时机作用
⚡ 快速通道结果 10 秒内返回当作普通一问一答,直接播报
🔄 延迟降级超过 10 秒立刻放话"还在做",任务转后台
💓 心跳保活每 30 秒插一句简短进度,证明 AI 还活着
🔍 关键词验证结果落地后确认结果真的被"说出口",没说到就重播
🅿️ 结果停放用户已挂断存进网关,下次通话开场补报

第一道防线:10 秒延迟降级,先答后做

关键常量定义在 agent/main.py#L345-L366:

QUICK_ANSWER_S = 10 # 工具调用可占用"对话回合"的秒数上限

代理核心逻辑 _run_delegated() 的流程是:发起后台任务后只等 10 秒。若结果按时回来,一切照旧;若没回来,立即向模型注入一条指令(而非一句固定台词):"任务需要更长时间,请在自己的话里简短告诉用户你还在处理,不要猜测答案。"

选 10 秒不是拍脑袋:简单任务在干净会话上实测 8~9 秒完成,10 秒恰好让它们"一次干净回答",而不必走"进度提示 + 后续补报"两段式。

网关侧是配套的双速回合(two-speed turns,见 gateway/README.md):请求等待上限(默认 30 秒)一到,立即返回一句确认,最终结果改走 WebSocket 事件推送。超时时返回的不是给人读的句子,而是带[task started]前缀的指令块(gateway/src/server.ts#L51-L55)——源码注释解释了原因:固定台词会被原样念出、像客服播报;让模型用自己的话"垫场",语气才融进当前对话。语音侧则通过GATEWAY_DEFERRAL_PREFIX认出这个前缀,永远不会把指令块当成答案转述给用户。

第二道防线:心跳,让"活着"被听见

任务转后台后,relay 循环 每隔HEARTBEAT_S = 30秒检查一次任务状态。没完成,就请模型补一句极短的进度提示——"几个词以内,融入对话,不要宣布、不要复述请求、不要说缺结果"。

两个细节值得注意:

  • 上限 4 次(MAX_HEARTBEATS = 4):心跳太多会变成唠叨,120 秒后闭嘴,静候结果;
  • 只在通道空闲时插话(_session_busy()检测):模型正在说话或用户正在说话时绝不抢话,避免和打断(barge-in)处理互相竞争——这个竞争正是早期版本里"用户回合无人应答"的元凶。

注释里有一句设计哲学值得所有做实时语音的开发者收藏:"一个工作中的 agent,永远不能和一个死掉的 agent 无法区分。"

第三道防线:关键词验证,确认结果真的被"说"了

结果落地后,模型并不保证一定会把它播报出来(可能忙着处理新对话)。VisionClaw 的答案是关键词验证,实现在 agent/main.py#L275-L294:

  1. 提取特征词:从结果文本中抽取长度 ≥7 的英文单词,过滤掉finished、result、summary等停用词;
  2. 定达标线:需要命中的词数随结果长度缩放(n // 10,至少 1 个)——短结果不要求超过它拥有的词数,否则一行话的结果永远"验证不过";
  3. 只数"交棒之后"的话:这是踩过坑的修正。早期版本把整段通话历史都算数,导致一句"还在等 Amazon 的结果……包装尺寸、价格、评分"因为和结果共享词汇而提前"验证成功",重播机制随即停手,结果实际从没被说;
  4. 轮询 + 重试 + 兜底:每 5 秒检查一次、最多看 45 秒;不达标就等空闲回合后再注入一次更强硬的指令("你还没告诉用户结果,现在就完整说");两轮都不行,进入下一道防线。

最后一里路:结果停放,挂断也不丢答案

如果用户已经挂断、会话都不存在了,_park_result() 会把"任务 + 结果"整体 POST 到网关的/pending-notifications端点;网关 queuePending() 将其持久化(每用户最多保留 20 条,超出丢最旧的),并记一条result_parked追踪事件。

下次用户拨通时,语音进程开场第一件事就是 _drain_pending() 取回积压结果,并注入专门措辞的指令(agent/main.py#L1270-L1329):"你不在的时候任务完成了,下面这段就是你该说的话,先别只顾打招呼。" 随后同样启动一套关键词验证轮询(90 秒,18 × 5s)——因为取回是破坏性的(取走即删),若模型这次仍没说到,宁可重新停放,留给下下次通话。

这条"停放—补报"链路也解释了为什么浏览类任务结束后,屏幕上的实时卡片会多保留 90 秒(BROWSE_KEEPALIVE_S):云端浏览器还停在最终页面,让结果在被说出的同时还能被"看见"。

关键参数速查

常量值位置含义
QUICK_ANSWER_S10sagent/main.py#L350快答预算,超过则延迟降级
HEARTBEAT_S30sagent/main.py#L354心跳间隔
MAX_HEARTBEATS4agent/main.py#L355单次任务心跳上限
DELIVER_WAIT_S8sagent/main.py#L358注入播报前等待空闲回合的上限
BROWSE_KEEPALIVE_S90sagent/main.py#L362浏览结果卡片保留时长
MAX_PENDING20 条gateway/src/notify.ts#L57每用户停放结果上限
QUICK_ANSWER_TIMEOUT_MS30sgateway/README.md网关侧快答预算

想深入阅读?

  • 语音工作进程全部逻辑(心跳、验证、停放):agent/main.py
  • 网关双速回合与确认指令:gateway/src/server.ts
  • 事件推送与结果停放队列:gateway/src/notify.ts
  • 网关整体说明与端点清单:gateway/README.md
  • 应用端(iOS / Android):samples/CameraAccess/ 与 samples/CameraAccessAndroid/

这套"快答 + 降级 + 心跳 + 验证 + 停放"的组合拳,本质上是把异步编程里"最终一致"的思想搬进了口语对话:答案可以迟到,但不能沉默,更不能丢失。如果你也在做实时语音 Agent,这套心跳与验证的取舍思路非常值得借鉴。

【免费下载链接】VisionClawReal-time AI assistant for Meta Ray-Ban smart glasses -- voice + vision + agentic actions via Gemini Live and OpenClaw项目地址: https://gitcode.com/gh_mirrors/vi/VisionClaw

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询