Skip to content

qwen-audio-agent 架构 ​

本文档定义产品边界。违反这些不变性的变更属于架构变更,而非局部功能开发。

历史重构计划见 Realtime Voice Chatbot Runtime Roadmap。 本文描述当前已实现并受测试保护的运行时行为;概念与部署关系见架构总览。

1. 用户可见模型 ​

用户与一个 qwen-audio 助手对话。逻辑架构由三个核心组件组成:

  1. 前台 Agent — 通过实时模型、提示词、上下文和工具进行自然交流,处理对话及轻量工具请求。
  2. 编排运行时(Orchestration Runtime) — 管理任务、权限、会话和事件,调度后台执行与结果投递,不增加一个负责推理的协调 Agent。
  3. 后台 Agent — 一个用户配置的办事 Agent,负责处理需要操作用户环境、持续执行或制作交付物的请求。

Gateway 是承载这些能力的服务化运行形态,负责应用装配与网络接入,不是第四个核心组件。 客户端负责 I/O 与展示,通过 Gateway Client Protocol 使用服务,不等于前台 Agent。 下文在服务部署语境中使用“Gateway 管理/投递”等表述时,业务行为由其承载的编排运行时完成。

后端可以是 OpenCode、OpenClaw、Qoder、Qwen Code、MiniMax Code、Kimi Code、Pi 等 ACP Agent, 也可以是远程 A2A Agent 或自定义 BackendPort Adapter。 它内部可以使用工具、技能、Agent 或其他 Session。这些都是后端私有实现细节, 不会创建额外的 qwen-audio-agent 层。ACP、A2A 或自定义协议细节只存在于各自 BackendPort Adapter 内;后端特定的启动和能力行为位于已注册的驱动程序中。

2. 非阻塞请求流 ​

text
final ASR
   │
   ├─ immediately answerable ───────────────► Realtime speech
   │
   └─ requires work
          │ spawn_thinking(objective)
          ▼
      Task accepted
          │ response returns to Realtime immediately
          ▼
      owner FIFO queue
          │
          ▼
      configured BackendPort
          │ the backend decides how to work
          ▼
      final presentation
          │ waits for a safe duplex insertion window
          ▼
      Realtime naturally speaks the result

spawn_thinking 永不等待所请求的工作完成。用户可以在多个 Task 项排队期间继续 说话。对于每个 owner,一次只有一个 Task 项被发送到配置的 BackendPort。

3. 实时边界 ​

实时前端可组合调用当前可用的工具完成范围明确的请求;不会仅因调用次数多就转交后台。 工具按提示词职责分为两类:

契约工具
核心契约spawn_thinking、get_agent_task_status、cancel_agent_task、respond_permission、respond_agent_input、get_current_time
可选能力memory、web_search、fetch_url、knowledge、recall、notes、schedule_reminder、enter_sleep;动态 MCP 工具同样随配置提供

固定的 config/frontend-agent/PROMPT.md 只可点名核心契约工具,维护对话、工作受理与结果、 取消和确认等稳定流程。可选工具的用途与调用条件随工具提供,不能在固定 Prompt 或核心工具中反向引用;可选工具之间也不写死对方的名称。 记忆持久化规则放在 server/src/memory/PROMPT.md,仅在工具可用时注入;基础 Prompt 保留用户偏好与事实上下文的一般信任边界。 字段含义与填写规则归工具 schema,当前回执或投递阶段的指令归对应事件, 可用性、权限和执行校验由 Gateway 负责,不依赖模型遵守提示词。

核心与可选是提示词维护约定,不是运行时分类;核心工具名单仅保留在边界测试中。 注册表只保存工具定义和实际生效的可选策略,例如能力要求、结果大小限制和重复调用处理, 不要求工具声明分组或执行模式。未配置后台时不提供 spawn_thinking,待确认工具仅在 存在真实请求时提供;即使属于核心契约,未提供的工具也不能调用。测试检查固定 Prompt、 工具引用和能力开关,防止新增可选能力重新耦合进核心规则。

当 Gateway 存在待确认的后台权限时,提供 respond_permission。 模型只回答权限请求;Gateway 负责查找请求及其关联 Task,再转发决定。

memory 通过一个扁平接口维护两份普通 Markdown 文档。每次调用只执行一个 原子操作:read、append 或 replace;replace 使用唯一匹配的原文定位, 找不到或匹配多处时安全失败。同一轮可逐项调用多次,Gateway 合并后续回应。 两个文档的权限边界不同:

  • user 是当前用户的长期个性化覆盖:称呼、关系、助手在其面前的名称、语言、表达 风格和默认做法。它作为用户授权的指令材料注入,覆盖 ASSISTANT.md 中的实例级 默认人设,并服从用户当前的话语。
  • memory 是只用于理解和回答的持久事实与决定,绝不作为行为指令。两者按是否具有 行为权威区分,而不是按主题机械分类。

两者都不能授权泄露内部结构、跳过权限检查,或改变任务和安全协议。随包的 PROMPT.md 是不可被个性化覆盖的核心规则;本地 ASSISTANT.md 只保存助手实例的默认 身份、人格、关系定位和表达风格。首次启动从随包模板创建,升级不覆盖,下一次语音会话 读取修改。助手不会通过记忆工具修改它。

除显式工具写入外,会话结束提取器还会整理遗漏的明确用户指令和持久事实,分别路由到 USER.md 与 MEMORY.md。它与 Realtime 工具共用同一个上下文服务,不直接写文件, 永远不能修改 ASSISTANT.md,并会拦截文档边界错误和敏感内容。补丁结果记录在本地审计 文件中;未配置文本模型 API 密钥时自动静默禁用。

notes 将用户命名的列表(购物清单、待办事项、阅读列表)作为前台自有的持久集合 进行管理:单次调用即可完成添加、展示、匹配移除、清空和删除,无需后端参与。列表是 条目数据,而非记忆;稳定事实保留在 memory 中,列表条目绝不会被写入用户偏好或 事实记忆。条目和列表解析首先匹配精确文本,然后匹配唯一的不区分大小写子串, 否则报告歧义并将候选名称返回给模型以澄清。清单保存到共享数据目录的 frontend-notes.json;写入通过跨进程文件事务,避免不同 Gateway 覆盖彼此的修改。

get_agent_task_status 是生命周期、进度和中间结果问题的唯一实时入口。 Gateway 直接读取自身持有的 Task 记录,包括 Adapter 归一化后的最新消息、活动和 产物摘要。状态查询不会创建另一个 Task,不调用协调 Agent,也不进入异步播报队列。

实时前端没有以下工具:

  • 选择、创建、继续或取消后端 Session;
  • 选择同步、异步、前台或后台执行;
  • 选择后端执行策略;
  • 选择工具、Agent 或子 Agent。

respond_permission 是实时前端不控制执行策略这一规则的唯一例外。 它只能转发由 Gateway 提供的、针对待处理的、owner 作用域权限请求的明确当前轮次 用户决策。它可以理解自然的肯定或否定措辞,如"可以"或"不允许",但不能在没有 当前轮次用户话语的情况下虚构同意、创建请求、选择工具或修改后端权限策略。 权限请求使用同一个短 permission_id 贯穿模型、卡片和 Gateway,不另设模型别名。 只有一个待确认请求时,工具只需 decision;多个请求时需明确 permission_id, 指定了无效 ID 时不会改为授权其他请求。通知保留 task_id 供理解上下文,调用时不必重复传入。 同一用户轮次的重复确认复用回执,不会扩大授权范围。ACP 原始选项 ID 由 Adapter 私下映射。 内置 ACP Adapter 使用随机短 ID,避免进程重启后从头编号而与旧上下文碰撞。 回复分为 task、always 和 reject:分别表示允许当前 Task 及其后续操作、 在当前前端会话中跨 Task 始终允许,以及拒绝当前操作。Gateway 的任务层 PermissionPolicy 由语音、客户端命令和定时工作共用,以 BackendPort 的逐次 once 决定放行后续请求,也处理同一 Task 已经排队的请求。Task 授权在完成、失败 或取消时失效,不传递给其他 Task。两种授权都不会写入后端持久规则,也不跨 Gateway 重启保存。 权限、进度和恢复上下文的协议标签由 Gateway 独占。模型生成的同名标签不构成事件, 不能启用相应工具,也不会写入持久对话。

传递给 spawn_thinking 的 objective 是对用户请求的保守解释,而非执行计划。 提交前必须结合当前对话把"继续那个页面"之类的指代解析成一条自包含指令。该指令就是 后台 Agent 唯一收到的模型可见文本;ASR 原文不再作为第二份任务描述附加。后台 Agent 不接收前台人格、长期记忆或最近聊天历史;与执行有关的事实必须先解析进指令,而不是 转发这些文档。

一次后台轮次可以返回结果,也可以提出继续工作所需的问题、选择、确认或补充信息。 前台自然转达后者;用户随后回答时,再通过 spawn_thinking 以同一工作的续办关系提交, 而不是在前台推测或模拟后续执行。这一语义不依赖具体问题内容或关键词。

当前轮附件由 Gateway 自动作为协议原生 Part 随任务传递,而不是放入模型可见的 JSON 清单。只有任务明确依赖此前轮次的图片或文件时,前台才通过可选的 input_refs 引用 Gateway 分配的会话内输入 ID。Task ID、owner、生命周期、时间戳和路由继续作为 Gateway/BackendPort 的结构化数据,不进入后台 Agent 的任务指令。工作目录和用户 时区也不重复拼入每轮文本;协议或后台自身的运行上下文负责这些信息。

4. 固定后端 Agent Session ​

这里的“协调 Session”属于 ACP 后台接入,不是框架的编排运行时。 编排运行时通过 BackendPort 管理工作,不要求 A2A 或自定义后台提供同样的 Session 结构。

ACP 适配器为每个 owner 和后端拥有一个持久协调器 Session 身份:

text
qwen-audio-agent:<owner>:backend

Gateway 在该稳定键之后存储原生 ACP Session ID,并在后续轮次调用 session/resume。项目委派同样在其记录的工作目录中恢复选定的原生 Session, 因此语音发起的工作保留在后端自己的 Session 历史中,而非 Gateway 副本中。

语音浏览器会话 ID 和 Task ID 不会更改该身份。因此,新的语音对话会继续使用 相同的后端 Agent 上下文。

Gateway 队列和 ACP 适配器都对写入进行串行化。这种双重保护防止并发消息在一个 后端 Session 内部发生竞争。

后端 Agent 拥有自己的执行策略。qwen-audio-agent 只提供一条自包含自然任务指令和 当前轮次的协议原生附件;它不转发前台历史或偏好,不规定状态 JSON,也不指导后端 Agent 如何使用后端特定能力。

5. Task 状态 ​

qwen-audio-agent Task 记录是交付回执,而非后端内部任务图的镜像。

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

公共字段仅限于用户请求、时间戳、最终结果/错误、通用活动、带可选安全操作详情的 有界待处理权限摘要和通知状态。不存在执行模式、交付模式、子 Agent 状态、后端 权限标识符、后端拓扑或后端取消内部信息。

UI 将 queued 和 running 呈现为相同的"处理中"状态。队列位置是内部调度细节, 不会改变用户的双工对话。

排队中和直接执行中的 Task 在 Gateway 重启后无法安全恢复,因此会变为 failed 并附带 明确的重启原因。委派 Task 只有在 Adapter 能确认持久化原生 Session 时才会重新挂接。 已完成的结果和通知交付状态会被持久化。

6. 进度动画 ​

进度是可观测性,而非控制。ACP 适配器将标准 session/update 通知投射为通用活动:

  • 工具名称、有界的用户安全详情和运行中/已完成状态;
  • 计划进度;
  • 不含思考内容的通用思考信号;
  • 有界的 Session 标题和当前模式元数据。

UI 将此映射为稳定的任务说明或短语,如"搜索中"、"读取中"、"生成图像"或当前模式; 通用思考信号继续显示任务目标。包括 thinking 在内的所有活跃后台工作,在桌面宠物上 统一呈现为 working;processing 只保留给前台 Realtime 轮次。Session ID、 子 Agent ID、原始权限载荷和原始思考内容不显示。待处理权限可以在类密钥值被脱敏后, 显示精确且有界的标题、说明、命令或路径,以便知情同意,但不会引入单独的 Agent 动画状态。

活动绝不会产生语音状态更新,也绝不影响队列。

7. 最终结果交付 ​

后端 Agent 通过标准 ACP 回合返回结果:正文来自 agent_message_chunk,图片、音频和 资源保留为原生 ContentBlock,回合以 session/prompt 的 PromptResponse 结束。 ACP Adapter 不再把这些内容压成一段文本,也不要求模型补写专有结果 JSON; 它将文本和非文本内容分别投射为 BackendPort 的 content 与 artifacts。只有 stopReason=end_turn 代表回合成功完成;取消、拒绝、Token 或 Agent 请求次数耗尽分别进入 Gateway 的取消或失败路径。Gateway 再根据客户端能力 决定对话展示、资源卡片和语音表达。

已完成的结果优先返回到发起对话。在全新连接时,可以恢复同一 owner 的旧对话中 未完成的结果。可续期声明防止两个实时前端呈现相同结果。结果被注入实时上下文, 音频播报由客户端 playback.started 回执确认已开始交付,而非仅凭服务端生成完成。 用户正在说话或有其他响应时等待安全窗口;已开始播放的结果不因中途打断而反复重播。 上下文注入和播放确认分别管理,重试有次数上限。

当后端 Agent 调用 session_start 或 session_send 时,委派成立的权威事实是 Session 工具已经成功创建或续接目标任务,并返回 Adapter 验证过的运行与 Session 标识,而不是模型输出的某个字段。ACP 只负责如实传递工具调用、工具结果和当前回合的 终止;Adapter 将验证后的关联发布给 TaskManager,后者据此把原始 Task 移至 delegated,并释放后端 Agent 串行化锁和 Task 调度通道。因此,其他语音请求可以在 目标 Session 运行期间使用协调器。

协调 Agent 可以在工具成功后自然结束当前 ACP 回合,但该文本不控制任务状态,也不会 被解释为完成信号。关联 ID、目标 Session 和生命周期完全保留在 Gateway 的 Task Registry 与 Adapter 运行时中,不要求模型回显。

适配器独立地保持 Task 生命周期和事件订阅存活。只有与委派 ID 关联的匹配 ACP 目标提示完成才能完成 Task。然后适配器短暂重新获取后端 Agent 锁,并将经验证的 结果及其原生 ContentBlock 发送给它整理最终答复。繁忙的目标、空结果、无关的 Session 更新或旧结果 都无法完成 Task。

ACP Agent 轮次不设人为墙钟超时。初始协调轮次、委派目标轮次和最终整理轮次, 只会在 ACP 报告完成、用户显式取消 Task,或后端进程退出/关闭时结束。连接初始化 和有界控制 RPC 仍保留超时,避免不可用的后端无限阻塞 Gateway 启动。

取消是确认式的,而非乐观式的。queued Task 在本地取消。running 或 finalizing Task 中止其活跃后端请求。对于 delegated Task,Adapter 根据已记录的关联取消目标执行, 不需要让协调模型先选择一个取消工具。Task 在取消请求处理期间保持 cancelling, 由 Gateway 根据返回结果更新终态;不能仅凭模型口头回应认定已取消。在适配器直接中止后,Gateway 会记录一个取消事实,并在下一个安全的 协调器轮次中注入一次。这样可以在不延迟取消或重复停止的情况下协调协调器的历史。

前台的受理确认来自 Gateway 已经创建的 Task,而不是协调 Agent 自报的委派状态。 协调轮次仍依 ACP 生命周期信号自然结束;Gateway 不会根据受理文本或 Session 工具调用成功来推断该轮次已完成。

8. 后端内部能力 ​

对于接受客户端提供的 MCP 服务器的 ACP 后端(包括 OpenCode、Qoder、Qwen Code、 MiniMax Code 和 Kimi Code),Gateway 向协调器注入相同的五个工具:Session list、start、 send、status 和 cancel。OpenClaw ACP 不接受客户端提供的 MCP 服务器, 因此相同的协调契约映射到 OpenClaw 的原生 Session 工具。session_start 和 session_send 返回不透明的委派 ID。在任一成功后,后端 Agent 不得轮询、 重复工作或从自己的上下文中回答;适配器负责等待、取消、权限路由和结果关联。

协调 MCP Server 还会通过 MCP 初始化响应的 instructions 字段发布稳定协调契约。 后台 Driver 只有在确认 Agent Host 会把 MCP Server instructions 投射进模型上下文后, 才声明 coordinatorMcpInstructions;这些后台每轮只接收动态自然任务指令,避免把 相同的路由与返回规则反复追加到持久 Session 历史。目前已确认 OpenCode、Qoder、Qwen Code 和 Claude Code,并将共享内容控制在 2 KiB 的可移植预算内。尚未验证的后台,或未来 超过预算的内容,继续使用完整的逐轮 Prompt 安全回退。该标志不表示后台是否普遍支持 MCP。项目 Session 不会连接协调 MCP Server。

session_status 仅用于观察。如果查询失败,后端 Agent 必须报告失败; 不得使用原生工具检查目标目录或复制委派的工作。

前端代码不得依赖于选择了哪个内部能力。前端任务快照只能暴露有界的标题和 通用委派状态,绝不暴露委派 ID、目标 Session ID、目录或原始事件。

9. 依赖方向 ​

逻辑调用与网络接入分别遵守以下边界:

  • 前台 Agent 通过 spawn_thinking 请求执行,编排运行时管理任务并调用 BackendPort;Adapter 将内部任务投影为后台支持的输入,适用于 ACP、A2A 和自定义后台。
  • 实时模型服务通过 Realtime Provider 接入。模型供应商的事件与协议细节不能进入通用任务逻辑。
  • 客户端通过 Gateway Client Protocol 接入 Gateway。传输层把协议事件交给运行时,并把运行时结果投影为客户端事件;HTTP 宿主管理接口不等于对话工具调用。

后端特定的 API 细节仅属于 server/src/backend/adapters。前台工具不得导入后端适配器。 UI 仅消费公共 Task 与对话事件。包级别的 shared 模块是基础运行时 工具;server core 和 process 可以依赖它们,但它们不得依赖 server 层。

源码目录 ​

编排运行时是逻辑职责的集合,不与单个目录或类一一对应。orchestration/ 负责共用任务操作与会话级投递协调,任务状态仍由 task/ 管理,模型会话与播报由 voice/ 管理;app/ 将这些模块装配成 Gateway 应用。

服务端按功能归属组织目录,避免同一个功能分散在多个技术层目录中:

  • memory/:记忆接口、运行时、工具、提示与上下文、自动学习,以及 Markdown/VoiceMem Provider。
  • knowledge/:知识库接口、工具、检索运行时、入库服务与内置本地 Provider。
  • frontend/:核心前台指令、工具装配与执行、MCP/OpenAPI 工具,以及网页检索和搜索 Provider。
  • voice/:Realtime Provider 协议、连接、音频轮次、打断与播报投递。
  • orchestration/:与传输无关的共用用户任务操作与会话级投递协调。
  • backend/:协议无关的 BackendPort 与执行逻辑;backend/adapters/ 管理 ACP/A2A 实现及适配器选择。
  • conversation/ 与 session/:分别管理对话上下文与投影、持久化事件回放,不作为全部记忆能力的容器。

app/ 保持为组合根,负责跨模块接线;可选模块自行装配内部 Provider 和路由。 操作审计、引用来源规范化、无状态文本模型调用等小型服务端公共能力放在 core/, 具体 Provider 跟随所属功能模块。 依赖测试区分模块核心与具体适配器:文件聚合不代表运行时可以导入具体 Provider, 也不代表前台工具可以导入 Realtime 或后台 Adapter。内部文件移动时,公共包导出 名称保持不变。详见源码导航。

裁剪记忆或知识库时,删除模块目录,并取消 app/optional-modules.mjs 与 frontend/optional-features.mjs 中对应的 import 和数组项;运行时服务、工具、路由和 专属提示同步移除。这是两处显式装配入口,不是新的插件框架。精简发行包还应清理对应 包导出、专属测试/文档和依赖。前台运行时只发布通用会话生命周期事实,记忆模块自行 管理学习观察器,退出时先等待观察完成再关闭 Provider。测试会真实删除其中一个或 两个模块,验证网关仍能完成对话。

server/src/orchestration/ 中的 TaskOperations(task-operations.mjs)统一任务 提交、查询、取消、权限决定和补充输入。app/ 将同一个实例注入前台工具与客户端命令, 定时后台工作复用其执行和权限链路。TaskManager 仍是任务状态的唯一权威; BackendWorkRuntime 只负责把执行请求转换为 BackendPort 调用,系统作业不并入用户工作。 语音授权工具立即返回本地受理回执,客户端卡片等待同一决定的后台确认。模型工具回执 和公开协议响应仍由各自入口生成,不引入额外 LLM、传输协议或任务状态机。

每条前台连接还拥有独立的 SessionTaskCoordinator,按 owner/session 观察用户任务、 协调待确认权限与补充输入,并从 TaskManager 领取最终结果通知。它不运行模型,也不解析 协议帧,更不是 ACP 后台的协调 Session。voice/realtime-task-presentation.mjs 生成模型可见文本并接入已有播报管理器, 场景仍通过 taskAnnouncementFactory 定制播报。

权限/补充输入投递前先暴露对应工具;前台忙碌或暂时不可用不会消耗待处理请求, 请求解决后排队中的对应回复失效。断开连接清理订阅与重试、释放通知领取,但不取消后台工作。 重连可重新领取结果,不会再次执行工作;结果可用与播放确认仍是两个独立阶段。 公开任务事件继续经过传输投影器发给客户端。

transport/gateway-client-transport.mjs 只保留传输职责:鉴权、能力协商、连接归属、心跳、协议 编解码和公开事件投影。它为每条客户端连接接入独立的 voice/realtime-session-runtime.mjs 前台运行时(createRealtimeSessionRuntime)。运行时 复用已有组件,管理模型会话与上下文、工具调用、音频轮次、播放、恢复和客户端休眠状态; 接收解码后的事件与可信身份,通过回调发出内部事件,不持有 Socket、凭据或协议握手。

app/frontend-runtime.mjs 负责前台装配:共用依赖、一次性工具源初始化、每条连接的 独立会话创建,以及退出时等待生命周期观察完成。传输层使用注入的运行时,不装配工具 或解析模型 Provider;工具源服务由持有它们的应用层关闭。

静音、语音打断、休眠和前台断连不取消已受理的后台工作。关闭前台会清理定时器、未完成的 前台工具调用、订阅和投递领取;迟到的模型回调不能创建新工作。显式任务取消仍由 TaskOperations 处理。app/ 仍是组合根,不新增服务、线上协议或共享模型会话。

#477 的三个增量均直接测试生产中的 任务操作、协调器和前台运行时,只模拟模型与后台边界,不依赖网络。已有 WebSocket 和 WebRTC 集成测试则验证传输层如何接入同一套运行时。

server/src/client 管理北向 Client Event Registry、客户端命令到任务操作的转换、 ClientActionPort 与幂等 Presence 状态机。Client Action 描述一次环境操作并等待 当前 Client 回传结果,不导入 Electron 或任何 UI 实现。该层只能依赖公开 shared 协议值、与供应商无关的 delivery 值,以及协议无关的 Task 与 Orchestration 层。 server/src/delivery 只管理 AgentDelivery 值,不依赖 Client、Realtime 或 Backend 具体实现。server/src/app 组合根把这些服务注入 Realtime Transport;语音链路与 Client 代码都不能导入其具体实现。

Gateway 可以将不可变的 web/dist 产物作为部署便利来提供,但这仅是静态托管。 Gateway 源码不得导入 UI 组件、呈现文本、样式、终端行为或桌面行为。 所有三个 UI 拥有自己的渲染,并将结构化协议字段映射到各自的标签和交互模式。

后台 Task 播报只保留一个代码级装配接缝:嵌入方可以向 createGatewayApplication 传入 taskAnnouncementFactory。默认 factory 原样组合 现有的最终结果播报与低频进度播报管理器;场景方也可以整体替换两者。这是产品代码的 依赖注入点,不是用户设置、策略注册表或新的线上协议。

10. 进程所有权 ​

当前标准部署以 Gateway 作为框架的服务宿主。逻辑组件不要求各自成为独立进程;后台生命周期由共享的 owned/external 归属模型管理:

  • owned:Gateway 启动后端所需的本地进程,并在退出时停止它们。后台原生进程负责 加载自己的用户配置、模型、工具和 MCP;适配器只提供协议参数和必要的公共能力。
  • external:仅适用于声明了外部服务能力的后端。Gateway 不启动、改端口或停止该 后台,只通过后端公开的协议地址连接,把配置与状态完全留给外部服务管理。

后台服务归属与 ACP 连接方式是两个相互独立的维度。每个后台 profile 声明一个 acpConnection;连接工厂当前实现 process,即启动一个本地 ACP stdio 子进程。 未来的远程 ACP bridge 可以新增另一种连接类型,而无需修改协调、权限、Task 或 Session 生命周期代码。声明外部后台服务,并不意味着 ACP 连接也自动变成远程连接。

每个后台通过一份经过校验的 Plugin 契约注册。目录项统一拥有身份、安装、原生配置 入口、进程环境和归属元数据;Agent Driver 与 Runtime Driver 必须显式声明完整的布尔 能力,缺失或互相矛盾时在启动阶段直接拒绝。后台子进程只接收跨平台运行所需的系统 变量和当前 Plugin 声明的凭证命名空间,Gateway 身份、Realtime、Memory 以及其他 后台的密钥不会跨过该边界。通用 ACP 命令如确有需要,可通过 QWEN_AUDIO_AGENT_ACP_FORWARD_ENV 显式列出额外变量名。

HTTP/WebSocket 应用由可注入的组合根构造。导入应用工厂不会监听端口;CLI 和桌面版 使用轻量 bootstrap,而测试及未来客户端可以注入彼此隔离的 Agent、任务、会话、 配置和日志服务。

共享适配器通常拥有一个 ACP stdio 子进程,并随 Gateway 一起停止。OpenCode、Qoder、 Qwen Code、MiniMax Code 和 Kimi Code 直接作为 ACP Agent 运行;OpenCode 还可以额外启动其原生本地 Session UI 服务。当前 OPENCODE_BASE_URL 表示这个 UI 服务地址,不是远程 ACP 执行端点, 因此 OpenCode 仍属于 owned。

OpenClaw 使用一个小型 ACP bridge。未显式配置地址时,Gateway 启动一个具有隔离运行时 和 Session 状态的 OpenClaw Gateway;显式配置 OPENCLAW_BASE_URL 时,则直接连接用户 已有的 OpenClaw Gateway,并且不读取、复制或修改其认证和 Agent 状态。此时服务归属为 external,ACP 连接仍是本地 process:本地官方 bridge 通过 WebSocket/WSS 连接远程 OpenClaw Gateway。外部连接不使用面向本地启动的短时端口探测,而由 bridge 报告实际的 网络、TLS 和认证结果。本地 bridge 退出只会中断 ACP 连接,不会触碰远程 Gateway。

Codex 也遵循同一边界:qwen-audio-agent 通过 ACP stdio 启动 codex-acp,该适配器再 通过自己的本地 stdio 协议启动 Codex App Server。Codex App Server 可以提供其他传输, 但它们不是远程 ACP 端点,不应泄漏进共享 ACP 适配层。

Desktop、TUI 和 WebUI 是可替换的 Gateway 客户端。Gateway 是当前 Realtime 模型的 唯一所有者,并通过 health 发布精确模型档案与传输能力。Desktop 只能配置和重启其 本地自有 Gateway;WebUI 与 TUI 将档案视为只读。借用或远程 Gateway 的模型不一致 会被拒绝,而不会被静默覆盖。关闭 UI 不会影响排队中的工作或固定的后端 Agent Session。更改实时或后端行为的配置在下次 Gateway 启动时生效;更改 UI 的 Gateway URL 仅重新连接该 UI。

macOS 桌面渲染器打包在应用程序内部。Electron 从私有的随机回环路径提供这些 不可变资源,并仅代理 Gateway HTTP API 和 Realtime WebSocket 流量。桌面 UI 资源不得从 Gateway 加载:重新构建桌面应用程序必须足以更新其外观,而无需升级 正在运行的 Gateway 前端。

11. 审查清单 ​

合并变更前,请验证:

  1. 后端工作排队或运行时,实时前端是否仍能对话?
  2. 工作是否经由 BackendPort 执行,并由 Adapter 保持正确的续办上下文(如 ACP 协调 Session)?
  3. 前台是否只依赖通用任务与授权语义,而不依赖后台私有 Session、子 Agent 或执行模式?
  4. 工具事件是否仅用于通用 UI 进度?
  5. 完成播报是否仅来自最终后端 Agent 结果?
  6. UI 是否通过宿主生命周期接口管理本机 Gateway,且不绕过它直接管理后台进程?
  7. 打断是否能在不取消已提交 Task 的情况下推迟语音?
  8. 测试是否覆盖 FIFO 串行化、固定 Session 复用、工具动画和交付重试?