首页 博文 分类 关于
AstroBlog
实时语音

Qwen Audio Agent 项目深度解析

#语音交互 #Agent

项目核心理念:“Agent,始终在场”

项目官网点击前往

传统的 Audio Agent 往往在遇到复杂的工具调用或长耗时任务时,会让整个语音交互卡顿、阻塞。 而 qwen-audio-agent 是一个支持全双工实时语音交互的 Agent 运行时(Real-time Audio Runtime),实现了前台语音对话与后台任务处理的彻底解耦与并行运行。


🌟 1. 项目核心功能特性

  1. 全双工实时语音交互与自然打断

    • 支持实时低延迟语音交流、打断检测(VAD)与热词唤醒(如“你好千问”)。
    • 前台实时响应极快,无需等待后台繁重任务完成即可先给出自然的陪伴式回应。
  2. 非阻塞异步任务委派(spawn_thinking

    • 当遇到检索、分析、写代码、操作电脑等复杂任务时,前台将请求包装为 spawn_thinking 投递给后台 Agent,并立即告知用户:“好的,我开始帮你处理了”。
    • 在任务处理期间,用户可继续与前台聊天、询问其他问题或追问任务进度。
  3. 丰富且可拓展的后台 Agent 生态(原生 ACP 适配)

    • 统一采用 ACP(Agent Communication Protocol) 协议作为后台 Agent 标准接口。
    • 原生或适配支持:OpenCodeOpenClawQoderQwen CodeKimi CodeHermesCodeBuddyCodexClaude Code 等多种 Agent。
    • 后台可开箱调用本地电脑操作(Computer-Use)、MCP 工具链和用户本地工具。
  4. 多端全平台客户端形态

    • 桌面端(Electron Desktop):常驻桌面的透明悬浮球(Orb),支持皮肤涂装(兼容 Awesome Codex Pet 宠物皮肤包)、自动休眠、全局快捷键呼出。
    • Web UI:富文本/多模态交互界面,实时呈现对话与后台任务的时间线。
    • 终端 TUI:为开发者打造的轻量级控制台交互模式。
  5. 轻量级记忆与易失性清单系统

    • 事实与偏好隔离:分层维护 USER.md(用户偏好与称呼)和 MEMORY.md(长效事实记忆),存储于本地 ~/.config/qwaudio/
    • 无感记忆提取:在每次语音会话结束时,由后台服务无感整理本次会话中的明确指令与重要事实,路由写入对应文件。
    • 易失性清单管理(notes:前台内置购物清单、待办事项等轻量工具,无需经过后台大模型即可秒级操作。
  6. 灵活的语音前台接入

    • 云端实时语音 API:默认对接阿里云 DashScope 实时语音(qwen-audio-3.0-realtime-plus/flash)。
    • 本地私有化 speech-to-speech 前台:支持基于本地 VAD + STT + LLM + TTS 的全链路闭环,无需依赖云端 API。

🏗️ 2. 系统架构设计

根据 architecture.zh.md,项目采用了清晰的分层与解耦设计:

 ┌─────────────────────────────────────────────────────────┐
 │               客户端层 (Clients Layer)                   │
 │   Desktop Orb / Web UI / Terminal TUI (无状态、轻量)       │
 └────────────────────────────┬────────────────────────────┘
                              │ WebSocket / HTTP
 ┌────────────────────────────▼────────────────────────────┐
 │         实时 Gateway 核心层 (Realtime Gateway)           │
 │  - WebSocket 全双工音频流管理                             │
 │  - 实时极简工具 (memory, notes, spawn_thinking...)        │
 │  - 串行 FIFO 队列与交付结果插入/重试机制                    │
 └────────────────────────────┬────────────────────────────┘
                              │ spawn_thinking
 ┌────────────────────────────▼────────────────────────────┐
 │       后端 Agent 协调层 (Backend Adapter & ACP)          │
 │  - 持久 Backend Session (qwen-audio-agent:<owner>:backend)│
 │  - 注入 5 大 ACP 操控工具 (session_start/send/status...)│
 └────────────────────────────┬────────────────────────────┘
                              │ stdio / WebSocket / Bridge
 ┌────────────────────────────▼────────────────────────────┐
 │             底座 Agent 运行引擎 (Backends)               │
 │  OpenCode / OpenClaw / Qwen Code / Kimi Code / Codex ... │
 └─────────────────────────────────────────────────────────┘

关键设计原则:

  1. 前后台职责严格分离
    • 实时前端:追求低延迟、极简工具集(仅包含 spawn_thinkingschedule_remindermemorynotesrespond_agent_permission 等)。它不做复杂多步编排,不感知子 Agent 拓扑。
    • 后台 Agent:拥有完整工具箱、代码环境与外部 Session。
  2. 固定 Backend Agent Session
    • ACP 适配器为每个用户维持一个持久的 Backend Session 身份(qwen-audio-agent:<owner>:backend)。
    • 即使开启新的语音对话,后端依然保留上一次的上下文记忆与操作状态,无需频繁重新建立上下文。
  3. 进程所有权隔离(Process Ownership Model)
    • owned:Gateway 自动管理本地子进程(启动与安全退出)。
    • external:如配置远程端口,Gateway 不操作外部进程,仅建立 ACP Bridge 连接。

🎙️ 3. 语音前台:本地 Speech-to-Speech 方案

第 2 节的架构图里,“语音前台”只是 Gateway 内部一个隐形的抽象。实际上 qwen-audio-agent 把”语音如何进出”抽象成了一个可插拔的 Provider:默认走 DashScope 云端端到端模型,也可以切到本地开源的 speech-to-speech(HuggingFace 出品),实现全本地语音闭环。

3.1 接入 STS 后的架构

把语音前台换成本地 speech-to-speech(下文简称 STS)后,整体架构变成:

┌──────────────────────────────────────────────────────────┐
│           客户端层 (Desktop Orb / Web UI / TUI)            │
└───────────────────────────┬──────────────────────────────┘
                            │ WebSocket / HTTP
┌───────────────────────────▼──────────────────────────────┐
│         实时 Gateway 核心层 (Realtime Gateway)             │
│  · 任务编排 / 记忆 / spawn_thinking / FIFO 队列             │
│  · 语音前台 Provider 抽象(协议适配层,换前台不动上层)         │
└──────────────┬────────────────────────────┬───────────────┘
               │                            │
     ┌─────────▼─────────┐        ┌─────────▼─────────────────┐
     │ ① DashScope 云端   │        │ ② speech-to-speech 本地    │
     │ qwen-audio-3.0-    │        │  VAD → STT → LLM → TTS    │
     │ realtime(端到端)   │        │  (级联,推测式打断)         │
     │ 原生全双工          │        │  LLM 可指本地/自建/云端     │
     └─────────┬─────────┘        └─────────┬─────────────────┘
               │                            │
               └────────────┬───────────────┘
                            ▼ spawn_thinking
┌───────────────────────────▼──────────────────────────────┐
│        后端 Agent 协调层 (Backend Adapter & ACP)           │
└───────────────────────────┬──────────────────────────────┘
                            ▼ stdio / WebSocket / Bridge
┌───────────────────────────▼──────────────────────────────┐
│     底座 Agent (OpenCode / OpenClaw / Qwen Code ...)      │
└──────────────────────────────────────────────────────────┘

关键点:接入 STS 后,Gateway 及其上的任务编排、记忆、后台 Agent 协调完全不变,只有语音前台这一层被替换——这正是 Provider 抽象的价值所在。

3.2 VAD → STT → LLM → TTS 的级联队列

STS 内部是一条级联 pipeline,四个组件各自运行在独立线程中,通过队列串联:

麦克风音频流(16kHz PCM)


┌─────────────────────────────────────────┐
│  ① VAD(Silero)                          │
│  语音活动检测 + 端点判定(Smart Turn)        │
└───────────────────┬─────────────────────┘
                    │ 完整 utterance 音频段

┌─────────────────────────────────────────┐
│  ② STT(Paraformer / Whisper / Parakeet) │
│  语音 → 文本转写                            │
└───────────────────┬─────────────────────┘
                    │ 转写文本

┌─────────────────────────────────────────┐
│  ③ LLM(本地 / 自建 / 云端)                │
│  生成回复(流式 token)                      │
└───────────────────┬─────────────────────┘
                    │ 回复文本

┌─────────────────────────────────────────┐
│  ④ TTS(Qwen3-TTS / Kokoro)              │
│  文本 → 合成音频(流式)                     │
└───────────────────┬─────────────────────┘
                    │ 音频流

              回传客户端播放

三个关键机制:

  • 线程 + 队列解耦:每一级是独立线程,下游慢时上游可继续缓存,天然形成流水线并行。
  • Smart Turn 推测式打断:VAD 判定”你说完了”后,STT/LLM 立刻推测式开工;若你在 800ms 内接着说话,已算到一半的结果会在播报前被丢弃,从头重来。
  • 流式贯穿:STT、LLM、TTS 都支持流式输出,尽可能压低首包延迟。

3.3 它是全双工吗?—— 协议层与语义层的分野

“全双工”这个词掩盖了两个不同维度的问题,必须拆开看:

维度问的是什么STS 的表现
传输 / 协议层音频能否同时上下行、能否发取消✅ OpenAI Realtime 协议原生支持
语义 / 交互层能否”边听边说”还保持语义连贯、自然打断⚠️ 用工程手段逼近
  • 协议层:STS 对外是 OpenAI Realtime 兼容 WebSocket,上行(input_audio_buffer.append)与下行(response.audio.delta)走独立通道,response.cancel 能打断,这一层毫无悬念是全双工。
  • 语义层:级联架构的每一级都是一个串行回合,打断意味着把算到一半的结果废弃重来——它的”自然打断”是靠 Smart Turn 的推测执行”赌”出来的,代价是算力浪费 + 可能的”抢话”感 + 被打断的半句被静默丢弃。

对比默认的 DashScope qwen-audio-3.0-realtime:那是端到端语音模型,语音直进直出,中间没有”转写成文字”这一步,打断是模型生成层面的”随时可停”,属于原生全双工

一句话区分:STS 的全双工,是”串行流水线 + 推测执行 + 取消”拼出来的够用但延迟高的方案;端到端模型是全双工模型原生能力。级联无论怎么调,首包延迟至少 = STT 转写完成 + LLM 首 token + TTS 首帧,这条链路省不掉。

3.4 为什么推荐它?—— Provider 与 Runtime 的解耦

关系本质:qwen-audio-agent 是上层 Runtime(网关 / 编排 / 记忆 / 后台协调),speech-to-speech 是下层语音前台的一个开源实现,两者靠 OpenAI Realtime 协议解耦。它在代码里是实打实的——server/src/voice/providers/ga-protocol.mjs 专门为 STS 写了 GA 方言适配器,文件头注释原话:

/**
 * Wire adapter for providers that speak the GA (2025+) dialect of the OpenAI
 * Realtime protocol, e.g. huggingface/speech-to-speech.
 */

即:DashScope 走 openAiCompatibleProtocol(beta 方言),STS 走 gaRealtimeProtocol(GA 方言),两者的协议差异(output_modalities vs modalities 等)全部隔离在适配器内部,网关层无感切换。

推荐它的四个理由(按价值排序):

  1. 数据不出本机——音频与转写内容不经云,是金融 / 客服 / 政务等合规敏感场景的刚需,也是它相对默认云端的唯一不可替代价值。
  2. 零 Key、零调用成本——HF_HUB_OFFLINE=1 还能完全离线跑。
  3. 全链路可替换——STT(Paraformer/Whisper)、LLM(transformers/vLLM/llama.cpp/OpenAI 兼容端点)、TTS(Qwen3-TTS/Kokoro)逐环独立选择,能按延迟和成本裁剪。
  4. 协议标准化带来的解耦红利——它把 VAD/STT/LLM/TTS 打包成一个 OpenAI Realtime 兼容服务,Runtime 无需理解内部级联细节。

一句话总结:STS 是 qwen-audio-agent 的本地私有化语音前台,两者靠 OpenAI Realtime 协议解耦;推荐它本质是给”不想上云”的用户一条数据本地化 + 零成本 + 全链路可换的路,代价是级联架构的延迟和”伪全双工”体验,换不来端到端模型的原生全双工。


3.5 安装方法与要求限制

安装方法(三步):

# 1. 安装(中文 STT 推荐 paraformer)
pip install "speech-to-speech[paraformer]"

# 2. 启动(按硬件选 device)
speech-to-speech serve \
  --stt paraformer \
  --llm_backend transformers \
  --tts qwen3 \
  --device cuda
  • --device 可选 cuda(NVIDIA)、mps(Apple Silicon)或 cpu(无独显)。
  • --llm_backend 除本地 transformers 外,还可通过 chat-completions / responses-api 指向自建或云端的 OpenAI 兼容端点。

第 3 步接入 qwen-audio-agent,只需在 config.env 里切前台:

QWEN_AUDIO_REALTIME_PROVIDER=speech-to-speech
SPEECH_TO_SPEECH_REALTIME_URL=ws://127.0.0.1:8765/v1/realtime

要求与限制

维度说明
硬件GPU 加速仅认 NVIDIA CUDA 与 Apple MLX;Intel 核显 / AMD 只能走 CPU,LLM 成为延迟瓶颈
环境需 Python 3.10 ~ 3.12(3.14 太新,torch 等 wheel 未跟上,安装易报错)
模型体积STT / LLM / TTS 各自拉取模型,动辄数 GB,磁盘与带宽都是成本
中文支持STT 选 paraformer、TTS 选 qwen3 中文较稳;默认的 parakeet-tdtkokoro 等偏英文
延时级联首包 = STT + LLM 首 token + TTS 首帧叠加,CPU 上更明显;LLM 指远程再加网络往返

小结:STS 的”全本地低延迟”高度依赖硬件与模型选型。无独显时,务实的做法是 STT / TTS 本地 + LLM 指向云端或自建服务


🔄 4. 核心数据流转

系统的典型请求流转如下图所示:

用户说话 (Mic input)


实时音频流 ──► Gateway ──► Realtime ASR

            ┌────────────────┴────────────────┐
     [可直接简单回答]                  [需要复杂处理/工具/代码]
            │                                 │
            ▼                                 ▼
   实时 TTS 播报回复                触发 spawn_thinking(objective)

                                              ├─► 实时前端立即回应:“好的,我来帮你做...”


                                       进入 Owner FIFO 队列


                                     推送到固定 ACP Backend Session

                                       (Backend 执行任务)

                                  ┌───────────┴───────────┐
                       [通知进度 session/update]     [任务完成返回结果]
                                  │                       │
                                  ▼                       ▼
                           UI 动画 ("搜索中...")    Presentation (speech + inline)


                                               Gateway 寻找安全插入窗口


                                                 自然语音播报最终结果

交付与双工安全窗口机制:

  • 后端 Agent 产生的最终结果由两部分组成:
    • speech:精炼的语音呈现文本。
    • inline:适合在屏幕上展示的完整 Markdown 格式、代码或链接。
  • 当后台任务完成时,Gateway 不会蛮干打断当前正在进行的对话;它会寻找安全双工插入窗口(即避开用户正在说话或打断的时间),顺畅自然地播放结果。

🔬 5. 深入工程实现

前面几节讲了概念与宏观架构,这一节从源码层面拆解它的工程实现,这才是它”完成度高”的真正原因。

5.1 任务状态机

Work 是交付回执,不是任务镜像(server/src/task/task-manager.mjs

后台任务并非简单的”发起 → 结束”,而是一个精细的状态机:

queued → running ─────────────────────────→ completed
   │        └→ delegated → finalizing ────────┘
   └────────────→ cancelling → cancelled
                            ↘ failed

几个关键设计:

  • 对外只暴露有限的公共字段:用户请求、时间戳、最终结果/错误、通用工具活动、待确认的权限摘要、通知状态。Session ID、子 Agent 状态、执行模式、后端拓扑、权限标识符等一律不对外。前端只消费”处理中 / 已完成”这类公共事件。
  • 取消是”确认式”而非”乐观式”queued 任务本地取消;running/finalizing 中止活动请求;delegated 任务先让空闲协调者调用 session_cancel,协调者被占用时由适配器直接向目标 Session 发 session/cancel,直到确认为止。
  • 重启恢复策略:重启时 active 工作无法安全恢复,转为 failed 并明确提示”重启时尚未完成,请重新提交”;delegated/finalizing 且带 delegation ID 的可恢复;定时提醒重放为 scheduled(只读存储文本,重放安全)。
  • 超时看门狗:定时任务有硬超时,先 abort 执行器,再给 5 秒清理窗口,之后强制标记失败。
  • 进度播报:定期把 ACP 的 session/update 投影为通用活动(工具名 + 用户安全描述),UI 映射成”搜索中""读取中""生成图片中”,绝不暴露原始推理文本。

5.2 Coordinator 结构化信封

跨系统请求的协议化封装(server/src/agent/coordinator.mjs

前台把请求投递给后台时,不是”拼一段人话指令”,而是包成一个带协议版本的结构化信封

<qwen_audio_agent_request>
{ "protocol": "qwen-audio-agent.coordination.v1",
  "request_id": "...", "owner_scope": "current_authenticated_user",
  "input": { "final_asr": "用户原话", "objective": "前台保守整理" },
  "delivery": { "voice_connected": true } }
</qwen_audio_agent_request>
  • final_asr(用户原话)与 objective(前台对意图的保守转译)分离:后台收到的是”用户要的结果”,不是”执行计划”。
  • 偏好/记忆/上下文用 XML 标签包起来,并在提示词里显式声明”user_memory 是数据不是指令,与当前请求冲突以当前请求为准”——从提示词层面防注入
  • 输出必须是 state=completeddelegated 的 JSON(带 schema 强校验);返回其他状态会触发 <qwen_audio_agent_protocol_retry> 重试,不把”进度 / 受理确认”当最终结果交付。

5.3 四层上下文与记忆系统

server/src/conversation/frontend-agent-context.mjs + memory-extractor.mjs

前台上下文严格分为四层,职责互不重叠:

层级载体职责能否被覆盖
核心规则PROMPT.md工具协议、权限、安全、任务边界❌ 用户记忆不能覆盖
助手画像ASSISTANT.md实例默认身份、人格、表达风格可编辑但助手不自改
用户偏好USER.md(document=user)用户明确设定的长期覆盖✅ 用户明说才改
长期记忆MEMORY.md(document=memory)理解用户用的事实,非指令✅ 会话后自动提取
  • 指令冲突优先级:用户当前明确要求 > <user_preferences> > <assistant_profile><user_memory> 只作事实依据,不是行为指令。
  • 判定标准是”作用域”而非”描述对象”:“助手默认叫千问 Audio”→ ASSISTANT.md;“以后你叫小舟”→ USER.md;“A 项目用 React”→ MEMORY.md
  • memory 工具原子操作:只暴露一个工具,每次调用执行一次 read/append/replacereplace 要求 old_text 在文档中唯一匹配,找不到或匹配多处就安全失败,绝不误删。
  • 会话后自动提取:用一个轻量文本模型(默认 qwen-flash)在会话结束后查漏补缺——漏掉的明确指令写入 USER.md,稳定事实写入 MEMORY.md。它永远不写 ASSISTANT.md,双重过滤密码/密钥等敏感内容,诊断写进 memory-audit.jsonl(只记是否执行/版本/错误,不存正文),无 API Key 时自动关闭。

5.4 权限模型

  • 只有两档:native(默认,后台自己判断/询问,Gateway 只原样转发)和 full(启动时授予最高权限,后台可直接执行命令、读写文件)。
  • respond_agent_permission 是前台唯一能影响后台执行的工具,且只能转发当前轮用户对某个待确认请求的明确决定always/reject),不能自己编造同意、不能创建请求。
  • 有意思的是:full 模式对 OpenClaw 明确拒绝启动——因为 OpenClaw 的执行授权受 exec approvalselevated 等配置约束,无法用一个统一开关安全表达。

5.5 健壮性工程细节

  • 降级策略:未配置后台 Agent 时,Gateway 以”仅前台模式”运行,纯语音聊天保持可用;需要后台执行的请求返回明确错误,不创建任务、不猜测结果。
  • 单实例保证gateway.lock 锁保证同一数据目录同时只有一个 Gateway,异常退出留下的锁会在确认原进程结束后自动回收。
  • 结构化日志 + 凭据脱敏:JSON Lines 格式,API Key/Token/密码写前脱敏,默认不记录麦克风音频、用户转写正文、模型回复正文、任务结果;单文件 10 MiB 轮转,保留 5 份。
  • 配置优先级:CLI 参数 > 环境变量 > .env.local > .env > 用户配置文件 > 内置默认值。

🔥 6. 项目技术亮点与创新点

  1. 解决 Voice Agent 工业级痛点 传统语音助手一旦调用外部 Tool (如 Google Search 或 Exec Command),音频流就会卡住,直至 HTTP 返回。Qwen Audio Agent 引入“实时语音+异步 ACP Agent 引擎”双轨并行架构,开创了流式全双工的新交互范式。
  2. 原生 ACP (Agent Communication Protocol) 架构设计 摒弃了写死单一大模型的做法,把后台能力剥离给标准的 ACP 接口,能与开源或商业的主流 Coding/General Agent 实现无缝对接。
  3. 双重呈现 separation(speech + inline 既保证了耳机/音箱听到的语音简洁明了、不繁杂念代码,又保证了电脑/手机屏幕上能看到完整的细节与文档。
  4. 高度重视安全的本地隐私防护
    • 记忆文件只写本地纯 Markdown 格式。
    • PROMPT.md 作为核心安全层,不可被用户个性化偏好动态覆盖,杜绝了提示词注入(Prompt Injection)破坏系统权限的风险。
  5. 极其出色的客户端与悬浮挂挂件设计 桌面版不仅是一个无感悬浮球,更是打通了 Codex Pet 皮肤生态,将 AI 助手实体化为充满趣味的桌面宠物。

📁 7. 源码目录结构速览

项目源码组织非常清晰,采用 ESM (ES Modules) 规范:

  • 📄 README_ZH.md:中文官方主入口指南与更新日志。
  • 📄 docs/architecture.zh.md:核心系统架构规范文档。
  • 📁 server/
    • src/voice/:Realtime Gateway、全双工 WebSocket 服务、DashScope 及 S2S 语音 Provider、语音工具集(realtime-gateway.mjs)。
    • src/agent/:ACP 后端适配器、Session 注册与协调器、各 Backend 驱动(acp-backend-adapter.mjs)。
    • src/task/:FIFO 任务队列、生命周期与状态追溯。
    • src/core/:系统配置、身份及权限安全校验。
  • 📁 desktop/:Electron 桌面悬浮球与轻量设置界面(main.mjs)。
  • 📁 cli/:命令行入口(qwenaudio)、环境检测与配置初始化。
  • 📁 tui/ & 📁 web/:终端与 Web 客户端界面。
  • 📁 shared/:跨端共享的协议类型定义与基础工具。

🚀 8. 快速开始与使用

依赖环境

  • Node.js ≥ 22.22.2 或 24.15.0+
  • npm ≥ 10

快速安装与配置

# 全局安装
npm install -g qwen-audio-agent

# 初始化配置 (写入 DashScope API Key 及选择 Backend)
qwenaudio config

# 启动 Gateway (web端 1)
qwenaudio

# 启动 TUI 交互界面 (终端 2)
qwenaudio tui

# 或启动 WebUI / 桌面悬浮球
qwenaudio webui

💡 总结

Qwen Audio Agent 是一套概念超前、设计严密且工程完成度极高的实时语音 Agent 运行时。它通过将低延迟全双工语音前台基于 ACP 的异步后台 Agent解耦,真正实现了“沟通不中断,任务持续推进”的理想 Agent 状态。

而在语音前台这一层,它又用 Provider 抽象进一步解耦了云端与本地:默认的 DashScope 端到端模型带来原生全双工的极致体验;本地 speech-to-speech 则以级联流水线换取了数据本地化与零成本,代价是延迟与”伪全双工”的取舍。二者共享同一套 OpenAI Realtime 协议、各取所需——这正是一个成熟 Runtime 应有的可插拔设计。

文章目录