> For the complete documentation index, see [llms.txt](https://docs.convai.com/api-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.convai.com/api-docs/zh/api-can-kao/core-api-reference/interaction-apis/interaction-api-beta.md).

# 交互 API（Beta）

{% hint style="danger" %}
此 API 仅适用于专业版及以上套餐。
{% endhint %}

## 概述

该 **交互式 API** 通过轻量级的 **REST + SSE** 接口。\n它允许你的应用向用户发送消息，并以最低延迟接收流式字符响应。

此 API 通过会话 ID 支持连续对话上下文，并将所有输出流式传输为 **服务器发送事件（SSE）** 以提供流畅的实时反馈。

***

## 身份验证 <a href="#authentication" id="authentication"></a>

所有 API 请求都需要在请求头中使用 API 密钥进行身份验证：

```http
X-API-Key: your_api_key_here
```

***

## 端点 <a href="#endpoint" id="endpoint"></a>

<mark style="color:绿色;">`POST`</mark> `https://live.convai.com/connect/stream`

向 AI 角色发送文本查询并接收流式响应。

**请求格式：** `multipart/form-data`

<table><thead><tr><th width="220">参数</th><th width="109.666748046875">类型</th><th>说明</th></tr></thead><tbody><tr><td>character_id<mark style="color:红色;">*</mark></td><td>UUID</td><td>AI 角色的唯一标识符</td></tr><tr><td>text_input<mark style="color:红色;">*</mark></td><td>string</td><td>你发送给角色的文本查询/消息</td></tr><tr><td>character_session_id</td><td>string</td><td>用于继续现有对话的会话 ID</td></tr></tbody></table>

### 示例请求

{% tabs %}
{% tab title="cURL" %}

```shell
curl -X POST https://live.convai.com/connect/stream \\
  -H "X-API-Key: YOUR_API_KEY" \\
  -F "character_id=7bd3274c-1745-11ee-a3af-42010a400002" \\
  -F "text_input=Hello, how are you?" \\
  --no-buffer
```

{% endtab %}

{% tab title="Python" %}

```python
import httpx
import json

async with httpx.AsyncClient(timeout=30.0) as client:
    async with client.stream(
        'POST',
        'https://live.convai.com/connect/stream',
        headers={'X-API-Key': 'YOUR_API_KEY'},
        data={
            'character_id': '7bd3274c-1745-11ee-a3af-42010a400002',
            'text_input': 'Hello, how are you?'
        }
    ) as response:
        async for line in response.aiter_lines():
            if line.startswith('data: '):
                data = json.loads(line[6:])
                if data.get('type') == 'bot-llm-text':
                    print(data['data']['text'], end='', flush=True)

```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const formData = new FormData();
formData.append('character_id', '7bd3274c-1745-11ee-a3af-42010a400002');
formData.append('text_input', 'Hello, how are you?');

const response = await fetch('https://live.convai.com/connect/stream', {
  method: 'POST',
  headers: { 'X-API-Key': 'YOUR_API_KEY' },
  body: formData,
});

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  const chunk = decoder.decode(value);
  const lines = chunk.split('\\n');

  for (const line of lines) {
    if (line.startsWith('data: ')) {
      const data = JSON.parse(line.slice(6));
      if (data.type === 'bot-llm-text') {
        console.log(data.data.text);
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

**响应：** 服务器发送事件（SSE）流

```
data: {"type": "connection-started", "message": {"session_id": "abc123", "transport": "sse", "character_session_id": "def456"}}

data: {"label": "rtvi-ai", "type": "bot-llm-started"}

data: {"label": "rtvi-ai", "type": "bot-llm-text", "data": {"text": "你好"}}

data: {"label": "rtvi-ai", "type": "bot-llm-text", "data": {"text": "！我很好，谢谢你的关心。"}}

data: {"label": "rtvi-ai", "type": "bot-transcription", "data": {"text": "你好！我很好，谢谢你的关心。"}}

data: {"label": "rtvi-ai", "type": "bot-llm-stopped"}

data: {"type": "connection-stoppped"}
```

***

{% hint style="warning" %}
**重要提示**

* 请求体必须是 `multipart/form-data` 格式。使用 `-F` 在 cURL 中使用 -F 标志（而不是 `-d`).
* 省略 `character_session_id` 以开始新对话。添加它以继续现有对话。
* 角色会话 ID 是字符串。请勿发送 `-1` 或其他占位值。
* 响应通过服务器发送事件（SSE）流式传输。请将超时设置为 30 秒以上。
* 所有交互均受内容审核政策约束。重复违规可能导致 API 密钥被暂停。
* 每个 API 密钥都有速率限制。对于速率限制错误，请实现指数退避。
  {% endhint %}

## 继续对话 <a href="#conversation-continuity" id="conversation-continuity"></a>

为保持对话上下文，请保存 `character_session_id` 首次响应中的内容，并在后续请求中包含它。

### 示例请求

{% tabs %}
{% tab title="cURL" %}
**首次请求：**

```bash
curl -X POST https://live.convai.com/connect/stream \\
  -H "X-API-Key: YOUR_API_KEY" \\
  -F "character_id=7bd3274c-1745-11ee-a3af-42010a400002" \\
  -F "text_input=My name is Alice" \\
  --no-buffer
```

**响应包括：**

```json
{"type": "connection-started", "message": {"character_session_id": "9a98ab9b-5a6c-406e-9721-cc5bb0d527bb", ...}}
```

**第二次请求（带会话 ID）：**

```bash
curl -X POST https://live.convai.com/connect/stream \\
  -H "X-API-Key: YOUR_API_KEY" \\
  -F "character_id=7bd3274c-1745-11ee-a3af-42010a400002" \\
  -F "text_input=What is my name?" \\
  -F "character_session_id=9a98ab9b-5a6c-406e-9721-cc5bb0d527bb" \\
  --no-buffer
```

**机器人记得：** “你的名字是 Alice。”
{% endtab %}

{% tab title="Python" %}

```python
import httpx
import json
import asyncio

session_id = None

async def chat(message):
    global session_id

    data = {
        'character_id': '7bd3274c-1745-11ee-a3af-42010a400002',
        'text_input': message,
    }

    if session_id:
        data['character_session_id'] = session_id

    async with httpx.AsyncClient(timeout=30.0) as client:
        async with client.stream(
            'POST',
            'https://live.convai.com/connect/stream',
            headers={'X-API-Key': 'YOUR_API_KEY'},
            data=data,
        ) as response:
            async for line in response.aiter_lines():
                if line.startswith('data: '):
                    msg = json.loads(line[6:])

                    if msg.get('type') == 'connection-started':
                        session_id = msg['message']['character_session_id']
                    elif msg.get('type') == 'bot-llm-text':
                        print(msg['data']['text'], end='', flush=True)
            print()

# 用法
await chat('My name is Alice')
await chat('What is my name?')  # 机器人记得："Your name is Alice"
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
let sessionId = null;

async function chat(message) {
  const formData = new FormData();
  formData.append('character_id', '7bd3274c-1745-11ee-a3af-42010a400002');
  formData.append('text_input', message);

  if (sessionId) {
    formData.append('character_session_id', sessionId);
  }

  const response = await fetch('https://live.convai.com/connect/stream', {
    method: 'POST',
    headers: { 'X-API-Key': 'YOUR_API_KEY' },
    body: formData,
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = '';

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split('\\n');
    buffer = lines.pop();

    for (const line of lines) {
      if (line.startsWith('data: ')) {
        const data = JSON.parse(line.slice(6));

        if (data.type === 'connection-started') {
          sessionId = data.message.character_session_id;
        } else if (data.type === 'bot-llm-text') {
          process.stdout.write(data.data.text);
        }
      }
    }
  }
  console.log();
}

// 用法
await chat('My name is Alice');
await chat('What is my name?');  // 机器人记得："Your name is Alice"
```

{% endtab %}
{% endtabs %}

## 响应消息类型 <a href="#response-message-types" id="response-message-types"></a>

#### connection-started <a href="#connection-started" id="connection-started"></a>

在连接建立时发送。

```json
{
  "type": "connection-started",
  "message": {
    "session_id": "550e8400-e29b-41d4-a716-446655440000",
    "transport": "sse",
    "character_session_id": "660e8400-e29b-41d4-a716-446655440001"
  }
}
```

**字段：**

* `session_id`：此连接的唯一标识符
* `transport`：传输类型（始终为 "sse"）
* `character_session_id`: **保存它以在未来的请求中继续对话**

#### bot-llm-started <a href="#bot-llm-started" id="bot-llm-started"></a>

在 LLM 开始生成响应时发送。

```json
{
  "label": "rtvi-ai",
  "type": "bot-llm-started"
}
```

#### bot-llm-text <a href="#bot-llm-text" id="bot-llm-text"></a>

机器人响应文本，在生成时按块流式传输。

```json
{
  "label": "rtvi-ai",
  "type": "bot-llm-text",
  "data": {
    "text": "你好呀！"
  }
}
```

**字段：**

* `text`：机器人响应文本的一部分

#### bot-transcription <a href="#bot-transcription" id="bot-transcription"></a>

机器人响应的完整转录（在所有文本块发送完毕后发送）。

```json
{
  "label": "rtvi-ai",
  "type": "bot-transcription",
  "data": {
    "text": "你好呀！完整的响应文本。"
  }
}
```

**字段：**

* `text`：机器人完整响应文本

#### bot-llm-stopped <a href="#bot-llm-stopped" id="bot-llm-stopped"></a>

在 LLM 生成完成时发送。

```json
{
  "label": "rtvi-ai",
  "type": "bot-llm-stopped"
}
```

#### connection-stoppped <a href="#connection-stoppped" id="connection-stoppped"></a>

在响应完成且连接即将关闭时发送。

```json
{
  "type": "connection-stoppped"
}
```

***

## 错误响应 <a href="#error-responses" id="error-responses"></a>

所有端点都返回标准 HTTP 错误代码：

<table><thead><tr><th width="174">状态码</th><th>说明</th></tr></thead><tbody><tr><td><code>400</code></td><td>错误请求 - 参数无效</td></tr><tr><td><code>401</code></td><td>未授权 - API 密钥无效或缺失</td></tr><tr><td><code>404</code></td><td>未找到 - 未找到角色</td></tr><tr><td><code>422</code></td><td>无法处理的实体 - 验证错误</td></tr><tr><td><code>429</code></td><td>请求过多 - 超出速率限制</td></tr><tr><td><code>500</code></td><td>服务器内部错误</td></tr></tbody></table>

**错误响应格式：**

```json
{
  "detail": "描述出错原因的错误消息"
}
```

**例如：**

```json
{
  "detail": "API 密钥无效"
}
```

## 结论

该 **交互式 API（测试版）** 通过文本与您的 Convai 角色进行动态实时通信。\n通过结合流式响应、上下文持久化和基于 SSE 的传输，它提供了适合聊天、游戏和交互式 AI 应用的响应迅速且低延迟的对话体验。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.convai.com/api-docs/zh/api-can-kao/core-api-reference/interaction-apis/interaction-api-beta.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
