跳到导航

以代码构建工作流

使用 Python 和 JavaScript SDK 构建、编辑并进行版本控制智能体工作流

概览

工作流以节点和边组成的图结构存储。手动编写该图意味着你需要自行确保边 ID、edge_order 列表和条件对象保持一致。Python 和 JavaScript SDK 中的 Workflow 构建器让你可以通过链式方法调用描述同一图结构,然后生成 agents.create() 和 agents.update() 已支持的 workflow 值。

可用于:

  • 通过代码构建工作流,并像审查其他代码变更一样审查它。
  • 加载智能体工作流,更改一个节点后推送回去,而不影响图中的其他部分。
  • 将工作流以 JSON 格式存入版本控制,并在 CI 中验证。

构建器完全在 SDK 内运行。它不会调用新的 API 端点,构建的任何内容都可以 在控制台的可视化编辑器中打开和编辑。

要求

  • Python:使用 elevenlabs 包。构建器从 elevenlabs.agent_workflows 导入。
  • JavaScript:使用 @elevenlabs/elevenlabs-js 包。构建器从包根目录导出。

构建工作流

Workflow 对象代表起始节点。每个动词方法都会添加一个节点并返回该节点,因此你可以从该节点继续链式调用下一步:

PythonJavaScript添加内容
converse(id, prompt, **options)converse(id, prompt, options?)一个子智能体节点。智能体会围绕添加到其指令中的 prompt 展开对话。
tool(id, tool_ids, **options)tool(id, toolIds, options?)一个运行指定工具的调度工具节点。
transfer(id, destination, **options)transfer(id, destination, options?)转接至电话号码、SIP URI 或其他智能体。
end(id="end")end(options?)一个结束节点。未指定 ID 的每次 end() 调用都会共用一个 "end" 节点。

路由方法决定哪条边通向下一个节点:

PythonJavaScript创建内容
when(condition)when(condition)一条条件边。字符串是 LLM 条件;Expr 是表达式。
on_success() / on_failure()onSuccess() / onFailure()一条来自工具节点的边,会在工具调用成功或失败时采用。
on_result(success=..., failure=...)onResult({ success, failure })同时创建两条工具结果边。
go_to(node)goTo(node)一条通向已有节点的边,用于合并和循环。
up()up()不创建边。返回当前节点链式调用所来自的节点,以便继续从该节点链式调用。参见将工作流写成一条链。

直接在工作流上调用的动词方法会通过无条件边从起始节点分支。以下示例将来电者路由至订单查询;如果查询失败,则重试身份识别;并转接 VIP 或感到沮丧的来电者:

from elevenlabs import ElevenLabs
from elevenlabs.agent_workflows import Destination, Expr, Workflow
elevenlabs = ElevenLabs()
wf = Workflow()
triage = wf.converse("triage", "Greet the caller and find out why they're calling")
identify = triage.when("Caller wants to check an order").converse(
"identify", "Ask for the order number and the email on the account"
)
lookup = identify.when("Caller gave an order number").tool("lookup_order", ["tool_get_order"])
lookup.on_result(
success=lambda ok: ok.converse("resolve", "Explain the order status")
.when("Caller is satisfied")
.end(),
failure=lambda failed: failed.go_to(identify),
)
triage.when(Expr.variable("tier").eq("gold")).transfer("vip", Destination.agent("agent_vip"))
triage.when("Caller wants a human").transfer("human", Destination.phone("+15551234567"))
elevenlabs.conversational_ai.agents.update("agent_id", workflow=wf.build())

build() 会验证整个图并返回请求模型。它还会:

  • 为起始节点分配 API 所需的 ID start_node。
  • 生成 source__target 格式的边 ID。
  • 按添加边的顺序填充每个节点的 edge_order。
  • 为所有未设置位置的节点定位,使图在可视化编辑器中从左到右排列。

将工作流写成一条链

up() 会返回当前节点链式调用所来自的节点,因此你可以在一个表达式中描述整个工作流,而无需将每个节点存入变量。在单条链中,请使用带节点 ID 的 go_to() / goTo(),因为没有可传入的变量:

wf = Workflow()
(
wf.converse("triage", "Find out why the customer is calling")
.when("Caller gave an order number")
.tool("lookup_order", ["tool_get_order"])
.on_result(
success=lambda ok: ok.converse("resolve", "Explain the order status").when("Customer is satisfied").end(),
failure=lambda failed: failed.go_to("triage"),
)
.up()
.when(Expr.variable("tier").eq("gold"))
.transfer("vip", Destination.agent("agent_vip_desk"))
)
elevenlabs.conversational_ai.agents.update("agent_id", workflow=wf.build())

对于通过 wf.node(id) 获取的节点句柄,up() 会返回第一条进入该节点的边所来自的节点。对于起始节点,它会返回起始节点。

节点选项

Python 中的可选设置通过关键字参数传入,JavaScript 中则通过末尾的选项对象传入。其名称与 API 中节点字段一致:

节点PythonJavaScript
converselabel, tool_ids, knowledge_base, conversation_config, entry_behaviorlabel, toolIds, knowledgeBase, conversationConfig, entryBehavior
toolschema_overridesschemaOverrides
transfertransfer_type, custom_sip_headers, post_dial_digits, delay_ms, transfer_message, …transferType, customSipHeaders, postDialDigits, delayMs, transferMessage, …
endidid

如果要自行放置节点,每个动词方法还接受 position。Python 中的 Workflow(prevent_subagent_loops=True),或 JavaScript 中的 new Workflow({ preventSubagentLoops: true }),会阻止对话再次进入已访问过的子智能体。

triage = wf.converse(
"triage",
"Greet the caller and find out why they're calling",
label="Triage",
tool_ids=["tool_crm_search"],
entry_behavior="generate_immediately",
)
triage.when("Caller is a VIP").transfer(
"vip_desk",
Destination.agent("agent_vip_concierge"),
transfer_message="Connecting you to our VIP team",
delay_ms=500,
)
identify.when("Caller refuses to share details").end(id="abandoned")

转接目标

PythonJavaScript转接至
Destination.phone(number)Destination.phone(number)电话号码
Destination.phone_variable(name)Destination.phoneVariable(name)动态变量中的电话号码
Destination.sip(uri)Destination.sip(uri)SIP URI
Destination.sip_variable(name)Destination.sipVariable(name)动态变量中的 SIP URI
Destination.agent(agent_id)Destination.agent(agentId)其他智能体

条件

when() 接受普通字符串、Condition 或 Expr:

  • 字符串是 LLM 条件:由 LLM 决定其是否成立。
  • Condition.llm(text, label=...) 的作用相同,但会在可视化编辑器的边上显示标签。使用 Condition.expr(expr, label=...) 为表达式添加标签。
  • Expr 会根据动态变量求值,无需调用 LLM。

使用 Expr.variable(name) 和 Expr.literal(value) 构建表达式。通过 eq、neq、gt、gte、lt 和 lte 比较,通过 add、sub、mul 和 div 进行算术运算。通过 all_of / allOf、any_of / anyOf 和 if_else / ifElse 组合表达式。普通值会自动包装为 Expr.literal()。Expr.llm(description) 会要求 LLM 从对话中提取一个值;传入 type 和 enum 可获取字符串、数字或固定集合中的一个值,而不是布尔值。

from elevenlabs.agent_workflows import Condition, Expr
is_vip = Expr.any_of(
Expr.variable("tier").eq("gold"),
Expr.all_of(Expr.variable("orders").gte(10), Expr.variable("churn_risk").eq(True)),
)
triage.when(Condition.expr(is_vip, label="VIP")).transfer("vip_desk", Destination.agent("agent_vip"))
big_refund = Expr.variable("unit_price").mul(Expr.variable("quantity")).gt(500)
resolve.when(big_refund).transfer("finance", Destination.phone("+15551234567"))
score = Expr.llm("How satisfied the caller sounds, from 1 to 5", type="integer")
resolve.when(score.lte(2)).converse("recovery", "Apologize and offer a discount code")

循环和反向边

API 允许任意两个节点之间存在一条边,方向不限。要让对话沿原路返回,请使用 back 为该边提供反向条件:

faq = triage.when("Caller has a general question", back="Question answered").converse(
"faq", "Answer using the FAQ knowledge base"
)

go_to() / goTo() 会为你处理常见情况。当目标节点已有一条边指向当前节点时,新边会成为该边的反向条件。在第一个示例中,failed.go_to(identify) 会作为 identify → lookup_order 边的反向条件发送。相同两个节点之间的任何其他第二条边,以及从节点指向自身的任何边,都会导致验证失败。

编辑现有智能体

Workflow.from_agent() / Workflow.fromAgent() 会加载由 agents.get() 返回的智能体工作流。节点 ID、边 ID、编辑器位置和边顺序都会保留,因此推送回去时只会更改你修改的内容。

PythonJavaScript作用
wf.node(id)wf.node(id)返回现有节点的句柄,用于编辑或从该节点继续链式调用。
node.update(**fields)node.update(fields)更改节点的提示词、工具、目标或选项。
wf.insert_between(source, target, fn)wf.insertBetween(source, target, fn)将新节点插入现有边中,并保留其条件。
wf.remove(id)wf.remove(id)删除节点及所有指向或来自该节点的边。

insert_between() / insertBetween() 会将一个携带被替换边条件的分支传入你的函数。函数返回其添加的最后一个节点,随后该节点会连接到原始目标。该方法返回该节点;若两个节点之间没有边,则返回 None / undefined。

agent = elevenlabs.conversational_ai.agents.get("agent_id")
wf = Workflow.from_agent(agent)
wf.node("resolve").update(prompt="Explain the status, then offer a discount code")
verify = wf.insert_between(
"identify",
"lookup_order",
lambda start: start.converse("verify", "Verify the caller's identity with a one-time code"),
)
if verify is not None:
verify.when("Caller can't verify").end(id="unverified")
wf.remove("vip")
elevenlabs.conversational_ai.agents.update("agent_id", workflow=wf.build())

在版本控制中存储工作流

to_json_string() / toJSONString() 会以 API 的传输格式写入工作流:使用 snake_case 键、每一级排序,并以 2 个空格缩进。它与 REST API 和 CLI 使用的结构相同,且两个 SDK 对相同工作流写出的文件逐字节一致,因此差异会保持很小。Workflow.from_json() / Workflow.fromJSON() 会将其读回,也接受包含 workflow 键的完整智能体配置,例如 CLI 的 agent_configs/ 目录中的文件。

from pathlib import Path
from elevenlabs.agent_workflows import Workflow
Path("workflows/support.json").write_text(wf.to_json_string())
stored = Workflow.from_json(Path("workflows/support.json").read_text())
from_cli = Workflow.from_json(Path("agent_configs/support.json").read_text())

典型设置:

1

初始化文件

使用 from_agent() / fromAgent() 加载智能体并写入文件,或通过 elevenlabs agents pull 拉取。

2

编辑

直接更改 JSON,或使用 from_json() / fromJSON() 加载后,在脚本中通过构建器 编辑。

3

在 CI 中验证

对每个拉取请求运行 Workflow.from_json(file).build() / Workflow.fromJSON(file).build(), 并将 elevenlabs agents workflow diagram <file> --format mermaid-md 作为评论发布,以便 审阅者查看图结构。

4

合并时部署

使用 agents.update() 推送构建后的工作流。

可视化工作流

CLI 会将工作流文件绘制为流程图,因此无需打开控制台即可检查代码构建的内容。它接受由 to_json_string() / toJSONString() 写入的纯工作流文件,或带有 workflow 键的智能体配置:

elevenlabs agents workflow diagram workflows/support.json

默认情况下,它会在终端中绘制方框和箭头,每条边的条件都会在边上编号,并在下方图例中完整列出。使用 --format mermaid 可改为输出 Mermaid 源码,或使用 --format mermaid-md 将其包装在 GitHub 可渲染的围栏代码块中,例如用于拉取请求评论:

elevenlabs agents workflow diagram workflows/support.json --format mermaid-md

该命令仅读取本地文件,因此无需 API 密钥,也可在 CI 中运行。

验证

链式调用本身不会失败。build()、to_json() / toJSON() 和 to_json_string() / toJSONString() 会检查整个图,并引发或抛出一个 WorkflowValidationError。其 issues 列表会列出每个问题,每项都包含 code、message 以及相关的 node_id / nodeId。

from elevenlabs.agent_workflows import WorkflowValidationError
try:
workflow = wf.build(on_warning=lambda issue: print("warning:", issue.message))
except WorkflowValidationError as err:
for issue in err.issues:
print(issue.code, issue.message)

错误包括:

  • 相同两个节点之间存在两条边,或存在从节点指向自身的边。
  • 指向起始节点的边,或从结束节点发出的边。
  • 从工具节点发出但并非成功或失败边的边,或从任何其他类型节点发出的成功或失败边。
  • 两个节点具有相同 ID,或存在指向不存在节点的边。

无法从起始节点到达的节点会通过 on_warning / onWarning 报告为警告,但不会阻止构建。

比 SDK 更新的节点类型

从 API 或文件加载的工作流可能包含 SDK 版本尚未识别的节点类型。它们会通过 from_json() / fromJSON() 和 to_json_string() / toJSONString() 原样往返保留,且 build() 会将每个此类节点报告为警告。类型化请求模型无法携带它们,因此请升级 SDK,或直接发送传输格式:

elevenlabs.conversational_ai.agents.update(
"agent_id",
request_options={"additional_body_parameters": {"workflow": wf.to_json()}},
)