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

# 流式输出(streaming)

> 通过 RuAPI 逐 token 接收大模型输出:Python、Node.js 与 curl 的 stream=True 示例

普通请求方式下,客户端要一直等到模型写完最后一个字才会显示结果。长回答时这就是好几秒的沉默。流式输出(streaming)在生成过程中就把文本一段段发出来——用户能看到答案逐字打出来,感知延迟几乎降到零。

原理并不神秘:服务端不再返回一个完整的 JSON,而是开启一条 Server-Sent Events(SSE)流,通过它推送小片段(chunk)。开启它只需一个参数——`stream=True`。

<Tip>
  流式输出与普通请求使用同一个 OpenAI 兼容端点,变化的只是 `stream` 参数和读取响应的方式。`base_url` 不变,仍然是 `https://www.ruapi.ai/v1`。
</Tip>

## 代码长什么样

你拿到的不再是单个 `response` 对象,而是一个迭代器。每次迭代得到一个 `chunk`,有用的文本在 `chunk.choices[0].delta.content` 里。随着片段到达,把它追加到输出即可。

<CodeGroup>
  ```python Python (OpenAI SDK) theme={null}
  from openai import OpenAI

  client = OpenAI(
      api_key="sk-你的密钥",
      base_url="https://www.ruapi.ai/v1",
  )

  stream = client.chat.completions.create(
      model="claude-opus-4-8",  # 准确名称见主站定价页
      messages=[{"role": "user", "content": "讲一个关于猫的小故事。"}],
      stream=True,
  )

  for chunk in stream:
      delta = chunk.choices[0].delta.content
      if delta:  # 最后一个 chunk 的 content 可能是 None——跳过它
          print(delta, end="", flush=True)
  print()
  ```

  ```javascript Node.js (OpenAI SDK) theme={null}
  import OpenAI from "openai";

  const client = new OpenAI({
    apiKey: "sk-你的密钥",
    baseURL: "https://www.ruapi.ai/v1",
  });

  const stream = await client.chat.completions.create({
    model: "claude-opus-4-8", // 准确名称见主站定价页
    messages: [{ role: "user", content: "讲一个关于猫的小故事。" }],
    stream: true,
  });

  for await (const chunk of stream) {
    const delta = chunk.choices[0]?.delta?.content;
    if (delta) process.stdout.write(delta);
  }
  process.stdout.write("\n");
  ```

  ```bash curl theme={null}
  curl https://www.ruapi.ai/v1/chat/completions \
    -H "Authorization: Bearer sk-你的密钥" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "claude-opus-4-8",
      "messages": [{"role": "user", "content": "讲一个关于猫的小故事。"}],
      "stream": true
    }'
  ```
</CodeGroup>

用 curl 时,响应以一行行 `data: {...}` 的形式到达,每行携带各自的片段。流结束的标志是 `data: [DONE]` 这一行。

## 切换模型

和普通请求一样,切换模型只需改 `model` 字段——流式代码本身不变。例如 `gpt-5.4`、`gemini-3.5-flash` 或 `deepseek-v4-pro`:

```python theme={null}
model="gpt-5.4"            # 替换 claude-opus-4-8
```

可用名称的完整列表见主站的\*\*「定价」\*\*页面。

## 通过 Anthropic 协议流式输出

如果你走的是原生 Anthropic 协议(Claude Code、Anthropic SDK),同样支持流式输出——通过 `client.messages.stream(...)`。注意这里的地址**不带** `/v1`:

```python theme={null}
import anthropic

client = anthropic.Anthropic(
    api_key="sk-你的密钥",
    base_url="https://www.ruapi.ai",  # Anthropic 协议——不带 /v1
)

with client.messages.stream(
    model="claude-opus-4-8",
    max_tokens=1024,
    messages=[{"role": "user", "content": "讲一个关于猫的小故事。"}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
print()
```

关于两种协议和基础地址的更多说明,见 [API 参考](/zh/api-reference)。

## 容易踩的坑

<AccordionGroup>
  <Accordion title="最后一个 chunk 的 delta.content 是 None">
    流的最后一个片段通常携带的是元信息(比如 `finish_reason`)而非文本,因此那里的 `delta.content` 是 `None`(在 Node.js 中是 `undefined`)。在打印或拼接之前一定要先判空,否则拼接时会抛出 `TypeError`。
  </Accordion>

  <Accordion title="流式输出时如何获取 token 用量">
    默认情况下,最终用量统计(`usage`)不会出现在流里。要拿到它,在请求中加上 `stream_options` 参数:

    ```python theme={null}
    stream = client.chat.completions.create(
        model="claude-opus-4-8",
        messages=[{"role": "user", "content": "你好!"}],
        stream=True,
        stream_options={"include_usage": True},
    )
    ```

    这样在流的最末尾会单独到达一个带 `usage` 字段的 chunk。该 chunk 的 `choices` 列表是空的——解析时要注意这一点。
  </Accordion>

  <Accordion title="响应一次性全部返回,而不是分段">
    如果文本是一整块出现而不是逐字打出,多半是你和 API 之间有一层会缓冲 SSE 的代理或负载均衡。请确认已关闭响应缓冲(例如 nginx 里的 `proxy_buffering off;`),并确认你的 HTTP 客户端没有自己把流攒起来。用 curl 时,加上 `-N`(`--no-buffer`)参数更稳妥。
  </Accordion>
</AccordionGroup>

## 接下来

* [快速开始](/zh/quickstart) — 注册、获取密钥、发出第一个请求
* [Claude 模型](/zh/claude-api) — 流式示例里默认用的就是 Claude,看看怎么选型
* [Telegram 机器人](/zh/recipes/telegram-bot) — 把流式回复接进一个真实应用
* [错误解析](/zh/errors) — 请求失败时该怎么办
