package voice // 客户端 ↔ 网关的单条 WebSocket 消息协议(设计 VOICE_DESIGN.md D3): // 一条连接同时承载上行音频、下行转写、下行 TTS 音频,用「帧类型 + JSON 消息类型」区分。 // // - 二进制帧(BinaryMessage):纯音频 PCM // · 上行 = 用户麦克风音频(喂 ASR) // · 下行 = Agent 回答的 TTS 音频(客户端播放) // - 文本帧(TextMessage, JSON):控制与事件(下方 ClientMsg / ServerMsg) // ClientMsg 是客户端发来的控制消息(文本帧)。音频走二进制帧,不在此。 type ClientMsg struct { Type string `json:"type"` // start:一轮语音开始(可带当前画布编排图,用它跑而非默认 DSL) Graph string `json:"graph,omitempty"` // 其它类型无额外字段:end(用户说完)、barge_in(打断,用户又开口)、bye(结束会话) } // 客户端消息类型。 const ( ClientStart = "start" // 一轮语音开始 ClientEnd = "end" // 用户说完(静音检测或手动结束)→ 触发任务 ClientBargeIn = "barge_in" // 打断:用户在 Agent 说话时又开口 → 停 TTS ClientBye = "bye" // 结束整个语音会话 ) // ServerMsg 是网关下发的控制/事件消息(文本帧)。TTS 音频走二进制帧,不在此。 type ServerMsg struct { Type string `json:"type"` // transcript:ASR 转写(Final=false 为实时部分结果,true 为最终) // reply:Agent 回答的增量文本(打字机效果,逐 token 下发,早于音频) Text string `json:"text,omitempty"` Final bool `json:"final,omitempty"` // task:转写完成、任务已提交,带 task_id 供客户端切运行视图 TaskID string `json:"task_id,omitempty"` // error:出错文案 Msg string `json:"msg,omitempty"` // action:JARVIS 让客户端执行的界面动作(P2 动作通道,见 JARVIS_BRAIN_DESIGN.md §2.2)。 // 目前仅 navigate:Action="navigate" + View(+可选 TaskID 聚焦某任务)。客户端按白名单执行。 Action string `json:"action,omitempty"` View string `json:"view,omitempty"` } // 服务端消息类型。 const ( ServerReady = "ready" // 会话就绪,可以开始说话 ServerTranscript = "transcript" // ASR 转写结果(部分/最终) ServerTask = "task" // 任务已提交(带 task_id) ServerReply = "reply" // Agent 回答增量文本(打字机;逐 token,早于音频) ServerSpeaking = "speaking" // Agent 开始出声(首段 TTS 音频将至) ServerTTSEnd = "tts_end" // 本轮 TTS 播放完毕 ServerError = "error" // 出错 ServerAction = "action" // 界面动作指令(navigate 等,客户端白名单执行) ServerAnnounce = "announce" // 主动播报(text 显示到对话流;随后照常走 speaking/音频/tts_end) ) // 音频格式(与火山 ASR/TTS 约定,客户端按此采集/播放)。 const ( AudioSampleRate = 16000 // 上行 ASR:16kHz AudioBits = 16 // 16bit AudioChannels = 1 // 单声道 )