> 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）

使用 Convai 的交互 API 启用实时对话式 AI。向你的 AI 角色发送文本消息，并通过服务器发送事件（SSE）接收自然、流式的回复。

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

## 概览

该 **交互 API** 通过轻量级的方式提供实时对话能力 **REST + SSE** 接口。\
它让你的应用能够向用户发送消息，并以最低延迟接收流式字符响应。

此 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=你好，你好吗？" \\
  --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': '你好，你好吗？'
        }
    ) 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', '你好，你好吗？');

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 中使用该标志（而不是 `-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=我的名字是 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=我叫什么名字？" \\
  -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('我的名字是 Alice')
await chat('我叫什么名字？')  # 机器人记得："你的名字是 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('我的名字是 Alice');
await chat('我叫什么名字？');  // 机器人记得："你的名字是 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>400 - 错误请求 - 无效参数</td></tr><tr><td><code>401</code></td><td>401 - 未经授权 - API 密钥无效或缺失</td></tr><tr><td><code>404</code></td><td>404 - 未找到 - 角色未找到</td></tr><tr><td><code>422</code></td><td>422 - 无法处理的实体 - 验证错误</td></tr><tr><td><code>429</code></td><td>429 - 请求过多 - 已超出速率限制</td></tr><tr><td><code>500</code></td><td>500 - 服务器内部错误</td></tr></tbody></table>

**错误响应格式：**

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

**示例：**

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

## 结论

该 **交互 API（Beta 版）** 可通过文本与您的 Convai 角色实现动态实时通信。\
通过结合流式响应、上下文持久化和基于 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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
