Files
Owner/openspec/changes/add-full-duplex-agent-voice-assistant/proposal.md
T

37 KiB
Raw Blame History

OpenSpec:完整全双工 Agent 语音助手架构

功能目标

完整业务价值

当前 Owner 语音桌宠已经从最初的“唤醒词 -> VAD -> STT -> LLM -> TTS -> 播放”演进到可真实运行的 run-live,并具备本地唤醒、实时字幕、噪音过滤、连续追问、结束提示音和初步播报打断规划。但现有核心仍然是 turn-based 语音桌宠:用户先唤醒,系统听完一段话,识别完再思考,回复播放完再决定是否继续。这个模型能完成普通问答,但距离“小爱同学式持续对话”和完整 Agent 仍有明显差距。

本变更的业务目标是把现有语音桌宠规划升级为完整全双工 Agent 语音助手:麦克风持续输入、播放端持续输出、WebRTC APM 提供 AEC/NS/AGC 音频底座、Streaming STT 实时生成用户话语、LLM 流式回复按句进入 Streaming TTS、用户在 AI 讲话时可以自然打断,长期记忆和 Tool Router 让助手不只聊天,还能在安全边界内检索记忆、执行只读系统任务、调用 Open Interpreter 外部后端和 Playwright 浏览器自动化。

本阶段只产出 OpenSpec 审核材料,不改运行代码、不安装依赖、不下载模型、不接入工具执行、不把本地未跟踪的 openinterpreter/ 复制进 Owner 代码,也不提交该目录。openinterpreter/ 在本变更中只被定义为外部 CLI/子进程工具后端候选。

目标用户场景

  1. 用户在 macOS 上启动语音助手后,不需要按住说话;麦克风常驻监听,系统通过本地唤醒或持续 VAD/STT 管理输入。
  2. 用户听 AI 回复时可以直接插话;系统在 200 ms 目标延迟内停止 TTS 播放、取消当前 LLM/TTS 任务,并把用户新话语作为下一轮输入。
  3. AI 自己的扬声器声音不会被误当成用户输入;WebRTC APM 的 AEC 使用播放 reference 音频消除回声,NS 降低环境噪声,AGC 统一麦克风音量。
  4. 用户可以连续追问,不必每轮都说“小杰小杰”;Conversation Manager 根据状态机和上下文判断当前是继续听、执行工具、播报结果还是恢复待机。
  5. 助手可长期记住非敏感偏好、事实、项目摘要和任务摘要;重启后仍可通过 FAISS+SQLite 召回相关记忆,但敏感信息默认不自动保存。
  6. 用户要求“整理下载目录”“查一下项目文件”“打开网页抓取内容”时,Tool Router 先做安全分类,低风险只读工具可执行,高风险写操作、删除、上传、交易、权限变更必须要求确认,第一版不自动做 GUI 点击/键盘控制。
  7. 后续桌宠 GUI 可订阅同一事件流展示听、想、说、工具执行、被打断、等待确认等状态,而不是重新实现 pipeline 逻辑。

量化成功指标 KPI

  1. 打断延迟:在可控音频测试环境中,speaking 状态下检测到有效用户语音后,进入 interrupted 并停止播放的 P95 延迟目标小于 200 ms。
  2. 回声抑制:播放 reference 音频注入 AEC 后,纯助手回放不触发用户 VAD/STT,不产生有效 barge_in_detected
  3. 实时识别:用户开始说话后,Streaming STT 首个稳定 partial transcript 的 P95 目标小于 800 ms。
  4. 端到端首音:LLM 首个可播报句子产生后,Streaming TTS 首个可播放 PCM chunk 的 P95 目标小于 1000 ms。
  5. 流式播报:LLM 不等待完整回复;中文回复按句切分进入 TTS,第一句可播放后立即播放。
  6. 取消可靠性:用户打断时,当前 LLM stream、TTS synthesis、playback、工具执行候选任务均收到 cancellation token;被取消内容不得继续写入上下文。
  7. 记忆召回准确性:保存的偏好/事实/项目摘要在相关查询中可被 Top-K 检索召回;关闭记忆时不得读写 SQLite/FAISS。
  8. 工具安全:未授权工具、越权路径、写操作、删除操作、账号/上传/交易类任务必须被拒绝或进入确认流程;自动化测试覆盖非法工具拒绝、防循环、超时、输出截断。
  9. 兼容性:现有 turn-based run-live 语义在迁移期不得被破坏;新全双工入口可分阶段实现,允许先以 feature flag 启用。
  10. OpenSpec 完整性:openspec validate add-full-duplex-agent-voice-assistant --strictopenspec validate --all --strict 必须通过。

预期影响

  1. 主规范 voice-pet-pipeline 将从“Python 桌宠语音 pipeline”升级为“全双工 Agent 语音助手 pipeline”,覆盖音频底座、并发模型、记忆、工具和安全策略。
  2. 后续实现将新增 WebRTC APM provider、全双工音频环形缓冲、Streaming STT/TTS provider、Conversation Manager、MemoryManager、ToolRouter、Open Interpreter adapter、Playwright browser adapter。
  3. 现有 GTCRN 降噪、主说话人音色门控和本地 KWS 保留为 fallback 或局部能力,不再作为完整全双工回声消除的主方案。
  4. 当前进程内临时上下文保留,但长期记忆成为独立层;临时会话历史和长期记忆必须有清楚边界。
  5. 工具执行引入新的安全面,必须规划确认策略、权限边界、路径限制、超时、输出截断、审计日志和敏感信息脱敏。
  6. 第一版电脑控制不做 GUI 点击/键盘/屏幕控制;只预留 ComputerControlProvider。Codex Computer Use 只参考安全确认策略,不复制私有或捆绑实现。

对现有问题的系统性总结

  1. 性能问题:现有 turn-based pipeline 在录音结束后才进入 final STT 和 LLM,用户感知延迟集中爆发;全双工架构要求持续识别和流式回复。
  2. 打断问题:当前打断规划仍围绕播放 chunk 检查或后台监听补丁,缺少全局 cancellation graph;用户插话无法统一取消 LLM、TTS、播放和工具任务。
  3. 回声问题:现有音色门控只能降低误触发,无法从音频底座消除扬声器回放;没有 AEC reference,后续 VAD/STT 容易被 AI 自己声音污染。
  4. 噪音问题:GTCRN 只覆盖正式问题采集阶段;全双工持续监听需要系统级 NS/AGC,避免背景噪声导致持续 STT 和打断误判。
  5. 架构问题:现有 pipeline 仍以“轮次”为主,状态机缺少 tool_runninginterruptedrecovering 等 Agent 必需状态。
  6. 记忆问题:当前上下文只存在于本次进程内;无法记住长期偏好、项目背景和任务摘要,也没有隐私分类和删除策略。
  7. 工具问题:当前 LLM 只能生成自然语言;没有结构化工具协议、工具路由、安全策略、执行预算、防循环机制和工具结果回注。
  8. Open Interpreter 边界问题:本地 openinterpreter/ 是未跟踪外部仓库,不能复制进 Owner;需要把它规划为可选外部 CLI 后端并限制风险。
  9. UI/UX 问题:后续桌宠如果直接绑定 runtime 方法,会继续重复逻辑;需要统一事件总线承载状态、字幕、音频、打断、工具和确认请求。
  10. 安全问题:长期记忆和工具执行都会扩大数据面与操作面;必须默认最小权限、敏感内容不自动保存、高风险操作确认、日志脱敏。

详细需求

功能需求

  1. 系统 SHALL 新增完整全双工 Agent 语音助手架构规划,保留现有 turn-based 能力作为迁移期兼容路径。
  2. 系统 SHALL 规划 ContinuousAudioRuntime,负责麦克风持续输入、扬声器播放 reference、音频环形缓冲、状态机事件和任务取消。
  3. 系统 SHALL 规划 WebRtcAudioProcessingStage,默认 OWNER_AUDIO_APM_PROVIDER=webrtc,开启 AEC、NS、AGC。
  4. 播放链路 SHALL 把 TTS PCM/render audio 提供给 AEC reference,麦克风 capture 音频经过 APM 后再进入 VAD、STT 和打断检测。
  5. 系统 SHALL 规划全双工状态机:idlelisteningthinkingspeakinginterruptedtool_runningrecovering
  6. 任意状态下检测到有效用户说话 SHALL 能进入 interrupted,但 tool_running 的中断语义必须区分可取消工具和不可安全取消工具。
  7. 系统 SHALL 规划 Silero VAD 或等价本地 VAD 作为打断触发的主要人声检测层;打断检测先判断“有人开始说话”,不依赖已识别出完整文本。
  8. 打断目标 SHALL 是在 OWNER_BARGE_IN_TARGET_LATENCY_MS=200 内停止播放和取消当前回复。
  9. 系统 SHALL 规划 Streaming STT provider;开发默认候选为 faster-whisper,产品候选为 SenseVoice,保留现有 sherpa-onnx 兼容 adapter。
  10. Streaming STT SHALL 输出 partial transcript、stable partial transcript 和 final transcript;只有 final transcript 或明确提交的 stable transcript 可进入 LLM。
  11. 系统 SHALL 规划 Streaming TTS provider;目标候选为 CosyVoice,支持句子级和流式 PCM 输出。
  12. LLM SHALL 流式输出 tokenSentence Segmenter SHALL 在检测到完整中文/英文句子或安全停顿时,把文本片段送入 TTS,不等待整段回复完成。
  13. TTS 播放 SHALL 支持中途停止;停止后未完整播出的 assistant 文本不得写入短期上下文或长期记忆。
  14. 系统 SHALL 规划 ConversationManager,负责短期会话历史、长期记忆召回、工具调用闭环和状态推进。
  15. 系统 SHALL 规划 MemoryManager,默认 OWNER_MEMORY_PROVIDER=faiss_sqliteSQLite 存文本和元数据,FAISS 存向量索引。
  16. 记忆类型 SHALL 至少包含 preferencefactprojecttask_summary
  17. 每轮用户输入进入 LLM 前 SHALL 根据当前用户文本、会话摘要和任务上下文检索 Top-K 长期记忆,并以明确的 memory context 注入 LLM。
  18. 敏感内容 SHALL 默认不自动保存;记忆写入必须经过分类器、安全策略或用户明确指令。
  19. 系统 SHALL 规划 ToolRouter 和结构化工具调用协议,工具请求包含 name、arguments、risk_level、requires_confirmation、timeout_ms、budget、cancellation_policy。
  20. 第一版工具 SHALL 以安全工具为主:memory.searchmemory.saveshell.readonlyopeninterpreter.runbrowser.playwright
  21. shell.readonly SHALL 限制为只读命令和允许目录,禁止删除、写文件、修改权限、网络上传、安装依赖等高风险行为。
  22. openinterpreter.run SHALL 作为外部 CLI/子进程 adapter,默认只允许低风险、受限目录、超时和输出截断任务。
  23. browser.playwright SHALL 作为浏览器自动化 adapter,默认只允许可审计、非支付、非账号敏感的浏览和提取流程。
  24. 第一版 SHALL NOT 实现 GUI 点击/键盘/屏幕控制;只在规范中预留 ComputerControlProvider,后续可基于 macOS Accessibility、Playwright、trycua 等公共能力实现。
  25. Codex Computer Use 能力 SHALL 仅作为安全确认策略参考,不复制私有实现、捆绑脚本或内部协议。
  26. 系统 SHALL 规划工具结果回注:工具输出进入 Tool Result Message,经过脱敏和长度限制后返回 LLM;工具失败进入可恢复错误路径。
  27. 系统 SHALL 规划防循环策略:单轮最大工具调用次数、最大总耗时、最大输出字节、重复工具调用检测。
  28. 系统 SHALL 规划安全确认策略:写文件、删除、上传、交易、账号、权限、联网提交、安装依赖、执行任意代码等高风险操作必须确认,第一版默认拒绝自动执行。
  29. 系统 SHALL 规划桌宠/终端共用事件总线,事件覆盖 audio、vad、stt、llm、tts、playback、memory、tool、confirmation、interruption、recovery。
  30. 本阶段 SHALL 只创建 OpenSpec 文档;不得创建或修改 src/tests/scripts/pyproject.toml.env、模型文件、音频资产或运行时代码。

非功能需求

  1. 性能优化:全双工音频处理必须以固定帧长和环形缓冲为基础,避免 Python 线程阻塞导致播放卡顿或输入积压。
  2. 延迟目标:VAD frame interval 默认不超过 20 ms;播放停止 chunk 默认不超过 30 ms;整体打断目标小于 200 ms。
  3. 稳定性:所有 provider 必须支持超时、取消、关闭和资源释放;异常必须进入 recovering,不得让后台线程泄漏。
  4. 可扩展性:APM、VAD、STT、TTS、LLM、Memory、Tool adapter 都必须是可替换 provider,不能把具体模型硬编码进状态机。
  5. UI/UX:终端和未来 GUI 不直接调用 provider,只消费事件;显示文案、字幕、工具确认和桌宠动画均由事件驱动。
  6. 安全:API key、Authorization header、原始音频、声纹特征、工具敏感输出不得写入日志或长期记忆。
  7. 隐私:默认不保存原始麦克风音频;长期记忆只保存文本摘要和必要元数据;用户必须能关闭记忆。
  8. 兼容性:现有 .env 中 LLM 配置继续可用;新增配置必须有默认值和迁移说明。
  9. 可测试性:每个并发 stage 必须可用 fake provider 和虚拟时钟测试;端到端模拟不依赖真实麦克风、扬声器或外部工具。
  10. 可观测性:事件必须携带 turn/session id、stage、时间戳、latency、error code 和脱敏 payload,便于定位卡顿、误触发和工具风险。

边缘案例

  1. APM 初始化失败:系统必须报告 AUDIO_APM_UNAVAILABLE,可按配置降级到现有 GTCRN/音色门控 fallback 或拒绝进入全双工模式。
  2. AEC reference 丢失:播放中没有 reference 音频时,系统必须降低打断置信度或临时禁用高风险 barge-in,避免 AI 自己声音触发。
  3. 麦克风权限缺失:启动失败并提示设备检查,不进入假监听状态。
  4. 用户在 AI 说第一个字前打断:取消 LLM/TTS stream,未播报文本不写入上下文。
  5. 用户在工具执行中打断:可取消工具立即取消;不可安全取消工具进入“正在收尾/等待结果”状态,并向用户播报或显示限制。
  6. LLM 已发起工具调用但用户打断:未执行工具调用应取消;已执行且低风险的只读工具结果可丢弃或标记为 stale。
  7. 记忆库损坏:FAISS 或 SQLite 不可用时,系统可禁用长期记忆并继续基本语音对话,但必须报告结构化错误。
  8. 记忆召回命中敏感内容:默认不注入 LLM,除非用户明确要求并通过安全策略。
  9. Open Interpreter 路径缺失:openinterpreter.run adapter 标记不可用,不影响其他工具。
  10. 工具输出过长:按配置截断并附 truncated=true 元数据,不把完整大输出塞进 LLM。
  11. Playwright 未安装或浏览器不可用:工具返回可恢复错误,不影响语音主循环。
  12. TTS 输出卡顿:播放队列应能反压 TTS 合成;卡顿事件必须可观测,不能阻塞麦克风监听线程。
  13. Streaming STT partial 抖动:只显示稳定 partialfinal transcript 才进入对话。
  14. 网络 LLM 慢或断线:取消和超时必须生效;恢复后回到 listening 或 idle。
  15. 多人同时说话:第一版不承诺身份鉴权,只要求 AEC 后的人声触发和用户体验合理;多人区分列为需人工澄清。

输入输出规格

新增或规划配置:

  1. OWNER_ASSISTANT_MODE=full_duplex_agent
  2. OWNER_AUDIO_APM_PROVIDER=webrtc
  3. OWNER_AUDIO_AEC_ENABLED=1
  4. OWNER_AUDIO_NS_ENABLED=1
  5. OWNER_AUDIO_AGC_ENABLED=1
  6. OWNER_AUDIO_FRAME_MS=20
  7. OWNER_AUDIO_RING_BUFFER_MS=3000
  8. OWNER_VAD_PROVIDER=silero
  9. OWNER_INTERRUPT_ENABLED=1
  10. OWNER_INTERRUPT_TARGET_LATENCY_MS=200
  11. OWNER_STREAMING_STT_PROVIDER=faster_whisper
  12. OWNER_STREAMING_STT_PRODUCT_CANDIDATE=sensevoice
  13. OWNER_STREAMING_TTS_PROVIDER=cosyvoice
  14. OWNER_LLM_STREAMING_ENABLED=1
  15. OWNER_MEMORY_ENABLED=1
  16. OWNER_MEMORY_PROVIDER=faiss_sqlite
  17. OWNER_MEMORY_TOP_K=5
  18. OWNER_MEMORY_AUTO_SAVE_SENSITIVE=0
  19. OWNER_TOOL_ROUTER_ENABLED=1
  20. OWNER_TOOL_MAX_CALLS_PER_TURN=5
  21. OWNER_TOOL_TIMEOUT_MS=30000
  22. OWNER_OPENINTERPRETER_ENABLED=0
  23. OWNER_OPENINTERPRETER_COMMAND=openinterpreter
  24. OWNER_BROWSER_PLAYWRIGHT_ENABLED=0
  25. OWNER_COMPUTER_CONTROL_ENABLED=0

核心事件输出:

  1. audio_capture_started
  2. audio_apm_started
  3. listening_started
  4. speech_started
  5. stt_partial
  6. stt_final
  7. llm_stream_started
  8. llm_sentence_ready
  9. tts_chunk_ready
  10. playback_started
  11. interrupt_detected
  12. playback_cancelled
  13. llm_cancelled
  14. memory_retrieved
  15. tool_call_requested
  16. tool_confirmation_required
  17. tool_call_started
  18. tool_call_finished
  19. tool_call_rejected
  20. session_recovered

数据验证规则

  1. Provider 配置必须在启动前校验,不允许未知 provider 静默回退。
  2. APM 输出帧采样率、声道数、帧长必须与 VAD/STT 输入一致;不一致必须显式 resample 或报错。
  3. Streaming STT partial 不得进入长期记忆;final transcript 必须经过空文本、重复文本、敏感内容和最小置信度检查。
  4. TTS 播报文本必须经过现有 TTS sanitizeremoji、表情包和 Markdown 图片不得进入语音。
  5. 长期记忆写入必须包含 type、text、source_turn_id、created_at、sensitivity、embedding_model、checksum。
  6. FAISS index 和 SQLite metadata 必须可一致性检查;缺失或 checksum 不匹配时不得返回伪造记忆。
  7. Tool Router arguments 必须按工具 schema 校验;未知字段、路径越界、命令注入风险必须拒绝。
  8. 工具结果进入 LLM 前必须截断、脱敏,并标注工具名、耗时、退出码和是否截断。
  9. 所有 cancellation token 必须可幂等触发,多次取消不得抛出未处理异常。

需人工澄清

  1. 全双工入口是替换 run-live,还是新增 run-agent-live 并保留 run-live 为稳定 turn-based 入口。
  2. WebRTC APM Python 绑定优先选择哪个包或本地封装,是否允许引入需要系统编译的依赖。
  3. 产品阶段是否确定采用 SenseVoice 和 CosyVoice,还是只在规范中保留候选。
  4. 长期记忆是否需要用户可视化管理、删除、导出和禁用命令。
  5. Open Interpreter CLI 的实际本机命令、工作目录、沙箱策略和是否允许写操作需要人工确认。
  6. Playwright 浏览器工具是否允许使用用户当前 Chrome 登录态,还是只允许独立 browser context。
  7. 多人说话场景是否需要主人声纹注册;本变更默认不做身份鉴权。
  8. 桌宠 GUI 和电脑控制是否必须同期开工;本变更建议第一版先做音频全双工、记忆和安全工具。

设计方案

文字版全新架构图

Microphone
  -> Capture Ring Buffer
  -> WebRtcAudioProcessingStage(AEC + NS + AGC, render reference from Speaker)
  -> SileroVadStage
      -> InterruptDetector
      -> StreamingSttStage(faster-whisper dev / SenseVoice candidate / sherpa fallback)
  -> ConversationManager
      -> ShortTermSessionContext
      -> MemoryManager(SQLite metadata + FAISS vectors)
      -> LlmStage(streaming OpenAI-compatible provider)
      -> ToolRouter(memory.search/save, shell.readonly, openinterpreter.run, browser.playwright)
  -> ResponseStream
  -> SentenceSegmenter
  -> StreamingTtsStage(CosyVoice candidate / local fallback)
  -> Playback Ring Buffer
  -> Speaker
  -> Render Reference back to WebRtcAudioProcessingStage

数据流

  1. ContinuousAudioRuntime 启动后初始化 capture device、playback device、APM、VAD、STT、TTS、LLM、Memory、ToolRouter。
  2. 麦克风音频以固定 20 ms 帧写入 capture ring buffer;播放 PCM 以相同时间轴写入 render reference buffer。
  3. WebRtcAudioProcessingStage 使用 render reference 对 capture frame 执行 AEC,再执行 NS/AGC。
  4. APM 后音频同时进入 VAD、Streaming STT 和 Interrupt Detector。
  5. listening 状态下,VAD/STT 产生用户输入;final transcript 进入 Conversation Manager。
  6. Conversation Manager 召回短期上下文和长期记忆,生成 LLM streaming request。
  7. LLM delta 进入 Sentence Segmenter;完整句子进入 Streaming TTSTTS chunk 立即入 playback queue。
  8. 播放开始后,播放 PCM 仍持续进入 render reference,使 AEC 能抑制助手回声。
  9. 如果用户讲话,Interrupt Detector 发出 interrupt_detectedCancellation Graph 同时取消 LLM stream、未完成 TTS、播放队列和可取消工具。
  10. Tool Router 在 LLM 请求工具时执行 schema 校验、安全分类、确认策略、执行、结果脱敏和回注。
  11. 任何 stage 失败进入 recovering,释放后台任务和音频资源后回到 listeningidle

接口定义

AudioFrame:
  samples: float32 PCM
  sample_rate: int
  channels: int
  timestamp_monotonic_ms: int
  frame_id: str
WebRtcAudioProcessingStage.process_capture(frame: AudioFrame) -> AudioFrame
WebRtcAudioProcessingStage.process_render(frame: AudioFrame) -> None
WebRtcAudioProcessingStage.reset_stream() -> None
Errors:
  AUDIO_APM_UNAVAILABLE
  AUDIO_APM_FORMAT_MISMATCH
  AUDIO_APM_PROCESS_FAILED
StreamingSttProvider.start_session(session_id: str) -> StreamingSttSession
StreamingSttSession.accept_audio(frame: AudioFrame) -> list[TranscriptEvent]
StreamingSttSession.finish() -> TranscriptFinal
StreamingSttSession.cancel(reason: str) -> None
StreamingTtsProvider.start_stream(voice: str, sample_rate: int) -> StreamingTtsSession
StreamingTtsSession.accept_text(text: str) -> list[AudioFrame]
StreamingTtsSession.flush() -> list[AudioFrame]
StreamingTtsSession.cancel(reason: str) -> None
MemoryManager.search(query: str, *, top_k: int, filters: dict) -> list[MemoryRecord]
MemoryManager.save(record: MemoryRecordInput) -> MemoryRecord
MemoryManager.delete(memory_id: str) -> None
MemoryManager.health_check() -> MemoryHealth
ToolRouter.route(call: ToolCallRequest, context: ToolContext) -> ToolDecision
ToolRouter.execute(decision: ToolDecision, cancellation: CancellationToken) -> ToolResult
ToolDecision:
  action: execute | reject | require_confirmation
  risk_level: low | medium | high | forbidden
  reason: str

状态机

idle
  -> listening
listening
  -> thinking        on final user transcript
  -> interrupted     on explicit cancel or new speech over assistant residue
  -> recovering      on audio/STT failure
thinking
  -> speaking        on first playable TTS chunk
  -> tool_running    on approved tool call
  -> interrupted     on user speech
  -> recovering      on LLM failure
speaking
  -> listening       on reply finished and no follow-up/tool pending
  -> interrupted     on valid user speech
  -> recovering      on TTS/playback failure
tool_running
  -> thinking        on tool result returned to LLM
  -> interrupted     on cancellable tool interrupted
  -> recovering      on tool failure
interrupted
  -> listening       after current tasks cancelled and buffered user audio retained
recovering
  -> listening       after cleanup when runtime can continue
  -> idle            when required provider unavailable

关键算法

  1. AEC reference 对齐:播放 PCM 写入 render buffer 时记录 monotonic timestampcapture frame 处理前取最近 reference window,丢帧或漂移超过阈值时发 audio_reference_drift
  2. 打断检测:APM 后音频先经 VAD 判断人声起点;连续人声超过最小阈值后结合 STT stable partial 或能量/频谱置信度触发 interrupt;纯 render echo 在 AEC 后应低于阈值。
  3. 句子切分:LLM delta 累积到中文句号、问号、感叹号、英文终止标点或最大等待阈值时切句;代码块、URL、数字小数点不得误切。
  4. 取消传播:每轮创建 root cancellation tokenLLM、TTS、playback、tool 子任务注册 child token;用户打断时 root token 广播,所有 stage 幂等收尾。
  5. 长期记忆召回:用户 final transcript 生成 embeddingFAISS 取 Top-NSQLite 取 metadata,按类型、敏感度、最近使用、相似度重排后注入 LLM。
  6. 记忆写入:Conversation Manager 在回合结束后提取候选记忆,按敏感分类和用户意图决定是否保存;敏感或不确定默认不保存。
  7. 工具路由:LLM tool call 先过 schema,再做风险分类和权限判断;低风险可执行,高风险进入确认,禁止类直接拒绝。

数据库/状态管理变更

  1. SQLite 表 memoriesidtypetextsummarymetadata_jsonsensitivitysource_turn_idcreated_atupdated_atlast_used_atembedding_idchecksum
  2. SQLite 表 tool_audit_logs:记录工具名、风险、确认状态、耗时、退出码、截断标记和脱敏摘要,不保存密钥。
  3. FAISS index 文件保存 embedding vectorsSQLite 保存 index 版本和 embedding model,启动时做一致性检查。
  4. 短期会话上下文仍在内存中,进程退出丢弃;长期记忆独立存储,可按配置禁用。

UI 组件重构方案

  1. 终端 reporter 只订阅事件,不直接读取 pipeline 内部状态。
  2. 未来桌宠 GUI 使用同一事件流展示 listeningthinkingspeakinginterruptedtool_runningrecovering
  3. 工具确认必须作为事件暴露,终端可先实现文本确认,GUI 后续实现按钮确认。
  4. 实时字幕分为 partial、stable partial、final 三种显示层级,避免把抖动 partial 当作最终用户输入。

依赖影响分析

  1. WebRTC APM:新增依赖风险最高,需确认 Python/macOS 可用绑定或自建 native wrapper。
  2. Silero VAD:新增本地模型依赖,需评估 ONNX Runtime 或 torch 路线。
  3. Faster Whisper:开发体验好,但模型体积和 Metal/CPU 性能需评估。
  4. SenseVoice:中文效果强,产品候选;需确认 license、模型大小、macOS 部署成本。
  5. CosyVoice:TTS 效果强,依赖较重;第一阶段可先保留现有本地 TTS fallback。
  6. FAISSmacOS 安装和 wheel 兼容性需评估;必要时提供 sqlite-only 或 numpy fallback。
  7. Playwright:浏览器自动化依赖和浏览器安装体积需评估;第一版默认关闭。
  8. Open Interpreter:作为外部 CLI 后端,不作为 Owner 包内依赖;路径缺失时 adapter 不可用。

风险与权衡

风险 概率 影响 缓解措施
WebRTC APM Python/macOS 绑定不可用或编译复杂 OpenSpec 中把 provider 抽象出来;先验证 fake APM 和最小 native 方案;保留 GTCRN/音色门控 fallback。
AEC reference 与 capture 时钟不同步 使用 monotonic timestamp、ring buffer drift 监控、reference gap 事件和回声测试 fixture。
全双工并发导致线程泄漏或播放卡顿 所有 stage 必须支持 cancellation token、bounded queue、backpressure 和统一 shutdown。
Streaming TTS 依赖过重导致落地慢 第一阶段先按句分段合成,CosyVoice 作为目标 provider,保留现有 Mac TTS fallback。
Faster Whisper/SenseVoice 模型性能不足 规范要求 provider 可替换,测试记录 partial/final latency,产品候选不在第一阶段强绑定。
长期记忆保存敏感信息 默认敏感不自动保存;记忆写入前分类;用户可关闭;日志和记忆脱敏。
Tool Router 执行危险操作 默认安全工具优先,高风险确认,禁止类拒绝,目录限制,超时,输出截断,审计日志。
Open Interpreter 外部后端越权 默认关闭;只允许受限目录、低风险任务;写操作必须确认或拒绝;不复制外部仓库进 Owner。
Playwright 使用登录态带来账号风险 第一版默认独立 context;涉及账号、支付、购买、提交必须确认或拒绝。
用户期望立即实现完整 GUI 控制 本变更明确第一版不做 GUI 点击/键盘/屏幕控制,只预留公共 provider。
FAISS 与 SQLite 一致性损坏 启动 health check、checksum、index rebuild 任务和 sqlite-only 降级。
LLM 工具循环 单轮最大工具次数、重复调用检测、总耗时预算和可恢复拒绝。
不确定是否继续对话造成体验不稳 Conversation Manager 规则优先,LLM 分类兜底,不确定默认 listening/idle 策略需人工确认。
OpenSpec scope 过大导致实现周期过长 实施计划分阶段:先音频全双工,再流式 STT/TTS,再记忆和工具,再电脑控制。

任务分解

本节定义 proposal 里的主要功能组或里程碑阶段。后续 tasks.md 必须把每组拆成不超过 1 小时的原子任务,并在完成每个大模块后按 Git 提交规范立即提交。

1. OpenSpec 与边界冻结

目标:只产出 add-full-duplex-agent-voice-assistant 文档,不改代码、不装依赖、不下载模型、不提交 openinterpreter/

验收:OpenSpec 变更目录包含 proposal、design、tasks、spec delta;严格校验通过。

2. WebRTC APM 与全双工音频底座规划

目标:定义 capture/render ring buffer、AEC reference、NS、AGC、音频格式、时钟对齐和 fallback 策略。

验收:spec 包含 APM SHALL 要求、fake reference 测试场景和回声不触发 VAD/STT 场景。

3. 全双工状态机、事件总线与取消机制规划

目标:定义 idle/listening/thinking/speaking/interrupted/tool_running/recovering 状态机、事件模型、cancellation graph 和 recovery。

验收:design 包含状态转移表和取消传播;tasks 包含状态机、事件顺序和错误恢复测试。

4. Streaming STT 与低延迟打断规划

目标:定义 Streaming STT provider、Silero VAD、partial/final transcript、打断检测和 200 ms 目标。

验收:spec 包含 partial/final 行为、speaking 中用户说话进入 interrupted、纯回声不打断。

5. Streaming TTS 与响应流规划

目标:定义 LLM token stream、句子切分、Streaming TTS、播放队列、可中断播放和上下文写入边界。

验收:spec 包含不等待完整回复、未播完文本不写上下文、TTS 卡顿可观测。

6. 长期记忆规划

目标:定义 MemoryManager、FAISS+SQLite、记忆类型、召回、写入安全、禁用和删除策略。

验收:spec 包含保存、检索、重启召回、关闭记忆不读写、敏感内容不自动保存。

7. Tool Router 与安全工具规划

目标:定义结构化工具协议、安全路由、工具预算、防循环、工具结果回注,以及 memory.search/saveshell.readonlyopeninterpreter.runbrowser.playwright

验收:spec 包含合法工具执行、非法工具拒绝、确认策略、Open Interpreter 缺失处理和 Playwright 安全边界。

8. Open Interpreter 与电脑控制边界规划

目标:明确 openinterpreter/ 是外部未跟踪后端候选,不复制进 Owner;第一版不做 GUI 控制,只预留 ComputerControlProvider

验收:proposal/design/tasks 均写明边界;git 提交不包含 openinterpreter/

9. 测试、性能、安全和验收规划

目标:定义 fake APM、VAD、STT、TTS、memory、tool、Open Interpreter、Playwright、端到端模拟、性能指标和安全验证。

验收:tasks 中每项有前置条件、优先级、验收标准和测试要点;validation 命令明确。

Spec Deltas

Capabilities

New Capabilities

本变更不创建独立新 capability 文件。原因:用户明确要求 delta 文件路径为 specs/voice-pet-pipeline/spec.md,且全双工音频、长期记忆和 Tool Router 都作为语音助手 pipeline 的能力升级纳入同一现有 capability。

Modified Capabilities

  • voice-pet-pipeline:从 turn-based Python 桌宠语音 pipeline 扩展为完整全双工 Agent 语音助手 pipeline,新增 WebRTC APM、持续监听、Streaming STT/TTS、低延迟打断、长期记忆、Tool Router、Open Interpreter 外部后端适配和安全工具执行要求。

与现有 openspec/specs/voice-pet-pipeline/spec.md 的精确差异

  1. 修改 Local microphone and speaker transport:从本机麦克风/扬声器 transport 扩展为 capture/render reference 双向音频流,播放音频必须提供给 AEC。
  2. 修改 VAD speech endpoint detection:从 turn-based 端点检测扩展为持续 VAD、打断检测和全双工 listening。
  3. 修改 Local STT transcription:从 captured segment final STT 扩展为 Streaming STT partial/stable/final。
  4. 修改 Cloud LLM streaming reply:从流式 LLM 输出扩展为 token-to-sentence-to-TTS response stream,并要求可取消。
  5. 修改 Local TTS synthesis and playback:从整段或分句播放扩展为 Streaming TTS、PCM chunk 播放和中途停止。
  6. 修改 Pipeline state machine:新增 listeningtool_runningrecovering 等全双工 Agent 状态,明确 speaking -> interrupted
  7. 修改 Audio feedback suppression:从播放期间抑制输入扩展为 AEC + VAD + interrupt,允许用户有效打断。
  8. 修改 Conversation context management:保留进程内上下文,同时新增长期记忆召回的边界。
  9. 修改 Security and privacy:新增长期记忆、工具执行、Open Interpreter 和浏览器自动化安全要求。
  10. 修改 Performance targets:新增打断延迟、Streaming STT 首字、TTS 首 chunk、APM 帧处理等指标。
  11. 新增 requirementWebRTC audio processing foundation
  12. 新增 requirementFull-duplex agent state machine
  13. 新增 requirementStreaming STT and realtime transcript
  14. 新增 requirementLow-latency interruption and cancellation
  15. 新增 requirementStreaming response and TTS playback
  16. 新增 requirementLong-term memory with FAISS and SQLite
  17. 新增 requirementTool Router and structured tool execution
  18. 新增 requirementOpen Interpreter external adapter
  19. 新增 requirementBrowser automation tool boundary
  20. 新增 requirementComputer control reservation

推翻重做的理由

  1. turn-based VAD/STT/TTS 无法自然支持“AI 讲话时用户插话”,只能不断添加补丁。
  2. 音色门控不能替代 AEC;没有 render reference 的系统无法稳定区分助手回放和真实用户。
  3. 只靠 final STT 会让用户等待过久;完整助手需要持续识别和实时字幕。
  4. 没有 Tool Router 和 MemoryManager 的语音助手只能聊天,不能完成 Agent 任务。
  5. 工具和记忆如果后补,会难以补齐安全边界;必须在 OpenSpec 阶段先定义。

实施计划

分阶段优先级顺序

  1. 阶段 A:OpenSpec 文档和边界冻结。
  2. 阶段 B:全双工音频底座和 fake APM 测试。
  3. 阶段 C:状态机、事件总线、取消机制和模拟端到端。
  4. 阶段 DStreaming STT、VAD 打断和回声抑制测试。
  5. 阶段 EStreaming LLM/TTS、句子切分和可中断播放。
  6. 阶段 F:长期记忆 FAISS+SQLite、记忆召回和隐私策略。
  7. 阶段 GTool Router、安全工具、Open Interpreter adapter 和 Playwright adapter。
  8. 阶段 H:文档、性能验收、安全审计和迁移收尾。

里程碑估时

里程碑 乐观 最可能 悲观
A OpenSpec 文档 0.5 天 1 天 1.5 天
B 音频底座 2 天 4 天 8 天
C 状态机与取消 2 天 3 天 6 天
D Streaming STT 与打断 3 天 5 天 10 天
E Streaming TTS 3 天 5 天 10 天
F 长期记忆 2 天 4 天 8 天
G Tool Router 3 天 6 天 12 天
H 验收与收尾 2 天 4 天 8 天

总耗时预估:乐观 17.5 天,最可能 32 天,悲观 63.5 天。

数据/状态迁移策略

  1. 现有进程内上下文不迁移为长期记忆,避免未经确认的历史被自动保存。
  2. 新长期记忆库首次启动为空;用户明确要求保存或分类器确认低敏偏好后才写入。
  3. 现有 .env 继续可用,新增变量采用默认值;全双工模式可通过 feature flag 启用。
  4. 现有 turn-based run-live 在迁移期保留,直到全双工验收稳定后再决定是否替换默认入口。
  5. openinterpreter/ 外部仓库不纳入 Owner 迁移;只记录 adapter 配置和安全策略。

Git 提交规范

  1. 每完成一个大模块,必须立即执行构建或相应验证,然后执行 git commit。
  2. 大模块定义为 proposal “任务分解”中的主要功能组或实施计划中的里程碑阶段。
  3. 提交信息必须使用中文,格式为:[模块名]:完成[具体功能描述],包含[关键变更]
  4. 提交前必须保证本模块验证通过,避免任何未提交的中间状态。
  5. 本 OpenSpec-only 阶段完成后提交信息固定为:[全双工Agent架构]:完成完整语音助手OpenSpec计划,包含WebRTC音频底座、长期记忆和Tool Router设计
  6. 本次提交只允许包含 openspec/changes/add-full-duplex-agent-voice-assistant/ 下的规划文档,不得提交 openinterpreter/、模型文件、依赖锁文件、.env 或运行代码。