> 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/character-crafting-apis/external-api.md).

# 外部 API

创建、列出、关联和删除外部 API 函数，以便你的角色能在对话中调用自定义 Python 代码。

{% hint style="danger" %}
此 API 仅适用于 Professional 方案及以上。
{% endhint %}

外部 API 函数是你角色可以在对话中作为工具调用的小型 Python 处理器。函数只需创建一次，然后将其关联到一个或多个角色，模型会根据函数名称和描述决定何时运行它。

典型流程：

1. 使用以下方式创建函数 `/functions/create/`
2. 使用以下方式将其关联到角色 `/character/update` (`status: "active"`)
3. 使用以下方式列出函数（可按角色筛选） `/functions/list/`
4. 使用以下方式将其与角色解除关联 `/character/update` (`status: "inactive"`），或者使用以下方式将其完全删除 `/functions/delete/`

关于 Playground UI 演示和示例处理器（天气、体育比分、Jira），请参见 [External API](/api-docs/zh/convai-playground/character-customization/external-api.md).

硬性限制（支持的模型、Python 运行时、库、schema、上限）已在以下位置统一说明： [外部 API 限制](/api-docs/zh/convai-playground/character-customization/external-api/external-api-limitations.md).

## 编写函数

函数运行在沙箱化的 **Python 3.11** 运行时中。请编写纯 Python，保持接口尽量精简，并返回模型可以读回对话中的可 JSON 序列化数据。

完整限制列表（允许的库、行数上限、模型支持、字符上限）请参见 [外部 API 限制](/api-docs/zh/convai-playground/character-customization/external-api/external-api-limitations.md).

### 入口点： `handle_event`

每个函数 **必须** 定义一个名为 `handle_event` 的顶级函数，它接受一个参数。该参数是模型根据你的 `input_description`.

```python
def handle_event(inputs):
    # `inputs` 是一个由 input_description 中定义的参数组成的字典。
    # 在这里调用你的外部 API，并返回一个可 JSON 序列化的字典。
    return {"result": "ok"}
```

要求：

* 名称必须恰好为 `handle_event`
* 它必须接受一个单独参数（通常命名为 `inputs` 或 `data`)
* 返回一个 **可 JSON 序列化** 的值，通常是一个 `dict`
* 不要依赖跨调用的全局可变状态——每次调用都是独立的

一个最小示例：读取一个参数并调用外部 API：

```python
import requests

API_KEY = "<your-api-key>"

def handle_event(inputs):
    city = inputs.get("city")
    url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={API_KEY}"
    response = requests.get(url, timeout=10)
    response.raise_for_status()
    weather = response.json()
    return {
        "city": city,
        "description": weather["weather"][0]["description"],
        "temp_k": weather["main"]["temp"],
    }
```

### 输入描述 schema

`input_description` 告诉模型可以（且必须）向 `handle_event`传递哪些参数。在创建/更新时，它会以 **JSON 字符串**的形式发送，而不是嵌套对象。解码后的 JSON 必须匹配以下 schema：

```json
{
  "type": "object",
  "properties": {
    "parameters": {
      "type": "object",
      "patternProperties": {
        "^[a-zA-Z_][a-zA-Z0-9_]*$": {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "enum": ["string", "integer", "boolean", "object", "array"]
            },
            "description": { "type": "string" }
          },
          "required": ["type", "description"]
        }
      },
      "additionalProperties": false
    },
    "required": {
      "type": "array",
      "items": { "type": "string" }
    }
  },
  "required": ["parameters", "required"]
}
```

在实践中，这意味着：

| 字段                              | 规则                                                          |
| ------------------------------- | ----------------------------------------------------------- |
| `parameters`                    | 其键为参数名的对象。此映射之外的额外键会被拒绝（`additionalProperties: false`).     |
| 参数名                             | 必须匹配 `^[a-zA-Z_][a-zA-Z0-9_]*$` （字母或 `_` 开头，然后是字母、数字或 `_`). |
| `parameters.<name>.type`        | 以下之一： `字符串`, `整数`, `布尔值`, `对象`, `数组`.                       |
| `parameters.<name>.description` | 模型用来决定应传入什么值的非空字符串。                                         |
| `required`                      | 必须存在的参数名数组。这里列出的名称也应存在于 `parameters`.                       |

相同规则已在以下位置总结： [外部 API 限制](/api-docs/zh/convai-playground/character-customization/external-api/external-api-limitations.md#input-description).

示例 `input_description` （作为对象——在发送到请求体之前请将其字符串化）：

```json
{
  "parameters": {
    "city": {
      "type": "string",
      "description": "要获取天气的城市名称（例如 'London'、'Tokyo'）"
    },
    "units": {
      "type": "string",
      "description": "可选的单位系统，例如 'metric' 或 'imperial'"
    }
  },
  "required": ["city"]
}
```

在 Python 中调用 create 时：

```python
input_description = json.dumps({
    "parameters": {
        "city": {
            "type": "string",
            "description": "要获取天气的城市名称"
        }
    },
    "required": ["city"]
})
```

{% hint style="info" %}
请保持参数描述具体明确。模型会根据这些描述选择参数，因此像 `"a value"` 这样含糊的文本会导致错误调用。请尽量在描述字符串中提供示例和单位。
{% endhint %}

***

## 创建函数

<mark style="color:绿色;">`POST`</mark> `https://api.convai.com/functions/create/`

在你的账户上创建一个新的 External API 函数。在你将其关联之前，该函数不会附加到任何角色。

#### 请求头

| 名称                                              | 类型  | 描述                                            |
| ----------------------------------------------- | --- | --------------------------------------------- |
| CONVAI-API-KEY<mark style="color:红色;">\*</mark> | 字符串 | 为每位用户提供的唯一 api-key。登录你的 Convai 账户后，可在钥匙图标下找到。 |
| Content-Type<mark style="color:红色;">\*</mark>   | 字符串 | 必须为 `application/json`                        |

#### 请求体

| 名称                                                  | 类型  | 描述                                                                                                                                                                                                                  |
| --------------------------------------------------- | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name<mark style="color:红色;">\*</mark>               | 字符串 | 函数的显示名称。请优先使用清晰的动词短语，模型更容易匹配（例如 `获取天气`).                                                                                                                                                                            |
| description<mark style="color:红色;">\*</mark>        | 字符串 | 角色应在何时调用此函数。模型会根据它来进行工具选择。                                                                                                                                                                                          |
| language<mark style="color:红色;">\*</mark>           | 字符串 | 实现语言。仅支持 `python` 。                                                                                                                                                                                                 |
| source\_code<mark style="color:红色;">\*</mark>       | 字符串 | 完整的 Python 3.11 源代码，包括 `handle_event`。最多 400 行。仅支持标准库 + `requests` 。请参见 [编写函数](#writing-functions) 和 [限制](/api-docs/zh/convai-playground/character-customization/external-api/external-api-limitations.md#runtime). |
| input\_description<mark style="color:红色;">\*</mark> | 字符串 | JSON **字符串** ，用于描述参数。请参见 [输入描述 schema](#input-description-schema) 和 [限制](/api-docs/zh/convai-playground/character-customization/external-api/external-api-limitations.md#input-description).                        |

#### 示例负载

```json
{
  "name": "获取天气",
  "description": "获取给定城市的当前天气",
  "language": "python",
  "input_description": "{\"parameters\":{\"city\":{\"type\":\"string\",\"description\":\"要获取天气的城市名称\"}},\"required\":[\"city\"]}",
  "source_code": "import requests\n\nAPI_KEY = \"<openweather-api-key>\"\n\ndef handle_event(data):\n    city = data.get(\"city\")\n    url = f\"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={API_KEY}\"\n    response = requests.get(url)\n    weather_data = response.json()\n    return {\"weather\": weather_data[\"weather\"][0][\"description\"]}\n"
}
```

{% tabs %}
{% tab title="201：已创建" %}

```json
{
  "transactionID": "<uuid>",
  "function": {
    "function_id": "<function uuid>",
    "name": "获取天气",
    "description": "获取给定城市的当前天气",
    "language": "python",
    "source_code": "import requests\n...",
    "base64_source_code": "<base64 encoded source>",
    "input_description": "{\"parameters\":{...},\"required\":[\"city\"]}",
    "created_at": "2026-03-18T12:34:56"
  }
}
```

{% endtab %}

{% tab title="400：错误请求" %}

```json
{
  "ERROR": "缺少必填字段：name",
  "Reference ID": "<uuid>"
}
```

其他常见的 400 消息：

* `字段 input_description 必须是有效的 json 字符串`
* `不支持语言 pythonx`
* 来自无效 `input_description`
* 源代码校验失败（为空、超过 400 行，或因恶意内容被阻止）
  {% endtab %}

{% tab title="401：未授权" %}

```json
{
  "ERROR": "提供的 API key 无效。"
}
```

{% endtab %}
{% endtabs %}

以下是一些示例代码，用于演示该端点的请求格式 -->

{% tabs %}
{% tab title="Python" %}
{% code overflow="wrap" %}

```python
import json
import requests

url = "https://api.convai.com/functions/create/"

input_description = {
    "parameters": {
        "city": {
            "type": "string",
            "description": "要获取天气的城市名称"
        }
    },
    "required": ["city"]
}

source_code = """
import requests

API_KEY = "<openweather-api-key>"

def handle_event(data):
    city = data.get("city")
    url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={API_KEY}"
    response = requests.get(url)
    weather_data = response.json()
    return {"weather": weather_data["weather"][0]["description"]}
""".strip()

payload = json.dumps({
    "name": "获取天气",
    "description": "获取给定城市的当前天气",
    "language": "python",
    "input_description": json.dumps(input_description),
    "source_code": source_code
})

headers = {
    "CONVAI-API-KEY": "<your api key>",
    "Content-Type": "application/json"
}

response = requests.post(url, headers=headers, data=payload)
print(response.text)
```

{% endcode %}
{% endtab %}

{% tab title="cURL" %}
{% code overflow="wrap" %}

```shell
curl --location --request POST 'https://api.convai.com/functions/create/' \
--header 'CONVAI-API-KEY: <your api key>' \
--header 'Content-Type: application/json' \
--data-raw '{
  "name": "获取天气",
  "description": "获取给定城市的当前天气",
  "language": "python",
  "input_description": "{\"parameters\":{\"city\":{\"type\":\"string\",\"description\":\"要获取天气的城市名称\"}},\"required\":[\"city\"]}",
  "source_code": "import requests\n\nAPI_KEY = \"<openweather-api-key>\"\n\ndef handle_event(data):\n    city = data.get(\"city\")\n    url = f\"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={API_KEY}\"\n    response = requests.get(url)\n    weather_data = response.json()\n    return {\"weather\": weather_data[\"weather\"][0][\"description\"]}\n"
}'
```

{% endcode %}
{% endtab %}
{% endtabs %}

***

## 列出函数

<mark style="color:绿色;">`POST`</mark> `https://api.convai.com/functions/list/`

返回你账户中的 External API 函数。传入 `character_id` 即可包含该角色的每个函数关联状态（`active` 或 `inactive`).

#### 请求头

| 名称                                              | 类型  | 描述                                            |
| ----------------------------------------------- | --- | --------------------------------------------- |
| CONVAI-API-KEY<mark style="color:红色;">\*</mark> | 字符串 | 为每位用户提供的唯一 api-key。登录你的 Convai 账户后，可在钥匙图标下找到。 |

#### 请求体

所有字段均为可选。空请求体会列出账户中的每个函数。

| 名称            | 类型  | 描述                                                        |
| ------------- | --- | --------------------------------------------------------- |
| character\_id | 字符串 | 如果设置了， प्रत्येक函数都会包含 `状态` 相对于该角色（`active` / `inactive`). |
| per\_page     | 整数  | 每页大小。默认值为 `-1` （返回全部）。设为正值时，会添加分页字段。                      |
| 页码，从          | 整数  | 开始。 `1`仅在 `per_page` 不为 `-1`时使用。默认 `1`.                   |

#### 示例负载

```json
{
  "character_id": "<character uuid>",
  "per_page": 20,
  "page": 1
}
```

{% tabs %}
{% tab title="200：成功" %}

```json
{
  "transactionID": "<uuid>",
  "functions": [
    {
      "function_id": "<function uuid>",
      "name": "获取天气",
      "description": "获取给定城市的当前天气",
      "language": "python",
      "created_at": "2026-03-18T12:34:56",
      "status": "active"
    }
  ],
  "total_pages": 1,
  "per_page": 20,
  "page": 1,
  "total": 1
}
```

`total_pages`, `per_page`, `页码，从`和 `total` 仅在 `per_page` 为正整数时出现。
{% endtab %}

{% tab title="400：错误请求" %}

```json
{
  "ERROR": "获取函数失败，响应：...",
  "Reference ID": "<uuid>"
}
```

{% endtab %}

{% tab title="401：未授权" %}

```json
{
  "ERROR": "提供的 API key 无效。"
}
```

{% endtab %}
{% endtabs %}

以下是一些示例代码，用于演示该端点的请求格式 -->

{% tabs %}
{% tab title="Python" %}
{% code overflow="wrap" %}

```python
import json
import requests

url = "https://api.convai.com/functions/list/"

payload = json.dumps({
    "character_id": "<character uuid>",
    "per_page": 20,
    "page": 1
})
headers = {
    "CONVAI-API-KEY": "<your api key>",
    "Content-Type": "application/json"
}

response = requests.post(url, headers=headers, data=payload)
print(response.text)
```

{% endcode %}
{% endtab %}

{% tab title="cURL" %}
{% code overflow="wrap" %}

```shell
curl --location --request POST 'https://api.convai.com/functions/list/' \
--header 'CONVAI-API-KEY: <your api key>' \
--header 'Content-Type: application/json' \
--data-raw '{
  "character_id": "<character uuid>",
  "per_page": 20,
  "page": 1
}'
```

{% endcode %}
{% endtab %}
{% endtabs %}

***

## 将函数关联到角色

<mark style="color:绿色;">`POST`</mark> `https://api.convai.com/character/update`

通过现有的 [Character Base API](/api-docs/zh/api-can-kao/core-api-reference/character-crafting-apis/character-api.md) 更新端点将一个或多个 External API 函数附加到角色。一旦关联（`status: "active"`），模型就可以在对话中调用这些函数。

一个角色最多可以有 **128** 个活动函数。你可以在一次请求中关联多个函数，也可以混合使用关联和 [解除关联](#unlink-functions-from-a-character) 条目。

#### 请求头

| 名称                                              | 类型  | 描述                                            |
| ----------------------------------------------- | --- | --------------------------------------------- |
| CONVAI-API-KEY<mark style="color:红色;">\*</mark> | 字符串 | 为每位用户提供的唯一 api-key。登录你的 Convai 账户后，可在钥匙图标下找到。 |
| Content-Type                                    | 字符串 | `application/json`                            |

#### 请求体

| 名称                                      | 类型  | 描述                                                    |
| --------------------------------------- | --- | ----------------------------------------------------- |
| charID<mark style="color:红色;">\*</mark> | 字符串 | 要更新的角色。                                               |
| functions                               | 数组  | 函数配置列表。每个项目都需要 `id` （函数 UUID）以及将 `状态` 设置为 `"active"`. |

#### 示例负载

```json
{
  "charID": "<character uuid>",
  "functions": [
    {
      "id": "<function uuid>",
      "status": "active"
    }
  ]
}
```

{% tabs %}
{% tab title="200：成功" %}

```json
{
  "STATUS": "SUCCESS"
}
```

{% endtab %}

{% tab title="400：错误请求" %}

```json
{
  "ERROR": "函数数量超限。一个角色最多只能连接 128 个函数。",
  "Reference ID": "<uuid>"
}
```

对于无效配置也会返回，例如：

* `functions 必须是一个配置列表`
* `函数配置中缺少必填字段：id`
* `函数状态必须是以下之一：active, inactive`
* `发现重复的函数 ID：<id>`
  {% endtab %}

{% tab title="401：未授权" %}

```json
{
  "ERROR": "提供的 API key 无效。"
}
```

{% endtab %}
{% endtabs %}

以下是一些示例代码，用于演示该端点的请求格式 -->

{% tabs %}
{% tab title="Python" %}
{% code overflow="wrap" %}

```python
import json
import requests

url = "https://api.convai.com/character/update"

payload = json.dumps({
    "charID": "<character uuid>",
    "functions": [
        {"id": "<function uuid>", "status": "active"}
    ]
})
headers = {
    "CONVAI-API-KEY": "<your api key>",
    "Content-Type": "application/json"
}

response = requests.post(url, headers=headers, data=payload)
print(response.text)
```

{% endcode %}
{% endtab %}

{% tab title="cURL" %}
{% code overflow="wrap" %}

```shell
curl --location --request POST 'https://api.convai.com/character/update' \
--header 'CONVAI-API-KEY: <your api key>' \
--header 'Content-Type: application/json' \
--data-raw '{
  "charID": "<character uuid>",
  "functions": [
    {"id": "<function uuid>", "status": "active"}
  ]
}'
```

{% endcode %}
{% endtab %}
{% endtabs %}

关联后，使用以下方式确认 `/functions/list/` 和 `character_id` set——已关联的函数会显示 `"status": "active"`.

***

## 将函数与角色解除关联

<mark style="color:绿色;">`POST`</mark> `https://api.convai.com/character/update`

在不从账户中删除函数的情况下，将其与角色断开。与关联使用相同的端点；将 `状态` 设为 `"inactive"`.

解除关联后，角色将不再能调用该函数。该函数仍可稍后重新关联，或附加到其他角色。

#### 请求头

| 名称                                              | 类型  | 描述                                            |
| ----------------------------------------------- | --- | --------------------------------------------- |
| CONVAI-API-KEY<mark style="color:红色;">\*</mark> | 字符串 | 为每位用户提供的唯一 api-key。登录你的 Convai 账户后，可在钥匙图标下找到。 |
| Content-Type                                    | 字符串 | `application/json`                            |

#### 请求体

| 名称                                      | 类型  | 描述                                                      |
| --------------------------------------- | --- | ------------------------------------------------------- |
| charID<mark style="color:红色;">\*</mark> | 字符串 | 要更新的角色。                                                 |
| functions                               | 数组  | 函数配置列表。每个项目都需要 `id` （函数 UUID）以及将 `状态` 设置为 `"inactive"`. |

#### 示例负载

```json
{
  "charID": "<character uuid>",
  "functions": [
    {
      "id": "<function uuid>",
      "status": "inactive"
    }
  ]
}
```

{% tabs %}
{% tab title="200：成功" %}

```json
{
  "STATUS": "SUCCESS"
}
```

{% endtab %}

{% tab title="400：错误请求" %}

```json
{
  "ERROR": "函数配置中缺少必填字段：status",
  "Reference ID": "<uuid>"
}
```

{% endtab %}

{% tab title="401：未授权" %}

```json
{
  "ERROR": "提供的 API key 无效。"
}
```

{% endtab %}
{% endtabs %}

以下是一些示例代码，用于演示该端点的请求格式 -->

{% tabs %}
{% tab title="Python" %}
{% code overflow="wrap" %}

```python
import json
import requests

url = "https://api.convai.com/character/update"

payload = json.dumps({
    "charID": "<character uuid>",
    "functions": [
        {"id": "<function uuid>", "status": "inactive"}
    ]
})
headers = {
    "CONVAI-API-KEY": "<your api key>",
    "Content-Type": "application/json"
}

response = requests.post(url, headers=headers, data=payload)
print(response.text)
```

{% endcode %}
{% endtab %}

{% tab title="cURL" %}
{% code overflow="wrap" %}

```shell
curl --location --request POST 'https://api.convai.com/character/update' \
--header 'CONVAI-API-KEY: <your api key>' \
--header 'Content-Type: application/json' \
--data-raw '{
  "charID": "<character uuid>",
  "functions": [
    {"id": "<function uuid>", "status": "inactive"}
  ]
}'
```

{% endcode %}
{% endtab %}
{% endtabs %}

使用以下方式确认 `/functions/list/` 和 `character_id` set——未关联的函数会显示 `"status": "inactive"`.

{% hint style="info" %}
解除关联只会移除角色关联。若要将函数从你的账户中完全删除（以及它关联的所有角色），请使用 [删除函数](#delete-a-function).
{% endhint %}

***

## 删除函数

<mark style="color:绿色;">`POST`</mark> `https://api.convai.com/functions/delete/`

删除你拥有的函数，并移除它与所有角色的关联。此操作不可撤销。

#### 请求头

| 名称                                              | 类型  | 描述                                            |
| ----------------------------------------------- | --- | --------------------------------------------- |
| CONVAI-API-KEY<mark style="color:红色;">\*</mark> | 字符串 | 为每位用户提供的唯一 api-key。登录你的 Convai 账户后，可在钥匙图标下找到。 |
| Content-Type<mark style="color:红色;">\*</mark>   | 字符串 | 必须为 `application/json`                        |

#### 请求体

| 名称                                            | 类型  | 描述          |
| --------------------------------------------- | --- | ----------- |
| function\_id<mark style="color:红色;">\*</mark> | 字符串 | 要删除的函数 UUID |

#### 示例负载

```json
{
  "function_id": "<function uuid>"
}
```

{% tabs %}
{% tab title="200：成功" %}

```json
{
  "transactionID": "<uuid>",
  "status": "success"
}
```

{% endtab %}

{% tab title="403：禁止访问" %}

```json
{
  "ERROR": "你没有权限删除此函数",
  "Reference ID": "<uuid>"
}
```

{% endtab %}

{% tab title="404：未找到" %}

```json
{
  "ERROR": "未找到 id 为 <function uuid> 的函数",
  "Reference ID": "<uuid>"
}
```

{% endtab %}

{% tab title="401：未授权" %}

```json
{
  "ERROR": "提供的 API key 无效。"
}
```

{% endtab %}
{% endtabs %}

以下是一些示例代码，用于演示该端点的请求格式 -->

{% tabs %}
{% tab title="Python" %}
{% code overflow="wrap" %}

```python
import json
import requests

url = "https://api.convai.com/functions/delete/"

payload = json.dumps({
    "function_id": "<function uuid>"
})
headers = {
    "CONVAI-API-KEY": "<your api key>",
    "Content-Type": "application/json"
}

response = requests.post(url, headers=headers, data=payload)
print(response.text)
```

{% endcode %}
{% endtab %}

{% tab title="cURL" %}
{% code overflow="wrap" %}

```shell
curl --location --request POST 'https://api.convai.com/functions/delete/' \
--header 'CONVAI-API-KEY: <your api key>' \
--header 'Content-Type: application/json' \
--data-raw '{
  "function_id": "<function uuid>"
}'
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
如果要在不删除函数的情况下将其与角色断开，请使用 [将函数与角色解除关联](#unlink-functions-from-a-character).
{% endhint %}


---

# 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, and the optional `goal` query parameter:

```
GET https://docs.convai.com/api-docs/zh/api-can-kao/core-api-reference/character-crafting-apis/external-api.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
