CopilotKit 语音输入 QA 验证指南:Claude Agent SDK(TypeScript)集成的转录链路与端到端测试
【免费下载链接】CopilotKitThe Frontend Stack for Agents & Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit
导读
本文面向需要对 CopilotKit 集成 Demo 进行质量验证的测试工程师与集成开发者,以仓库内showcase/integrations/claude-sdk-typescript的语音输入(Voice Input)Demo 为对象,系统梳理从环境准备、人工 QA 到自动化 E2E 的完整验证路径。通过本文,你将掌握:如何验证基于 OpenAI Whisper 的语音转录链路是否健康、如何区分"样例音频注入"与"真实麦克风转录"两条路径、以及缺少OPENAI_API_KEY时应当观察到怎样的确定性错误行为,并能在源码层面定位对应实现,为排查问题提供依据。
一、被测功能概览与文档定位
语音输入 Demo 是 Claude Agent SDK(TypeScript)集成中的一个交互类示例,其在 Demo 清单 manifest.yaml 中的描述为"Mic + sample audio transcription via a guarded OpenAI Whisper service"(麦克风 + 通过带守卫的 OpenAI Whisper 服务进行样例音频转录),页面路由为/demos/voice。
整个 Demo 提供两条语音入口:
- 麦克风按钮:由
<CopilotChat />在运行时通过/info端点声明audioFileTranscriptionEnabled: true后自动渲染,点击录制、再点击停止,音频经/transcribe端点走真实转录链路; - Play sample(样例音频)按钮:一个确定性的测试/Demo 辅助按钮,点击后直接向输入框同步注入预设文本,不经过转录端点(详见 sample-audio-button.tsx 的注释说明)。
仓库中与本文主题直接相关的 QA 文档为showcase/integrations/claude-sdk-typescript/qa/voice.md,下文将以该清单的四个测试组(基础功能、样例音频转录、麦克风录音、错误处理)为骨架逐项展开,并结合源码、E2E 用例与配置进行深化。
二、前置条件与环境准备
执行语音 Demo 的 QA 验证前,需满足以下条件(继承自 QA 清单并补充说明):
| 前置条件 | 说明与验证方式 |
|---|---|
| Demo 已部署且可访问 | 前端页面(Next.js)已启动并对外可达,能打开/demos/voice;本仓库中该 Demo 的页面实现位于 page.tsx,运行时路由位于 route.ts |
| Agent 后端健康 | 语音 Demo 的 Agent 后端复用AGENT_URL指向的 Claude 服务(默认http://localhost:8000),经createClaudeHttpAgent接入运行时;需确保后端/health探测与 Agent 运行正常 |
OPENAI_API_KEY已配置 | 转录服务由 OpenAI Whisper 承担。若缺失该 Key,转录路径将被显式阻断并返回确定性错误(见第五节),这也是"错误处理"测试组的前提 |
需要特别指出的是,Demo 的前端声明了useSingleEndpoint={false}与runtimeUrl="/api/copilotkit-voice"(见 page.tsx),即前端通过独立的运行时路由与后端通信,而非与其它 Demo 共用单端点;同时显式设置了enableInspector={false},避免本地开发环境自动挂载的 web-inspector 遮罩拦截样例按钮的点击事件,从而保证 QA 与自动化脚本在本地与生产环境行为一致。
三、测试步骤一:基础功能加载验证
对应 QA 清单 "Basic Functionality" 组,逐项检查:
- 浏览器访问
/demos/voice,页面标题(<h1>)应显示"Voice input"——该标题由 voice-chat.tsx 渲染; - 页面头部右侧应可见"Play sample"按钮(实际渲染文案为 "Try a sample audio",含 🎙 图标),其
data-testid为voice-sample-audio-button,点击行为为调用onTranscribed(sampleText)同步注入文本; - 聊天区应渲染麦克风按钮。该按钮并非无条件出现:只有当运行时在
/info响应中广告audioFileTranscriptionEnabled: true(即运行时确实挂载了transcriptionService)时,react-core的 V2CopilotChatInput才会渲染它,其data-testid为copilot-start-transcribe-button。由于它要等客户端完成/info往返后才出现,冷启动的开发服务器上可能需要数秒延迟——自动化断言时需预留足够超时(E2E 中设为 15 秒,见 voice.spec.ts)。
判读要点:若页面加载正常但麦克风按钮缺失,优先怀疑运行时/info是否已声明audioFileTranscriptionEnabled,即转录服务是否真正被挂载到CopilotRuntime上,而不是只停留在前端组件的开关。
四、测试步骤二:样例音频转录验证
对应 QA 清单 "Sample audio transcription" 组:
- 点击Play sample按钮;
- 观察按钮状态由 "Transcribing…" 恢复为空闲;
- 验证聊天输入框被填入形如"What is the weather in Tokyo?"的文本。
需要澄清的是,"Play sample" 按钮本身是同步注入、无状态切换的:QA 文档描述的是 UI 层面的整体行为预期,而从源码看,该按钮点击即直接向 textarea 注入预设文案What is the weather in Tokyo?,不存在真实的 "Transcribing…" 中间态(见 sample-audio-button.tsx 与 voice-chat.tsx)。其注入实现绕过了 React 受控输入的常规 API:通过原生HTMLTextAreaElement的 value setter 写入并派发input事件,从而让 React 感知到受控组件的值变化(见 voice-chat.tsx)。
因此,这组测试实际验证的是:样例按钮是否将预设文本成功填充到输入框,以及该文本能否作为后续对话的输入发送给 Agent。后续点击发送按钮后,语音 Demo 复用后端的中性 Agent 图谱,若运行时配置了天气类工具渲染,会出现weather-card或get_weather工具卡片,否则也会出现普通 assistant 消息——E2E 对此的断言是"宽松"的:只要求某一种 Agent 产出出现在界面上(见 voice.spec.ts)。相关天气工具定义可在 headless-complete-prompt.ts 中查看。
五、测试步骤三:麦克风录音与真实转录验证
对应 QA 清单 "Mic recording" 组:
- 点击 composer 中的麦克风按钮(
copilot-start-transcribe-button); - 若浏览器弹出权限请求,允许麦克风权限;
- 简短说话后再次点击按钮停止录制;
- 验证转录文本出现在输入框中。
这是唯一真正走转录链路的路径:录音结束后,音频文件被客户端发送至运行时/transcribe端点,由TranscriptionService处理并返回文本(麦克风按钮由react-core的 V2CopilotChatInput渲染,MediaRecorder逻辑在无头环境下难以稳定模拟,因此自动化套件刻意将麦克风路径排除在 E2E 之外,交由本文所述的人工 QA 清单覆盖,见 voice.spec.ts)。
5.1 运行时侧:转录服务的挂载与守卫
/api/copilotkit-voice是一个专用运行时路由(route.ts),它直接使用 V2CopilotRuntime(注释说明 V1 包装器会丢弃transcriptionService选项),并将自定义的GuardedOpenAITranscriptionService挂载到运行时上:
- 构造时读取
process.env.OPENAI_API_KEY:若存在,则内部创建TranscriptionServiceOpenAI(来自@copilotkit/voice,底层为 OpenAI 客户端);若不存在,delegate保持为null; transcribeFile()在delegate为空时直接抛出带明确文案的 Error,错误信息提示设置OPENAI_API_KEY以启用语音转录,而不是让请求继续打到 Whisper 后再失败。
这种"守卫(Guard)"设计保证了:缺少 Key 时,错误在进入外部转录服务之前就被拦截并确定性地暴露,这正是 QA 清单中"返回干净的 401 而不是 500/503"这一预期得以成立的关键(从源码结构可以推断,守卫抛出的确定性错误由运行时统一映射为 4xx 类响应,从而避免 Whisper 底层连接类错误的 5xx 噪音)。
5.2 转录服务实现:配置项与底层调用
transcription-service-openai.ts 是@copilotkit/voice包导出的转录实现(入口见 index.ts),其核心行为:
- 模型:默认
whisper-1,可通过model覆盖; - 语言:
language(ISO-639-1,如en/de/fr)——QA 场景中建议显式传入以提升准确率与响应速度; - 提示:
prompt可选,用于引导风格或衔接上一段内容,语言需与音频一致; - 温度:
temperature取值 0~1,越低越确定、越高越有创造性; - 底层调用
openai.audio.transcriptions.create(),将TranscribeFileOptions中的audioFile与上述参数一并提交,返回response.text作为转录结果。
在当前集成中,运行时只传入了openai实例(含 Key),模型、语言、温度等均保持默认值,见 route.ts。该 Demo 使用的依赖版本为@copilotkit/voice@1.68.2与openai@5.9.0(见 package.json)。
六、测试步骤四:缺少 API Key 的错误处理验证
对应 QA 清单 "Error handling (key missing)" 组:
- 在一个未配置
OPENAI_API_KEY的部署环境中打开/demos/voice; - 点击样例按钮(或触发任何需要转录的路径);
- 验证界面呈现干净的 401 类错误(而非 500/503 服务端错误),并携带可读的错误信息。
结合 route.ts 的守卫实现,此场景下transcribeFile()会立即抛出如下文案的错误:
OPENAI_API_KEY not configured for this deployment (api key missing). Set OPENAI_API_KEY to enable voice transcription.
验证重点是错误类型与可读性:错误应能被用户与日志明确归因为"配置缺失"而不是"服务故障"。若观察到 500/503 或含 Whisper/OpenAI 底层堆栈的错误,说明转录服务未经守卫直接暴露了上游异常,属于实现缺陷。
七、自动化对照:Playwright E2E 与人工 QA 的边界
人工 QA 清单(qa/voice.md)与仓库内自动化套件 voice.spec.ts 是互补关系,两者的覆盖边界值得在测试计划中明确:
| 覆盖项 | 人工 QA(qa/voice.md) | Playwright E2E(voice.spec.ts) |
|---|---|---|
| 页面加载、标题、样例按钮、输入框、麦克风按钮 | ✅ 步骤 1 | ✅ 第一条用例(麦克风按钮超时放宽至 15s) |
| 样例按钮注入预设文本 | ✅ 步骤 2 | ✅ 第二条用例(断言 textarea 匹配/weather|tokyo/i) |
| 发送转录文本后出现 Agent 产出 | — | ✅ 第三条用例(宽松断言,超时 45s,套件超时放宽至 90s) |
| 真实麦克风转录 | ✅ 步骤 3 | ❌ 无头环境下 MediaRecorder 难以稳定模拟,刻意排除 |
| 缺少 Key 的 401 行为 | ✅ 步骤 4 | ❌ 依赖无 Key 环境,未纳入常规套件 |
E2E 的稳定性预期为:对 Railway 部署连续 3 次运行必须全部通过。另外,样例音频文件约定存放在 public/demo-audio/README.md 描述的目录中:要求为16kHz 单声道、3~5 秒、小于 100KB 的 WAV,内容为朗读 "What is the weather in Tokyo?",用于客户端主动拉取并 POST 到转录端点、从而在无麦克风权限的情况下也能演练转录流程(README 同时给出了 macOSsay+ ffmpeg、Linuxespeak-ng、Windows PowerShell 三种本地生成方式)。
八、验收标准与预期结果速查
QA 清单给出的验收标准汇总如下,可作为 CI 门禁或发布前检查表:
- 聊天界面在 3 秒内加载完成(含 "Voice input" 标题、样例按钮与输入框;麦克风按钮受
/info往返影响,首次出现可能更晚,自动化断言需放宽超时); - 样例音频的转录在 8 秒内完成(人工场景含状态切换预期;纯样例按钮路径为同步注入,实际耗时接近瞬时,E2E 断言超时设为 1s);
- 认证失败路径返回 HTTP 401 且错误信息可读(由
GuardedOpenAITranscriptionService的 Key 守卫保证,缺失OPENAI_API_KEY时不产生 5xx)。
九、常见问题排查指引
- 麦克风按钮未出现:检查运行时
/info是否声明audioFileTranscriptionEnabled: true,确认transcriptionService已传入CopilotRuntime(对照 route.ts); - 样例按钮点击无效果:确认页面仍使用
useSingleEndpoint={false}与独立runtimeUrl,并检查 textarea 的注入逻辑是否被 web-inspector 遮罩拦截(生产环境无此问题,本地开发已通过enableInspector={false}规避); - 转录报错且文案含"api key missing":即为预期内的确定性守卫错误,属于配置问题而非服务故障,按提示配置
OPENAI_API_KEY即可; - 转录结果不准确或超时:可在 TranscriptionServiceOpenAI 的配置中显式指定
language(ISO-639-1)、调整temperature或使用更优的 Whispermodel参数(本文集成当前均采用默认值)。
【免费下载链接】CopilotKitThe Frontend Stack for Agents & Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考