# 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 --strict` 与 `openspec 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_running`、`interrupted`、`recovering` 等 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 规划全双工状态机:`idle`、`listening`、`thinking`、`speaking`、`interrupted`、`tool_running`、`recovering`。 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 流式输出 token;Sentence Segmenter SHALL 在检测到完整中文/英文句子或安全停顿时,把文本片段送入 TTS,不等待整段回复完成。 13. TTS 播放 SHALL 支持中途停止;停止后未完整播出的 assistant 文本不得写入短期上下文或长期记忆。 14. 系统 SHALL 规划 `ConversationManager`,负责短期会话历史、长期记忆召回、工具调用闭环和状态推进。 15. 系统 SHALL 规划 `MemoryManager`,默认 `OWNER_MEMORY_PROVIDER=faiss_sqlite`,SQLite 存文本和元数据,FAISS 存向量索引。 16. 记忆类型 SHALL 至少包含 `preference`、`fact`、`project`、`task_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.search`、`memory.save`、`shell.readonly`、`openinterpreter.run`、`browser.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 抖动:只显示稳定 partial;final 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 sanitizer;emoji、表情包和 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 和电脑控制是否必须同期开工;本变更建议第一版先做音频全双工、记忆和安全工具。 ## 设计方案 ### 文字版全新架构图 ```text 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 TTS;TTS chunk 立即入 playback queue。 8. 播放开始后,播放 PCM 仍持续进入 render reference,使 AEC 能抑制助手回声。 9. 如果用户讲话,Interrupt Detector 发出 `interrupt_detected`,Cancellation Graph 同时取消 LLM stream、未完成 TTS、播放队列和可取消工具。 10. Tool Router 在 LLM 请求工具时执行 schema 校验、安全分类、确认策略、执行、结果脱敏和回注。 11. 任何 stage 失败进入 `recovering`,释放后台任务和音频资源后回到 `listening` 或 `idle`。 ### 接口定义 ```text AudioFrame: samples: float32 PCM sample_rate: int channels: int timestamp_monotonic_ms: int frame_id: str ``` ```text 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 ``` ```text StreamingSttProvider.start_session(session_id: str) -> StreamingSttSession StreamingSttSession.accept_audio(frame: AudioFrame) -> list[TranscriptEvent] StreamingSttSession.finish() -> TranscriptFinal StreamingSttSession.cancel(reason: str) -> None ``` ```text 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 ``` ```text 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 ``` ```text 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 ``` ### 状态机 ```text 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 timestamp;capture frame 处理前取最近 reference window,丢帧或漂移超过阈值时发 `audio_reference_drift`。 2. 打断检测:APM 后音频先经 VAD 判断人声起点;连续人声超过最小阈值后结合 STT stable partial 或能量/频谱置信度触发 interrupt;纯 render echo 在 AEC 后应低于阈值。 3. 句子切分:LLM delta 累积到中文句号、问号、感叹号、英文终止标点或最大等待阈值时切句;代码块、URL、数字小数点不得误切。 4. 取消传播:每轮创建 root cancellation token;LLM、TTS、playback、tool 子任务注册 child token;用户打断时 root token 广播,所有 stage 幂等收尾。 5. 长期记忆召回:用户 final transcript 生成 embedding,FAISS 取 Top-N,SQLite 取 metadata,按类型、敏感度、最近使用、相似度重排后注入 LLM。 6. 记忆写入:Conversation Manager 在回合结束后提取候选记忆,按敏感分类和用户意图决定是否保存;敏感或不确定默认不保存。 7. 工具路由:LLM tool call 先过 schema,再做风险分类和权限判断;低风险可执行,高风险进入确认,禁止类直接拒绝。 ### 数据库/状态管理变更 1. SQLite 表 `memories`:`id`、`type`、`text`、`summary`、`metadata_json`、`sensitivity`、`source_turn_id`、`created_at`、`updated_at`、`last_used_at`、`embedding_id`、`checksum`。 2. SQLite 表 `tool_audit_logs`:记录工具名、风险、确认状态、耗时、退出码、截断标记和脱敏摘要,不保存密钥。 3. FAISS index 文件保存 embedding vectors;SQLite 保存 index 版本和 embedding model,启动时做一致性检查。 4. 短期会话上下文仍在内存中,进程退出丢弃;长期记忆独立存储,可按配置禁用。 ### UI 组件重构方案 1. 终端 reporter 只订阅事件,不直接读取 pipeline 内部状态。 2. 未来桌宠 GUI 使用同一事件流展示 `listening`、`thinking`、`speaking`、`interrupted`、`tool_running`、`recovering`。 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. FAISS:macOS 安装和 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/save`、`shell.readonly`、`openinterpreter.run`、`browser.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`:新增 `listening`、`tool_running`、`recovering` 等全双工 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. 新增 requirement:`WebRTC audio processing foundation`。 12. 新增 requirement:`Full-duplex agent state machine`。 13. 新增 requirement:`Streaming STT and realtime transcript`。 14. 新增 requirement:`Low-latency interruption and cancellation`。 15. 新增 requirement:`Streaming response and TTS playback`。 16. 新增 requirement:`Long-term memory with FAISS and SQLite`。 17. 新增 requirement:`Tool Router and structured tool execution`。 18. 新增 requirement:`Open Interpreter external adapter`。 19. 新增 requirement:`Browser automation tool boundary`。 20. 新增 requirement:`Computer 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. 阶段 D:Streaming STT、VAD 打断和回声抑制测试。 5. 阶段 E:Streaming LLM/TTS、句子切分和可中断播放。 6. 阶段 F:长期记忆 FAISS+SQLite、记忆召回和隐私策略。 7. 阶段 G:Tool 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` 或运行代码。