语音序列节点参考
警告: 语音序列处于预览阶段。步骤、入口数据和序列定义格式可能发生变化,导致现有序列和 API 请求失效。
使用本参考编写通过创建通话发送的 sequence.definition。Bird 会运行一次内联定义且不会保存它。如需保留序列,请在控制台编辑器中构建。
定义
| 字段 | 值 |
|---|---|
schema_version | 1。 |
expression_environment | "bird.cel.v1"。 |
nodes | 节点对象数组。数组顺序不影响执行。 |
settings | 省略该字段或发送 {}。非空对象会返回 unsupported_contract 错误;非对象值会返回 invalid_envelope 错误。 |
presentation | 可选的编辑器布局和标签。不影响执行,但计入大小限制。 |
每个节点包含以下字段:
| 字段 | 值 |
|---|---|
id | 在定义中唯一。以小写字母开头,后接小写字母、数字或 _,最多 64 个字符。 |
type | 本页中的一种节点类型,例如 voice.say。 |
type_version | 本页每种节点类型均为 1,但 voice.dial 为 3。 |
config | 固定节点形态的配置,例如声明的结果。节点没有配置时使用 {}。 |
input | 节点运行时使用的值。节点没有参数时使用 {}。 |
connections | 从结果名称到 { "node_id": "<target>", "port": "input" } 的映射。 |
连接不能形成循环,且每个节点必须从某个入口可达。未出现在 connections 中的结果为未连接状态。未连接的结果会正常结束运行,除非节点另有说明。
值和表达式
大多数字段接受字面量 JSON。标记为 binds 的字段还接受 $expr 或 $template;标记为 expr 的字段仅额外接受 $expr:
{"$expr": "trigger.data.customer_name"}返回一个带类型的值。{"$template": ["Hello ", {"$expr": "trigger.data.customer_name"}, "."]}返回文本。模板是数组;{{name}}保持为纯文本。
表达式使用 CEL,可以读取以下值:
| 值 | 内容 |
|---|---|
trigger.data | 入口数据,与入口的 data_schema 匹配。 |
trigger.node_id | 启动本次访问的入口 ID。 |
trigger.type | 启动本次访问的入口类型,例如 trigger.start_call。 |
steps.<id>.output | 本次访问中先前已完成节点的输出。 |
variables.<key> | 本次访问中由 data.set 节点保存的值。 |
execution.id、execution.started_at | 运行 ID 及其开始时间。在语音中使用 string(execution.started_at)。 |
execution.call | 通话的 id、session_id,以及原始参与方 orig 和 dest,每个都可以是 null。 |
读取可选值之前先做防护检查,例如 has(trigger.data.name) ? trigger.data.name : 'caller'。has 宏可用;map、filter、all、exists 和 exists_one 不可用。
入口
trigger.start_call
通话的起始位置。创建通话请求中的 entry_node_id 指定此节点。
| 字段 | 值 |
|---|---|
config.data_schema | 可选的 JSON Schema(draft 2020-12)对象模式,用于 trigger_data。仅支持本地 $ref。若未提供,入口仅接受 {}。 |
| 结果 | event。 |
入口数据限制为 16 KiB。
语音与音频
voice.say
| 字段 | 值 |
|---|---|
input.text | 必填,binds。要朗读的文本,最多 160 字节。 |
input.language | 必填。语音语言代码,例如 en。 |
| 结果 | next。 |
voice.play
| 字段 | 值 |
|---|---|
input.asset_id | 必填。此工作区中可用的音频资源 ID。不可用的资源会在该步骤运行时导致通话失败。 |
| 结果 | next。 |
voice.tone
| 字段 | 值 |
|---|---|
input.frequency_hz | 必填。100 到 3000。 |
input.duration_ms | 必填。1 到 10000。 |
| 结果 | next。 |
logic.pause
在通话保持连接的状态下等待。
| 字段 | 值 |
|---|---|
input.duration_ms | 必填。1 到 60000。 |
| 结果 | next。 |
键盘输入
voice.gather
播放提示音,按键可中断提示,并收集键盘输入的数字。
| 字段 | 值 |
|---|---|
input.prompts | 必填。最多 4 个提示。每个提示为 {"type": "say", "text", "language"}、{"type": "play", "asset_id"}、{"type": "tone", "frequency_hz", "duration_ms"} 或 {"type": "pause", "duration_ms"},限制与对应节点相同。音调和暂停时长合计最多 60000 毫秒。提示的 text 支持 binds。 |
input.max_digits | 必填。1 到 32。 |
input.timeout_seconds | 必填。等待第一个按键的秒数,1 到 10。 |
input.inter_digit_timeout_seconds | 必填。按键之间等待的秒数,1 到 5。 |
input.finish_on_key | 必填。结束输入的按键,为 0–9、*、#、A–D 之一,或 null。 |
input.matches | 必填。从结果名称到选中该结果的精确按键数字的映射,例如 {"hours": "1"}。名称和数字必须唯一。名称不能是 input、timeout 或 fallback。数字最长为 max_digits,且不能包含 finish_on_key。 |
input.private | 可选,默认 false。当值为 true 时,后续步骤无法读取按键数字,且数字不会出现在追踪记录中。 |
| 结果 | matches 中的每个名称、无按键时的 timeout,以及其他任何输入对应的 fallback。 |
| 输出 | digits 和 reason:initial_timeout、max_digits、finish_key 或 inter_digit_timeout。 |
逻辑与数据
logic.branch
按顺序检查条件,沿第一个为真的条件继续执行。
| 字段 | 值 |
|---|---|
config.cases | 必填。至少一个 {"port": "<name>", "when": {"$expr": "<boolean>"}}。 |
config.default_port | 必填。没有条件为真时的结果名称。 |
| 结果 | cases 中的每个 port,以及 default_port。 |
| 输出 | branch:所选的结果名称。 |
data.set
保存值供同一次访问中的后续步骤使用。
| 字段 | 值 |
|---|---|
input | 值对象,每个值支持 binds。每个键会变为 variables.<key>。所有值根据该节点执行前的变量状态计算。 |
| 结果 | next。 |
| 输出 | 已保存的对象。 |
通话与转接
voice.dial
呼叫一个电话号码或您工作区某条入站 SIP 中继上的号码,或振铃一个振铃组,并将其接入当前通话。使用 type_version 3。
| 字段 | 值 |
|---|---|
input.type | 必填。pstn 表示呼叫电话号码,sip_trunk 表示通过您的某条入站 SIP 中继呼叫号码,ring_group 表示振铃振铃组中的在线成员。 |
input.pstn | 当 type 为 pstn 时必填,否则省略。包含 dest 和可选 orig 的对象。 |
input.sip_trunk | 当 type 为 sip_trunk 时必填,否则省略。包含 trunk_id、dest 和可选 orig 的对象。 |
input.ring_group | 当 type 为 ring_group 时必填,否则省略。包含 ring_group_id 的对象。 |
trunk_id | 在 sip_trunk 中必填。您工作区中某个入站 SIP 中继的 ID,例如 spt_01jzj6c0p4f8b3x2m9k7q5r1tw。仅接受字面量值。 |
ring_group_id | 在 ring_group 中必填。您工作区中某个振铃组的 ID(包括其默认组),例如 vrg_01jzj6c0p4f8b3x2m9k7q5r1tw。仅接受字面量值。 |
dest | 必填,binds。对于 pstn,为要呼叫的 E.164 号码,例如 +12025550123。对于 sip_trunk,为号码、分机或名称,例如 1001:1 到 64 个字母、数字和 +*._-。中继的入站路由会按照呼叫是针对此值到达的方式投递该呼叫。 |
orig | 可选,binds。如果省略 orig 属性,入站通话会显示被拨打的 Bird 号码,出站通话会显示发起号码。对于 pstn,请使用工作区主叫号码或有效来电方的 E.164 号码。来电方号码以未签名方式发送,可能会被接收方运营商筛查。对于 sip_trunk,可取值与 dest 相同,例如 sales。 |
input.timeout_seconds | 对于 pstn 和 sip_trunk 必填,对于 ring_group 省略,expr。等待接听的秒数(包括中继网关之间的故障转移时间),1 到 120。未接听的呼叫进入 no_answer;已接听的通话不受此限制。 |
| 结果 | success、busy、no_answer 和 failure。success 在已接通的通话结束且原始通话音频恢复后触发,而非在被叫方接听时触发。如果 SIP 中继拒绝或转发该通话,会选择 failure。未连接的 failure 会导致运行失败。对于振铃组,busy 表示其成员忙碌、拒接或正在进行其他通话,no_answer 表示 60 秒内无人接听或无人在线,failure 表示 Bird 无法使该组振铃。 |
版本 2 没有 ring_group 目标,并且始终要求 timeout_seconds。所有版本 2 的输入在版本 3 中同样有效,因此升级步骤不会改变其他任何内容。
版本 1 从 input.to 中读取号码,且没有 orig。使用该版本的步骤会继续运行。在编辑器中升级版本 1 的步骤时,其 to 值会移至 pstn.dest。
logic.voice_goto
在同一定义中启动另一个入口。新的访问会获得新的 trigger.data,并清除之前步骤的输出和变量;通话继续进行。
| 字段 | 值 |
|---|---|
input.entry_node_id | 必填。此定义中某个 trigger.start_call 节点的 ID。 |
input.data | 可选,expr,默认 {}。该节点 schema 对应的入口数据。 |
| 结果 | 无。 |
自动化
automation.send_event
向自动化发送应用事件,不等待其运行。事件进入队列后通话即继续;是否启动了运行只能在自动化中查看。
| 字段 | 值 |
|---|---|
input.automation_id | 必填。此工作区中某个自动化的 ID。 |
input.event | 必填。该自动化的事件名称:1 到 128 个字母、数字、点、下划线或连字符。 |
input.data | 可选,默认 {}。事件的全部数据。其顶层值支持 bind,也可以将整个对象设为一个 $expr。嵌套更深的值保持为字面量。整个 input 最多 16 KiB。 |
| 结果 | 事件进入队列后触发 success,无法进入队列时触发 failure。两者都不反映自动化的运行情况。未连接的 failure 会导致运行失败。 |
结束通话
logic.exit
以一个结果结束通话。
| 字段 | 值 |
|---|---|
config.status | 必填。succeeded 或 failed。 |
config.reason | 可选,expr。由小写字母、数字和 _ 组成的稳定代码,以字母或数字开头,最多 64 个字符。省略该字段而非发送 null。 |
input.output | 必填对象,可以为 {}。其顶层值支持 binds。 |
| 结果 | 无。 |
voice.hangup
结束通话,不产生自定义结果。该节点没有字段,也没有结果。
有关 voice.webhook,请参阅在语音序列中使用应用数据。
示例:转接至某人
此定义向接听方播放问候语,拨打客服号码,并记录转接是否成功:
{
"schema_version": 1,
"expression_environment": "bird.cel.v1",
"nodes": [
{
"id": "start",
"type": "trigger.start_call",
"type_version": 1,
"config": {},
"input": {},
"connections": { "event": { "node_id": "greeting", "port": "input" } }
},
{
"id": "greeting",
"type": "voice.say",
"type_version": 1,
"config": {},
"input": { "text": "Connecting you to our support team.", "language": "en" },
"connections": { "next": { "node_id": "support", "port": "input" } }
},
{
"id": "support",
"type": "voice.dial",
"type_version": 3,
"config": {},
"input": { "type": "pstn", "pstn": { "dest": "+12025550123" }, "timeout_seconds": 30 },
"connections": {
"success": { "node_id": "connected", "port": "input" },
"busy": { "node_id": "unavailable", "port": "input" },
"no_answer": { "node_id": "unavailable", "port": "input" },
"failure": { "node_id": "unavailable", "port": "input" }
}
},
{
"id": "connected",
"type": "logic.exit",
"type_version": 1,
"config": { "status": "succeeded", "reason": "transferred" },
"input": { "output": {} }
},
{
"id": "unavailable",
"type": "logic.exit",
"type_version": 1,
"config": { "status": "failed", "reason": "support_unavailable" },
"input": { "output": {} }
}
]
}将其作为 sequence.definition 连同 "entry_node_id": "start" 和 "trigger_data": {} 一起发送。
相关资源
继续查看此主题的文档、指南和示例。