本书定义 TideSync 仓库内受控的 Vision Agents Qwen3.5 Omni Realtime WebSocket adapter 派生源码对象,以及围绕该源码对象成立的配置契约、事件契约、状态机契约、错误契约、测试契约、PR 证据契约和符合性声明。

本书服务五类消费者:开发者用它确定必须实现和不得偷换的 adapter 行为;测试与验收工程师用它逐条判定实现是否符合;项目负责人用它保护“原生一等公民支持”不被 demo、模型能力宣传或外层配置替代;安全、运营与成本治理负责人用它审查工具、搜索、usage 和错误投影;维护者用它追溯上游来源、运行时依赖路径、证据材料和未知项边界。

本书不定义实时全模态视频通话体验本身。该体验由 08 号书的体验对象定义。本书把 08 号书中需要 Qwen3.5 Omni Realtime WebSocket adapter 承担的体验条件,转写为 adapter 层可实现、可观察、可测试、可审查的机器规约。

本书属于 实时全模态视频通话体验规约书架。

源格式为 AsciiDoc。书稿以稳定 ID、role、rel 和 normative 字段表达可维护的规约结构。

TideSync 的实时全模态视频通话体验依赖一个事实链:用户通过 GetStream WebRTC 向 Vision Agents 提供音频和视频,Vision Agents Qwen adapter 通过 DashScope WebSocket 向 Qwen3.5 Omni Realtime 发送音频与图像,Qwen 服务端返回语音、文本、VAD、响应生命周期、工具、搜索、usage 和错误事件,Vision Agents core 再把这些事件变成可听输出、会话状态、字幕、消息、错误和打断控制。

当前缺陷不在于 Qwen3.5 Omni Realtime 没有实时全模态能力,也不在于 Vision Agents core 完全没有承载结构。缺陷在于上游 Vision Agents v0.6.4 的 Qwen adapter 只接入基础 WebSocket 语音视频路径,没有完整承担 Qwen3.5 Omni Realtime WebSocket contract,也没有把关键服务端事件完整投射到 Vision Agents core。

本书的写作目标,是把“原生支持 Qwen3.5 Omni Realtime”从一句愿望转化为可审查契约。契约必须能失败。契约失败时,开发者不能用一次演示成功、模型官方能力、日志打印、外部 fork 或“后续优化”抵消。

读者角色入口

本书服务五类消费者。每类消费者进入本书时,需要读取的对象不同;角色入口把读者动作和书内合同表面连接起来。

读者角色 入口 读取目标

开发者

Qwen WebSocket Adapter 人工制品;源码与运行契约;Session 配置契约;服务端事件契约;封闭状态模型;打断契约

确定受控源码、运行时加载、配置字段、事件映射、状态职责和打断控制语义,不得用外层配置、日志或单点补丁替代 adapter contract。

测试与验收工程师

断言表面;源码与运行路径断言;事件映射断言;打断断言;覆盖映射;符合性检查表

按前置状态、输入事件、期望 adapter 行为、期望 core 投影、期望 client 事件、不符合表现和证据要求逐条验收实现。

项目负责人

原生支持定义;核心符合;强要求偏离;不符合判定;PR 符合性声明;Live 验证边界

判断交付是否保护“原生一等公民支持”,区分核心符合、强要求偏离、扩展能力、未知项阻塞和假成功。

安全、运营与成本治理负责人

工具、搜索与 Usage 契约;错误契约;Usage 保留;Metrics 投影;结构化错误;文档冲突登记

审查外部能力、搜索互斥、工具调用、usage 计量、错误结构化字段和文档冲突是否进入可治理表面。

维护者

上游来源与派生治理;导入范围;上游同步;维护边界;结构化表面约定;术语表

追溯派生源码来源、审查上游差异、维护 role 和 rel 词表、判断未来上游替换或范围扩展是否仍满足本书合同。

角色入口不替代正文。读者的正式判断仍以被入口引用的对象定义、合同章节、断言章节和证据章节为准。

本书以 TideSync 08 号体验规约、本地 Qwen3.5 Omni Realtime 文档、Vision Agents v0.6.4 源码审计和前置 deep-research 报告为证据基础。

Part I: 对象、事实与边界

本部分定义本书对象、问题世界、期望改变、系统边界和符合性模型。后续事实图、契约、断言和证据都依赖这些边界。

1. Qwen WebSocket Adapter 人工制品

TideSync 的 Qwen WebSocket Adapter 人工制品,是 TideSync 仓库内受控的 Vision Agents Qwen3.5 Omni Realtime WebSocket adapter 派生源码对象,以及使该源码对象进入 TideSync 运行时的依赖解析、配置表面、事件映射、状态机、错误处理、测试证据和 PR 符合性声明。

这个对象不是 Qwen3.5 Omni Realtime 模型本身。模型提供实时音频、图像、文本、语音、VAD、工具、搜索和 usage 等能力;adapter 的职责是把这些能力对应的 WebSocket contract 接入 Vision Agents 和 TideSync 的运行系统。

这个对象不是 TideSync 外层 create_agent() 配置。外层配置可以选择模型、地域、音色、帧率和能力开关;它不能替代 provider 层对 Qwen 服务端事件、输入轮次、响应状态、本地音频 flush、工具调用、搜索 usage 和错误恢复的处理。

这个对象不是 external 目录中的参考仓库。external 是证据来源和上游源码参考区;可修改源码必须进入 TideSync 仓库的可审查范围,才能成为 TideSync PR 的完成物。

这个对象不是 Qwen 原生 WebRTC transport。当前对象使用 Qwen WebSocket API。Qwen 原生 WebRTC 的 SDP、RTP 音频轨道、RTP 视频轨道和 DataChannel 是相邻 transport,不能冒充当前 WebSocket adapter 的完成条件。

1.1. 原生支持定义

Vision Agents 在 TideSync 当前边界内对 Qwen3.5 Omni Realtime WebSocket 的原生一等公民支持,指 TideSync 运行时加载的 Qwen adapter 源码对象完整承担 Qwen3.5 Omni Realtime WebSocket 的配置、输入、输出、状态、打断、工具、搜索、usage 和错误合同,并通过本书定义的测试和 PR 证据验收。

原生支持不等于能连接模型。连接只证明 WebSocket、鉴权和 session.update 起点存在。原生支持也不等于模型官方具备某项能力。模型能力只有穿透 adapter、Vision Agents core 和 TideSync 运行路径,成为可配置、可观察、可测试的行为时,才进入本书的符合性判断。

1.2. 构成性条件

Qwen WebSocket Adapter 人工制品的构成性条件包括八项。缺少任一条件,当前对象不能被判定为符合本书定义的原生支持。

第一,源码必须在 TideSync 仓库内可审查。所有修改必须出现在 TideSync PR diff 中。external、site-packages、本机 editable clone、个人远程 fork 或未纳入 PR 的源码,不能作为完成证据。

第二,TideSync 运行时必须加载 TideSync 仓库内的 adapter 源码。代码存在但运行时仍从 PyPI 上游包加载 Qwen adapter 时,当前对象没有进入系统。

第三,adapter 配置必须以 Qwen3.5 Omni Realtime WebSocket contract 为来源。模型名、连接地址、输出模态、音色、音频格式、输入转写、VAD 类型、搜索、工具和互斥规则必须有官方合同、TideSync配置表面和测试证据。

第四,adapter 必须完整表达当前 WebSocket contract 的输入、输出和响应生命周期。音频输入、图片输入、VAD 事件、响应创建、文本输出、音频输出、转写完成、响应完成、工具调用、搜索 usage 和错误事件必须进入状态机或被明确归档为非当前承诺。

第五,adapter 必须把 Qwen 服务端事件投射到 Vision Agents core 的公共承载物。用户 speech started/ended、agent speech started/ended、audio output done、interrupted audio done、transcript delta/final 和 error 不能停留在 Qwen adapter 私有变量或日志中。

第六,adapter 必须定义本轮输入和当前响应的状态。全局布尔值不能替代当前输入轮次、图片发送许可、response id、cancelled response、tool call、search usage 和错误恢复状态。

第七,adapter 必须提供可重复测试投影。事件 replay、fake WebSocket、配置测试、状态机测试、工具与搜索测试、错误注入测试和 TideSync import 路径测试是主要证据。单次真实通话演示只能作为辅助证据。

第八,adapter 必须公开未知项和偏离项。文档冲突、live API 未验证、服务端兼容性未知和非当前 transport 能力必须归档,不能伪装成已经完成的合同。

1.3. 本书的判定角色

本书把 Qwen WebSocket Adapter 人工制品从愿望、路线和实现物中分离出来。开发者、测试工程师、项目负责人和维护者可以使用日常项目语言沟通,但正式完成判断必须回到本书定义的对象、合同、断言、证据和偏离声明。

当一个实现声称 Vision Agents 已经原生支持 Qwen3.5 Omni Realtime WebSocket 时,本书要求它回答五个问题。

第一,源码是否进入 TideSync 仓库并出现在 PR diff 中。

第二,运行时是否加载这份源码,而不是加载 PyPI、external 或本机未提交路径。

第三,adapter 是否覆盖本书定义的 Qwen WebSocket 配置、事件、状态和错误合同。

第四,测试证据是否逐条覆盖本书的核心断言。

第五,未知项、文档冲突、live 验证阻塞和非当前承诺是否公开登记。

这五个问题共同构成本书的符合性判定入口。

2. 领域事实

领域事实描述本书对象介入之前已经存在的现实条件。事实不授权方案;事实为后续规约提供前提。

2.1. TideSync 体验依赖

TideSync 的目标体验由 08 号书的实时全模态视频通话体验定义。该体验要求用户能够在同一会话中提供实时语音、提供当前视觉现场、听到 AI 的自然语音回应、打断 AI 回答、围绕视觉现场继续追问,并通过公共投影理解采集、处理、回应、错误和限制状态。

Qwen WebSocket Adapter 不定义这个体验。它承担这个体验在模型接入层的必要机器规约:把 TideSync 和 Vision Agents 运行时收到的音频、视频、会话动作和外部能力,转换为 Qwen WebSocket 输入;把 Qwen 服务端返回的事件和媒体,转换为 Vision Agents core 承载物能消费的输出、状态和控制语义。

2.2. Qwen 官方能力事实

Qwen-Omni-Realtime 官方文档把该系列定义为实时音视频聊天模型,能够理解流式音频和图像输入,并实时生成文本和音频输出。Qwen3.5-Omni-Flash-Realtime 的本地模型摘要列出文本、图片、视频、音频输入以及文本、图片、视频、音频输出,同时标注 Function Calling 和联网搜索能力。

官方 WebSocket contract 使用 session.update 配置会话,使用 input_audio_buffer.append 发送音频,使用 input_image_buffer.append 发送图片,使用响应生命周期事件返回文本、音频、转写、工具调用、usage 和错误。官方文档同时区分 WebSocket 与 WebRTC:WebSocket 音频输出通过 response.audio.delta 返回;WebRTC 音频通过 RTP 输出,不通过该事件返回音频。

官方文档列出 turn_detection.type 可取 server_vad 或 semantic_vad。本地文档写明 Qwen3.5 Omni Realtime 推荐 semantic_vad。官方文档也定义 WebSocket Manual 模式:turn_detection 为 null,客户端显式发送 input_audio_buffer.commit 和 response.create。

2.3. Vision Agents 当前事实

本地 external/Vision-Agents 源码为 v0.6.4,commit 9c5efe1。该版本 Qwen adapter 只实现 DashScope WebSocket client,不实现 Qwen 原生 WebRTC transport。

当前 Qwen adapter 能建立 WebSocket、发送 session.update、发送音频、发送图片、发送 response.cancel,并处理基础响应事件。它没有完整处理 Qwen3.5 Omni Realtime WebSocket lifecycle,也没有完整使用 Vision Agents core 已有的 speech、audio done、interrupt 和 flush 抽象。

Vision Agents core 已经存在 RealtimeAudioOutputDone、RealtimeUserSpeechStarted、RealtimeUserSpeechEnded、RealtimeAgentSpeechStarted、RealtimeAgentSpeechEnded 等事件对象。RealtimeInferenceFlow 收到 RealtimeAudioOutputDone(interrupted=True) 时,会调用 interrupt,清 transcript、清 AudioOutputStream,并向下游发送 AudioOutputFlush。Agent audio producer 收到 flush 后,会调用 GetStream audio track 的 flush。

这个事实说明:本地音频打断清理的承载物存在,缺口在 Qwen adapter 没有把 Qwen 服务端打断相关事件投射到这些承载物。该缺口由 打断投射缺口记录,并由 打断契约约束目标行为。

2.4. TideSync 当前运行事实

TideSync 当前 src/tidesync/agent.py 的默认模型是 qwen3.5-omni-flash-realtime,默认 WebSocket 地址是中国内地 DashScope realtime 地址,默认音色是 Tina,默认 FPS 是 1。RealtimeSettings 只从环境变量读取 model、base_url、voice 和 fps。

TideSync 当前 create_agent() 实例化 qwen.Realtime 时传入 model、base_url、voice、fps,并固定 include_video=True。TideSync 没有暴露 VAD 类型、Manual 模式、搜索、工具、usage、音频格式、输入转写模型或错误策略配置。

TideSync 当前 pyproject.toml 依赖 vision-agents[qwen,getstream]>=0.6.4,<0.7。在没有 TideSync 内受控源码和依赖解析证明时,运行时 Qwen adapter 行为来自上游包,而不是来自 TideSync PR 中可审查的派生源码。

2.5. 组织可追责事实

TideSync 团队要求所有修改代码有精准 PR,并在 TideSync PR 日志中留痕。workbench 约束同时规定 external 目录用于外部仓库临时参考,不作为本仓库长期项目或可修改实现对象。

因此,Qwen adapter 派生源码只有进入 TideSync 仓库可审查范围,并通过 TideSync 依赖解析进入运行时,才能作为当前对象的完成证据。外部 fork 可以作为上游同步通道或参考来源,不能替代 TideSync PR 完成物。

3. 期望改变

期望改变描述本书对象应当使问题世界发生的变化。它不是实施顺序,也不是功能愿望清单。

3.1. 中心期望改变

TideSync 必须拥有一个可审查、可运行、可测试、可验收的 Qwen3.5 Omni Realtime WebSocket adapter 源码对象,使 Qwen3.5 Omni Realtime 官方 WebSocket contract 能够通过 Vision Agents core 正确服务 TideSync 的实时全模态视频通话体验。

这个改变成立后,开发者不能用“模型支持”替代 adapter 支持,不能用“WebSocket 已连接”替代响应生命周期完整,不能用“发送了 response.cancel”替代打断完成,不能用“external 已改”替代 TideSync PR,不能用“演示成功一次”替代断言证据。

3.2. Adapter 层结果

符合实现必须把 Qwen WebSocket 事件变成 Vision Agents core 可消费的事件、音频输出、转写、控制语义和错误语义。input_audio_buffer.speech_started 必须改变用户 turn、本地音频输出和当前响应命运;response.audio.done 必须形成音频输出完成边界;response.audio_transcript.done 必须形成助手转写完成边界;工具调用、搜索 usage 和结构化错误必须保留它们的 Qwen 语义。

符合实现不得把 Qwen WebSocket contract 压缩成“音频输入、图片输入、音频输出”三项。实时交互的可打断性、上下文轮次、错误恢复、外部工具、搜索计量和响应边界都依赖 adapter 层的状态与事件映射。

3.3. 验收层结果

测试与验收工程师必须能够依据 断言表面逐条判定实现。每条核心断言必须给出前置状态、输入事件或用户动作、期望 adapter 输出、期望 Vision Agents core 事件、期望 client 发送事件、禁止行为和证据要求。

验收不得依赖单一演示。真实通话、录屏和日志可以补充说明现象;核心证据来自 测试证据契约定义的可重复测试、event replay、fake WebSocket、运行时 import 路径断言、PR diff 和符合性声明。

3.4. 治理层结果

项目负责人必须能够区分四类状态:核心符合、强要求偏离、扩展能力未声明、未知项阻塞。开发者不能把未知项写成完成项,也不能把非当前对象写成当前承诺。

符合实现必须公开 Qwen 官方文档与上游源码之间的差异。例如本地官方文档中的 pcm 与当前 adapter 的 pcm16/pcm24 差异、Qwen3.5 输入转写模型与当前 adapter 默认 gummy-realtime-v1 的差异、response.cancel 后 stale delta 行为的未知,都必须进入证据和未知项表面。

4. 系统边界

系统边界决定当前对象承担什么,以及哪些相邻对象不进入当前合同。

4.1. 当前 WebSocket 边界

当前对象承担 Qwen3.5 Omni Realtime WebSocket adapter。WebSocket 边界包括 DashScope WebSocket 建连、session.update、音频 append、图片 append、VAD 或 Manual 控制事件、响应生命周期事件、文本和音频输出事件、工具调用事件、搜索配置与 usage、错误事件和连接恢复。

当前对象不承担 Qwen 原生 WebRTC transport。Qwen WebRTC 的 SDP over HTTP、RTP 音频输入、RTP 视频输入、RTP 音频输出和 DataChannel 控制事件,需要单独的 transport 对象。GetStream WebRTC 负责用户与 Vision Agents 的通话媒体;它不等同于 Qwen 官方 WebRTC。

4.2. 运行源码边界

当前对象的可修改源码必须在 TideSync 仓库内可审查。受控源码目录名必须表达上游派生身份和 Qwen adapter 责任面;forks/vision-agents-qwen-native 是本书用于该身份的规范目录表面。若团队仓库规范要求其他目录名,该目录名必须保留同等身份信息,并满足 受控源码和 运行时加载的边界:源码不得只存在于 external,不得通过嵌套 git 仓库隐藏 diff,不得依赖开发者本机未提交路径,不得让 TideSync 运行时继续加载 PyPI 旧 adapter。

当前对象可以引用 external 中的上游 Vision Agents clone 作为证据来源。证据来源不是完成物。完成物必须进入 TideSync PR。

4.3. Vision Agents Core 边界

Vision Agents core 是当前对象的运行承载。adapter 应优先使用 core 已有的 RealtimeAudioOutputDone、speech started/ended、transcript、interrupt、audio output flush、FunctionRegistry 和 metrics 抽象。

当 Qwen contract 无法由现有 core 抽象表达时,符合实现可以扩展 core,但扩展必须被 Core 限制和目标合同授权,并在 TideSync PR 中说明公共契约、消费者、兼容性和测试证据。adapter 不得为了绕过 core 契约而把可观察状态藏在 provider 私有变量里。

4.4. 相邻对象

Qwen 原生 WebRTC adapter 是相邻对象。它有独立 transport、独立媒体路径和独立 live 验证条件。

声音复刻创建 API 是相邻对象。声音复刻通过独立 HTTP API 创建专属音色,返回的音色名可以作为当前 adapter 的 voice 参数;创建、查询、删除音色不属于 WebSocket 会话内联 contract。

图片输出和视频输出是相邻或未知对象。控制台模型摘要列出图片和视频输出能力;本地 realtime API 响应章节只定义文本和音频输出 transport。没有官方 realtime 图片或视频输出事件时,当前 WebSocket adapter 不承担图片或视频输出。

长期记忆、多模型切换、数字人形象、前端UI改造和产品营销文案是相邻对象。它们可以参与 TideSync 产品,但不能冒充当前 Qwen WebSocket adapter 的完成条件。

4.5. 未知项边界

未知项必须保留未知身份。pcm16/pcm24 是否被服务端宽容接受、gummy-realtime-v1 是否仍被 Qwen3.5 接受、response.cancel 后是否仍可能收到旧 delta、semantic VAD 的细粒度误触发边界、WebRTC 工具与搜索样例,都需要官方补充或 live API 验证。

未知项不阻止本书定义 adapter contract。未知项阻止的是把未经确认的兼容行为写成完成事实。

5. 符合性模型

符合性模型说明一个实现如何被判定为满足本书对象。符合性来自源码身份、运行路径、contract 覆盖、测试证据和公开偏离的共同成立。

5.1. 核心符合

一个实现只有同时满足以下条件,才能声明核心符合。

第一,TideSync PR 中包含受控 Qwen adapter 派生源码、依赖解析修改、测试和本书要求的符合性声明。

第二,运行时 import 路径证明 TideSync 加载的是 PR 中的 adapter 源码。

第三,adapter 配置、客户端事件、服务端事件、状态机、打断、音频输出、转写、工具、搜索、usage 和错误处理覆盖本书 must 合同。

第四,核心断言全部通过。失败断言不能被演示成功、日志输出、模型官方能力或开发者说明抵消。

第五,所有未知项、未实现扩展和偏离项进入符合性声明。

5.2. 强要求偏离

normative=should 的要求用于约束强体验和强维护能力。失败时,实现不能沉默通过,必须在 PR 符合性声明中写明偏离、影响、证据和修复条件。

强要求偏离不自动取消核心符合,但会影响项目负责人对交付质量、上线范围和风险承受的判断。

5.3. 扩展能力符合

normative=may 的要求只在实现声明扩展能力时适用。未声明 Qwen 原生 WebRTC、声音复刻创建、图片输出或视频输出时,这些能力不作为当前对象失败项。

实现一旦声明扩展能力,该能力必须接受本书或对应扩展书稿的断言验收。扩展声明不能只停留在 README、演示或配置开关。

5.4. 不符合判定

以下情况构成不符合。

代码只存在于 external、site-packages、本机路径或外部 fork,未进入 TideSync PR。

TideSync 运行时仍加载上游 PyPI Qwen adapter。

adapter 只连接 WebSocket 或只播放音频,却没有覆盖 Qwen WebSocket lifecycle 和 Vision Agents core 投射。

打断只发送 response.cancel,没有本地音频 flush、旧响应隔离和 turn 状态重定向。

response 完成只依赖队列自然耗尽,没有 RealtimeAudioOutputDone 或等价完成边界。

工具、搜索、usage 或错误事件被日志吞掉,未进入结构化事件、状态或证据。

测试只包含真实通话演示,没有 event replay、fake WebSocket、配置断言、状态断言和运行路径断言。

未知项被写成已完成能力。

Part II: 现有系统事实图

本部分记录当前系统事实。事实图回答当前 TideSync 如何加载 Qwen adapter、Qwen 官方 WebSocket contract 包含什么、上游 Vision Agents Qwen adapter 覆盖了什么、Vision Agents core 已经能承载什么、缺口如何从事实对比中产生。

6. TideSync 运行路径

TideSync 当前运行路径由 Python 包依赖、agent 入口、环境变量、Vision Agents 插件导入和 GetStream edge 共同组成。这个路径决定 Qwen adapter 修改是否真的进入运行系统。

6.1. Python 依赖事实

TideSync 当前 pyproject.toml 在 [project].dependencies 中声明 vision-agents[qwen,getstream]>=0.6.4,<0.7。这个依赖范围允许解析上游 Vision Agents v0.6.x 包。它没有指向 TideSync 仓库内的派生源码。

该事实产生一个验收约束:PR 中即使新增 adapter 源码,如果 pyproject.toml、workspace、editable install 或等价依赖解析没有让运行时加载该源码,适配工作仍未进入 TideSync 系统。

6.2. Agent 入口事实

TideSync 当前 src/tidesync/agent.py 从 vision_agents.plugins 导入 getstream 和 qwen。create_agent() 调用 qwen.Realtime(…​) 构造实时模型对象,并用 Agent(edge=getstream.Edge(), llm=llm, …​) 进入 Vision Agents realtime flow。

该入口说明 TideSync 并不直接管理 Qwen WebSocket 事件。Qwen WebSocket 输入、输出、响应 lifecycle、打断、工具、搜索和错误处理都发生在 qwen.Realtime provider 层。TideSync 外层只能传入配置参数,不能补齐 provider 内部状态机。

6.3. 配置表面事实

RealtimeSettings 当前只包含 model、base_url、voice 和 fps 四个字段。默认模型是 qwen3.5-omni-flash-realtime,默认 WebSocket 地址是 wss://dashscope.aliyuncs.com/api-ws/v1/realtime,默认音色是 Tina,默认 FPS 是 1。

create_agent() 将这些字段传给 qwen.Realtime,并固定 include_video=True。TideSync 当前没有配置字段表达 turn_detection.type、semantic_vad、Manual 模式、enable_search、search_options、tools、audio format、input transcription model、usage 采集、错误策略或 tool/search 互斥规则。

该事实产生一个边界:TideSync 当前能够选择模型和少量输出参数,但不能让 Qwen3.5 Omni Realtime 的完整 WebSocket contract 成为一等配置面。09 号书后续的配置契约必须指定哪些字段由 adapter 内部合同固定,哪些字段通过 TideSync 配置暴露,哪些字段作为扩展配置保留。

6.4. 运行路径证据要求

运行路径必须被测试证明。符合实现需要提供一条测试或命令输出,显示 vision_agents.plugins.qwen 或等价 provider 模块路径位于 TideSync 仓库内受控源码目录,而不是 site-packages 的上游包路径。

运行路径证据必须与 PR diff 对应。若测试通过时依赖开发者本机临时 PYTHONPATH、未提交 editable clone 或 external 目录,证据无效。

7. Qwen 官方 WebSocket 合同

Qwen 官方合同是 adapter 规约的协议来源。当前书稿只使用已被本地文档或官方页面确认的合同面;文档冲突或 live 未验证行为进入未知项。

7.1. 连接与会话

Qwen-Omni-Realtime WebSocket 连接使用 realtime WebSocket 地址,并在查询参数中指定模型。请求头使用 Bearer token。连接建立后,客户端发送 session.update 配置会话。

session.update 的核心字段包括 modalities、voice、instructions、input_audio_format、output_audio_format、input_audio_transcription 和 turn_detection。本地 Qwen3.5 文档示例使用 input_audio_format: "pcm" 和 output_audio_format: "pcm"。当前上游 adapter 使用 pcm16 和 pcm24。该差异是合同差异,不是可忽略实现细节。

7.2. VAD 与 Manual

Qwen WebSocket 支持 VAD 模式和 Manual 模式。VAD 模式通过 turn_detection.type 表达,可取 server_vad 或 semantic_vad。本地文档写明 Qwen3.5 Omni Realtime 推荐 semantic_vad。

Manual 模式通过把 turn_detection 设置为 null 表达。Manual 模式下,客户端显式发送 input_audio_buffer.commit 和 response.create,并可发送 input_audio_buffer.clear 清理缓冲。WebRTC 不支持 Manual;该限制属于 Qwen 原生 WebRTC 相邻对象,不改变 WebSocket contract。

7.3. 输入事件

WebSocket 音频输入使用 input_audio_buffer.append。音频数据为 Base64 编码的 PCM 数据,输入约束是 16 kHz 单声道 PCM。WebSocket 图片输入使用 input_image_buffer.append。图片输入存在时序约束:VAD 模式下应在 input_audio_buffer.speech_stopped 前发送图片;Manual 模式下图片发送依赖当前输入轮次中已经存在音频 append。

这些约束说明,图片发送许可是“当前输入轮次”的状态,不是“这个 Python 对象曾经发送过音频”的状态。adapter 若只使用全局 once 布尔值,就无法表达当前轮次的 audio-before-image 合同。

7.4. 输出事件

WebSocket 文本输出使用 response.text.delta 和 response.text.done。WebSocket 文本加音频输出中,音频通过 response.audio.delta 返回,音频完成通过 response.audio.done 标记;音频对应转写通过 response.audio_transcript.delta 和 response.audio_transcript.done 返回。

响应生命周期还包括 response.created、response.output_item.added、conversation.item.created、response.content_part.added、response.content_part.done、response.output_item.done 和 response.done。这些事件为 adapter 提供 response、item、content、audio、text 和 finalization 边界。

7.5. 工具、搜索与 usage

Qwen3.5 Omni Realtime 支持工具调用。客户端通过 session.update.session.tools 注入工具定义。工具定义使用函数 schema。服务端通过 response.function_call_arguments.delta 和 response.function_call_arguments.done 输出工具调用参数;done 事件提供完整 name、arguments 和 call_id。客户端执行工具后,用 conversation.item.create 回传 function_call_output,随后发送 response.create 触发最终回答。

Qwen-Omni-Realtime 不支持 tool_choice 和 parallel_tool_calls。联网搜索与工具调用不兼容。搜索通过 enable_search 和 search_options.enable_source 配置。搜索计量出现在 response.done.usage.plugins.search。

7.6. 错误事件

Qwen 服务端错误事件顶层类型为 error,错误对象包含 type、code、message 和 param 等字段。session.update 参数错误、无可取消响应的 response.cancel、空缓冲 input_audio_buffer.commit、无效字段值、工具或搜索配置错误都可能返回错误事件或通用错误码。

本地文档没有封闭所有 Realtime 专属错误码和可恢复性。adapter 合同必须保留结构化错误字段,并定义已知错误对会话状态、输入状态、响应状态和视频发送许可的影响;未知错误码必须进入未知项或保守错误处理路径。

8. Vision Agents Qwen Adapter 当前行为

本章描述上游 Vision Agents v0.6.4 Qwen adapter 的当前行为。它是事实基线,不是目标规约。

8.1. Provider 身份

上游 Qwen provider 类为 Qwen3Realtime,导出为 vision_agents.plugins.qwen.Realtime。WebSocket client wrapper 为 Qwen3RealtimeClient。Qwen3Realtime 继承 Vision Agents core 的 Realtime 抽象,因此它的输出应该进入 core realtime event stream。

当前 adapter 默认模型为 qwen3.5-omni-plus-realtime,TideSync 默认模型为 qwen3.5-omni-flash-realtime。当前 adapter 默认 base URL 为国际站 DashScope 地址,TideSync 默认 base URL 为中国内地 DashScope 地址。当前 adapter 默认音色为 Cherry,TideSync 默认音色为 Tina。

这些默认值差异不是直接缺陷,但它们说明 TideSync 不能依赖上游 README 或 adapter 默认值表达自身目标。TideSync 必须明确运行时配置和验收模型。

8.2. 当前 session 配置

当前 connect() 生成的 session config 包含 modalities: ["text", "audio"]、voice、instructions、input_audio_format: "pcm16"、output_audio_format: "pcm24"、input_audio_transcription 和 turn_detection。

turn_detection.type 当前写死为 server_vad。VAD 参数包括 threshold、prefix padding 和 silence duration。adapter 没有配置入口表达 semantic_vad,也没有表达 Manual 模式。

input_audio_transcription 当前默认模型为 gummy-realtime-v1。本地 Qwen3.5 文档和补充报告指向 qwen3-asr-flash-realtime。gummy-realtime-v1 在 Qwen3.5 上是否仍被宽容接受属于 live 未知,不能作为 Qwen3.5 合同默认值。

8.3. 当前客户端事件

当前 client 能发送 session.update、input_audio_buffer.append、input_audio_buffer.commit、input_image_buffer.append 和 response.cancel。

provider 层当前实际调用音频 append、图片 append 和 response cancel。provider 没有 Manual mode 控制流,因此当前 client 虽然有 commit_audio() sender,运行时没有合同驱动它按轮次使用。client 当前没有 input_audio_buffer.clear、conversation.item.create 或 response.create sender,因此无法完成 Manual 清理和工具调用结果回传。

8.4. 当前视频发送

当前 adapter 使用 Vision Agents VideoForwarder 按 FPS 抽取视频帧,将帧转为 JPEG 后发送 input_image_buffer.append。发送前检查 _audio_emitted_once。该字段在任意一次音频 append 后变为真。

该行为只能阻止会话开始阶段先发图,不能表达 Qwen 当前输入轮次的图片发送许可。track 断开重连、speech stopped、committed、clear、服务端图片时序错误、下一轮用户输入都会改变服务端对图片 append 的接受条件;全局 once 布尔值不会随这些条件重置。

8.5. 当前服务端事件处理

当前 event loop 处理以下事件:error、session.created、response.created、response.output_item.added、response.done、input_audio_buffer.speech_started、response.audio.delta、conversation.item.input_audio_transcription.completed、response.audio_transcript.delta。

当前 event loop 没有处理:session.updated、input_audio_buffer.speech_stopped、input_audio_buffer.committed、conversation.item.created、response.content_part.added、response.text.delta、response.text.done、response.audio_transcript.done、response.audio.done、response.content_part.done、response.output_item.done、response.function_call_arguments.delta、response.function_call_arguments.done、response.done.usage 和搜索 usage。

该事实说明当前 adapter 不是 Qwen3.5 Omni Realtime WebSocket contract 的完整投射。它只接入基础语音响应路径。

8.6. 当前打断处理

当前 adapter 在收到 input_audio_buffer.speech_started 且 _is_responding 为真时调用 _on_interruption()。_on_interruption() 如果有当前 response id,则发送 response.cancel,然后把 _is_responding、_current_response_id 和 _current_item_id 清空。

当前打断处理没有调用 _emit_user_speech_started(),没有发 RealtimeAudioOutputDone(interrupted=True),没有发 RealtimeAgentSpeechEnded(interrupted=True),没有触发 Vision Agents audio output flush,没有按 response id 或 epoch 屏蔽迟到 delta。

因此,当前打断处理只覆盖远端取消的一部分,不覆盖本地可听输出和当前会话焦点的完整变换。

8.7. 当前测试覆盖

上游 Qwen plugin tests 主要是跳过的 integration tests,断言可以收到 RealtimeAudioOutput。它们没有覆盖 session config、semantic VAD、Manual、search、tools、usage、event mapping、interruption flush、audio done、transcript done、图像时序、错误恢复或 stale delta。

当前测试覆盖不能作为完整 adapter 符合性的证据。

9. Vision Agents Core 承载物

Vision Agents core 已经提供多个可承载 Qwen Realtime contract 的对象。adapter 目标不是绕开 core,而是把 Qwen 事件投射到这些承载物,并在承载物不足时定义扩展契约。

9.1. Realtime 输出事件

Core Realtime 抽象定义以下输出事件:RealtimeAudioOutput、RealtimeAudioOutputDone、RealtimeUserTranscript、RealtimeAgentTranscript、RealtimeUserSpeechStarted、RealtimeUserSpeechEnded、RealtimeAgentSpeechStarted、RealtimeAgentSpeechEnded。

这些事件提供了 Qwen 服务端事件进入 Vision Agents flow 的公共表面。Qwen adapter 不需要把 speech started、speech stopped、audio done 或 agent speech ended 写成私有日志;core 已经有可消费对象。

9.2. RealtimeInferenceFlow 消费语义

RealtimeInferenceFlow 消费 RealtimeAudioOutput 时,将音频写入 AudioOutputStream。消费 RealtimeAudioOutputDone(interrupted=False) 时,发送 final audio chunk。消费 RealtimeAudioOutputDone(interrupted=True) 时,调用 interrupt。

RealtimeInferenceFlow 消费用户 speech started/ended 时,发送 UserTurnStartedEvent 和 UserTurnEndedEvent。消费 agent speech started/ended 时,发送 AgentTurnStartedEvent 和 AgentTurnEndedEvent。消费 transcript 事件时,同步到 conversation。

这些消费语义说明,Qwen adapter 只要正确 emit core 事件,就能让已有 flow 产生用户 turn、agent turn、音频输出、音频 final、打断 flush 和 transcript 投影。

9.3. 本地音频 flush 承载物

RealtimeInferenceFlow.interrupt() 会调用 LLM interrupt、flush agent transcript、flush user transcripts、清空 AudioOutputStream 并发送 AudioOutputFlush。Agent audio producer 收到 AudioOutputFlush 后调用 GetStream audio track flush。

这个路径是当前本地播放打断的承载物。Qwen adapter 需要通过 RealtimeAudioOutputDone(interrupted=True) 或等价公共事件触发它。只发送 Qwen response.cancel 不能触发本地音频队列 flush。

9.4. 工具执行承载物

Core FunctionRegistry 能注册 async 函数,生成 ToolSchema,并通过 call_function(name, arguments) 执行工具。ToolSchema 包含工具名、描述和 JSON schema 参数。

Qwen 工具调用需要一个 provider 映射层:把 core ToolSchema 转成 Qwen session.update.session.tools;把 Qwen response.function_call_arguments.done 转成 registry 调用;把执行结果转成 Qwen conversation.item.create 的 function_call_output;再发送 response.create 触发最终回答。

这个事实说明工具执行模型不需要从零发明,但 Qwen protocol 映射必须进入 adapter。

9.5. Core 表达限制

Core 已有承载物不能自动覆盖全部 Qwen contract。usage.plugins.search、结构化 Qwen error、response content part 状态、工具调用中间状态、Qwen-specific session config、input buffer 状态和视频发送许可,都需要 adapter 内部状态、metrics 扩展或新事件表面表达。

当 core 需要扩展时,扩展必须服务明确消费者。消费者可以是 RealtimeInferenceFlow、TideSync 配置、QA 测试、metrics、PR 符合性声明或维护者。没有消费者动作的新增事件不是合格设计点。

10. 缺口图

缺口来自官方 contract、TideSync 当前运行路径、Vision Agents adapter 当前行为和 core 承载物之间的对比。缺口不是实现任务顺序;缺口是当前对象不符合原生支持定义的事实位置。

10.1. 源码与运行路径缺口

TideSync 当前没有受控 Vision Agents Qwen adapter 派生源码。运行时依赖上游包。external 中的 Vision Agents clone 可以作为证据来源,不能作为 TideSync 完成物。

缺口影响:开发者即使在 external 或 site-packages 中修改 adapter,也不能让 TideSync PR 获得可审查、可追责的完成证据。

10.2. 会话配置缺口

当前 adapter 使用 pcm16、pcm24、gummy-realtime-v1 和写死 server_vad。官方合同和本地补充报告要求将 pcm、Qwen3.5 转写模型、server_vad、semantic_vad、Manual、搜索、工具和互斥规则纳入配置判断。

缺口影响:adapter 不能表达 Qwen3.5 的官方配置面,也不能让 TideSync 对关键能力进行可审查配置。

10.3. 输入轮次与视频时序缺口

当前 _audio_emitted_once 不能表达当前输入轮次。Qwen 图片输入要求绑定本轮音频和 speech window。track 重连、speech stopped、commit、clear 和错误恢复都会改变图片发送合法性。

缺口影响:adapter 可能在服务端认为当前轮次尚未有音频时继续发送图片,造成 append image before append audio 类错误,并让视觉现场与用户语音轮次错位。

10.4. 事件映射缺口

当前 adapter 没有完整处理 Qwen 服务端事件。用户 speech ended、input committed、audio done、audio transcript done、text delta/done、content part done、output item done、function call、usage 和 search usage 都未被映射。

缺口影响:Vision Agents core 无法观察完整用户 turn、agent turn、音频完成、转写完成、工具调用、搜索计量和响应边界。

10.5. 打断缺口

当前 adapter 在 speech_started 时只在特定状态下发送 response.cancel,并清私有变量。它没有触发本地 audio flush,没有发 interrupted audio done,没有隔离旧 response delta,没有完整重定向用户 turn 和 agent turn。

缺口影响:用户可听层面可能继续听到旧回答;旧回答可能继续进入 transcript 或 conversation;新输入不能可靠成为当前焦点。

10.6. 工具、搜索与 usage 缺口

当前 adapter 不注入 tools,不处理 function call 事件,不回传 function_call_output,不发送工具后的 response.create。当前 adapter 不配置 enable_search 和 search_options,不解析 usage.plugins.search,不执行 tools 与 search 互斥规则。

缺口影响:Qwen3.5 的工具和联网搜索能力不能穿透到 TideSync;成本和搜索计量无法审查;错误配置可能被推迟到服务端报错。

10.7. 错误语义缺口

当前 adapter 对 Qwen error 事件只生成通用 Exception 字符串。结构化 error.type、code、message、param 和 event_id 没有进入公共错误对象。错误对状态机的影响没有定义。

缺口影响:QA 无法判断错误是否被正确处理,维护者无法区分参数错误、输入时序错误、工具错误、搜索互斥错误、连接错误和未知服务端错误。

10.8. 测试证据缺口

当前没有覆盖 Qwen adapter contract 的单元测试、event replay 测试、fake WebSocket 测试、状态机测试、TideSync import 路径测试、工具搜索测试和错误注入测试。

缺口影响:开发者可以用真实通话成功或模型能力说明替代可重复证据,QA 无法逐条判定实现是否完整。

Part III: 目标 Adapter 契约

本部分定义目标 Qwen WebSocket adapter 必须承担的机器规约。规约描述可控源码对象应如何配置会话、发送客户端事件、处理服务端事件、维护封闭状态集合、完成打断、接入工具与搜索、保留 usage、处理错误。

11. 源码与运行时契约

源码与运行时契约确保实现物进入 TideSync 可审查系统。没有该契约,adapter 行为无法被 TideSync PR 追责。

11.1. 受控源码

Qwen adapter 派生源码必须位于 TideSync 仓库内,并出现在 TideSync PR diff 中。该源码不得只存在于 external、site-packages、本机未提交路径、外部远程 fork 或嵌套 git 仓库。

受控源码目录必须包含上游来源说明。来源说明记录上游仓库、来源 tag、来源 commit、导入日期、导入范围、删减范围、本地修改原则和同步规则。来源说明不是装饰;它让维护者判断本地派生与上游的差异。

11.2. 运行时加载

TideSync 运行时必须加载受控源码中的 Qwen adapter。符合实现必须提供测试或命令输出,证明 vision_agents.plugins.qwen 或等价 provider 模块路径位于 TideSync 受控源码目录。

PR 中新增源码但运行时仍加载 PyPI 上游包时,源码不构成当前对象完成。PR 中修改 pyproject.toml 但没有 import 路径证据时,依赖解析不构成验收事实。

11.3. 依赖解析

TideSync 的 Python 依赖解析必须显式指向受控 adapter 源码。该指向的公共证据包括依赖配置、锁文件、安装命令和 import 路径测试;这些证据必须共同证明本地开发环境、测试环境和 CI 环境加载同一源码对象。

依赖解析不得要求开发者在本机手动设置未提交 PYTHONPATH。测试环境和 CI 环境必须能重复加载同一源码对象。

11.4. 上游兼容边界

受控源码应保留上游 Vision Agents 的包结构和公共 import 表面,除非本书合同要求改变。保留结构服务两类消费者:开发者能比较上游差异,维护者能评估未来上游合并。

删减上游文件时,删减必须依据运行、测试、打包和审查边界。纯示例、无关插件、大型资产和非当前 transport 代码可以不进入受控源码;任何删减都必须在来源说明中登记。

12. Session 配置契约

Session 配置契约定义 adapter 发送给 Qwen 的 session.update.session 对象。该对象是 Qwen WebSocket 会话的公共合同,不是 provider 私有默认值。

12.1. 模型与连接

Adapter 必须显式支持 qwen3.5-omni-flash-realtime 作为 TideSync 默认模型。qwen3.5-omni-plus-realtime 可以作为同族兼容模型进入配置和值域,但不能替代 TideSync 默认目标。

Adapter 必须支持中国内地 DashScope WebSocket 地址作为 TideSync 默认 base URL,并保留通过配置选择其他官方地域 endpoint 的能力。连接 URL 必须把模型作为 query 参数传给 Qwen WebSocket endpoint。

12.2. 输出模态、音色与指令

Adapter 必须支持 modalities: ["text", "audio"],因为 TideSync 08 号体验要求自然语音回应,并需要文本投影支撑字幕和会话记录。若 adapter 声明支持 text-only 模式,必须处理 response.text.delta 和 response.text.done;未声明 text-only 模式时,该模式不进入核心符合判断。

Adapter 必须把 voice 作为会话配置字段发送。voice 可以是官方音色名,也可以是声音复刻外部 API 返回的专属音色名。声音复刻创建过程不属于当前 WebSocket 会话 contract。

Adapter 必须发送 TideSync agent instructions。instructions 是模型行为约束的一部分,不能在派生 adapter 中丢失。

12.3. 音频格式

Adapter 的合同配置必须以官方原生 JSON 字段为准:input_audio_format 和 output_audio_format 使用 pcm。输入音频数据流承担 16 kHz 单声道 PCM 约束;输出音频事件按 24 kHz PCM 解码。

当前上游 adapter 使用 pcm16 和 pcm24。服务端是否宽容接受这两个字符串属于 live 兼容性未知。符合实现不得把宽容兼容写成合同默认值,除非提供官方补充或 live 验证证据,并在兼容性说明中登记。

12.4. 输入转写

Adapter 必须启用 input_audio_transcription,因为 TideSync 需要用户 transcript 投影支撑会话记录和验收观察。Qwen3.5 合同默认转写模型必须以本地官方文档和在线文档为准登记。

当官方文档冲突或 live 兼容性未知时,adapter 配置必须把合同目标和兼容性未知分开表达,并把冲突登记到 文档冲突登记。旧示例中的 gummy-realtime-v1 不能在没有 Qwen3.5 证据时作为合同默认值。

12.5. Turn Detection

Adapter 必须表达三种 WebSocket turn detection 状态:server_vad、semantic_vad 和 Manual null。server_vad 与 semantic_vad 共享 VAD 服务端事件处理;Manual 使用显式 commit 和 response create。

semantic_vad 必须作为 Qwen3.5 推荐配置进入可配置值域。实现可以选择默认值,但默认值必须在 TideSync 配置说明和测试中可见,不能被写死在 provider 内部。

Manual 只属于 WebSocket adapter 当前对象。WebRTC Manual 不属于当前对象,因为官方 WebRTC 仅支持 VAD。

12.6. Tools 与 Search 配置

Adapter 必须能把 Vision Agents ToolSchema 映射为 Qwen session.update.session.tools。Qwen 工具定义必须使用 {"type":"function","function":{…​}} 形状。

Adapter 必须能表达 enable_search 和 search_options.enable_source。当 tools 非空时,adapter 不得同时启用 enable_search。互斥应在配置层被拒绝或形成结构化错误,不能把明显非法配置推迟到服务端失败。

Adapter 不得向 Qwen-Omni-Realtime 发送 tool_choice 或 parallel_tool_calls。

13. 客户端事件契约

客户端事件契约定义 adapter 可以向 Qwen WebSocket 发送的事件集合。当前 WebSocket adapter 的客户端事件集合为封闭集合。

13.1. 客户端事件集合

Adapter 必须实现并测试以下客户端事件:

session.update

建立或更新会话配置。

input_audio_buffer.append

发送当前输入轮次的音频数据。

input_audio_buffer.commit

Manual 模式提交当前音频缓冲。

input_audio_buffer.clear

清理当前音频缓冲,用于 Manual 或错误恢复。

input_image_buffer.append

在当前输入轮次允许时发送图片帧。

response.cancel

取消当前响应。

conversation.item.create

回传工具执行结果,item 类型为 function_call_output。

response.create

Manual 模式或工具结果回传后触发模型生成响应。

这些事件之外的 Qwen client event 不进入当前核心合同。实现若新增其他事件,必须说明官方来源、消费者、状态影响和测试证据。

13.2. 音频 Append

input_audio_buffer.append 必须发送 16 kHz 单声道 PCM 数据的 Base64 字符串。adapter 可以从 GetStream PcmData 重采样,但发送前必须保持 Qwen 输入约束。

每次音频 append 必须更新当前输入轮次状态。该状态用于 VAD、Manual、图片发送许可、用户 participant 归属和错误恢复。

13.3. 图片 Append

input_image_buffer.append 只能在当前输入轮次满足图片发送许可时发送。许可条件由 turn 模式决定:VAD 模式下,当前轮次必须已有音频 append,且尚未进入 speech stopped 后的本轮关闭状态;Manual 模式下,图片前必须已有音频 append。

Adapter 不得使用全局 once 布尔值作为唯一图片许可。图片许可必须随输入轮次、speech stopped、commit、clear、track 断开、track 重连和图片时序错误变化。

13.4. Response Cancel

response.cancel 用于通知 Qwen 服务端取消当前响应。adapter 只有在存在可取消 response 或本地状态认为需要取消时发送该事件;若服务端返回无可取消响应错误,该错误不得使本地 audio flush 失效。

response.cancel 不是打断完成条件。打断完成还要求本地音频输出停止或失去会话支配地位、旧 response 被隔离、用户新输入进入当前焦点。

13.5. 工具结果事件

工具执行完成后,adapter 必须发送 conversation.item.create,其中 item 类型为 function_call_output,call_id 等于服务端工具调用事件中的 call id,output 是工具执行结果字符串。

工具结果发送后,adapter 必须发送 response.create 触发 Qwen 基于工具结果生成最终回答。工具失败也必须回传可解释的工具结果,不得让工具调用悬空。

14. 服务端事件契约

服务端事件契约定义 adapter 必须处理的 Qwen WebSocket 服务端事件集合。每个事件必须改变状态、产生 core 投影、更新 metrics、发送客户端事件或进入结构化错误;不能只打印日志。

14.1. Session 事件

Adapter 必须处理 session.created 和 session.updated。成功事件应确认会话配置已被服务端接受。配置错误返回 error 时,adapter 必须保留结构化错误字段。

session.created 不等同所有配置已正确应用。若 Qwen 在 session.updated 中返回实际 session 配置,adapter 测试应断言关键字段与目标配置一致或登记服务端归一化差异。

14.2. Input Buffer 事件

Adapter 必须处理 input_audio_buffer.speech_started、input_audio_buffer.speech_stopped 和 input_audio_buffer.committed。

speech_started 必须 emit RealtimeUserSpeechStarted,并在存在当前或本地仍可播放的 agent 输出时触发打断契约。

speech_stopped 必须 emit RealtimeUserSpeechEnded,并关闭当前输入轮次的图片追加窗口。

committed 必须把当前输入轮次推进到等待响应状态,并阻止继续向该轮次追加图片。

14.3. Response Lifecycle 事件

Adapter 必须处理 response.created、response.output_item.added、conversation.item.created、response.content_part.added、response.content_part.done、response.output_item.done 和 response.done。这些事件共同构成 response、item、content 和 finalization 边界。

response.created 必须绑定当前 response id。首个可输出事件或 response.created 必须触发 RealtimeAgentSpeechStarted,具体触发点必须由实现保持一致,并在测试中固定。

response.done 必须关闭当前 response 状态,解析 usage,处理搜索 usage,并清理未完成边界。它不能用空 assistant transcript final 替代真实 transcript done。

14.4. 音频输出事件

Adapter 必须处理 response.audio.delta 和 response.audio.done。response.audio.delta 必须解码为 24 kHz PCM,并以当前有效 response id 发送 RealtimeAudioOutput。已取消、已中断或过期 response 的音频 delta 不得进入可播放输出。

response.audio.done 必须发送 RealtimeAudioOutputDone(interrupted=False) 或等价完成事件,使 Vision Agents flow 能发出 final audio boundary。音频完成不得只依赖本地播放队列自然耗尽。

14.5. 转写事件

Adapter 必须处理 conversation.item.input_audio_transcription.delta 和 conversation.item.input_audio_transcription.completed。delta 进入 RealtimeUserTranscript(mode=delta);completed 进入 RealtimeUserTranscript(mode=final)。

Adapter 必须处理 response.audio_transcript.delta 和 response.audio_transcript.done。delta 进入 RealtimeAgentTranscript(mode=delta);done 进入 RealtimeAgentTranscript(mode=final),并使用服务端提供的完整文本或本地累计文本。空 final 只能在服务端确实返回空文本时出现。

若 adapter 声明支持 text-only 输出,必须处理 response.text.delta 和 response.text.done,并映射到 agent transcript。

14.6. 工具调用事件

Adapter 必须处理 response.function_call_arguments.delta 和 response.function_call_arguments.done。delta 只作为观察和累计状态;done 是工具调用的完整参数源。

done 事件必须提供 name、arguments 和 call_id。adapter 必须解析 arguments JSON,调用 FunctionRegistry,生成工具结果,发送 conversation.item.create(function_call_output),再发送 response.create。

工具调用期间 WebSocket reader 不得被同步工具执行阻塞。工具执行应使用后台任务或等价异步机制,并保留 call_id 与 response id 的关联。

14.7. Usage 事件

Adapter 必须从 response.done 中解析 usage。usage 必须保留 total tokens、input tokens、output tokens、input token details、output token details 和原始 raw usage。

当 usage 中包含 plugins.search 时,adapter 必须保留该结构。搜索 usage 不能被普通 token usage 抹掉。

14.8. Error 事件

Adapter 必须处理 Qwen error 事件,并保留 error.type、error.code、error.message、error.param 和 event_id。结构化字段必须进入测试可观察对象、日志或 LLMErrorEvent metadata 的等价表面。

错误事件必须影响状态机。图片时序错误影响图片发送许可;session配置错误影响会话可用状态;工具错误影响工具调用状态;无可取消响应错误影响 cancel 状态但不取消本地打断收敛。

15. 封闭状态模型

状态模型定义当前 WebSocket adapter 必须维护的状态集合。该集合是当前对象的封闭集合;实现可以用不同代码结构表达,但不能省略这些状态职责。

本章各状态集合的成员构成当前合同全集。实现可以增加内部辅助状态,但辅助状态不得替代、合并或取消 封闭状态模型列出的状态职责;PR 符合性声明必须说明辅助状态如何投影到本章状态集合。

15.1. 会话状态

会话状态集合包括:disconnected、connecting、session_update_sent、session_active、reconnecting、closing、closed、failed。

session_active 只能在服务端接受会话配置后成立。reconnecting 必须清理或隔离旧 response、旧输入轮次和旧视频许可。failed 必须携带结构化错误原因。

15.2. 配置状态

配置状态集合包括:draft_config、sent_config、accepted_config、rejected_config、normalized_config。

normalized_config 用于记录服务端返回配置与客户端发送配置的差异。格式字符串、ASR 模型、VAD 类型、tools/search互斥、voice 和 modalities 都属于配置状态观察对象。

15.3. 输入音频状态

输入音频状态集合包括:turn_empty、audio_appended、speech_started、speech_stopped、committed、cleared、waiting_response。

该状态按输入轮次维护。新一轮用户输入必须创建或重置输入轮次状态。speech_stopped 和 committed 后,当前轮次不再接受图片追加。

15.4. 视频帧状态

视频帧状态集合包括:no_track、track_available_waiting_audio、send_allowed、send_closed_for_turn、track_removed、track_reconnected_waiting_audio、suspended_after_image_timing_error。

send_allowed 必须依赖当前输入轮次已有音频事实。track_reconnected_waiting_audio 必须阻止新 track 在没有当前轮音频时立即发图。suspended_after_image_timing_error 必须阻止继续盲发,直到输入轮次状态重新满足许可条件。

15.5. 响应状态

响应状态集合包括:no_response、response_created、output_item_open、content_part_open、audio_streaming、text_streaming、transcript_streaming、function_call_pending、cancel_requested、interrupted、completed、failed。

响应状态按 response id 维护。当前 response id、cancelled response ids 和 completed response ids 必须可区分。属于旧 response 的 delta 不得进入当前可播放输出。

15.6. 本地音频输出状态

本地音频输出状态集合包括:no_audio_output、audio_output_streaming、audio_done_received、audio_output_done_emitted、audio_interrupted、audio_flush_emitted、stale_audio_blocked。

audio_interrupted 必须导致 audio_flush_emitted 或等价下游 flush。audio_done_received 必须导致 audio output done。stale_audio_blocked 必须记录旧 response delta 被丢弃或隔离。

15.7. 用户与助手 Turn 状态

Turn 状态集合包括:user_turn_idle、user_turn_started、user_turn_ended、agent_turn_idle、agent_turn_started、agent_turn_ended、agent_turn_interrupted。

Qwen VAD 与 response lifecycle 必须推动这些状态进入 Vision Agents core 事件。缺少 turn 投影时,上层无法判断谁正在说话、谁被打断、当前焦点在哪里。

15.8. 转写状态

转写状态集合包括:user_transcript_empty、user_transcript_delta、user_transcript_final、agent_transcript_empty、agent_transcript_delta、agent_transcript_final、transcript_interrupted_boundary。

被打断回答的 transcript 不得被当作已完整交付内容。transcript_interrupted_boundary 必须防止未输出或已取消的旧文本继续补全 conversation。

15.9. 工具、搜索与 Usage 状态

工具状态集合包括:tools_disabled、tools_registered、function_call_delta_seen、function_call_ready、tool_running、tool_succeeded、tool_failed、tool_output_sent、tool_response_requested。

搜索状态集合包括:search_disabled、search_enabled、search_source_enabled、search_rejected_due_to_tools、search_usage_seen、search_usage_missing。

Usage 状态集合包括:usage_absent、usage_seen、usage_parsed、usage_parse_failed、raw_usage_retained。

这些状态服务工具调用、成本治理和搜索证据。它们不能只存在于日志。

15.10. 错误状态

错误状态集合包括:no_error、session_config_error、connection_error_recoverable、connection_error_terminal、input_timing_error、audio_format_error、transcription_model_error、cancel_error、tool_schema_error、tool_execution_error、search_tools_conflict_error、usage_parse_error、unknown_qwen_error。

每个错误状态必须定义影响范围:会话状态、输入轮次、视频发送许可、响应状态、本地音频输出、工具状态或 usage 状态。错误状态没有影响范围时,只是日志标签,不是 adapter 规约。

16. 打断契约

打断契约定义用户在 agent 回答期间重新取得交互控制权时,adapter 必须执行的状态变换。打断不是单个事件,也不是单个客户端请求。

16.1. 打断触发

当 adapter 收到 Qwen input_audio_buffer.speech_started,必须 emit RealtimeUserSpeechStarted。如果当前存在 agent response、agent audio output、未完成 transcript 或本地仍可能播放的旧音频,adapter 必须进入打断路径。

打断路径不得依赖 _is_responding 这类单一布尔值。打断判断必须考虑 response state、本地音频输出状态和旧 response 迟到 delta 风险。

16.2. 远端取消

打断路径必须在存在可取消 response 时发送 response.cancel。无可取消 response 时,adapter 不得阻止本地打断收敛;若服务端返回 cancel error,应记录结构化错误,并继续完成本地 flush 和旧 response 隔离。

16.3. 本地音频 Flush

打断路径必须向 Vision Agents core 发出 RealtimeAudioOutputDone(interrupted=True) 或等价公共事件,使 RealtimeInferenceFlow.interrupt() 清理 transcript、AudioOutputStream 和下游 GetStream audio track buffer。

本地音频 flush 是打断成立的必要控制语义。只发送 response.cancel 不满足本书打断契约。

16.4. 旧响应隔离

打断路径必须把当前 response id 标记为 cancelled 或 interrupted。之后到达的同一 response 的 response.audio.delta、response.audio_transcript.delta、response.text.delta 和 completion 事件不得作为当前可播放输出或完整回答继续投影。

官方文档没有保证 response.cancel 后旧 delta 绝不抵达。adapter 必须以 response id、epoch 或等价机制提供 stale event 防护。

16.5. Turn 重定向

打断路径必须 emit RealtimeAgentSpeechEnded(interrupted=True) 或等价 agent turn ended 事件,并让用户新输入成为当前焦点。被打断回答中未完成或未播放内容不得被当作用户已经接收的完整交流内容。

打断完成不要求立即给出最终新答案。打断完成要求旧回答停止或失去会话支配地位,用户新输入进入听取、处理或新响应路径。

16.6. 响应性测量

实现应记录从 input_audio_buffer.speech_started 到本地 flush 触发对象之间的时间差,并在真实通话 smoke 中记录用户可听旧回答停止的观察结果。

本书不把固定毫秒数写成核心阈值。数值阈值只有在项目定义产品 SLO、测量起点、测量终点、网络条件和设备条件后,才能进入符合性声明。

17. 工具、搜索与 Usage 契约

工具、搜索与 usage 契约保证 Qwen3.5 的外部能力和成本计量不会被 adapter 丢失或混淆。

17.1. 工具 Schema 映射

Adapter 必须把 Vision Agents ToolSchema 映射为 Qwen tools 数组。每个工具项必须包含 type: "function" 和 function 对象。function 对象包含工具名、描述和 JSON schema 参数。

Adapter 不得向 Qwen-Omni-Realtime 发送 tool_choice 或 parallel_tool_calls。这些字段不属于当前 Qwen-Omni-Realtime contract。

17.2. 工具调用执行

Adapter 必须以 response.function_call_arguments.done 作为完整工具调用参数来源。arguments 必须按 JSON 解析。解析失败进入 tool_execution_error 或等价错误状态,并回传可解释工具失败结果。

Adapter 必须调用 Vision Agents FunctionRegistry 或等价受控工具注册表。未注册工具必须形成工具失败结果,不能让调用悬空。

工具执行成功后,adapter 回传工具结果并发送 response.create。工具执行失败后,adapter 回传错误结果并发送 response.create,除非错误合同明确要求终止响应。

17.3. 搜索配置与互斥

Adapter 必须通过 enable_search 表达联网搜索开关,通过 search_options.enable_source 表达来源开关。搜索关闭时不得伪造搜索 usage。

Adapter 必须拒绝 tools 与 search 同时启用的配置。该互斥来自 Qwen 官方限制。互斥失败必须在配置或结构化错误中出现,不能作为运行时随机服务端错误被动暴露。

17.4. Usage 保留

Adapter 必须解析并保留 response.done 中的 usage。保留字段包括 total tokens、input tokens、output tokens、input token details、output token details 和 raw usage。

当 usage 包含 plugins.search 时,adapter 必须保留搜索计量结构,包括 count、strategy 和服务端返回的其他字段。Usage 解析失败必须进入 usage error 状态,并保留 raw payload 供审查。

17.5. Metrics 投影

Adapter 应将 usage 与搜索 usage 投影到 Vision Agents metrics、LLM event metadata、测试记录或符合性声明可读取的表面。投影表面必须服务成本治理和验收审查。

只在 debug 日志中打印 usage 不满足强要求,因为日志不一定进入 PR 证据或测试断言。

18. 错误契约

错误契约定义 adapter 如何处理 Qwen 服务端错误、连接错误、状态机错误和本地执行错误。错误处理是状态规约,不是日志装饰。

18.1. 结构化错误

Adapter 必须保留 Qwen error 事件的结构化字段:event_id、error.type、error.code、error.message、error.param。若 core LLMErrorEvent 不足以承载这些字段,adapter 必须提供等价 metadata、测试事件或错误对象。

错误字符串可以作为人类可读摘要存在,但不能替代结构化字段。

18.2. 可恢复性

Adapter 必须区分可恢复错误和终止错误。连接关闭 1011、1012、1013、1014 可以进入重连路径;鉴权、无效模型、无效 session 配置可以进入 failed 状态;图片时序错误可以收敛到视频发送暂停并等待新输入轮次。

可恢复错误必须定义恢复后哪些状态被清理。重连后不能沿用旧 response id、旧输入轮次许可或旧本地音频输出状态。

18.3. 图片时序错误

当服务端返回图片早于音频或等价输入时序错误时,adapter 必须进入 suspended_after_image_timing_error 或等价视频状态,停止当前盲发路径,直到新的输入轮次满足图片发送许可。

Adapter 不得在记录错误后继续按原 FPS 向同一非法轮次发送图片。

18.4. 工具错误

工具 schema 错误、未知工具名、参数 JSON 解析错误和工具执行异常必须进入工具错误状态。工具错误必须生成可解释工具结果或终止当前 response,并在符合性证据中可见。

工具错误不得阻塞 WebSocket reader,不得让 function call 永久 pending。

18.5. Usage 错误

Usage 缺失不一定构成核心失败,因为服务端可能在某些错误或取消路径不返回 usage。Usage payload 存在但解析失败时,实现必须记录 parse failure,保留 raw payload,并进入偏离或错误证据。

Part IV: 符合性断言与不符合案例

本部分把 adapter 契约写成可执行断言。每条断言描述前置状态、输入事件或用户动作、期望 adapter 输出、期望 core 事件、期望 client 发送事件、不符合表现和证据要求。

19. 断言表面

本书断言检查 adapter 层公共行为。它们不是纯用户黑盒体验断言。08 号书断言表面检查用户体验;本书检查支撑该体验的 Qwen WebSocket adapter 是否正确发送事件、接收事件、更新状态、投射 core 事件、保留错误和产生证据。

19.1. 断言字段

每条断言使用稳定字段。

前置状态

adapter、TideSync 运行时、Qwen fake server 或真实服务在动作发生前的状态。

输入事件或动作

Qwen 服务端事件、用户动作、测试注入事件或配置动作。

期望 adapter 行为

adapter 必须更新的状态、产生的输出、保留的字段或执行的发送动作。

期望 core 投影

Vision Agents core 应收到的 Realtime 事件、turn 事件、audio output 或 error 投影。

期望 client 事件

adapter 应向 Qwen WebSocket 发送的客户端事件。

不符合表现

看到这些表现时,断言失败。

证据要求

测试、event replay、fake websocket 记录、import 路径、PR diff 或 live smoke 证据。

覆盖对象

被该断言验证的契约章节。

19.2. 断言强度

normative=must 的断言验证核心合同。失败时,当前 adapter 不能声明核心符合。

normative=should 的断言验证强要求。失败时,PR 必须写偏离声明。

normative=may 的断言验证声明后才适用的扩展能力。

断言失败不能被一次真实通话成功抵消。一个 fake WebSocket replay 中的核心事件映射失败,表示 adapter contract 失败;真实服务偶然没有触发该路径,不构成通过。

20. 运行源码断言

运行源码断言验证 adapter 修改是否进入 TideSync 可审查运行路径。

20.1. Adapter 源码进入 TideSync PR

前置状态

开发者提交 Qwen adapter 实现 PR。

输入事件或动作

审查 PR diff。

期望 adapter 行为

PR diff 包含 TideSync 仓库内受控 adapter 派生源码、依赖解析修改、测试和来源说明。

期望 core 投影

不适用。

期望 client 事件

不适用。

不符合表现

修改只存在于 external;修改只存在于 site-packages;修改只存在于个人 fork;PR 只改文档或 TideSync 外层配置;PR 使用 submodule 或嵌套 git 隐藏主要 diff。

证据要求

PR 文件列表、UPSTREAM 来源说明、依赖解析文件和测试文件。

覆盖对象

受控源码。

20.2. 运行时加载受控 Adapter

前置状态

TideSync 测试环境安装完成。

输入事件或动作

运行 import 路径断言测试。

期望 adapter 行为

vision_agents.plugins.qwen 或等价 Qwen provider 模块的 file 路径位于 TideSync 受控源码目录。

期望 core 投影

不适用。

期望 client 事件

不适用。

不符合表现

模块路径位于 .venv/site-packages 的上游包;模块路径位于 external;测试依赖未提交 PYTHONPATH;CI 与本地解析路径不一致。

证据要求

自动测试输出或命令输出,并记录解析路径。

覆盖对象

运行时加载。

20.3. 依赖解析可复现

前置状态

新环境或 CI 环境从 TideSync 仓库安装依赖。

输入事件或动作

执行项目规定的安装和测试命令。

期望 adapter 行为

安装后 import 路径与受控源码一致;锁文件或 workspace 配置能复现该解析。

期望 core 投影

不适用。

期望 client 事件

不适用。

不符合表现

只有开发者本机可运行;需要手动复制文件到 .venv;锁文件未更新;CI 加载旧上游包。

证据要求

安装命令、锁文件 diff、import 路径测试和 CI 输出。

覆盖对象

依赖解析。

21. Session 配置断言

Session 配置断言验证 session.update 是否表达 Qwen3.5 WebSocket adapter contract。

21.1. Session Update 使用 Qwen3.5 合同值

前置状态

adapter 准备连接 Qwen fake WebSocket。

输入事件或动作

调用 adapter connect,捕获发送的 session.update。

期望 adapter 行为

session.update.session 包含 model 对应连接参数、modalities、voice、instructions、input_audio_format、output_audio_format、input_audio_transcription 和 turn_detection。音频格式合同值使用 pcm,或在兼容模式中显式登记与测试服务端接受证据。

期望 core 投影

RealtimeConnectedEvent 或等价连接事件携带 session_config。

期望 client 事件

WebSocket 发送 session.update。

不符合表现

仍无说明地发送 pcm16 和 pcm24;丢失 instructions;未启用 input_audio_transcription;连接事件不暴露 session_config。

证据要求

fake WebSocket 发送记录和配置断言测试。

覆盖对象

Session 配置契约。

21.2. Semantic VAD 可配置

前置状态

TideSync 或 adapter 配置选择 semantic_vad。

输入事件或动作

调用 adapter connect,捕获 session.update。

期望 adapter 行为

session.turn_detection.type 为 semantic_vad,并保留 Qwen 支持的 VAD 参数。

期望 core 投影

连接事件中的 session_config 可观察到 semantic_vad。

期望 client 事件

WebSocket 发送包含 semantic_vad 的 session.update。

不符合表现

adapter 写死 server_vad;配置字段存在但发送时被忽略;测试只能检查 Python 对象属性,不能检查发送给 Qwen 的事件。

证据要求

配置测试和 fake WebSocket 发送记录。

覆盖对象

Turn Detection。

21.3. Manual 模式可表达

前置状态

adapter 配置选择 WebSocket Manual 模式。

输入事件或动作

调用 adapter connect,发送一段音频后触发提交。

期望 adapter 行为

session.turn_detection 为 null;音频 append 后 adapter 能发送 input_audio_buffer.commit 和 response.create。

期望 core 投影

用户 turn 和 agent response 仍通过 core 事件投影。

期望 client 事件

WebSocket 发送 session.update、input_audio_buffer.append、input_audio_buffer.commit、response.create。

不符合表现

client 有 commit sender 但 provider 没有模式;Manual 配置被忽略;Manual 与 WebRTC 混淆;Manual 模式下图片仍不受音频前置约束。

证据要求

Manual fake WebSocket 事件测试。

覆盖对象

Turn Detection;客户端事件集合。

21.4. Tools 与 Search 互斥

前置状态

adapter 配置同时提供 tools 和 enable_search。

输入事件或动作

构造 adapter 或调用 connect。

期望 adapter 行为

adapter 在配置层拒绝该组合,或产生结构化配置错误,不发送非法 session.update。

期望 core 投影

错误事件可观察。

期望 client 事件

不发送同时包含 tools 和 enable_search 的 session.update。

不符合表现

同时发送 tools 和 enable_search;只依赖服务端报错;错误只出现在日志字符串。

证据要求

配置负向测试和 fake WebSocket 发送记录。

覆盖对象

Tools 与 Search 配置。

22. 事件映射断言

事件映射断言验证 Qwen 服务端事件是否进入 adapter 状态和 Vision Agents core 投影。

22.1. Speech Events 映射用户 Turn

前置状态

adapter 已连接,当前 participant 已由音频输入绑定。

输入事件或动作

fake server 依次发送 input_audio_buffer.speech_started 和 input_audio_buffer.speech_stopped。

期望 adapter 行为

输入音频状态进入 speech_started 后进入 speech_stopped;图片发送许可在 speech_stopped 后关闭。

期望 core 投影

依次 emit RealtimeUserSpeechStarted 和 RealtimeUserSpeechEnded,并由 flow 产生 UserTurn started/ended。

期望 client 事件

无必需发送事件,除非 speech_started 触发打断路径。

不符合表现

speech_started 只触发 cancel;speech_stopped 被忽略;没有 user turn 投影;图片窗口仍然开放。

证据要求

event replay 测试和 core event 捕获。

覆盖对象

Input Buffer 事件。

22.2. Audio Delta 与 Done 映射音频输出

前置状态

adapter 当前 response id 为 resp_1,response 未取消。

输入事件或动作

fake server 发送 response.audio.delta,随后发送 response.audio.done。

期望 adapter 行为

delta 解码为 24 kHz PCM 并绑定 response id;done 关闭本地音频输出状态。

期望 core 投影

emit RealtimeAudioOutput(response_id="resp_1"),随后 emit RealtimeAudioOutputDone(interrupted=False, response_id="resp_1") 或等价完成事件。

期望 client 事件

无。

不符合表现

只发送音频 delta,不发送 audio done;done 被 response.done 替代且没有明确边界;音频自然 drain 被当成完成;response id 丢失。

证据要求

fake audio event replay,PCM输出和 done event 断言。

覆盖对象

音频输出事件。

22.3. Transcript Delta 与 Done 映射转写边界

前置状态

adapter 当前 response 正在输出助手音频转写。

输入事件或动作

fake server 发送多个 response.audio_transcript.delta,随后发送 response.audio_transcript.done。

期望 adapter 行为

累计或保留 delta;done 形成助手 transcript final 边界。

期望 core 投影

多个 RealtimeAgentTranscript(mode=delta) 后出现 RealtimeAgentTranscript(mode=final),final 文本来自服务端 done 或本地累计文本。

期望 client 事件

无。

不符合表现

只在 response.done 发空 final;done 被忽略;delta 后没有 final;被打断旧 transcript 继续补全。

证据要求

transcript replay 测试和 conversation update 断言。

覆盖对象

转写事件。

22.4. Response Done 解析 Usage

前置状态

adapter 当前 response 正在输出。

输入事件或动作

fake server 发送包含 usage 和 plugins.search 的 response.done。

期望 adapter 行为

response 状态进入 completed;usage 被解析;raw usage 被保留;search usage 被保留。

期望 core 投影

metrics、event metadata、测试对象或符合性证据可读取 usage。

期望 client 事件

无。

不符合表现

response.done 只清私有变量;usage 丢失;plugins.search 丢失;解析失败无错误状态。

证据要求

response.done fixture 和 usage 断言。

覆盖对象

Usage 事件。

23. 打断断言

打断断言验证 Qwen speech_started 是否改变远端 response、本地音频、旧响应和 turn 焦点。

23.1. Speech Started 中断当前响应

前置状态

当前 response resp_1 正在输出音频;本地 audio output 处于 streaming。

输入事件或动作

fake server 发送 input_audio_buffer.speech_started。

期望 adapter 行为

当前 response 标记为 interrupted 或 cancelled;用户 turn started;本地音频状态进入 audio_interrupted;旧 response 后续 delta 被隔离。

期望 core 投影

emit RealtimeUserSpeechStarted;emit RealtimeAudioOutputDone(interrupted=True) 或等价 flush 触发对象;emit RealtimeAgentSpeechEnded(interrupted=True) 或等价 agent turn interrupted。

期望 client 事件

若存在可取消 response,发送 response.cancel。

不符合表现

只发送 response.cancel;只清私有变量;本地 audio flush 不触发;旧音频继续播放;没有用户 turn started;旧 response delta 继续进入输出。

证据要求

barge-in event replay,core output 捕获,fake client send log。

覆盖对象

打断契约。

23.2. Cancel Error 不阻止本地 Flush

前置状态

adapter 已进入打断路径并发送 response.cancel。

输入事件或动作

fake server 返回无可取消响应的 error。

期望 adapter 行为

记录结构化 cancel error;保持本地打断收敛;旧 response 仍被隔离。

期望 core 投影

本地 flush 已触发或继续保持;错误事件可观察。

期望 client 事件

已发送 response.cancel。

不符合表现

cancel error 导致 flush 被撤销;旧音频继续播放;错误只出现在日志字符串;adapter 回到正常响应状态。

证据要求

cancel error replay 测试。

覆盖对象

远端取消;错误契约。

23.3. 打断后旧 Delta 被屏蔽

前置状态

response resp_1 已被打断;新输入轮次已开始。

输入事件或动作

fake server 迟到发送 resp_1 的 response.audio.delta 和 response.audio_transcript.delta。

期望 adapter 行为

识别这些事件属于旧 response;不把它们投射为当前可播放输出或完整 transcript。

期望 core 投影

不出现针对旧 response 的新 RealtimeAudioOutput;不出现补全旧回答的 agent transcript。

期望 client 事件

无。

不符合表现

旧回答停止后又继续出声;字幕继续补全被打断内容;旧 response 被当成新 response。

证据要求

delayed delta replay 测试。

覆盖对象

旧响应隔离。

23.4. 打断响应性被测量

前置状态

真实或集成 smoke 环境中 agent 正在说话。

输入事件或动作

用户开口打断。

期望 adapter 行为

记录 speech_started 到 flush 触发对象的事件差;记录旧可听输出停止或失去会话支配地位的观察结果。

期望 core 投影

可观察 flush 或 interrupted audio done。

期望 client 事件

可观察 response.cancel 或无可取消 response 的结构化错误。

不符合表现

PR 只写“打断很快”;没有测量起点和终点;用固定毫秒数但无来源;只记录 response.cancel 耗时,不记录本地音频停止。

证据要求

smoke 日志、事件时间戳和偏离声明。

覆盖对象

响应性测量。

24. 视频与输入轮次断言

视频与输入轮次断言验证图片发送是否绑定当前输入轮次。

24.1. 当前轮音频前不发送图片

前置状态

有视频 track;当前输入轮次状态为 turn_empty。

输入事件或动作

VideoForwarder 产生帧。

期望 adapter 行为

不发送 input_image_buffer.append;视频状态保持等待当前轮音频。

期望 core 投影

无。

期望 client 事件

无图片 append。

不符合表现

使用历史 audio_emitted_once 允许发图;track 重连后立即发图;fake client 收到 image append。

证据要求

视频帧发送许可测试。

覆盖对象

图片 Append;视频帧状态。

24.2. Speech Stopped 后关闭本轮图片窗口

前置状态

当前轮次已有音频,视频状态为 send_allowed。

输入事件或动作

fake server 发送 input_audio_buffer.speech_stopped,随后 VideoForwarder 产生帧。

期望 adapter 行为

输入状态进入 speech_stopped;视频状态进入 send_closed_for_turn;不再向本轮发送图片。

期望 core 投影

emit RealtimeUserSpeechEnded。

期望 client 事件

speech_stopped 后无图片 append。

不符合表现

speech_stopped 被忽略;图片继续发送到同一轮;用户已停说后旧帧继续污染当前输入。

证据要求

speech_stopped 后视频帧 replay 测试。

覆盖对象

Input Buffer 事件;视频帧状态。

24.3. 图片时序错误暂停发送

前置状态

adapter 正在发送视频帧。

输入事件或动作

fake server 返回图片早于音频或等价输入时序错误。

期望 adapter 行为

视频状态进入 suspended_after_image_timing_error;停止当前盲发路径;等待新输入轮次满足音频前置条件。

期望 core 投影

结构化错误可观察。

期望 client 事件

错误后不继续按原 FPS 发送图片。

不符合表现

错误只打印日志;继续发图;没有状态收敛;下一帧重复触发同类错误。

证据要求

error injection 测试和 fake client send log。

覆盖对象

图片时序错误。

24.4. Track 重连等待当前轮音频

前置状态

视频 track 断开后重连;旧轮次已经结束或状态未知。

输入事件或动作

新 track 产生帧。

期望 adapter 行为

视频状态进入 track_reconnected_waiting_audio;等待当前轮次音频 append 后才允许图片发送。

期望 core 投影

无。

期望 client 事件

重连后当前轮音频前无图片 append。

不符合表现

依赖旧 audio_emitted_once 立即发图;track 重连导致 append image before audio 错误。

证据要求

track reconnect fake forwarder 测试。

覆盖对象

视频帧状态。

25. 工具、搜索与 Usage 断言

本章断言验证 Qwen3.5 的外部能力和计量是否穿透 adapter。

25.1. Tool Schema 注入 Session Update

前置状态

Vision Agents registry 中注册一个工具。

输入事件或动作

调用 adapter connect。

期望 adapter 行为

将 ToolSchema 映射为 Qwen tools 数组。

期望 core 投影

连接事件或测试对象可观察 session_config 中的 tools。

期望 client 事件

session.update.session.tools 包含 function schema。

不符合表现

registry 有工具但 session.update 无 tools;schema 形状不是 Qwen function schema;发送 unsupported tool_choice 或 parallel_tool_calls。

证据要求

tools session_config 测试。

覆盖对象

工具 Schema 映射。

25.2. Function Call 执行并回传结果

前置状态

adapter 已注册工具 lookup_order。

输入事件或动作

fake server 发送 response.function_call_arguments.done,包含 name、arguments 和 call_id。

期望 adapter 行为

解析 arguments;调用 registry;发送 function_call_output;发送 response.create;工具状态从 function_call_ready 到 tool_output_sent。

期望 core 投影

工具执行错误或成功可通过测试对象、metrics 或事件观察。

期望 client 事件

发送 conversation.item.create 和 response.create。

不符合表现

工具事件被当成文本;reader 被工具执行阻塞;只调用工具不回传结果;回传结果后不发送 response.create;call_id 丢失。

证据要求

function call replay 测试和 fake client send log。

覆盖对象

工具调用执行。

25.3. 工具错误回传可解释结果

前置状态

fake server 请求一个未注册工具或传入非法 arguments。

输入事件或动作

发送 response.function_call_arguments.done。

期望 adapter 行为

进入工具错误状态;生成可解释工具失败结果;回传 function_call_output 或按错误合同终止 response。

期望 core 投影

结构化错误可观察。

期望 client 事件

发送 function_call_output 和 response.create;只有在错误合同明确要求终止 response、且对应测试断言覆盖该终止路径时,才允许不发送工具结果。

不符合表现

工具调用永久 pending;WebSocket reader 阻塞;错误只打印日志;模型永远等不到工具结果。

证据要求

tool error replay 测试。

覆盖对象

工具错误。

25.4. Search 配置发送且 Usage 保留

前置状态

adapter 配置 enable_search=true,enable_source=true,tools 为空。

输入事件或动作

connect 后 fake server 返回包含 search usage 的 response.done。

期望 adapter 行为

session.update 包含 enable_search 和 search_options;response.done usage 中 plugins.search 被保留。

期望 core 投影

metrics、event metadata、测试对象或符合性证据可读取 search usage。

期望 client 事件

发送搜索配置。

不符合表现

搜索配置没有发送;usage.plugins.search 被丢弃;搜索 usage 只在日志里;tools 和 search 同时启用。

证据要求

search config 和 usage replay 测试。

覆盖对象

搜索配置与互斥;Usage 保留。

26. 错误断言

错误断言验证 adapter 是否把错误作为状态处理,而不是只写日志。

26.1. Qwen Error 保留结构化字段

前置状态

adapter 已连接。

输入事件或动作

fake server 发送 error,包含 event_id、type、code、message、param。

期望 adapter 行为

保留结构化字段,并进入对应错误状态。

期望 core 投影

LLMErrorEvent、metadata、测试对象或等价投影可读取结构化字段。

期望 client 事件

无。

不符合表现

只构造 Exception(str(error));丢失 code 或 param;错误无法被测试断言读取。

证据要求

error event replay 测试。

覆盖对象

结构化错误。

26.2. 可恢复连接错误重置状态

前置状态

adapter 正在响应,存在当前 response、输入轮次和视频发送许可。

输入事件或动作

fake WebSocket 触发可恢复 close code。

期望 adapter 行为

进入 reconnecting;清理或隔离旧 response、旧输入轮次和旧视频许可;重连后重新发送 session.update。

期望 core 投影

连接状态变化可观察。

期望 client 事件

重连后发送新的 session.update。

不符合表现

重连后沿用旧 response id;继续发送旧轮次图片;旧音频继续播放;没有重新 session.update。

证据要求

reconnect fake WebSocket 测试。

覆盖对象

可恢复性。

26.3. Session 配置错误使会话不可用

前置状态

adapter 发送 session.update。

输入事件或动作

fake server 返回 session 参数错误。

期望 adapter 行为

配置状态进入 rejected_config;会话状态进入 failed 或受限不可用;结构化错误保留。

期望 core 投影

错误事件可观察;不得投影 connected-ready 状态。

期望 client 事件

不继续发送音频、图片或 response.create。

不符合表现

session.update 出错后仍继续发送媒体;错误只打印日志;TideSync 误以为 realtime 可用。

证据要求

session error replay 测试。

覆盖对象

结构化错误;会话状态。

26.4. Usage 解析失败保留 Raw Payload

前置状态

adapter 收到 response.done。

输入事件或动作

fake server 返回格式异常但存在 usage 字段的 payload。

期望 adapter 行为

usage 状态进入 usage_parse_failed;raw usage 保留;偏离可记录。

期望 core 投影

测试对象、metadata 或符合性声明可读取 raw payload。

期望 client 事件

无。

不符合表现

usage 解析异常导致 reader 崩溃;raw payload 丢失;失败沉默。

证据要求

malformed usage replay 测试。

覆盖对象

Usage 错误。

27. 不符合案例

不符合案例把常见假成功写成可判定失败。它们不替代断言;它们帮助评审者识别偷换。

27.1. 只改 External

实现只在 external/Vision-Agents、个人 fork 或 site-packages 中修改 Qwen adapter。TideSync PR 中看不到源码 diff,运行时 import 路径也不指向 TideSync 受控源码。

27.2. 只发送 Response Cancel

实现收到 input_audio_buffer.speech_started 后只发送 response.cancel,没有发 interrupted audio done,没有触发本地 flush,没有隔离旧 response delta,也没有投射 user turn。

该实现不符合打断契约。用户仍可能听到旧回答继续播放。

27.3. 只处理 Audio Delta

实现处理 response.audio.delta 并播放声音,但不处理 response.audio.done、response.audio_transcript.done 和 response.done.usage。

该实现只能证明音频流可播放,不能证明 response lifecycle、audio completion、transcript final 或 usage 成立。

27.4. 用 Once Flag 管理图片

实现继续用“曾经发送过音频”的全局布尔值决定是否发送图片。track 重连、speech stopped、commit、clear 或图片时序错误后,adapter 仍可能把图片发送到非法轮次。

该实现不符合视频帧状态模型。

27.5. 只有真实通话演示

实现提供一次真实通话录屏,展示能听、能看、能回答,但没有 fake WebSocket、event replay、状态机、import 路径和错误注入测试。

该实现不能证明 adapter contract 成立。真实通话录屏只能补充用户体验观察,不能替代可重复证据。

27.6. 用模型能力替代 Adapter 证据

实现引用 Qwen 官方文档说明模型支持 semantic VAD、工具调用或搜索,但 adapter 没有配置字段、事件处理、状态投影和测试。

该实现把模型能力冒充 adapter 能力,不符合原生支持定义。

该实现违反 原生支持定义。

28. 覆盖映射

覆盖映射把断言连接到对象合同。评审者可以通过本表检查核心合同是否都有断言。

合同对象 核心断言 证据类型

受控源码与运行路径

Adapter 源码进入 TideSync PR;运行时加载受控 Adapter;依赖解析可复现

PR diff、import 路径测试、安装测试

Session 配置

Session Update 使用 Qwen3.5 合同值;Semantic VAD 可配置;Manual 模式可表达;Tools 与 Search 互斥

fake WebSocket 发送记录、配置测试

Input buffer 与 turn

Speech Events 映射用户 Turn

event replay、core event 捕获

音频输出;transcript

Audio Delta 与 Done 映射音频输出;Transcript Delta 与 Done 映射转写边界

audio fixture、transcript replay

打断

Speech Started 中断当前响应;Cancel Error 不阻止本地 Flush;打断后旧 Delta 被屏蔽

barge-in replay、fake client send log

视频轮次

当前轮音频前不发送图片;Speech Stopped 后关闭本轮图片窗口;图片时序错误暂停发送;Track 重连等待当前轮音频

video fake forwarder、error injection

工具、搜索、usage

Tool Schema 注入 Session Update;Function Call 执行并回传结果;工具错误回传可解释结果;Search 配置发送且 Usage 保留;Response Done 解析 Usage;Usage 解析失败保留 Raw Payload

function call replay、search usage fixture

错误

Qwen Error 保留结构化字段;可恢复连接错误重置状态;Session 配置错误使会话不可用;Usage 解析失败保留 Raw Payload

error replay、reconnect fake WebSocket

Part V: 测试证据、PR治理与未知项

本部分定义完成声明所需证据、PR 符合性声明、上游来源、live 验证边界和维护边界。证据治理防止运行成功、演示成功或模型能力宣传冒充 adapter contract 完成。

29. 测试证据契约

测试证据契约规定 adapter 完成声明必须提交哪些可重复证据。证据必须能被 QA、维护者和项目负责人独立审查。

29.1. 静态配置测试

静态配置测试必须覆盖 session.update 的目标字段和值域。测试应捕获 fake WebSocket 收到的 session.update,断言 model、modalities、voice、instructions、audio format、input transcription、turn detection、tools、search 和互斥规则。

静态配置测试必须包含负向配置。tools 与 search 同时启用、无效 VAD 类型、Manual 与 WebRTC 混淆、缺失必要字段等情况必须产生结构化错误或配置拒绝。

29.2. Event Replay 测试

Event replay 测试必须用固定 Qwen 服务端事件序列驱动 adapter,并断言 adapter 输出、core 投影、client 发送事件和状态变化。

核心 replay 序列包括:正常 VAD 语音响应、用户转写 delta/completed、助手 audio transcript delta/done、audio delta/done、response.done usage、speech_started 打断、cancel error、stale delta、图片时序错误、function call、search usage、session error 和 reconnect。

Replay fixture 必须保留事件原始 payload。测试不得只用简化事件名,否则无法验证 response id、item id、call id、usage 和 error param。

29.3. Fake WebSocket 测试

Fake WebSocket 测试必须同时记录 server-to-adapter 事件和 adapter-to-server 事件。它用于验证 adapter 是否发送 response.cancel、conversation.item.create、response.create、input_audio_buffer.commit、input_audio_buffer.clear 和正确 session.update。

Fake WebSocket 不能只模拟成功路径。它必须支持迟到事件、错误事件、连接关闭和非法配置响应。

29.4. 状态机测试

状态机测试必须覆盖本书定义的封闭状态集合。测试不要求代码中使用同名 enum,但要求可观察地证明这些状态职责成立。

状态机测试必须覆盖跨维度变化。例如 speech_stopped 同时改变输入音频状态、用户 turn 状态和视频发送许可;打断同时改变 response 状态、本地音频状态、agent turn 状态和 transcript 边界;重连同时改变会话状态、输入轮次、response 和视频许可。

29.5. 运行路径测试

运行路径测试必须证明 TideSync 加载受控 adapter 源码。测试应在项目标准安装命令后执行,并输出或断言 provider 模块路径。

该测试必须进入普通测试套件或 CI 可运行集合。只在开发者本机手动执行的路径打印不满足核心证据要求。

29.6. 真实服务 Smoke

真实服务 smoke 用于验证官方服务端与本地 fake contract 的一致性。它应覆盖轻量连接、session.update、一次语音响应、一次打断、一次图片输入、一次工具或搜索路径。执行条件包括有效 API key、可接受费用、网络条件和模型服务可用。

缺少 API key 或费用授权时,真实服务 smoke 可以被阻塞登记。阻塞不取消 fake WebSocket 和 event replay 的核心要求。

29.7. 证据保存

PR 必须保存测试命令、测试输出摘要、失败项、跳过原因、live 阻塞原因和证据路径。证据可以位于 PR 描述、符合性声明文件或项目约定的测试报告目录。

证据不得只存在于聊天记录或个人终端滚动缓冲。

30. PR 符合性声明

PR 符合性声明是开发者交付时必须提交的审查表面。它把实现范围、断言结果、证据、偏离和未知项放在同一处。

30.1. 声明必需性

任何声称完成本书核心合同的 PR 必须包含符合性声明。没有符合性声明的 PR 不得被判定为完成,即使测试通过。

符合性声明可以是 PR 描述中的稳定章节,也可以是仓库内文件。若为仓库内文件,文件路径必须在 PR 描述中引用。

30.2. 声明字段

符合性声明必须包含以下字段。

实现范围

本 PR 修改的 adapter、core、TideSync配置、测试和文档范围。

上游来源

Vision Agents 来源 tag、commit、导入范围和本地修改摘要。

运行路径证据

provider 模块 import 路径、安装方式和依赖解析说明。

断言结果

本书核心断言逐条结果,包含通过、失败、不适用、阻塞。

测试命令

实际运行的命令和输出摘要。

Live 验证

已执行 smoke、未执行原因、费用或凭据阻塞。

偏离项

should 失败、条件限制和项目负责人需要知道的风险。

未知项

文档冲突、live 未验证、服务端行为未封闭。

回滚影响

本 PR 回滚后哪些 TideSync 功能退回上游行为。

30.3. 声明失败

符合性声明不得把失败项写成“后续优化”。核心 must 断言失败时,声明必须标记核心不符合。强要求 should 失败时,声明必须标记偏离。

声明不得用“模型支持”“本地体验正常”“演示通过”替代断言结果。断言结果必须绑定证据。

31. 上游来源与派生治理

上游来源治理使 TideSync 内 adapter 派生源码可追溯、可比较、可维护。

31.1. 来源记录

受控源码目录必须包含来源记录。来源记录必须写明:

上游仓库

GetStream/Vision-Agents 或实际导入来源。

来源 tag

例如 v0.6.4。

来源 commit

例如 9c5efe1。

导入日期

源码进入 TideSync 仓库的日期。

导入范围

保留的包、插件、测试、配置和辅助文件。

删减范围

未导入的上游目录和原因。

本地修改原则

哪些修改服务 Qwen WebSocket adapter contract。

同步规则

未来如何比较上游新版本和本地派生差异。

31.2. 导入范围

导入范围由运行、测试、打包和审查依赖决定。凡是运行受控 adapter、执行测试、解析包依赖或审查上游差异必须使用的文件,必须进入 TideSync 可审查范围。

无关插件、示例资产、演示文件和非当前 transport 代码可以不进入受控源码;删减必须登记,避免维护者误以为它们不存在于上游。

31.3. 上游同步

维护者应能比较 TideSync 派生源码与上游 Vision Agents 的差异。同步记录应说明上游 release 中哪些变化被吸收、哪些被拒绝、哪些与 Qwen adapter contract 冲突。

当上游未来原生修复 Qwen adapter 时,TideSync 不得直接删除本地 contract。必须先用本书断言验证上游行为是否满足 TideSync 对 Qwen3.5 Omni Realtime WebSocket 的合同。

32. Live 验证边界

Live 验证边界区分官方文档事实、fake contract 测试和真实服务行为。真实服务验证重要,但不能替代本地 contract 测试。

32.1. 当前 Live 阻塞项

以下项目需要有效 API key、费用授权、白名单或服务端条件。没有这些条件时,必须登记阻塞,不得伪造通过。

pcm16 与 pcm24 兼容性

官方合同值为 pcm。旧 adapter 值是否被 Qwen3.5 服务端接受需要 live 验证。

旧 ASR 模型兼容性

gummy-realtime-v1 是否仍被 Qwen3.5 接受需要 live 验证。合同默认不得依赖该未知。

Cancel 后 stale delta

response.cancel 后是否仍可能收到旧 audio 或 transcript delta 需要 live 验证。adapter 必须防护该竞态。

Semantic VAD 细节

背景音、附和声、语义打断和误触发恢复需要 live 或官方补充验证。

WebRTC 工具与搜索

Qwen 原生 WebRTC 下工具和搜索事件样例需要白名单或官方补充。当前对象是 WebSocket adapter。

声音复刻创建

创建音色需要 API key、费用授权和合规音频样本。当前 WebSocket adapter 只接受 voice 字符串。

32.2. Live 不替代 Replay

真实服务成功不能替代 event replay。Live 服务可能因为网络、服务端策略、模型状态或样例路径未覆盖而没有暴露缺口。Replay 测试必须覆盖正常、打断、错误、迟到、工具、搜索和 usage 路径。

Replay 成功也不能替代 live smoke。Replay 验证本地合同;live smoke 验证官方服务与本地合同的兼容性。两者服务不同证据问题。

32.3. 文档冲突登记

官方英文页、本地抓取文档、SDK示例和上游 adapter 默认值之间出现差异时,PR 必须登记冲突和处理策略。冲突不能被平均化为一个“默认值”。

合同字段优先以官方 Realtime JSON contract 为准;SDK enum、旧模型示例和上游 adapter 默认值只能作为兼容性线索。

33. 维护边界

维护边界定义本书如何在后续开发、上游同步和范围扩展中保持有效。

33.1. 书稿更新

当官方 Qwen contract、Vision Agents core 抽象、TideSync 运行路径或团队 PR 规则发生变化时,本书必须更新相应事实、合同、断言和未知项。不能只改代码不改契约。

书稿更新必须维护 cross reference、role、rel 和 normative 字段。新增断言必须进入覆盖映射。

33.2. 范围扩展

Qwen 原生 WebRTC、声音复刻创建、图片输出、视频输出、长期记忆和多模型路由进入实现前,必须先形成对应对象定义和验收合同。它们不能以“顺手支持”的方式污染当前 WebSocket adapter。

若扩展能力依赖当前 WebSocket adapter,扩展合同必须说明依赖关系和不变量。

33.3. 上游替换

当上游 Vision Agents 新版本声称已修复 Qwen adapter 时,TideSync 可以评估替换本地派生。替换必须经过本书断言测试。上游版本通过全部核心断言后,TideSync 才能移除或缩减本地派生源码。

上游版本未通过核心断言时,不能因为 release note 声称支持 Qwen 而替换本地 contract。

Appendix A: 附录 A:符合性检查表

本检查表供 PR 作者、评审者和 QA 使用。

34. 源码与运行路径

  • Qwen adapter 派生源码进入 TideSync 仓库。

  • PR diff 可见主要源码修改。

  • 受控源码目录包含上游来源说明。

  • 依赖解析指向受控源码。

  • import 路径测试证明运行时加载受控 adapter。

35. 配置与事件

  • session.update 使用 Qwen3.5 合同字段。

  • server_vad、semantic_vad 和 WebSocket Manual 可表达。

  • tools 与 search 互斥被配置层处理。

  • 客户端事件集合完整实现。

  • 服务端事件集合完整处理。

36. 状态与打断

  • 输入音频状态按轮次维护。

  • 视频发送许可按轮次维护。

  • response state 绑定 response id。

  • audio done 形成 output done。

  • speech_started 触发用户 turn、本地 flush、远端 cancel 和旧 response 隔离。

37. 工具、搜索、usage、错误

  • ToolSchema 映射为 Qwen tools。

  • function call 执行并回传结果。

  • search 配置发送且 usage.plugins.search 保留。

  • response.done usage 保留 raw payload。

  • transcript done 形成 final 边界。

  • cancel error 不阻止本地 flush。

  • stale delta 被屏蔽。

  • tool error 回传可解释结果或进入有断言覆盖的终止路径。

  • usage 解析失败保留 raw payload。

  • Qwen error 保留结构化字段并影响状态。

38. 证据

  • 静态配置测试通过。

  • event replay 测试通过。

  • fake WebSocket 测试通过。

  • 状态机测试通过。

  • error injection 测试通过。

  • PR 符合性声明完整。

  • live smoke 已执行或阻塞原因已登记。

Appendix B: 附录 B:断言模板

Adapter 层断言使用以下模板。

[#stable-assertion-id.assertion, normative=must]
=== 读者可见断言标题

前置状态:: adapter、TideSync 运行时、Qwen fake server 或真实服务在动作发生前的状态。

输入事件或动作:: Qwen 服务端事件、用户动作、测试注入事件或配置动作。

期望 adapter 行为:: adapter 必须更新的状态、产生的输出、保留的字段或执行的发送动作。

期望 core 投影:: Vision Agents core 应收到的 Realtime 事件、turn 事件、audio output 或 error 投影。

期望 client 事件:: adapter 应向 Qwen WebSocket 发送的客户端事件。

不符合表现:: 会导致断言失败的典型行为。

证据要求:: 测试、event replay、fake websocket 记录、import 路径、PR diff 或 live smoke 证据。

覆盖对象:: 使用真实存在的目标契约 ID 写作,例如 `xref:actual-contract-id[目标契约标题, rel=verifies]`。

模板中的 actual-contract-id 是占位符,不是本书锚点。正式断言必须替换为本书内真实存在的契约、状态或要求标题。

断言标题表达行为判定。断言正文不使用无法观察的内部心理状态,也不把实现步骤冒充对象要求。

Appendix C: 附录 C:UPSTREAM 来源说明模板

受控源码目录应包含以下来源说明。

= Upstream Provenance

== Upstream

Repository:: https://github.com/GetStream/Vision-Agents
Tag:: v0.6.4
Commit:: 9c5efe1
Import date:: YYYY-MM-DD

== Imported Paths

* agents-core/...
* plugins/qwen/...
* 其他运行、测试或打包所需路径

== Omitted Paths

* path: omission reason

== Local Modification Policy

本地修改只服务 Qwen3.5 Omni Realtime WebSocket adapter contract、TideSync 运行路径、测试证据和必要 core 承载扩展。

== Sync Policy

上游新版本进入前,必须运行 09 号书核心断言。未通过核心断言时,不得用上游 release note 替代本地验收。

Appendix D: 附录 D:结构化表面约定

本书使用稳定标题、role、rel 和 normative 字段表达可维护合同。

39. Role 词表

concept

概念定义或人工制品身份。

domain

已确认事实、源码事实、官方合同事实或运行事实。

goal

期望改变和利益相关者目标。

requirement

统摄性要求。

boundary

当前对象、相邻对象或未知项边界。

contract

可控机器规约、公共契约、证据契约或治理规则。

state

adapter 状态集合。

assertion

符合性断言。

nonconformance

不符合案例。

role 标明标题身份,不替代正文判断。

40. Rel 词表

verifies

当前断言或覆盖条目验证目标契约、状态或要求。

violates

当前不符合案例违反目标断言、契约、状态或要求。

covers

当前覆盖映射条目覆盖目标断言或契约。

depends-on

当前判断依赖目标章节提供的事实或规则。

constrains

当前章节约束目标对象的合法范围。

defines

当前章节定义目标对象。

41. Normative 字段

must

核心合同。失败时,当前 adapter 不能核心符合。

should

强要求。失败时,PR 必须写偏离声明。

may

声明后适用的扩展能力。

解释性章节、事实章节和普通例子不使用 normative 字段。

Qwen WebSocket Adapter

TideSync 仓库内受控的 Vision Agents Qwen3.5 Omni Realtime WebSocket adapter 派生源码对象。

原生支持

Qwen 官方 WebSocket contract 通过 adapter、Vision Agents core 和 TideSync 运行路径成为可配置、可观察、可测试行为。

受控源码

进入 TideSync 仓库、出现在 PR diff、可被运行路径加载和审查的源码。

Event Replay

用固定 Qwen 服务端事件序列驱动 adapter,并断言输出、状态和发送事件的测试。

Fake WebSocket

测试中替代真实 Qwen WebSocket 的可控双向事件记录器。

Stale Delta

response 被取消、中断或过期后迟到的音频、文本或转写增量。

Function Call Output

Qwen 工具调用结果回传 item,类型为 function_call_output。

Usage

Qwen response.done 中的计量字段,包括 token details 和 plugins.search。

PR 符合性声明

实现 PR 中记录实现范围、断言结果、测试命令、运行路径、上游来源、偏离项和未知项的审查表面。

受控源码目录

TideSync 仓库内承载 Vision Agents 派生源码的目录;该目录必须进入 PR diff,并通过依赖解析进入运行时。

文档冲突登记

记录官方页面、本地抓取文档、SDK示例、上游默认值和 live 行为差异的维护表面。

Qwen WebSocket Adapter

Qwen WebSocket Adapter 人工制品

原生支持

原生支持定义

受控源码

受控源码

运行时加载

运行时加载

Session 配置

Session 配置契约

客户端事件

客户端事件契约

服务端事件

服务端事件契约

状态模型

封闭状态模型

打断契约

打断契约

工具契约

工具、搜索与 Usage 契约

搜索契约

搜索配置与互斥

Usage 契约

Usage 保留

错误契约

错误契约

符合性断言

断言表面

PR 符合性声明

PR 符合性声明

Live 验证边界

Live 验证边界

文档冲突登记

文档冲突登记

读者角色入口

读者角色入口