> ## Documentation Index
> Fetch the complete documentation index at: https://platform.stepfun.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 语音对话

阶跃星辰端到端语音模型支持在对话过程中传入音频内容，并生成语音回复。用户可以通过 Chat Completion API 发送语音输入，模型会同时返回文本和音频响应，实现真正的语音到语音交互体验。

> [!NOTE]
>
> 推荐使用 `step-audio-2` 或 `step-audio-2-mini` 模型，这些模型具备最新的语音理解和生成能力。`step-audio-r1.5` 具有出色的声音理解与推理能力，能够并行边想边说，适合需要高精度语音理解的场景。

## 能力概述

* **语音输入理解**：支持用户通过语音提问，模型可以理解语音中的内容和语调
* **语音输出生成**：模型可以直接生成语音回复，无需额外的 TTS 步骤
* **多种声音选择**：支持多种预置声音风格，满足不同场景需求
* **流式输出**：支持流式返回音频数据，实现低延迟的语音交互
* **工具调用**：支持用户自定义函数调用（`step-audio-r1.5` 模型不支持）

## 核心参数

在调用 chat completions 接口时，指定 model 为以下模型，可以获得语音回复能力。

| 模型名称 | 说明 |
| - | - |
| `step-audio-r1.5` | 超强语音理解与推理模型，理解各类声音与文字，边想边说，推理能力出众 |
| `step-audio-2` | 最新的端到端语音模型，支持语音输入输出、toolcall，支持音色复刻（realtime 接口） |
| `step-audio-2-mini` | 轻量版端到端语音模型，轻便快速 |
| `step-1o-audio` | 上一代语音模型，音色多样 |

### audio 参数

`audio` 参数用于控制音频输出，只在端到端语音模型下生效。

* `voice` `string` ***required***\
  指定生成音频的声音 ID。对于 `step-audio-2` 系列模型，可用以下声音：

  * `wenrounansheng`（温柔男声）
  * `qingchunshaonv`（青春少女）
  * `livelybreezy-female`（活力少女）
  * `elegantgentle-female`（高雅女声）

  对于 `step-1o-audio` 和 `step-audio-r1.5` 模型，可通过[获取声音列表接口](https://platform.stepfun.com/docs/zh/api-reference/audio/list-voice.md)查询可用的声音 ID。

* `format` `string` ***required***\
  指定生成音频的格式：
  * `wav`：在非流式场景下（stream=false）支持
  * `pcm`：在流式场景下（stream=true）使用，格式为 24kHz、单声道、16bit

### modalities 参数

`modalities` 参数指定输出的模态类型，支持 `text`、`audio` 两种类型。

如果需要模型输出音频，则需要将 `audio` 添加到该参数中，例如：`["text", "audio"]`。

## 快速开始

### 文本输入，语音输出

最简单的使用方式是发送文本消息，让模型返回语音回复。

> [tab] python
>
> ```python copy theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
> import base64
> from openai import OpenAI
>
> client = OpenAI(api_key="YOUR_STEP_API_KEY", base_url="https://api.stepfun.com/v1")
>
> completion = client.chat.completions.create(
>     model="step-audio-2",
>     modalities=["text", "audio"],
>     messages=[
>         {
>             "role": "system",
>             "content": "你是一个老北京大爷，喜欢用京片子和用户聊天。",
>         },
>         {
>             "role": "user",
>             "content": "今儿个北京的天气咋样啊？",
>         }
>     ],
>     audio={
>         "voice": "wenrounansheng",
>         "format": "wav",
>     },
>     stream=False,
> )
>
> # 获取文字回复
> print(completion.choices[0].message.audio.transcript)
>
> # 保存音频文件
> with open("response.wav", "wb") as f:
>     f.write(base64.b64decode(completion.choices[0].message.audio.data))
> # 打开 response.wav 即可收听音频回复
> ```

> [tab] js
>
> ```js copy theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
> import OpenAI from "openai";
> import fs from "fs";
>
> const openai = new OpenAI({
>     apiKey: "YOUR_STEP_API_KEY",
>     baseURL: "https://api.stepfun.com/v1",
> });
>
> async function main() {
>     const completion = await openai.chat.completions.create({
>         model: "step-audio-2",
>         modalities: ["text", "audio"],
>         messages: [
>             {
>                 "role": "system",
>                 "content": "你是一个老北京大爷，喜欢用京片子和用户聊天。",
>             },
>             {
>                 "role": "user",
>                 "content": "今儿个北京的天气咋样啊？",
>             }
>         ],
>         audio: {
>             voice: "wenrounansheng",
>             format: "wav",
>         },
>         stream: false,
>     });
>
>     // 获取文字回复
>     console.log(completion.choices[0].message.audio.transcript);
>
>     // 保存音频文件
>     fs.writeFileSync("response.wav", Buffer.from(completion.choices[0].message.audio.data, 'base64'));
>     // 打开 response.wav 即可收听音频回复
> }
>
> main();
> ```

> [tab] curl
>
> ```bash copy theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
> curl https://api.stepfun.com/v1/chat/completions \
>   -H "Content-Type: application/json" \
>   -H "Authorization: Bearer $STEP_API_KEY" \
>   -d '{
>     "model": "step-audio-2",
>     "modalities": ["text", "audio"],
>     "messages": [
>       {
>         "role": "system",
>         "content": "你是一个老北京大爷，喜欢用京片子和用户聊天。"
>       },
>       {
>         "role": "user",
>         "content": "今儿个北京的天气咋样啊？"
>       }
>     ],
>     "audio": {
>       "voice": "wenrounansheng",
>       "format": "wav"
>     },
>     "stream": false
>   }'
> ```

### 语音输入，语音输出

支持用户通过语音提问，模型返回语音回复，实现端到端的语音对话。

> [tab] python
>
> ```python copy theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
> import base64
> from openai import OpenAI
>
> client = OpenAI(api_key="YOUR_STEP_API_KEY", base_url="https://api.stepfun.com/v1")
>
> # 读取本地音频文件并转换为 base64
> with open("question.wav", "rb") as f:
>     audio_data = base64.b64encode(f.read()).decode('utf-8')
>
> completion = client.chat.completions.create(
>     model="step-audio-2",
>     modalities=["text", "audio"],
>     messages=[
>         {
>             "role": "system",
>             "content": "你是一个动物声音识别专家，请根据用户提供的声音进行识别和回答。",
>         },
>         {
>             "role": "user",
>             "content": [
>                 {
>                     "type": "text",
>                     "text": "听听这是什么声音？",
>                 },
>                 {
>                     "type": "input_audio",
>                     "input_audio": {
>                         "data": f"data:audio/wav;base64,{audio_data}",
>                     }
>                 }
>             ],
>         }
>     ],
>     audio={
>         "voice": "qingchunshaonv",
>         "format": "wav",
>     },
>     stream=False,
> )
>
> # 获取文字回复
> print(completion.choices[0].message.audio.transcript)
>
> # 保存音频文件
> with open("response.wav", "wb") as f:
>     f.write(base64.b64decode(completion.choices[0].message.audio.data))
> ```

> [tab] js
>
> ```js copy theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
> import OpenAI from "openai";
> import fs from "fs";
>
> const openai = new OpenAI({
>     apiKey: "YOUR_STEP_API_KEY",
>     baseURL: "https://api.stepfun.com/v1",
> });
>
> // 从 URL 下载音频并转换为 base64
> const downloadAudioAsBase64 = async (url) => {
>     const response = await fetch(url);
>     const arrayBuffer = await response.arrayBuffer();
>     const buffer = Buffer.from(arrayBuffer);
>     return "data:audio/wav;base64," + buffer.toString('base64');
> }
>
> async function main() {
>     const completion = await openai.chat.completions.create({
>         model: "step-audio-2",
>         modalities: ["text", "audio"],
>         messages: [
>             {
>                 "role": "system",
>                 "content": "你是一个动物声音识别专家，请根据用户提供的声音进行识别和回答。",
>             },
>             {
>                 "role": "user",
>                 "content": [
>                     {
>                         type: "text",
>                         text: "听听这是什么声音",
>                     },
>                     {
>                         type: "input_audio",
>                         input_audio: {
>                             data: await downloadAudioAsBase64("https://stepchat-static.tos-cn-shanghai.volces.com/resource_tmp/cat-meow.wav"),
>                         }
>                     }
>                 ],
>             }
>         ],
>         audio: {
>             voice: "qingchunshaonv",
>             format: "wav",
>         },
>         stream: false,
>     });
>
>     // 获取文字回复
>     console.log(completion.choices[0].message.audio.transcript);
>
>     // 保存音频文件
>     fs.writeFileSync("response.wav", Buffer.from(completion.choices[0].message.audio.data, 'base64'));
> }
>
> main();
> ```

## 流式语音输出

当 `stream=true` 时，API 会以流式方式返回音频数据，实现超低延迟的语音交互体验。

> [tab] python
>
> ```python copy theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
> import base64
> import struct
> from openai import OpenAI
>
> client = OpenAI(api_key="YOUR_STEP_API_KEY", base_url="https://api.stepfun.com/v1")
>
> completion = client.chat.completions.create(
>     model="step-audio-2",
>     modalities=["text", "audio"],
>     messages=[
>         {
>             "role": "user",
>             "content": "请用温柔的语气给我讲一个睡前故事",
>         }
>     ],
>     audio={
>         "voice": "elegantgentle-female",
>         "format": "pcm",  # 流式场景下使用 pcm 格式
>     },
>     stream=True,
> )
>
> audio_chunks = []
> transcript = ""
>
> for chunk in completion:
>     delta = chunk.choices[0].delta
>
>     # 处理音频数据
>     if hasattr(delta, 'audio') and delta.audio:
>         if hasattr(delta.audio, 'data') and delta.audio.data:
>             audio_chunks.append(base64.b64decode(delta.audio.data))
>         if hasattr(delta.audio, 'transcript') and delta.audio.transcript:
>             transcript += delta.audio.transcript
>             print(delta.audio.transcript, end="", flush=True)
>
> print("\n\n完整文字回复:", transcript)
>
> # 合并所有 PCM 数据
> pcm_data = b''.join(audio_chunks)
>
> # 生成 WAV 文件头（24kHz，单声道，16bit）
> def create_wav_header(pcm_size, sample_rate=24000, channels=1, bits_per_sample=16):
>     byte_rate = sample_rate * channels * bits_per_sample // 8
>     block_align = channels * bits_per_sample // 8
>     return struct.pack(
>         '<4sI4s4sIHHIIHH4sI',
>         b'RIFF',
>         36 + pcm_size,       # 文件大小 - 8
>         b'WAVE',
>         b'fmt ',
>         16,                   # fmt chunk 大小
>         1,                    # 音频格式 (1 = PCM)
>         channels,
>         sample_rate,
>         byte_rate,
>         block_align,
>         bits_per_sample,
>         b'data',
>         pcm_size
>     )
>
> # 保存为 WAV 文件
> with open("response.wav", "wb") as f:
>     f.write(create_wav_header(len(pcm_data)))
>     f.write(pcm_data)
> # 打开 response.wav 即可收听音频回复
> ```

> [tab] js
>
> ```js copy theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
> import OpenAI from "openai";
> import fs from "fs";
>
> const openai = new OpenAI({
>     apiKey: "YOUR_STEP_API_KEY",
>     baseURL: "https://api.stepfun.com/v1",
> });
>
> // 生成 WAV 文件头（24kHz，单声道，16bit）
> function createWavHeader(pcmSize, sampleRate = 24000, channels = 1, bitsPerSample = 16) {
>     const byteRate = sampleRate * channels * bitsPerSample / 8;
>     const blockAlign = channels * bitsPerSample / 8;
>     const header = Buffer.alloc(44);
>
>     header.write('RIFF', 0);
>     header.writeUInt32LE(36 + pcmSize, 4);
>     header.write('WAVE', 8);
>     header.write('fmt ', 12);
>     header.writeUInt32LE(16, 16);           // fmt chunk 大小
>     header.writeUInt16LE(1, 20);            // 音频格式 (1 = PCM)
>     header.writeUInt16LE(channels, 22);
>     header.writeUInt32LE(sampleRate, 24);
>     header.writeUInt32LE(byteRate, 28);
>     header.writeUInt16LE(blockAlign, 32);
>     header.writeUInt16LE(bitsPerSample, 34);
>     header.write('data', 36);
>     header.writeUInt32LE(pcmSize, 40);
>
>     return header;
> }
>
> async function main() {
>     const completion = await openai.chat.completions.create({
>         model: "step-audio-2",
>         modalities: ["text", "audio"],
>         messages: [
>             {
>                 "role": "user",
>                 "content": "请用温柔的语气给我讲一个睡前故事",
>             }
>         ],
>         audio: {
>             voice: "elegantgentle-female",
>             format: "pcm",  // 流式场景下使用 pcm 格式
>         },
>         stream: true,
>     });
>
>     const audioChunks = [];
>     let transcript = "";
>
>     for await (const chunk of completion) {
>         const delta = chunk.choices[0].delta;
>
>         // 处理音频数据
>         if (delta.audio) {
>             if (delta.audio.data) {
>                 audioChunks.push(Buffer.from(delta.audio.data, 'base64'));
>             }
>             if (delta.audio.transcript) {
>                 transcript += delta.audio.transcript;
>                 process.stdout.write(delta.audio.transcript);
>             }
>         }
>     }
>
>     console.log("\n\n完整文字回复:", transcript);
>
>     // 合并 PCM 数据并保存为 WAV 文件
>     const pcmData = Buffer.concat(audioChunks);
>     const wavHeader = createWavHeader(pcmData.length);
>     fs.writeFileSync("response.wav", Buffer.concat([wavHeader, pcmData]));
>     // 打开 response.wav 即可收听音频回复
> }
>
> main();
> ```

流式返回的音频格式为 24kHz、单声道、16bit 的 PCM 数据，您可以直接将数据流式送入播放器进行实时播放，也可以按上述示例将其保存为标准的 WAV 文件。

## 多轮语音对话

支持在多轮对话中保持语音上下文。您可以将用户的语音历史放入 messages 中，AI 的输出推荐将文字部分作为 assistant 回复填入 content。

```python copy theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
import base64
from openai import OpenAI

client = OpenAI(api_key="YOUR_STEP_API_KEY", base_url="https://api.stepfun.com/v1")

# 读取当前轮用户语音
with open("current_question.wav", "rb") as f:
    current_audio = base64.b64encode(f.read()).decode('utf-8')

completion = client.chat.completions.create(
    model="step-audio-2",
    modalities=["text", "audio"],
    messages=[
        {
            "role": "system",
            "content": "你是一个友好的助手，请用自然的语气回答用户的问题。",
        },
        # 第一轮对话（可以用 ASR 文本表示历史语音）
        {
            "role": "user",
            "content": "你好，请问今天天气怎么样？",
        },
        {
            "role": "assistant",
            "content": "你好！今天天气晴朗，气温适宜，非常适合出门活动。",
        },
        # 当前轮语音输入
        {
            "role": "user",
            "content": [
                {
                    "type": "input_audio",
                    "input_audio": {
                        "data": f"data:audio/wav;base64,{current_audio}",
                    }
                }
            ],
        }
    ],
    audio={
        "voice": "wenrounansheng",
        "format": "wav",
    },
    stream=False,
)

print(completion.choices[0].message.audio.transcript)
```

> [!NOTE]
>
> **上下文限制说明**：
>
> * `step-audio-2` / `step-audio-r1.5` 支持上下文传入 wav 格式音频
> * 请求最大支持 10MB（指 base64 后的总大小，大致能传 7.5MB 的 wav 文件）
> * 最多支持 30 段声音（如果单段声音超过 25s，会被自动分段，每 25s 视为一段）
> * `step-1o-audio` 目前不支持上下文中传入 wav（但支持语音输出）

## 结合 Tool Call 使用

`step-audio-2` / `step-1o-audio` 语音模型支持 Tool Call 功能，可以在语音对话中调用外部工具。完整流程包括：检测函数调用、执行函数、返回结果并获取最终语音回复。

```python copy theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
import base64
import json
from openai import OpenAI

client = OpenAI(api_key="YOUR_STEP_API_KEY", base_url="https://api.stepfun.com/v1")

# 定义工具函数
def get_weather(city: str) -> str:
    """模拟获取天气信息"""
    # 实际应用中这里调用真实的天气 API
    return f"{city}：晴，气温 25°C，空气质量良好"

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "获取指定城市的天气信息",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "城市名称，如：北京、上海"
                    }
                },
                "required": ["city"]
            }
        }
    }
]

messages = [
    {
        "role": "user",
        "content": "北京今天天气怎么样？",
    }
]

# 第一次调用：模型可能返回 tool_calls
completion = client.chat.completions.create(
    model="step-audio-2",
    modalities=["text", "audio"],
    messages=messages,
    tools=tools,
    audio={
        "voice": "wenrounansheng",
        "format": "wav",
    },
    stream=False,
)

assistant_message = completion.choices[0].message

# 检查是否有 tool_calls
if assistant_message.tool_calls:
    # 将 assistant 消息加入上下文
    messages.append({
        "role": "assistant",
        "content": assistant_message.audio.transcript if assistant_message.audio else None,
        "tool_calls": [
            {
                "id": tc.id,
                "type": "function",
                "function": {
                    "name": tc.function.name,
                    "arguments": tc.function.arguments
                }
            }
            for tc in assistant_message.tool_calls
        ]
    })

    # 执行每个工具调用并添加结果
    for tool_call in assistant_message.tool_calls:
        function_name = tool_call.function.name
        function_args = json.loads(tool_call.function.arguments)

        # 执行函数
        if function_name == "get_weather":
            result = get_weather(function_args["city"])
        else:
            result = "未知函数"

        # 将工具执行结果加入上下文
        messages.append({
            "role": "tool",
            "tool_call_id": tool_call.id,
            "content": result,
        })

    # 第二次调用：获取最终的语音回复
    final_completion = client.chat.completions.create(
        model="step-audio-2",
        modalities=["text", "audio"],
        messages=messages,
        tools=tools,
        audio={
            "voice": "wenrounansheng",
            "format": "wav",
        },
        stream=False,
    )

    # 获取最终回复
    final_message = final_completion.choices[0].message
    print("文字回复:", final_message.audio.transcript)

    # 保存音频
    with open("response.wav", "wb") as f:
        f.write(base64.b64decode(final_message.audio.data))
else:
    # 没有 tool_calls，直接输出回复
    print("文字回复:", assistant_message.audio.transcript)
    with open("response.wav", "wb") as f:
        f.write(base64.b64decode(assistant_message.audio.data))
```

### Tool Call 返回示例

第一次调用返回（text+audio 模态，带 tool\_call 字段）：

```json theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
{
  "id": "019b55f32eb27db7a0cc933be0216686",
  "choices": [
    {
      "finish_reason": null,
      "index": 0,
      "message": {
        "role": "assistant",
        "audio": {
          "data": "[base64 wav data]",
          "transcript": "让我帮您查一下北京的天气。"
        },
        "tool_calls": [
          {
            "id": "call-1766673564415832077",
            "function": {
              "arguments": "{\"city\": \"北京\"}",
              "name": "get_weather"
            },
            "type": "function",
            "index": 0
          }
        ]
      }
    }
  ],
  "model": "step-audio-2",
  "usage": {
    "completion_tokens": 106,
    "prompt_tokens": 410,
    "total_tokens": 516
  }
}
```

> [!NOTE]
>
> **注意**：模型可能会在返回 tool\_calls 的同时输出语音（如"让我帮您查一下"），您可以在等待函数执行时播放这段语音，提升用户体验。

## 音频格式说明

### 输入音频格式

* 支持 mp3 和 wav 格式
* Base64 编码格式示例：`data:audio/mpeg;base64,${base64_string}` 或 `data:audio/wav;base64,${base64_string}`

### 输出音频格式

| 场景 | 格式 | 规格 |
| - | - | - |
| 非流式（stream=false） | wav | 标准 wav 文件格式 |
| 流式（stream=true） | pcm | 24kHz，单声道，16bit |

## Chat Completions vs Realtime API

阶跃星辰提供两种语音对话方案，适用于不同的场景需求：

| 特性 | Chat Completions API | Realtime API |
| - | - | - |
| 连接方式 | HTTP 请求 | WebSocket 长连接 |
| 语音识别 (ASR) | 需自行实现或使用第三方服务 | 内置，自动识别用户语音 |
| 上下文管理 | 需自行维护 messages 列表 | 内置，自动管理对话历史 |
| 语音活动检测 (VAD) | 需自行实现 | 内置，自动检测用户说话 |
| 联网搜索 | 需自行实现搜索接口 | 内置 web\_search 工具 |
| 知识库检索 | 需自行实现 | 内置 retrieval 工具 |
| 延迟 | 较低（流式输出） | 极低（双向流式交互） |
| 适用场景 | 离线处理、批量任务、简单集成 | 实时对话、语音助手、客服机器人 |
| 音色复刻 | 不支持 | `step-audio-2` 支持音色复刻 |

### 使用 Realtime API 的优势

如果您需要构建实时语音对话应用，推荐使用 [Realtime API](https://platform.stepfun.com/docs/zh/guides/developer/realtime.md)：

* **开箱即用**：内置 ASR、VAD、上下文管理，无需额外集成
* **超低延迟**：基于 WebSocket 的实时双向通信
* **内置工具**：支持 `web_search` 联网搜索和 `retrieval` 知识库检索
* **自定义 Tool Call**：同样支持自定义函数调用
* **音色复刻**：支持使用自定义音色进行语音生成

> [card] [实时语音互动开发指南](https://platform.stepfun.com/docs/zh/guides/developer/realtime.md)

## 常见问题

### 流式播放 PCM 音频

在浏览器环境中播放流式 PCM 音频，需要使用 Web Audio API。以下是一个简单的播放器示例：

```javascript theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
class SimplePCMPlayer {
  constructor() {
    this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
    this.bufferedData = [];
    this.isPlaying = false;
    this.nextStartTime = 0;
  }

  // 追加 PCM 数据（16位有符号小端，24kHz，单声道）
  appendPCM(pcmArrayBuffer) {
    const int16Array = new Int16Array(pcmArrayBuffer);
    const float32Array = new Float32Array(int16Array.length);
    for (let i = 0; i < int16Array.length; i++) {
      float32Array[i] = int16Array[i] / 32768;
    }
    this.bufferedData.push(float32Array);
    if (!this.isPlaying) {
      this.playNext();
    }
  }

  playNext() {
    if (this.bufferedData.length === 0) {
      this.isPlaying = false;
      return;
    }
    this.isPlaying = true;
    const data = this.bufferedData.shift();
    const audioBuffer = this.audioContext.createBuffer(1, data.length, 24000);
    audioBuffer.copyToChannel(data, 0);
    const source = this.audioContext.createBufferSource();
    source.buffer = audioBuffer;
    source.connect(this.audioContext.destination);
    const startTime = Math.max(this.audioContext.currentTime, this.nextStartTime);
    this.nextStartTime = startTime + audioBuffer.duration;
    source.start(startTime);
    source.onended = () => this.playNext();
  }

  // 清空缓冲并停止播放
  clear() {
    this.bufferedData = [];
    this.isPlaying = false;
    this.nextStartTime = this.audioContext.currentTime;
  }
}
```

### 优化首字延迟

如果需要更低的首字延迟：

1. 使用 `step-audio-2-mini` 模型，它的响应速度更快
2. 使用流式输出（stream=true），可以边生成边播放
3. 考虑使用 [Realtime API](https://platform.stepfun.com/docs/zh/guides/developer/realtime.md) 获得最低延迟体验


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.