模型能力
语音到语音
构建由 Grok 驱动的实时语音应用。通过 WebSocket 双向流式传输音频和文本,适用于语音助手、电话智能体和交互式语音系统。
快速入门
连接 Speech to Speech API 并开始对话:
import asyncio
import json
import os
import websockets
async def voice_agent():
async with websockets.connect(
"wss://api.x.ai/v1/realtime?model=grok-voice-latest",
additional_headers={"Authorization": f"Bearer {os.environ['XAI_API_KEY']}"}
) as ws:
# Configure session
await ws.send(json.dumps({
"type": "session.update",
"session": {
"voice": "eve",
"instructions": "You are a helpful assistant.",
"turn_detection": {"type": "server_vad"}
}
}))
# Send a text message
await ws.send(json.dumps({
"type": "conversation.item.create",
"item": {"type": "message", "role": "user",
"content": [{"type": "input_text", "text": "Hello!"}]}
}))
await ws.send(json.dumps({"type": "response.create"}))
# Receive audio/text responses
async for msg in ws:
event = json.loads(msg)
print(f"Event: {event['type']}")
asyncio.run(voice_agent())使用测试应用开始体验
iOS 测试应用:一款基于 Swift 的 iOS 应用,可指导你在自己的应用中配置语音智能体。
Web 智能体(WebSocket):使用 WebSocket 的 Web 应用语音智能体。
WebRTC 智能体:使用 WebRTC 的 Web 应用语音智能体。
电话智能体:使用 Twilio、可通过电话呼叫的智能体。
身份验证
使用以下任一方式验证 WebSocket 连接:
临时 token(推荐)— 面向客户端应用(浏览器、移动端)的短期 token,可避免在客户端暴露 API key。
API Key,直接在
Authorizationheader 中传入 xAI API key。仅限服务端使用。
更多信息请参阅 API 文档。
事件
WebSocket 打开后,即可开始双向事件通信。客户端事件用于提供对话信息并向 Voice API 发送用户音频,服务端事件则包含音频和文本响应。
模型选择
将 model 作为 query parameter 传入;使用带版本的名称可固定到特定 release。
MODEL = "grok-voice-latest"
url = f"wss://api.x.ai/v1/realtime?model={MODEL}"| Model | 说明 |
|---|---|
grok-voice-latest | 的别名。grok-voice-think-fast-2.0 |
grok-voice-think-fast-2.0 | 旗舰语音模型 |
会话参数
会话创建后,客户端可以发送 session.update 事件来配置会话。
| 参数 | 类型 | 说明 |
|---|---|---|
instructions | string | 系统 prompt。推荐结构请参阅 Prompting 指南。 |
reasoning.effort | "high" \| "none" \| 可选 | 控制模型是否使用推理。默认值为 "high"。 |
voice | string | 语音选择:任意内置语音(例如 eve)或 custom voice ID(请参阅 可用语音) |
tools | array | 语音智能体可用的工具。支持 file_search、web_search、x_search、mcp 和 function 类型。请参阅 使用工具。 |
turn_detection.type | string \| null | "server_vad" 表示自动检测,null 表示手动文本轮次 |
turn_detection.threshold | number \| 可选 | VAD 激活阈值(0.1–0.9)。值越高,需要越响的音频才能触发。默认值:0.85。 |
turn_detection.silence_duration_ms | number \| 可选 | 服务端结束当前轮次前,用户必须保持静音的时长(毫秒,0–10000)。值越高,用户可停顿更久而不会被打断。 |
turn_detection.prefix_padding_ms | number \| 可选 | 检测到语音开始前要包含的音频时长(毫秒,0–10000)。有助于捕获原本可能被 VAD 截掉的词首。默认值:333。 |
turn_detection.idle_timeout_ms | number \| 可选 | 设置后,若助手完成响应后在指定毫秒数内未检测到语音,服务端会主动再次与用户互动。计时器会在每次响应后重新启动,因此会每隔 idle_timeout_ms 重复触发,直到用户开口。默认值:null。 |
resumption.enabled | boolean \| 可选 | 选择启用 会话恢复:服务端按 conversation_id 缓存对话轮次,并在重新连接时重放它们,使模型继续基于之前的上下文。默认值为 false。请参阅 会话恢复。 |
audio.input.format.type | string | 输入编解码器:"audio/pcm"、"audio/pcmu"、"audio/pcma" 或 "audio/opus" |
audio.input.format.rate | number | 输入采样率(仅 PCM):8000、16000、22050、24000、32000、44100、48000 |
audio.input.transport | "json" \| "binary" \| 可选 | 输入音频的传输路径。默认值:"json"(在 input_audio_buffer.append 中使用 Base64)。"binary":将原始编解码器字节作为 WebSocket 二进制帧。请参阅 音频传输。 |
audio.output.format.type | string | 输出编解码器:"audio/pcm"、"audio/pcmu"、"audio/pcma" 或 "audio/opus" |
audio.output.format.rate | number | 输出采样率(仅 PCM):8000、16000、22050、24000、32000、44100、48000 |
audio.output.transport | "json" \| "binary" \| 可选 | 助手音频的传输路径。默认值:"json"(在 response.output_audio.delta / response.audio.delta 中使用 Base64)。"binary":将原始编解码器字节作为 WebSocket 二进制帧。会话中途的更改会在下一个响应边界生效。请参阅 音频传输。 |
audio.input.transcription.language_hint | string | BCP-47 语言代码(例如 "ja"、"ar"、"es-MX"、"pt-BR"),用于引导 ASR 转录偏向特定语言。可在会话中途更新。请参阅 语言提示。 |
audio.input.transcription.keyterms | array | 用于引导转录的关键词列表(例如产品名、专有名词)。最多 100 个词,每个最长 50 个字符。可在会话中途更新。请参阅 关键词。 |
audio.output.speed | number | 助手音频输出的播放速度倍数。范围为 0.7–1.5。默认值:1.0。低于 1.0 会减慢语速,高于 1.0 会加快语速。 |
replace | object \| 可选 | 在 TTS 前应用于模型输出的“短语到朗读替代项”映射,例如 {"Acme Mobile": "Acme Mobull"}。通过改变朗读的音频而不改变转录文本来修正发音。请参阅 发音替换。 |
Prompting
instructions 是系统 prompt。请使用第二人称,并采用固定的章节顺序,让 Agent 的输入尽量贴近训练分布。推荐结构、工具使用规范和升级处理模式请参阅 Prompting 指南。
可用语音
Speech to Speech API 和 Text to Speech API 使用同一组语音。可在 语音表 中浏览包含音色说明和示例的完整列表,或通过 GET /v1/tts/voices 以编程方式获取。将小写语音 ID 作为 voice 的参数传入 session.update。
自定义语音
需要列表之外的语音?使用 Custom Voices API 从简短的参考片段克隆任意语音。生成的 voice_id 可作为 voice 的参数传入 session.update 使用,行为与内置语音完全相同。
选择语音
在会话配置中使用 voice 参数指定语音:
# Configure session with a specific voice
session_config = {
"type": "session.update",
"session": {
"voice": "eve", # any built-in voice or custom voice ID
"instructions": "You are a helpful assistant.",
# Audio format settings (these are the defaults if not specified)
"audio": {
"input": {"format": {"type": "audio/pcm", "rate": 24000}},
"output": {"format": {"type": "audio/pcm", "rate": 24000}}
}
}
}
await ws.send(json.dumps(session_config))音频
当 turn_detection.type 设置为 server_vad 时,我们会执行语音活动检测(VAD),并自动检测用户何时结束说话。使用服务端 VAD 时,只需要 input_audio_buffer.append 事件。
否则,需要在用户结束说话后发送 commit 事件,并使用 clear 丢弃所有已追加但尚未提交的音频。
配置音频格式
在 audio 会话参数 中指定音频编解码器和采样率。输入与输出分别指定,无需保持一致。编解码器(format)与传输路径(transport);请参阅 音频传输。
| 格式 | 编码 | 容器类型 | 采样率 |
|---|---|---|---|
audio/pcm(默认) | Linear16,小端序 | 原始数据、WAV、AIFF | 可配置(见下文) |
audio/pcmu | G.711 μ-law(Mulaw) | 原始数据 | 8000 Hz |
audio/pcma | G.711 A-law | 原始数据 | 8000 Hz |
audio/opus | Opus | 原始数据包(每个 payload 一个数据包) | 24000 Hz |
使用 audio/pcm 格式时,可将 sample rate 配置为以下任一支持值:
| 采样率 | 质量 | 说明 |
|---|---|---|
| 8000 Hz | 电话音频 | 窄带,适合语音通话 |
| 16000 Hz | 宽带 | 适合语音识别 |
| 22050 Hz | 标准 | 质量与带宽均衡 |
| 24000 Hz(默认) | 高 | 推荐用于大多数场景 |
| 32000 Hz | 很高 | 提升音频清晰度 |
| 44100 Hz | CD 音质 | 音乐 / 媒体的标准规格 |
| 48000 Hz | 专业级 | 录音室级音频 |
可以在会话配置中分别设置输入与输出的音频格式和采样率:
# Configure audio format with custom sample rate for input and output
session_config = {
"type": "session.update",
"session": {
"audio": {
"input": {
"format": {
"type": "audio/pcm", # or "audio/pcmu" or "audio/pcma"
"rate": 16000 # Only applicable for audio/pcm
}
},
"output": {
"format": {
"type": "audio/pcm", # or "audio/pcmu" or "audio/pcma"
"rate": 16000 # Only applicable for audio/pcm
}
}
},
"instructions": "You are a helpful assistant.",
}
}
await ws.send(json.dumps(session_config))音频传输
format 用于选择 编解码器。transport 用于选择这些字节在 WebSocket 上的传输方式:
| 输入 | 输出 | |
|---|---|---|
json(默认) | 在 input_audio_buffer.append | 在 response.output_audio.delta / response.audio.delta |
binary | 将原始编解码器字节作为 WebSocket 二进制 帧(无协议头) | 使用相同的二进制帧;生命周期事件(response.created、response.done、转录文本等)仍为 JSON |
省略 transport(或设置为 "json"),即可保持现有客户端不变。
输入双通道兼容:配置输入格式后,服务端会同时接受该编解码器的 两种方式,即 JSON 追加和二进制帧。请使用 input.transport 作为客户端的首选发送路径;无需在会话中途先排空一个通道再使用另一个。
输出是严格单通道的:助手音频只会通过 output.transport 输出。在会话中途更改 output.transport 会在下一个响应边界生效,因此单个话语绝不会混用 JSON 增量和二进制帧。
Opus:每个 JSON delta / audio 字段或每个二进制帧都是一个原始 Opus 数据包(24 kHz 单声道)。二进制帧不包含额外的封帧头。
示例:两个方向均通过二进制帧传输 PCM:
session_config = {
"type": "session.update",
"session": {
"audio": {
"input": {
"format": {"type": "audio/pcm", "rate": 24000},
"transport": "binary",
},
"output": {
"format": {"type": "audio/pcm", "rate": 24000},
"transport": "binary",
},
},
},
}
await ws.send(json.dumps(session_config))
# Send mic audio as raw PCM16 little-endian frames (not base64 JSON)
await ws.send(pcm16_bytes) # WebSocket binary message
# Receive: binary messages are audio; text messages are JSON events
async for message in ws:
if isinstance(message, bytes):
# raw PCM16 (or Opus packets if format is audio/opus)
play(message)
else:
event = json.loads(message)
# response.done, transcripts, etc.接收并播放音频
当 output.transport 为 "json"(默认)时,解码并播放从 API 收到的 Base64 PCM16 音频。请使用与配置相同的采样率。对于 transport: "binary",直接播放二进制帧 payload(相同的编解码器字节,无需 Base64)。
import base64
import numpy as np
# Configure session with 16kHz sample rate for lower bandwidth (input and output)
session_config = {
"type": "session.update",
"session": {
"instructions": "You are a helpful assistant.",
"voice": "eve",
"turn_detection": {
"type": "server_vad",
},
"audio": {
"input": {
"format": {
"type": "audio/pcm",
"rate": 16000 # 16kHz for lower bandwidth usage
}
},
"output": {
"format": {
"type": "audio/pcm",
"rate": 16000 # 16kHz for lower bandwidth usage
}
}
}
}
}
await ws.send(json.dumps(session_config))
# When processing audio, use the same sample rate
SAMPLE_RATE = 16000
# Convert audio data to PCM16 and base64
def audio_to_base64(audio_data: np.ndarray) -> str:
"""Convert float32 audio array to base64 PCM16 string."""
# Normalize to [-1, 1] and convert to int16
audio_int16 = (audio_data * 32767).astype(np.int16)
# Encode to base64
audio_bytes = audio_int16.tobytes()
return base64.b64encode(audio_bytes).decode('utf-8')
# Convert base64 PCM16 to audio data
def base64_to_audio(base64_audio: str) -> np.ndarray:
"""Convert base64 PCM16 string to float32 audio array."""
# Decode base64
audio_bytes = base64.b64decode(base64_audio)
# Convert to int16 array
audio_int16 = np.frombuffer(audio_bytes, dtype=np.int16)
# Normalize to [-1, 1]
return audio_int16.astype(np.float32) / 32768.0发音替换
使用 replace 参数修正模型对特定单词或短语的发音。每个键都会在模型输出中进行不区分大小写的匹配,并在文字转语音 之前之前替换为对应值,因此只有朗读的音频会改变,用户看到的转录文本仍保留原始文本。
这适用于模型发音不正确的品牌名、缩写或领域术语。例如,将 "Acme Mobile" 映射为 "Acme Mobull" 可让音频正确发音,而转录文本仍显示 "Acme Mobile"。
await ws.send(json.dumps({
"type": "session.update",
"session": {
"voice": "eve",
"instructions": "You are a helpful assistant.",
"replace": {"Acme Mobile": "Acme Mobull"}
}
}))匹配行为:
匹配不区分大小写;替代内容会按你提供的大小写形式朗读。
必须匹配完整单词边界,因此
Acme, Mobile、Acme-Mobile和Acme Mobiles都不会匹配。当多个 key 具有相同 prefix 时,最长匹配优先。
可在会话中途通过另一个
session.update更新映射;已应用的映射会通过session.updated。
支持的语言
Speech to Speech API 支持 20 多种语言,并提供母语级口音。模型会自动检测输入语言,并以相同语言自然回应,无需配置。
| 语言 | 代码 |
|---|---|
| 英语 | en |
| 阿拉伯语(埃及) | ar-EG |
| 阿拉伯语(沙特阿拉伯) | ar-SA |
| 阿拉伯语(阿拉伯联合酋长国) | ar-AE |
| 孟加拉语 | bn |
| 中文(简体) | zh |
| 法语 | fr |
| 德语 | de |
| 印地语 | hi |
| 印度尼西亚语 | id |
| 意大利语 | it |
| 日语 | ja |
| 韩语 | ko |
| 葡萄牙语(巴西) | pt-BR |
| 葡萄牙语(葡萄牙) | pt-PT |
| 俄语 | ru |
| 西班牙语(墨西哥) | es-MX |
| 西班牙语(西班牙) | es-ES |
| 土耳其语 | tr |
| 越南语 | vi |
模型也能使用上述列表之外的其他语言进行对话,准确度因语言而异。你可以在系统指令中指定首选语言或口音,以获得一致的多语言体验。
语言提示
通过设置 audio.input.transcription.language_hint(位于 session.update 中)引导转录偏向特定语言。请使用 支持的语言 表格中的 BCP-47 代码。可在会话中途更改。
对于西班牙语和葡萄牙语,必须指定地区变体(例如 "es-MX"、"es-ES"、"pt-BR"、"pt-PT"),不接受单独的 "es" 和 "pt"。无法识别的代码会被静默忽略,并回退到自动语言检测。
await ws.send(json.dumps({
"type": "session.update",
"session": {
"audio": {
"input": {
"transcription": {
"language_hint": "ja"
}
}
}
}
}))关键词
通过设置 audio.input.transcription.keyterms(位于 session.update),引导转录偏向领域专用词汇,例如模型可能误转录的产品名、专有名词、品牌名或技术术语。请提供字符串数组,最多 100 个词,每个词最多 50 个字符。关键词可在会话中途更新。
await ws.send(json.dumps({
"type": "session.update",
"session": {
"audio": {
"input": {
"transcription": {
"keyterms": ["xAI", "Grok", "Understand The Universe"]
}
}
}
}
}))在 Grok Speech to Speech API 中使用工具
Grok Speech to Speech API 支持多种可在会话中配置的工具,用于增强语音智能体的能力。可在 session.update 消息中配置工具。
可用工具类型
Collections Search(
file_search):搜索已上传的文档集合Web Search(
web_search):搜索网络以获取最新信息X Search(
x_search):在 X(Twitter)中搜索帖子和信息远程 MCP 工具(
mcp),连接外部 MCP(Model Context Protocol) 服务器以使用自定义工具自定义函数:使用 JSON Schema 定义自己的函数工具
使用 file_search
使用 file_search 工具,让语音智能体能够搜索文档集合。需要先通过 Collections API。
COLLECTION_ID = "your-collection-id" # Replace with your collection ID
session_config = {
"type": "session.update",
"session": {
...
"tools": [
{
"type": "file_search",
"vector_store_ids": [COLLECTION_ID],
"max_num_results": 10,
},
],
},
}Web Search 和 X Search
配置 Web Search 和 X Search 工具,让语音智能体能够访问网络和 X(Twitter)上的最新信息。两种工具均在服务端运行,只需将它们列入 session.tools 即可启用;还可选择使用与文本 API Web Search 和 X Search 工具相同的过滤参数。
session_config = {
"type": "session.update",
"session": {
...
"tools": [
{
"type": "web_search",
"allowed_domains": ["x.ai", "docs.x.ai"],
"location": {"country": "US", "city": "San Francisco"},
},
{
"type": "x_search",
"allowed_x_handles": ["xai"],
"from_date": "2025-01-01",
"to_date": "2025-06-01",
},
],
},
}Web Search 参数
| 参数 | 必需 | 说明 |
|---|---|---|
allowed_domains | 否 | 仅包含来自这些域名的结果(不含协议或路径,例如 example.com)。最多 5 个。与 excluded_domains 互斥,不要在同一个 tool 中同时设置二者。 |
excluded_domains | 否 | 排除来自这些域名的结果。最多 5 个。与 allowed_domains 互斥,不要在同一个 tool 中同时设置二者。 |
enable_image_understanding | 否 | 允许智能体查看 Web Search 找到的图像。 |
location | 否 | 按位置引导结果:country(ISO 3166-1 alpha-2 或完整名称)、city、region、timezone(IANA,例如 America/Los_Angeles)。也接受文本 API 中使用的名称 user_location。 |
X Search 参数
| 参数 | 必需 | 说明 |
|---|---|---|
allowed_x_handles | 否 | 仅包含来自这些 X 账号的帖子(不含 @)。最多 20 个。与 excluded_x_handles 互斥,不要在同一个 tool 中同时设置二者。 |
excluded_x_handles | 否 | 排除来自这些 X 账号的帖子。最多 20 个。与 allowed_x_handles 互斥,不要在同一个 tool 中同时设置二者。 |
from_date | 否 | 仅考虑从该日期开始的帖子,使用 ISO-8601 YYYY-MM-DD。不得晚于 to_date。 |
to_date | 否 | 仅考虑截至该日期的帖子,使用 ISO-8601 YYYY-MM-DD。 |
enable_image_understanding | 否 | 允许 Agent 查看帖子中的图像。 |
enable_video_understanding | 否 | 允许 Agent 查看帖子中的视频。 |
无效配置,例如条目过多、同时设置 allowed_* 和 excluded_*,或日期窗口格式错误或前后颠倒时,会通过 error 事件拒绝该配置并说明问题。会话会保持连接,先前配置仍然有效。
远程 MCP 工具
使用 mcp 工具类型,将语音智能体连接到外部 MCP(Model Context Protocol) 服务器。这样可以通过第三方或自定义工具扩展语音智能体,而无需将其实现为客户端函数;xAI 会代为管理 MCP 服务器连接和工具执行。
session_config = {
"type": "session.update",
"session": {
...
"tools": [
{
"type": "mcp",
"server_url": "https://mcp.example.com/mcp",
"server_label": "my-tools",
},
],
},
}MCP Tool 参数
| 参数 | 必需 | 说明 |
|---|---|---|
server_url | 是 | MCP 服务器的 URL。仅支持 Streaming HTTP 和 SSE 传输。 |
server_label | 是 | 用于标识服务器的标签(用于工具调用前缀)。 |
server_description | 否 | 服务器所提供内容的说明。 |
allowed_tools | 否 | 允许使用的特定工具名称列表。省略时,服务器中的所有工具均可用。 |
authorization | 否 | 在发往 MCP 服务器的请求中,通过 Authorization 请求头设置的 token。 |
headers | 否 | 发往 MCP 服务器的请求中要包含的额外请求头。 |
高级 MCP 配置
可以限制可用工具、提供身份验证并添加自定义请求头:
session_config = {
"type": "session.update",
"session": {
...
"tools": [
{
"type": "mcp",
"server_url": "https://mcp.example.com/mcp",
"server_label": "my-tools",
"server_description": "Custom business tools for order management",
"allowed_tools": ["lookup_order", "check_inventory"],
"authorization": "Bearer your-token-here",
"headers": {
"X-Custom-Header": "value"
},
},
],
},
}多个 MCP 服务器
可以同时连接多个 MCP 服务器,每个服务器提供不同能力:
session_config = {
"type": "session.update",
"session": {
...
"tools": [
{
"type": "mcp",
"server_url": "https://mcp.deepwiki.com/mcp",
"server_label": "deepwiki",
},
{
"type": "mcp",
"server_url": "https://your-tools.example.com/mcp",
"server_label": "custom-tools",
"allowed_tools": ["search_database", "format_data"],
},
],
},
}自定义函数工具
可以使用 JSON Schema 定义自定义函数工具,以扩展语音智能体的能力。
session_config = {
"type": "session.update",
"session": {
...
"tools": [
{
"type": "function",
"name": "generate_random_number",
"description": "Generate a random number between min and max values",
"parameters": {
"type": "object",
"properties": {
"min": {
"type": "number",
"description": "Minimum value (inclusive)",
},
"max": {
"type": "number",
"description": "Maximum value (inclusive)",
},
},
"required": ["min", "max"],
},
},
],
},
}组合多种工具
可以在同一个会话配置中组合多种工具类型,包括服务端工具(Web Search、X Search、集合、MCP)和客户端函数工具:
session_config = {
"type": "session.update",
"session": {
...
"tools": [
{
"type": "file_search",
"vector_store_ids": ["your-collection-id"],
"max_num_results": 10,
},
{
"type": "web_search",
},
{
"type": "x_search",
},
{
"type": "mcp",
"server_url": "https://mcp.example.com/mcp",
"server_label": "my-tools",
},
{
"type": "function",
"name": "generate_random_number",
"description": "Generate a random number",
"parameters": {
"type": "object",
"properties": {
"min": {"type": "number"},
"max": {"type": "number"},
},
"required": ["min", "max"],
},
},
],
},
}处理函数调用响应
定义自定义函数工具后,语音智能体会在对话中调用这些函数。你需要处理并执行这些函数调用,然后返回结果以继续对话。
函数调用流程
智能体决定调用函数,发送
response.function_call_arguments.done事件你的代码执行函数,处理参数并生成结果
将结果返回给智能体,发送
conversation.item.create,其中包含函数输出请求继续执行,发送
response.create,让智能体继续
完整示例
import json
import websockets
# Define your function implementations
def get_weather(location: str, units: str = "celsius"):
"""Get current weather for a location"""
# In production, call a real weather API
return {
"location": location,
"temperature": 22,
"units": units,
"condition": "Sunny",
"humidity": 45
}
def book_appointment(date: str, time: str, service: str):
"""Book an appointment"""
# In production, interact with your booking system
import random
confirmation = f"CONF{random.randint(1000, 9999)}"
return {
"status": "confirmed",
"confirmation_code": confirmation,
"date": date,
"time": time,
"service": service
}
# Map function names to implementations
FUNCTION_HANDLERS = {
"get_weather": get_weather,
"book_appointment": book_appointment
}
async def handle_function_call(ws, event):
"""Handle function call from the voice agent"""
function_name = event["name"]
call_id = event["call_id"]
arguments = json.loads(event["arguments"])
print(f"Function called: {function_name} with args: {arguments}")
# Execute the function
if function_name in FUNCTION_HANDLERS:
result = FUNCTION_HANDLERS[function_name](**arguments)
# Send result back to agent
await ws.send(json.dumps({
"type": "conversation.item.create",
"item": {
"type": "function_call_output",
"call_id": call_id,
"output": json.dumps(result)
}
}))
# Request agent to continue with the result
await ws.send(json.dumps({
"type": "response.create"
}))
else:
print(f"Unknown function: {function_name}")
# In your WebSocket message handler
async def on_message(ws, message):
event = json.loads(message)
# Listen for function calls
if event["type"] == "response.function_call_arguments.done":
await handle_function_call(ws, event)
elif event["type"] == "response.output_audio.delta":
# Handle audio response
pass函数调用事件
| 事件 | 方向 | 说明 |
|---|---|---|
response.function_call_arguments.done | Server → Client | 触发函数调用,并包含完整参数 |
conversation.item.create(function_call_output) | Client → Server | 返回函数执行结果 |
response.create | Client → Server | 请求智能体继续处理 |
并行工具调用
当模型判断需要多个函数调用才能完成请求时,会在任何音频响应之前发出多个 response.function_call_arguments.done 事件。在这种情况下,必须处理 全部 函数调用并返回结果,然后才能发出 response.create。
预期行为:
接收多个
response.function_call_arguments.done事件(每个函数调用一个)执行所有函数(可并行执行以提高性能)
为
conversation.item.create发送包含function_call_output的 每个 函数调用仅在发送所有函数输出后,发出一条
response.create以继续
强制消息
使用 force_message 让智能体说出一段硬编码、由 TTS 合成的语句,无需调用模型。这适用于预设问候语、合规声明(例如“本次通话正在录音”)、IVR 提示语或任何必须逐字传达的话语。
为conversation.item.create event,并将 item.type 设置为 "force_message":
await ws.send(json.dumps({
"type": "conversation.item.create",
"item": {
"type": "force_message",
"role": "assistant",
"interruptible": False,
"content": [{"type": "output_text", "text": "This call is being recorded."}]
}
}))
# Do NOT send response.create — the force_message IS the turn.| 字段 | 必需 | 默认值 | 说明 |
|---|---|---|---|
item.type | 是 | — | 必须为 "force_message" |
item.content[].text | 是 | — | 要通过 TTS 逐字合成的文本 |
item.interruptible | 否 | true | 当 false 时,在播放完成前会丢弃呼叫方音频 |
服务端会注入完整的响应生命周期(response.created → response.output_audio.delta → response.done),因此强制消息在客户端看来与普通模型轮次相同。
单次响应指令
通过在 instructions 上设置 response.create:
await ws.send(json.dumps({
"type": "response.create",
"response": {
"instructions": "Respond in Spanish for this turn only."
}
})),可以为单次响应覆盖会话级系统提示词。该覆盖仅适用于当前响应,后续响应会恢复使用会话 instructions。这适用于注入动态上下文(例如 CRM 数据、呼叫方信息),或在不更新会话的情况下临时改变行为。
会话恢复
默认情况下,/v1/realtime 连接会在 WebSocket 关闭时丢失对话历史。会话恢复 会缓存每个轮次,并在重新连接时重放之前的上下文,使模型继续基于此前对话。
要跨连接继续会话,请保存服务端的 conversation.created.conversation.id,并在重新连接时将其作为 ?conversation_id=<id> 传回(同时保持相同的选择启用)。
连接并选择启用,发送
resumption.enabled: true上设置session.update。从conversation.created中读取并保存分配的 ID。使用该 ID 重新连接。使用
?conversation_id=<id>重新打开 WebSocket 并再次选择启用。缓存的轮次会在第一个新轮次前重放,并以conversation.item.created事件的形式返回。
import json, websockets
# resume_id is None on a fresh conversation; pass the saved id to resume.
async def connect(resume_id=None):
url = f"wss://api.x.ai/v1/realtime?model={MODEL}"
if resume_id:
url += f"&conversation_id={resume_id}"
async with websockets.connect(url, additional_headers=headers) as ws:
# Opt in to resumption (required to cache and to replay).
await ws.send(json.dumps({
"type": "session.update",
"session": {"resumption": {"enabled": True}},
}))
async for raw in ws:
event = json.loads(raw)
if event["type"] == "conversation.created":
# Server-assigned id. Save it and pass it as
# ?conversation_id= on your next connect to resume.
saved_id = event["conversation"]["id"]
# ... handle the rest of the session会持久化并重放:用户和助手的转录文本、助手工具调用,以及你的 function_call_output 结果。
两端都必须选择启用。只有恢复会话也发送
resumption.enabled: true。过期时间。非活动 30 分钟后会删除历史记录。
最佳实践
本节介绍使用 xAI Speech to Speech API 构建低延迟、可靠且体验自然的语音应用时应遵循的关键建议。
通过并行初始化降低感知延迟
并行启动 WebSocket 连接和麦克风输入流。
应尽早建立 WebSocket 连接(包括使用临时 token 或 API key 进行身份验证)尽可能早,最好在语音界面加载或用户打开启用麦克风的页面时。
同时开始采集麦克风音频(浏览器中使用
getUserMedia,移动端或原生平台使用等价 API)。不要不会等待 WebSocket
open事件后才开始收集麦克风采样。
音频缓冲示例
// 1. Immediately request mic access and start capturing
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const audioContext = new AudioContext({ sampleRate: 24000 });
const source = audioContext.createMediaStreamSource(stream);
const processor = audioContext.createScriptProcessor(4096, 1, 1); // or AudioWorklet for better perf
source.connect(processor);
processor.connect(audioContext.destination); // optional
// Buffer incoming PCM data immediately
let earlyAudioBuffer = []; // Float32Array[] or Int16Array[]
processor.onaudioprocess = (e) => {
const input = e.inputBuffer.getChannelData(0);
earlyAudioBuffer.push(new Float32Array(input)); // or convert to PCM16
};
// 2. In parallel – connect WebSocket (may take time)
const ws = new WebSocket("wss://api.x.ai/v1/realtime?model=grok-voice-latest", [
`xai-client-secret.${token}`,
]);
ws.onopen = () => {
// Send session.update configuration
ws.send(JSON.stringify({ type: "session.update", session: { ... } }));
// Flush any buffered audio now that we're connected
if (earlyAudioBuffer.length > 0) {
flushBufferedAudioToWS(earlyAudioBuffer);
earlyAudioBuffer = [];
}
};生产环境建议
缓冲或刷新前先转换为 24 kHz PCM16 小端序。
以大小适当的消息刷新(每条包含 100ms 采样),以实现流畅传输。
重新连接时立即恢复缓冲。
避免工具调用期间音频重叠
当模型在语音响应期间调用工具时,服务端会先发送全部音频增量,然后发送函数调用事件和 response.done。如果 client 立即发送 conversation.item.create(包含函数结果),随后发送 response.create,服务端会立即开始生成下一条响应,即使客户端仍在播放上一轮的音频,从而导致音频重叠。
推荐顺序:
接收
response.function_call_arguments.done,然后执行工具发送
conversation.item.create,其中包含function_call_output等待当前轮次的音频播放完成(或接近完成)
然后发送
response.create
等待播放完成时,显示可视化的“思考中”指示器(例如动画圆点),让用户知道智能体正在处理。这样可在模型的语音响应与工具结果之后的后续响应之间形成自然停顿。
ws.on("message", async (message) => {
const event = JSON.parse(message);
if (event.type === "response.function_call_arguments.done") {
// 1. Execute the tool
const result = await executeFunction(event.name, JSON.parse(event.arguments));
// 2. Send the function result immediately
ws.send(JSON.stringify({
type: "conversation.item.create",
item: {
type: "function_call_output",
call_id: event.call_id,
output: JSON.stringify(result),
},
}));
// 3. Show a "thinking" indicator in the UI
showThinkingIndicator();
// 4. Wait for current audio playback to finish
await waitForPlaybackComplete();
// 5. Now request the next response
ws.send(JSON.stringify({ type: "response.create" }));
hideThinkingIndicator();
}
});
其他重要建议
优先使用 临时 token,保障客户端安全。
启用
server_vad,实现自动且自然的插话。保持输入 / 输出格式一致(24 kHz PCM),避免重采样。
立即流式传输输出音频增量(
response.output_audio.delta)到扬声器,不要等待完整响应。实现平滑重连,同时继续缓冲新音频。
监控 WebSocket 健康状态,并在需要时使用指数退避。
面向企业级语音
电话系统集成:通过 SIP、WebSocket 或 LiveKit 连接。原生支持 G.711 μ-law/A-law 编解码器,无转码开销。
工具调用:在实时对话期间通过函数调用连接 CRM、日历、数据库以及任意 REST 或 GraphQL 端点。
20 多种语言,支持自然发音、口音处理,以及在同一对话中无缝切换语言。
领域专业能力:精准转录医疗、法律、金融和技术术语,包括姓名、代码和地址。
SIP 电话呼叫
将 PSTN、呼叫中心或 PBX 呼叫路由到 Speech to Speech API 会话。请参阅 SIP 电话呼叫,了解使用 CreatePhoneNumberV2 进行 API 集成、呼叫控制、DTMF 和电话服务商示例。
从 OpenAI Realtime 迁移
如果已有基于 OpenAI Realtime API 构建的应用,只需进行少量更改即可切换到 Grok Speech to Speech API:更新基础 URL、更换 API key,并选择 Grok 语音模型。
第 1 步:更新 Base URL 和 API Key
使用 OpenAI SDK
如果使用官方 OpenAI SDK,请将客户端指向 xAI 端点,并提供 xAI API key:
import asyncio
from openai import AsyncOpenAI
# Before (OpenAI)
# client = AsyncOpenAI(api_key=os.environ["OPENAI_API_KEY"])
# After (xAI)
client = AsyncOpenAI(
api_key=os.environ["XAI_API_KEY"],
base_url="https://api.x.ai/v1",
)
async def main():
async with client.realtime.connect(
model="grok-voice-latest"
) as conn:
await conn.session.update(session={
"voice": "eve",
"instructions": "You are a helpful assistant.",
"turn_detection": {"type": "server_vad"},
})
# ... rest of your application code
asyncio.run(main())使用原始 WebSocket
如果通过 WebSocket 直接连接,请更改 URL 和 Authorization 请求头:
import os
import websockets
# Before (OpenAI)
# url = "wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview"
# headers = {"Authorization": f"Bearer {os.environ['OPENAI_API_KEY']}"}
# After (xAI)
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:
# Your existing event handling code works as-is
pass第 2 步:选择模型
建立连接时传入模型名称:
# Pass the model in connect()
async with client.realtime.connect(model="grok-voice-latest") as conn:
...第 3 步:模型特定最佳实践
grok-voice-think-fast-2.0(推荐)
这是最新的语音模型。新集成请使用 grok-voice-latest,使应用跟随当前推荐模型。迁移时:
简化系统提示词。该模型的能力显著增强,因此提示词应大幅缩短。请让 Grok 概括现有系统提示词,而不是逐字迁移。
移除变通提示词。不再需要为 GPT 模型编写的提示词技巧和边界情况修复。请删除仅为修补上一模型的 bug 或限制而添加的指令。
默认启用推理。默认的
reasoning.effort为"high",适用于复杂的多步指令、细腻语气和模糊查询。将其设置为"none"可禁用推理。
OpenAI Realtime API 兼容性
Grok Speech to Speech API 与 OpenAI Realtime API 兼容。大多数 OpenAI 客户端库和 SDK 只需将基础 URL 更改为 wss://api.x.ai/v1/realtime,即可使用 xAI 端点。本节记录事件命名差异和不支持的事件。
事件命名差异
对于少数 payload 不同的事件,xAI API 使用不同的事件名称:
OpenAI 的
conversation.item.input_audio_transcription.delta在 xAI API 中名为conversation.item.input_audio_transcription.updated。updated事件包含累计转录文本(可能包含对先前更新的修正),而不是增量变化。仅在audio.input.transcription.model设置为"grok-transcribe"。
不支持的客户端事件
| OpenAI 事件 | 说明 |
|---|---|
conversation.item.retrieve | 不支持。 |
output_audio_buffer.clear | 仅限 WebRTC/SIP。 |
不支持的服务端事件
| OpenAI 事件 | 说明 |
|---|---|
conversation.item.done | 不会发出。 |
conversation.item.input_audio_transcription.failed | 不会发出。 |
conversation.item.input_audio_transcription.segment | 不支持。 |
conversation.item.retrieved | 不支持。 |
output_audio_buffer.started | 仅限 WebRTC/SIP。 |
output_audio_buffer.stopped | 仅限 WebRTC/SIP。 |
output_audio_buffer.cleared | 仅限 WebRTC/SIP。 |
rate_limits.updated | 不会发出。 |
xAI 扩展
以下事件和功能为 xAI 特有,不属于 OpenAI Realtime API:
| 事件 / 功能 | 说明 |
|---|---|
force_message | 新的 conversation.item.create 项目类型,用于由 TTS 合成的预设话语。请参阅 强制消息。 |
resumption | 位于 session.update 上的字段,用于缓存对话轮次并在重新连接时重放。请参阅 会话恢复。 |
replace | 位于 session.update,用于将短语映射到 TTS 前应用的朗读替代项,在不更改转录文本的情况下修正发音。请参阅 发音替换。 |
最后更新:2026 年 9 月 12 日