---
title: "智能体步骤与交接"
description: "将智能体拆分为多个步骤，每个步骤都有各自的指令、工具和已收集的值，并可在通话过程中在智能体之间交接通话。"
---

单个提示词会一次性将整个任务交给模型。**步骤**会一次处理通话的一个阶段：每个步骤都有自己的指令、自己的工具、可选的逐字朗读语句，以及在继续之前必须收集的值。**转交**可让您组织中的另一名智能体在通话过程中接手，并保留完整的通话记录，因此来电者无需重复说明。

每个智能体最初都是单个提示词。在您添加步骤或转交之前，它不会发生任何变化。

## 使用步骤还是单个提示词？

| 在以下情况下使用步骤 | 在以下情况下保留单个提示词 |
| --- | --- |
| 通话包含必须按顺序进行的阶段：先验证，再预约。 | 对话是开放式的：常见问题、前台接待、一般支持。 |
| 智能体必须先获取某个值才能继续。 | 无需按固定顺序收集任何内容。 |
| 某条规则绝不能变通，例如“未满 18 岁的来电者转给监护人”。 | 模型的判断足以处理每个分支。 |
| 必须在通话中的固定节点执行查询。 | 来电者提出请求时，随时都可以调用工具。 |
| 通话的一部分属于另一个智能体，且其拥有自己的提示词和语音。 | 一个角色处理所有事项。 |

步骤并不是脚本化的电话树。在一个步骤中，模型会像使用单个提示词时一样开展对话。步骤决定它当前处理的内容、可使用的工具，以及离开该步骤前需要获取的信息。

## 分步骤智能体的构建方式

当智能体包含步骤时，其 `prompt` 保存**共享指令**：适用于每个步骤的角色设定和规则。每个步骤会在此基础上添加自己的指令。每一轮中，模型都会看到共享指令、当前步骤的名称和指令，以及目前已收集的值。

在构建器中，将智能体从**单个提示词**切换为**步骤**。您会看到每个步骤对应一张卡片，其中包含名称、指令、朗读语句、工具、已收集的值和转换、转交选择器，以及步骤的只读流程图。切换回单个提示词时，系统会提供一个合并后的提示词，其中整合了共享指令和每个步骤。

通过 API，`steps` 和 `handoffs` 是
[智能体](/api-reference/agents)上的字段。与所有智能体字段一样，`PATCH` 会将它们暂存到草稿中；`POST /v1/agents/{agent_id}/deploy` 会将它们发布上线。发送
`"steps": null` 会将智能体切换回单个提示词。测试通话和**通话**使用草稿。

<Tip>
  [副驾驶](/zh/guides/ask-the-copilot)可以根据描述构建步骤
  （“添加一个在预约前验证出生日期的步骤”），也可以编辑某一个步骤而不影响其他步骤。
</Tip>

### 步骤字段

| 字段 | 含义 |
| --- | --- |
| `id` | 必填。小写字母、数字和下划线，以字母开头，最多 40 个字符。在智能体内必须唯一。 |
| `name` | 必填显示名称，长度为 1 至 80 个字符。 |
| `instructions` | 必填，最多 20,000 个字符。与 `prompt` 一样，支持 `{{var}}` [通话变量](/zh/guides/call-variables)。 |
| `say` | 可选。在进入步骤时逐字朗读的语句，最多 1,000 个字符。支持 `{{var}}`。 |
| `tools` | `null` 会让该步骤使用附加到智能体的所有工具。列表会按名称将该步骤限制为仅使用其中的工具。`end_call` 和 `transfer_call` 等内置操作在每个步骤中都遵循智能体的设置。 |
| `collect` | 该步骤收集的值，最多 20 个。每个值都有 `name`、`type`、`description` 和 `required`（默认为 `true`）。 |
| `on_enter_tool` | 可选工具名称，在进入步骤时自动调用。请参阅 [Webhook 步骤](#webhook-steps)。 |
| `transitions` | 步骤接下来可前往的位置的有序列表。请参阅[转换](#transitions)。 |
| `global_when` | 可选。使该步骤可从任意步骤到达。请参阅[全局步骤](#global-steps)。 |
| `voice` | 此步骤的可选语音 ID。智能体使用自定义语音配置时会忽略该字段。 |

流程图本身为 `{"start_step": "<step id>", "steps": [...]}`。通话从 `start_step` 开始。

## 转移

每个转移都包含 `to`（一个步骤 ID），以及以下其中一项：

- **`when`**：自然语言条件，由**模型决定**。智能体会获得一个内置的 `go_to_step` 工具，其中列出可跳转的步骤，每个 `when` 都是选择该步骤的原因。
- **`condition`**：针对已收集值的规则，由**代码决定**。模型无法跳过它，也无法通过对话绕过它。
- **`"always": true`**：无需检查，直接继续。代码会优先决定：一旦进入某个步骤，或收集到其必填值后，第一个匹配的 `condition` 或 `always` 转移就会生效，此时模型尚未能选择 `when`。因此，请将 `always` 放在各项条件之后作为后备方案，或单独使用它将一个步骤直接串联到下一个步骤。包含 `always` 的步骤绝不会等待 `when` 选择。

`condition` 的格式为 `{"value": <collected name or dotted path>, "op": <op>, "target": <literal>}`：

| `op` | 当值满足以下条件时为真 |
| --- | --- |
| `eq`, `ne` | 等于或不等于 `target` |
| `lt`, `lte`, `gt`, `gte` | 小于、不大于、大于或不小于 `target` |
| `in`, `not_in` | 是或不是 `target` 列表中的某个值 |
| `exists`, `missing` | 已收集或尚未收集（无需 `target`） |

`eq`、`ne`、`in` 和 `not_in` 会在去除空格后以不区分大小写的方式比较文本。数字按数值比较，日期按 `YYYY-MM-DD` 字符串比较。未曾收集的值会使除 `missing` 以外的所有操作符均为假。

将 `when` 和 `global_when` 写成条件，例如“来电者想预约”，而不要写成要说出的内容。智能体会将其视为内部路由备注，并被要求绝不朗读或提及正在切换步骤。

当模型调用 `go_to_step` 时：

1. 步骤中的每个 `required` 值都必须存在。若缺少某项，模型会获知缺少哪一项，并向来电者询问。
2. 值会被存储。
3. 会按顺序检查 `condition` 和 `always` 转移。第一个匹配项生效。
4. 否则，智能体会移动到模型选择的步骤。
5. 若没有任何匹配项，且模型未指定步骤，智能体将停留在当前位置。

跳转到[全局步骤](#global-steps)是例外：来电者更改了话题，因此即使当前步骤仍缺少必填值，也会继续跳转，并且会在检查该步骤条件之前进行。

每次进入步骤时，也会检查 `condition` 和 `always` 转移，因此一连串由代码决定的步骤会一次性执行。链条会在以下情况停止：

- 到达本次运行中已访问过的步骤；
- 经过 8 次跳转后；
- 到达仍有必填值待收集的步骤，除非该步骤具有 `on_enter_tool`（webhook 步骤始终根据其结果进行路由）。

只会说出链条停止所在步骤的 `say` 行。

没有转移的步骤是**终止步骤**。智能体会在此完成流程，并使用其常规内置操作结束或转接通话。

## 收集值

在 `collect` 中列出某个步骤所需的内容。模型在调用 `go_to_step` 时传入这些值，它们会在通话剩余期间保持可用。

| `type` | 可存储内容 |
| --- | --- |
| `string` | 任意文本 |
| `number` | 数字 |
| `boolean` | `true` 或 `false` |
| `date` | 格式为 `YYYY-MM-DD` 的日期 |

收集的值属于通话内容。它们会与转录文本一同显示在通话中，并遵循相同的保留、删除和脱敏规则。当智能体的通话录音关闭时，收集的值既不会被存储，也不会发送到 webhook；步骤时间线仍会保留。请参阅[在通话中查看的位置](#where-to-see-it-on-a-call)。

## 说话内容

当智能体进入步骤时，`say` 中的内容会严格按原文说出。随后，模型会在同一回复中继续处理新步骤，并且知道这句话刚刚已经说过，因此不会重复。将 `say` 用于问候、必需的告知事项和固定措辞，其余回复交由模型处理。

起始步骤的 `say` 是智能体在任何同意声明之后、首次发言时的开场白。当来电者先开口时，它会作为智能体首次对真人作出的回复开头，绝不会对 IVR 或语音信箱问候语说出。

当通话通过 `__previous__` 或交回返回某个步骤时，该步骤的 `say` 不会再次说出。请按实际应有的发音方式编写：将语音可能误读的内容拼写完整。说话内容支持 `{{var}}` 通话变量，但不支持已收集的值。

## Webhook 步骤

将 `on_enter_tool` 设置为智能体的某个工具，步骤一进入便会立即运行，无需等待模型。其参数为名称与工具参数名称匹配的已收集值。结果会存储为名为 `<tool>_result` 的已收集值，并截断为 4 KB；系统会立即检查该步骤的 `condition` 和 `always` 转换，因此查询可以根据自身结果路由通话。

存储的值包含两个字段：

| 字段 | 内容 |
| --- | --- |
| `status` | 您的端点返回的 HTTP 状态码。 |
| `response` | 响应正文。 |

使用 `lookup_account_result.response.<field>` 根据正文中的字段路由，使用 `lookup_account_result.status` 根据状态码路由。

查询最多运行 8 秒，或者使用工具自身的超时时间（如果更短）。如果耗时超过 3 秒，智能体会说一句简短的话，避免来电者陷入静默。如果您的端点返回 400 或更高的状态码，或者未能及时响应，智能体会告知来电者目前无法完成该检查，并继续处理其能够完成的事项。

## 全局步骤

带有 `global_when` 的步骤可在满足该描述时从任何步骤进入。与 `when` 一样，由模型决定是否进入。将其用于来电者可能随时提出的话题：账单问题、请求人工服务、投诉。

全局步骤上带有 `"to": "__previous__"` 的转换会将通话返回到此前处于活动状态的步骤，让来电者从中断处继续。该步骤会恢复：其 `on_enter_tool` 不会再次运行，`say` 不会重复，智能体会继续执行该步骤，而不会重新开始。

## 交接

`handoffs` 列出您所在组织中可以接管通话的其他智能体：

```json
[{"agent_id": 413, "when": "The caller asks about billing or a refund"}]
```

智能体会获得内置的 `hand_off` 工具，其中包含这些智能体和对应原因。与转场一样，每个 `when` 都是内部路由说明，绝不会被读出来。

进行交接时，智能体会用自己的声音说一句简短的话，告知来电者将被转接到哪里，例如“当然，我会将您转接到我们的账单团队”。随后，目标智能体的提示词、步骤、工具和声音将接管，通话会在同一份记录中继续。新的智能体会简短地介绍自己并回应来电者的最后一个请求；不会宣布发生了转接。此后，目标智能体的知识库即可使用。

通话首次到达某个智能体时，会从该智能体的 `start_step` 开始：该步骤的 `on_enter_tool` 会运行，并朗读其 `say`。当通话交接回此前已访问过的智能体时，该智能体会从其之前所在的步骤继续，不会运行 `on_enter_tool`、重复其 `say` 或再次介绍自己。通话中收集的值由所有智能体共享。

- 目标必须是同一组织中的智能体，并且智能体不能将通话交接给自身。
- 所有通话级别的设置都保留在接听通话的智能体中：产品、语言、由谁先说话、语音信箱、同意声明及所有其他通话设置。
- 目标智能体可以继续交接，也可以交接回来。一通电话最多可到达 8 个智能体。
- 测试通话会运行您测试的智能体草稿版本。其交接到的智能体会运行各自已部署的版本。
- 如果两个智能体拥有同名但定义不同的工具，通话中只会保留一个，并且始终以接听通话的智能体为准。保存时，构建器会发出警告。
- 智能体可以在没有步骤的情况下设置交接。其完整提示词会按原样使用。

对于具有步骤或交接的智能体，`go_to_step` 和 `hand_off` 是保留名称，因此您自己的工具不能使用这些名称。

## 如何在通话中查看

在 **通话记录** 中打开该通话。记录旁边的步骤时间线会显示通话进入过的每个步骤及其原因，并同时列出收集的值。

[通话对象](/api-reference/calls#retrieve-a-call) 和通话完成 webhook（[`telephony.complete` / `web.complete`](/zh/webhooks/call-complete)）会以 `steps_trace` 和 `collected_values` 提供相同的数据。没有步骤的通话中，这两项均不存在。

```json
"steps_trace": [
  {"t_ms": 0, "agent": "a412", "step": "greet", "via": "start"},
  {"t_ms": 51230, "agent": "a412", "step": "verify", "via": "model"},
  {"t_ms": 98410, "agent": "a412", "step": "minor", "via": "condition"}
],
"collected_values": {"full_name": "Dana Reyes", "dob": "2010-04-02", "age": 15}
```

`agent` 是 `a` 后跟智能体 ID。`via` 说明通话是如何转移的：

| `via` | 含义 |
| --- | --- |
| `start` | 通话从此步骤开始。 |
| `model` | 模型通过 `go_to_step`、`when` 或 `global_when` 选择了该步骤。 |
| `condition` | 匹配了 `condition` 或 `always` 转场。 |
| `on_enter` | 根据 webhook 步骤的结果匹配了转场。 |
| `handoff` | 另一个智能体接管了通话。 |

## 步骤的运行位置

步骤可原生运行于 **Spark**、**Bolt** 和所有 **Storm** 产品，适用于呼入、呼出和营销活动通话、测试通话，以及网页和小组件通话。

在其他所有位置，智能体仍可正常工作，只是没有这种结构：它会按顺序运行一个合并了共享指令和所有步骤的扁平化提示词。这包括：

- 实验性 Bolt 产品；
- [Realtime](/zh/guides/use-with-openai-realtime) 会话，包括已保存智能体的会话；
- 由[动态通话配置](/zh/guides/dynamic-call-config) webhook 返回的内联配置。返回 `agent_id` 的 webhook 会使用该智能体的步骤。

## 限制

| 限制 | 值 |
| --- | --- |
| 每个智能体的步骤数 | 100 |
| 每个步骤收集的值数量 | 20 |
| 每个智能体的交接目标数 | 8 |
| 单次通话中可触达的智能体数 | 8 |
| `steps` JSON 的大小 | 200 KB |
| 步骤 `instructions` | 20,000 个字符 |
| `say` 行 | 1,000 个字符 |
| 存储的 `on_enter_tool` 结果 | 4 KB |

违反规则的保存会返回 `400`，并附带错误字段的路径，例如
`steps.steps[2].transitions[0].to`。`tools` 和
`on_enter_tool` 中的工具名称必须是已附加到该智能体的工具。

暂不支持按步骤设置语言、跳过模型的脚本化步骤，以及 say 行内的收集值。

## 导入的智能体

从 Retell、Bland、Vapi 或 ElevenLabs [导入](/zh/guides/import-agents)
会将多提示词智能体、流程、路径和工作流导入为步骤，并将小队和智能体转接导入为已导入智能体之间的交接。导入前，审核页面会同时显示两者。

## 示例：牙科接诊

按顺序包含三个步骤：问候、收集患者信息、预约。

```json
{
  "prompt": "You are Ava, the front desk assistant at Acme Dental. Be warm and brief. Ask one question at a time.",
  "steps": {
    "start_step": "greet",
    "steps": [
      {
        "id": "greet",
        "name": "Greeting",
        "instructions": "Greet the caller and ask how you can help.",
        "say": "Thanks for calling Acme Dental, this is Ava. How can I help you today?",
        "tools": null,
        "collect": [],
        "transitions": [
          {"to": "details", "when": "The caller wants to book an appointment"}
        ]
      },
      {
        "id": "details",
        "name": "Patient details",
        "instructions": "Ask for the caller's full name, date of birth and the reason for the visit. Read the date of birth back and ask the caller to confirm it.",
        "tools": null,
        "collect": [
          {"name": "full_name", "type": "string", "description": "Caller's full name", "required": true},
          {"name": "dob", "type": "date", "description": "Date of birth", "required": true},
          {"name": "reason", "type": "string", "description": "Reason for the visit", "required": true},
          {"name": "new_patient", "type": "boolean", "description": "First visit to the practice", "required": false}
        ],
        "transitions": [
          {"to": "book", "when": "The caller has confirmed their details"}
        ]
      },
      {
        "id": "book",
        "name": "Book",
        "instructions": "Offer the two earliest open slots from book_slot and book the one the caller picks. Confirm the date and time, then say goodbye and end the call.",
        "tools": ["book_slot"],
        "collect": [],
        "transitions": []
      }
    ]
  }
}
```

使用 `PATCH /v1/agents/{agent_id}` 暂存，通过 **通话** 试用，然后部署。
在收集姓名、出生日期和就诊原因之前，`details` 步骤无法交接至预约；`new_patient` 为可选项。

## 示例：年龄限制分支

模型可以与任何人交谈，但由代码决定谁可以预约。收集年龄，然后在考虑模型自身选择之前，依据年龄进行路由。

```json
{
  "id": "verify",
  "name": "Verify age",
  "instructions": "Ask for the caller's age.",
  "tools": null,
  "collect": [
    {"name": "age", "type": "number", "description": "Caller's age in years", "required": true}
  ],
  "transitions": [
    {"to": "guardian", "condition": {"value": "age", "op": "lt", "target": 18}},
    {"to": "book", "when": "The caller has given their age"}
  ]
}
```

无论模型如何决定，说自己十五岁的来电者都会进入 `guardian`。
其他所有人都会进入 `book`。两者都是图中其他位置的常规步骤。

## 示例：账户查询 webhook 步骤

来电者提供账户号码，进入下一步骤时会运行查询，其结果会决定路由。为简洁起见，省略了 `not_found`、`collections` 和 `support` 步骤。

```json
[
  {
    "id": "identify",
    "name": "Identify",
    "instructions": "Ask for the caller's account number and read it back digit by digit.",
    "tools": null,
    "collect": [
      {"name": "account_number", "type": "string", "description": "Account number", "required": true}
    ],
    "transitions": [
      {"to": "lookup", "when": "The caller has confirmed their account number"}
    ]
  },
  {
    "id": "lookup",
    "name": "Look up account",
    "instructions": "Tell the caller what you found on their account and ask how you can help.",
    "tools": ["lookup_account"],
    "collect": [],
    "on_enter_tool": "lookup_account",
    "transitions": [
      {"to": "not_found", "condition": {"value": "lookup_account_result.response.found", "op": "eq", "target": false}},
      {"to": "collections", "condition": {"value": "lookup_account_result.response.account_status", "op": "in", "target": ["past_due", "suspended"]}},
      {"to": "support", "when": "The caller needs help with an active account"}
    ]
  }
]
```

`lookup_account` 会接收 `account_number`，因为其参数使用了该名称。如果响应正文包含 `"found": false`，通话将转至 `not_found`。如果其 `account_status` 为 `past_due` 或 `suspended`，则转至 `collections`。有效账户会停留在 `lookup`，模型将在此提供帮助，然后转至 `support`。如需将查询失败发送至独立步骤，请先添加状态条件：错误响应会存储其状态码，因此 `{"value": "lookup_account_result.status", "op": "gte", "target": 400}` 可以捕获该情况；超时或无响应会存储 `"error"`，因此 `{"value": "lookup_account_result.status", "op": "eq", "target": "error"}` 可以捕获该情况。

## 示例：前台转接至账单部门

两个智能体。前台（智能体 `412`）接听所有通话，并将账单相关问题转交给账单智能体（智能体 `413`），后者拥有自己的提示词、工具和语音。

在前台中：

```json
{
  "handoffs": [
    {"agent_id": 413, "when": "The caller asks about a bill, a payment or a refund"}
  ]
}
```

在账单智能体中，以便其可将来电者转回：

```json
{
  "handoffs": [
    {"agent_id": 412, "when": "The billing question is resolved and the caller needs something else"}
  ]
}
```

前台会说一句简短的话，例如“我帮您转接至账单团队”，账单智能体会进行自我介绍，并从前台结束的位置继续对话。账单部门转回后，前台会从原位置继续，无需再次向来电者问候。通话的产品、语言、同意声明及其他通话设置会保留在前台，因为前台接听了通话。

如果账单部门只需要自己的指令，而不需要独立的智能体，请改用前台中的全局步骤。问题得到解答后，通话会返回来电者之前所在的步骤：

```json
{
  "id": "billing",
  "name": "Billing questions",
  "instructions": "Answer questions about bills and payments. Offer to send a payment link by email.",
  "tools": null,
  "collect": [],
  "transitions": [
    {"to": "__previous__", "when": "The billing question is answered"}
  ],
  "global_when": "The caller asks about a bill or payment at any point"
}
```

## 后续步骤

<CardGroup cols={2}>
  <Card title="提示词编写指南" icon="pen" href="/zh/guides/prompting">
    编写适用于电话场景的共享指令和步骤指令。
  </Card>
  <Card title="构建工具集成" icon="code" href="/zh/guides/build-tool-integration">
    创建您的步骤和 Webhook 步骤调用的工具。
  </Card>
  <Card title="测试您的智能体" icon="flask" href="/zh/guides/test-agents">
    在部署前，针对每个分支运行相同的场景。
  </Card>
  <Card title="智能体 API" icon="robot" href="/api-reference/agents">
    所有智能体字段、草稿和部署。
  </Card>
</CardGroup>
