关于 add-voice
add-voice 是一项为现有应用集成 Grok Speech to Speech 的技能,适用于需要双向语音交互的应用开发者。它说明实时 WebSocket 会话、客户端音频处理和安全认证的实现方式。
主要功能
- 01建立实时双向语音会话
- 02通过临时令牌保护客户端
- 03采集并发送麦克风音频
- 04接收并播放音频响应
- 05支持函数工具调用
适用场景
- 01为应用添加语音模式
- 02将语音交互接入聊天界面
- 03替换现有语音级联流程
- 04用实时转录显示会话文本
MCP 相关
技能说明可在会话中配置 `mcp` 工具,也支持 `web_search`、`x_search`、`file_search` 和自定义函数。
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ cursor/ plugins --skill add-voice安装后在 Claude Code 中输入/add-voice确认已加载。
详细介绍
添加语音
将 Grok 语音对语音功能添加到现有应用。在 /add-voice、输入 语音模式,或明确表示“添加 Grok 语音”时运行。
目标
实现可用的双工路径:用户应用的麦克风输入、音频输出、wss://api.x.ai/v1/realtime?model=grok-voice-latest、安全的身份验证。Cursor 没有原生麦克风;请接入应用(或示例客户端),而不是 IDE。
协议优先
与语言无关的事件循环。默认使用 TypeScript 示例。 仅在客户端 API 不同时提供简短的 Python 对照示例(例如 ws 与 websockets)。
文档
- https://docs.x.ai/developers/model-capabilities/audio/speech-to-speech
- https://docs.x.ai/developers/model-capabilities/audio/ephemeral-tokens
- 定价(仅引用文档):https://docs.x.ai/developers/pricing(约 $0.08/分钟 STS + $0.004/文本项;最长会话 120 分钟)
步骤
-
梳理应用
- 技术栈:无、OpenAI Realtime、STT→LLM→TTS 级联、仅 TTS/STT。
- 客户端:Web / Node / iOS / Android / 服务端。
- 如果存在级联或 OpenAI Realtime:将其替换为下面的单一双工循环(URL、模型、语音、事件差异);仅当产品仍需要在 Agent 之外进行单次聆听或说话时,才保留独立的
/v1/stt或/v1/tts。
-
身份验证
- 服务端:Bearer
XAI_API_KEY。 - 浏览器/移动端:后端调用
POST https://api.x.ai/v1/realtime/client_secrets,客户端使用临时令牌(Bearer 或浏览器sec-websocket-protocol:xai-client-secret.<token>)。 - 切勿将长期有效的密钥放入客户端包中。不要在聊天中粘贴密钥。
- 服务端:Bearer
-
连接 + 会话
- URL:
wss://api.x.ai/v1/realtime?model=grok-voice-latest - 连接打开时:发送
session.update,设置voice(默认为eve)、instructions、turn_detection: { type: "server_vad" }(或设为null以启用按住说话),除非应用已统一使用其他采样率,否则使用 24 kHz PCM。 - 设置
audio.input.transcription.model: "grok-transcribe",否则不会收到用户转录文本(conversation.item.input_audio_transcription.updated是累积内容,而非增量内容)。 - 如有需要,可使用工具:
web_search、x_search、file_search、mcp、自定义function。
- URL:
-
音频 I/O(应用端)
- 每个会话使用一个
AudioContext进行采集和播放,并在用户手势中创建(符合自动播放策略)。请求 24 kHz;如果浏览器提供了其他采样率,请在发送前重采样。 - 麦克风 → AudioWorklet,以约 100 ms 为单位分块 →
input_audio_buffer.append(或使用二进制传输)。并行启动 WS 和麦克风;缓存较早到达的音频,并在连接打开后发送。 - 立即播放
response.output_audio.delta;提前约 150 ms 排程,让各音频块无缝衔接。收到input_audio_buffer.speech_started时,停止所有已排程的音频(打断)。 - 转录文本行:收到
input_audio_buffer.committed(item_id)时创建用户行,在…transcription.updated中填充;从response.output_audio_transcript.delta/.done获取助手文本,在response.done时结束轮次。 - 使用函数工具时:发送
function_call_output,播放完成后再发送response.create。
- 每个会话使用一个
-
Composer UI 约定
- 在 Composer 右侧放置一个主按钮。Composer 为空时显示 波形图标(描边样式,例如 Phosphor
WaveformIcon weight="bold";绝不使用fill样式,它在 16 px 下会显示为一团);点击后启动语音模式。存在任何文本时,显示经典的发送箭头;在语音模式下,该文本会发送到实时会话中(conversation.item.create+response.create)。文本回复流式传输时,显示停止方块。 - 语音处于活动状态时,同一按钮显示动画波形(4 条柱,每条约 3 px 宽、间隔 2 px、高约 16 px,最小缩放比例为 0.4,以确保在 28 px 的按钮中仍清晰可见),点击后结束会话。动画由阶段驱动:聆听时较慢,说话时较快,连接中/思考时较慢且稍微变暗(不透明度 ≥ 0.75)。遵循
prefers-reduced-motion。不要使用 X 按钮或脉冲环。 - 状态应显示在 Composer 内,而不是周围:占位文本显示
Connecting…/Listening…/Thinking…/Speaking…,并添加sr-onlyrole="status"。不要单独放置状态行。 - 麦克风图标仅用于听写(
/add-dictation)。绝不要将其用于语音模式。
- 在 Composer 右侧放置一个主按钮。Composer 为空时显示 波形图标(描边样式,例如 Phosphor
-
TS 骨架(默认)
const url = "wss://api.x.ai/v1/realtime?model=grok-voice-latest";
// Node: pass Authorization header. Browser: use xai-client-secret.<token> protocol.
const ws = new WebSocket(url /* , { headers: { Authorization: `Bearer ${token}` } } */);
ws.addEventListener("open", () => {
ws.send(JSON.stringify({
type: "session.update",
session: {
voice: "eve",
instructions: "You are a helpful voice agent.",
turn_detection: { type: "server_vad" },
},
}));
});
ws.addEventListener("message", (ev) => {
const event = JSON.parse(String(ev.data));
if (event.type === "response.output_audio.delta") {
// decode base64 PCM and play
}
});
- Python 版本(仅当应用使用 Python 时)
import json, os, websockets
url = "wss://api.x.ai/v1/realtime?model=grok-voice-latest"
headers = {"Authorization": f"Bearer {os.environ['XAI_API_KEY']}"}
async with websockets.connect(url, additional_headers=headers) as ws:
await ws.send(json.dumps({
"type": "session.update",
"session": {
"voice": "eve",
"instructions": "You are a helpful voice agent.",
"turn_detection": {"type": "server_vad"},
},
}))
async for raw in ws:
event = json.loads(raw)
if event.get("type") == "response.output_audio.delta":
pass # decode and play
-
添加监测(首次人工测试前)
- 运行
/debug-voice:它会提出计划,然后安装仅用于开发环境的日志接收器(POST /api/voice/log→.voice-logs/<sessionId>.ndjson,已加入 gitignore)、将音频缩减为字节数的客户端日志记录器,以及 UI 中的会话 ID;所有内容均使用应用自身的语言。 - 同一个技能还包含修复循环,以及“症状 → 日志特征 → 修复”表。
- 运行
-
冒烟测试
- 通过
conversation.item.create+response.create进行文本轮次;确认收到音频或转录事件。 - 确认客户端中没有长期有效的密钥(在构建后的客户端包中 grep 环境变量名称和
client_secrets)。 - 使用耳机将应用交给用户。在扬声器模式下,麦克风会收录回复,模型就会回答自己;这是回声,不是循环中的错误。
- 使用
/debug-voice迭代调试。
- 通过
不在范围内
- 仅语音转文本(
/add-dictation)、朗读文本(/add-read-aloud) - 图像生成和纯文本推理
- 虚构的端点、事件或 CLI 标志
评论
支持 Markdown