> 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/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/actions.md).

# 处理角色动作

声明角色可以使用的动作、对象和工具，接收它选择的动作，并返回你应用运行的工具结果。

允许角色在你的场景中行动：在连接时声明它能做什么，并在对话过程中处理它选择的动作。当角色需要移动、拾取物品或调用你应用中的函数时，使用此页面。你的应用仍负责授权并执行每个动作。

SDK 支持两种契约。默认的语义动作会返回带可选目标的命名动作。客户端工具是可选启用的，会返回带类型的工具调用，由你的应用以结果作答。

### 先决条件

* 已安装 Convai Web SDK，并且有一个可连接的客户端。参见 [Web SDK 快速入门](/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/quickstart.md).
* 你的场景支持的动作、对象和角色名称。

### 声明语义动作

传入 `actionConfig` 当你创建客户端时。同样的选项也适用于 `new ConvaiClient(...)` 并且 `useConvaiClient(...)`.

```ts
import { ConvaiClient } from '@convai/web-sdk/core';

const client = new ConvaiClient({
  apiKey: 'YOUR_API_KEY',
  characterId: 'YOUR_CHARACTER_ID',
  actionConfig: {
    actions: ['Move To', 'Pick Up', 'Drop', 'Wave'],
    objects: [
      { name: 'fire extinguisher', description: '安装在门旁的红色灭火器' },
      { name: 'valve', description: '北墙上的燃气截止阀' },
    ],
    characters: [
      { name: 'Trainee', bio: '接受安全演练的人' },
    ],
    current_attention_object: 'valve',
  },
});
```

`actions`, `objects`，以及 `characters` 必须写在 `actionConfig`. `current_attention_object` 是可选的，并且必须与 `objects`. 配置会在会话连接时发送，因此要更改它需要重新连接。

### 接收语义动作

订阅 `actionResponse`. 每个事件按顺序携带一个轮次的动作。

```ts
import type { ActionResponseEvent } from '@convai/web-sdk/core';

client.on('actionResponse', ({ actions }: ActionResponseEvent) => {
  for (const action of actions) {
    if (action.target) {
      // 参数化："Move To" -> "valve"
      runTargetedAction(action.name, action.target);
    } else {
      // 简单："Wave"
      playAnimation(action.name);
    }
  }
});
```

* `名称` 是来自 `actionConfig.actions`.
* `目标` 当角色对特定对象或角色采取行动时会存在，而简单动作则不存在。应根据 `目标`进行分支，而不是根据动作名称：同一个动作可能带或不带它出现。
* 空的 `actions` 数组是一个没有动作的有效轮次。
* SDK 不会执行或调度动作。它还会在聊天消息中添加一条 `action` 条目，并用动作名称标注。

像“拾起灭火器并把它带给受训者”这样的请求，可能会在一个事件中产生多个动作：

```json
{
  "actions": [
    { "name": "Move To", "target": "fire extinguisher" },
    { "name": "Pick Up", "target": "fire extinguisher" },
    { "name": "Move To", "target": "Trainee" },
    { "name": "Drop", "target": "Trainee" }
  ]
}
```

### 更新角色当前关注的内容

用 `updateContext`告诉角色当前聚焦的是哪个对象，这样它就能解析“它”或“那个”之类的词。该值必须与 `actionConfig.objects`中的名称匹配，空字符串会清除它。

```ts
// 静默更改关注对象
client.updateContext({ current_attention_object: 'fire extinguisher', run_llm: 'false' });

// 更改关注对象，并让 Convai 决定是否回应
client.updateContext({
  text: '受训者现在正在看阀门。',
  current_attention_object: 'valve',
  run_llm: 'auto',
});

// 清除关注对象
client.updateContext({ current_attention_object: '', run_llm: 'false' });
```

要描述周围环境而不让它们可被操作，请使用 `updateSceneMetadata`。它不会更改 `actionConfig`，因此这样发送的项目永远不会成为动作目标。参见 [在运行时更新角色上下文](/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/dynamic-context.md).

### 在没有输入的情况下触发响应

`sendTriggerMessage(triggerName?, triggerMessage?)` 让角色在没有使用你应用的人的消息时也能回应，例如在演练开始时。两个参数都是可选的。

```ts
client.sendTriggerMessage('drill_start');
client.sendTriggerMessage(undefined, '请让受训者找到最近的灭火器。');
```

### 选择启用客户端工具

客户端工具增加了带类型的参数和关联结果。请在 `capabilities`中请求动作协议版本 2 和模型输出版本 2，并在 `actionConfig.tools`中声明工具。省略 `capabilities` 会保留语义动作契约。

```ts
const client = new ConvaiClient({
  apiKey: 'YOUR_API_KEY',
  characterId: 'YOUR_CHARACTER_ID',
  capabilities: {
    actionProtocolVersion: 2,
    modelOutputVersion: 2,
  },
  actionConfig: {
    actions: [],
    objects: [],
    characters: [],
    tools: [
      {
        name: 'open_training_record',
        description: '在受训者确认后打开训练记录。',
        inputSchema: {
          type: 'object',
          properties: { recordId: { type: 'string' } },
          required: ['recordId'],
        },
      },
    ],
  },
});
```

`capabilities` 包含三个字段：

| 字段                      | 值                    | 当省略时                      |
| ----------------------- | -------------------- | ------------------------- |
| `actionProtocolVersion` | `1` 或 `2`            | 语义动作                      |
| `modelOutputVersion`    | `1` 或 `2`            | 旧版输出消息，无 `modelOutput` 事件 |
| `botLlmTextMode`        | `'legacy'` 或 `'raw'` | 中的过滤文本 `bot-llm-text` 消息  |

`'raw'` 会把 `bot-llm-text` 聊天消息切换为动作解析前的完整提供方输出。它是聊天文本，不是工具调用流：绝不要从中解析或执行动作。

Convai 会在其连接响应中确认所选能力。 `connect()` 当所选内容与请求不匹配时会拒绝：

| 错误                                 | 原因                  |
| ---------------------------------- | ------------------- |
| `Core 未选择请求的动作协议版本 2`              | 请求了版本 2，但未获批准。      |
| `Core 在没有明确的客户端选择加入情况下选择了动作协议版本 2` | 版本 2 在未请求的情况下被批准。   |
| `Core 未选择请求的模型输出版本 2`              | 请求了模型输出版本 2，但未获批准。  |
| `Core 未选择请求的原始机器人 LLM 文本模式`        | `'raw'` 被请求了，但未获批准。 |

声明会在连接时发送，因此要更改工具需要重新连接。

### 处理模型输出和工具调用

使用 `modelOutputVersion: 2`，订阅 `modelOutput`。每个事件都是一个经过验证的信封，其中 `items` 按顺序包含消息、情绪、语义动作、工具调用和扩展。SDK 会在此模式下抑制 `actionResponse` ，这样同一操作不会被处理两次，并且会丢弃其 `output_id` 已分发的信封。

```ts
import type {
  ActionResult,
  ModelOutputMessage,
  ModelOutputProtocolError,
  ModelOutputToolCallItem,
} from '@convai/web-sdk/core';

client.on('modelOutput', (output: ModelOutputMessage) => {
  for (const item of output.items) {
    if (item.type === 'message') renderMessage(item.content);
    if (item.type === 'semantic_action') runTargetedAction(item.name, item.target);
    if (item.type === 'tool_call') void runTool(item);
  }
});

client.on('modelOutputProtocolError', (error: ModelOutputProtocolError) => {
  console.error(error.code, error.message);
});

async function runTool(call: ModelOutputToolCallItem) {
  let result: ActionResult;
  try {
    // 在此检查权限并请求确认。
    const output = await openTrainingRecord(call.arguments);
    result = { id: call.id, status: 'completed', output };
  } catch (error) {
    result = {
      id: call.id,
      status: 'error',
      error: { message: error instanceof Error ? error.message : '工具失败' },
    };
  }
  client.sendActionResult(result);
}
```

仅渲染并运行经过验证的 `items`. `原始` 保存提供方输出用于诊断，绝不能执行。当信封格式错误或包含不支持的项目时，SDK 会拒绝整个信封并发出 `modelOutputProtocolError` ，代码为 `invalid_model_output`.

多个信封可以共享一个 `logical_turn_id`，以及 `final: true` 会关闭一个信封，而不是整个轮次。SDK 不会跟踪待处理调用、跨信封去重工具调用 ID、重试结果，或保证恰好执行一次。请把这些记账工作留在你的应用中。

### 返回工具结果

`sendActionResult(result)` 会验证结果并将其发送给 Convai。它不返回任何内容；不抛出异常的返回表示结果已发送，而不是已被接受。

| 字段                   | 必需 | 注意                                        |
| -------------------- | -- | ----------------------------------------- |
| `id`                 | 是  | 这里使用的 `id` 的工具调用。                         |
| `status`             | 是  | `'completed'`, `'error'`，或 `'cancelled'`. |
| `输出`                 | 否  | 有限 JSON 数据。                               |
| `error`              | 否  | 有限 JSON 数据。不允许与 `'completed'`.            |
| `characterSessionId` | 否  | 默认为当前会话。                                  |

当结果无效时，它会在发送前抛出：

| 错误                    | 原因                                    |
| --------------------- | ------------------------------------- |
| `会话准备好之前无法发送操作结果`     | 传输未连接。                                |
| `需要动作结果 ID`           | `id` 为空。                              |
| `不支持的动作结果状态`          | `status` 不是这三个值之一。                    |
| `已完成的动作结果不能包含 error`  | `status` 是 `'completed'` 并且 `error` 。 |
| `动作结果输出只能包含有限 JSON 值` | `输出` 包含了非 JSON 值。                     |
| `动作结果错误只能包含有限 JSON 值` | `error` 包含了非 JSON 值。                  |

Convai 会通过 `serverResponse` 事件，其 `event_type` 是 `'action-result'`确认结果。检查其 `status` 并且 `message`.

```ts
client.on('serverResponse', (response) => {
  if (response.event_type === 'action-result' && response.status === 'error') {
    console.error('结果被拒绝：', response.message);
  }
});
```

要将带类型的轮次与其输出相关联，请传入 `sendUserTextMessage(text, { logicalTurnId })`。ID 必须非空且最多 128 字节 UTF-8，否则该方法会抛出 `logicalTurnId 必须非空且最多 128 个 UTF-8 字节`.

### 后续步骤

{% content-ref url="/pages/4c37a8e73c580fc9f3ebe503a43373bd3551a22e" %}
[在运行时更新角色上下文](/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/dynamic-context.md)
{% endcontent-ref %}

{% content-ref url="/pages/8c6e3631e7e7da5b3c9acf5a0019d7737dc79e19" %}
[Web SDK 事件参考](/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/event-reference.md)
{% endcontent-ref %}

{% content-ref url="/pages/dff296725774f9173a7391e28344f5f94cf35b60" %}
[配置和类型参考](/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/vanilla-typescript/best-practices-and-type-definitions.md)
{% endcontent-ref %}

{% content-ref url="/pages/70d752d330612b0fbf1290588e718236d6a18f0e" %}
[连接 API](/api-docs/zh/api-can-kao/core-api-reference/live-apis-beta/connect-api.md)
{% endcontent-ref %}

{% content-ref url="/pages/138cedadc568671d93b18cf90042de5055cbffd5" %}
[客户端到服务器消息](/api-docs/zh/api-can-kao/core-api-reference/live-apis-beta/client-to-server-messages.md)
{% endcontent-ref %}


---

# 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/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/actions.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.
