ナビゲーションにスキップ

コードによるワークフロー管理

PythonおよびJavaScript SDKでエージェントのワークフローを構築、編集、バージョン管理

概要

ワークフローは、ノードとエッジのグラフとして保存されます。このグラフを手動で記述する場合、エッジID、edge_order リスト、条件オブジェクトの整合性を自分で保つ必要があります。PythonおよびJavaScript SDKの Workflow ビルダーを使うと、同じグラフをメソッドチェーンで記述できます。ビルダーは、agents.create() と agents.update() がそのまま受け取れる workflow 値を生成します。

次の用途に使用できます。

  • ワークフローをコードで構築し、ほかのコード変更と同じようにレビューする。
  • エージェントのワークフローを読み込み、グラフのほかの部分に影響を与えずに1つのノードだけを変更して反映する。
  • ワークフローを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() は、1つの "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()エッジは作成しません。現在のノードのチェーン元を返し、そこからチェーンを続けられるようにします。ワークフローを1つのチェーンとして記述するを参照してください。

ワークフロー上で動詞メソッドを直接呼び出すと、無条件エッジによって開始ノードから分岐します。次の例では、発信者を注文検索へ誘導し、検索に失敗した場合は本人確認を再試行し、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 を設定します。
  • 位置が指定されていないノードを配置し、ビジュアルエディターでグラフが左から右へ並ぶようにします。

ワークフローを1つのチェーンとして記述する

up() は現在のノードのチェーン元を返すため、各ノードを変数に保持せずに、ワークフロー全体を1つの式で記述できます。単一のチェーン内では渡す変数がないため、ノード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では末尾のoptionsオブジェクトに指定します。名前は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では、任意の2つのノード間にどちらか一方向のエッジを1つ作成できます。会話が来た経路を戻れるようにするには、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 エッジの後方条件として送信されます。同じ2つのノード間にほかの2本目のエッジがある場合や、ノードからそのノード自身へのエッジがある場合は、検証に失敗します。

既存のエージェントを編集する

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() は、置き換えるエッジの条件を保持するブランチを関数に渡します。関数は最後に追加したノードを返し、そのノードが元の対象ノードに接続されます。このメソッドはそのノードを返します。2つのノード間にエッジがない場合は、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() で再度読み込めます。また、CLIの agent_configs/ ディレクトリ内のファイルなど、workflow キーを持つ完全なエージェント設定も読み込めます。

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

デフォルトでは、ターミナルにボックスと矢印が描画されます。各エッジの条件にはエッジ上で番号が付けられ、下部の凡例に内容が表示されます。代わりにMermaidソースを出力するには --format mermaid を使用します。プルリクエストのコメントなどで、GitHubがレンダリングできるコードブロックとして出力するには --format mermaid-md を使用します。

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)

エラーには次のものがあります。

  • 同じノードのペア間に2つのエッジがある、またはノードからそのノード自身へのエッジがある。
  • 開始ノードに入るエッジ、または終了ノードから出るエッジがある。
  • ツールノードから出るエッジが成功エッジでも失敗エッジでもない、またはほかの種類のノードから成功エッジや失敗エッジが出ている。
  • 同じIDを持つノードが2つある、または存在しないノードへのエッジがある。

開始ノードから到達できないノードは、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()}},
)