> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

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

## 概要

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

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

* ワークフローをコードで構築し、ほかのコード変更と同じようにレビューする。
* エージェントのワークフローを読み込み、グラフのほかの部分に影響を与えずに1つのノードだけを変更して反映する。
* ワークフローをJSONとしてバージョン管理に保存し、CIで検証する。

> **Note**
>
> ビルダーはSDK内だけで動作します。新しいAPIエンドポイントは呼び出しません。また、ビルダーで構築したものはすべて
> ダッシュボードのビジュアルエディターで開いて編集できます。

## 要件

* Python：`elevenlabs` パッケージ。ビルダーは `elevenlabs.agent_workflows` からインポートします。
* JavaScript：`@elevenlabs/elevenlabs-js` パッケージ。ビルダーはパッケージルートからエクスポートされます。

## ワークフローを構築する

`Workflow` オブジェクトは開始ノードを表します。各動詞メソッドはノードを追加してそのノードを返すため、そこから次のステップをチェーンできます。

| Python                                 | JavaScript                            | 追加するもの                                              |
| -------------------------------------- | ------------------------------------- | --------------------------------------------------- |
| `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"` ノードを共有します。 |

ルーティングメソッドは、次のノードへ進むエッジを決定します。

| Python                                | JavaScript                       | 作成するもの                                                                                                            |
| ------------------------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `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つのチェーンとして記述する](#write-a-workflow-as-one-chain)を参照してください。 |

ワークフロー上で動詞メソッドを直接呼び出すと、無条件エッジによって開始ノードから分岐します。次の例では、発信者を注文検索へ誘導し、検索に失敗した場合は本人確認を再試行し、VIPまたは不満を抱えている発信者を転送します。

```python
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())

```

```typescript
import { Destination, ElevenLabsClient, Expr, Workflow } from "@elevenlabs/elevenlabs-js";

const elevenlabs = new ElevenLabsClient();

const wf = new Workflow();

const triage = wf.converse("triage", "Greet the caller and find out why they're calling");
const identify = triage
  .when("Caller wants to check an order")
  .converse("identify", "Ask for the order number and the email on the account");

const lookup = identify
  .when("Caller gave an order number")
  .tool("lookup_order", ["tool_get_order"]);
lookup.onResult({
  success: (ok) =>
    ok.converse("resolve", "Explain the order status").when("Caller is satisfied").end(),
  failure: (failed) => failed.goTo(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"));

await elevenlabs.conversationalAi.agents.update("agent_id", { workflow: wf.build() });
```

`build()` はグラフ全体を検証し、リクエストモデルを返します。また、次の処理も行います。

* APIで必須のID `start_node` を開始ノードに付けます。
* `source__target` 形式でエッジIDを生成します。
* エッジを追加した順に、各ノードの `edge_order` を設定します。
* 位置が指定されていないノードを配置し、ビジュアルエディターでグラフが左から右へ並ぶようにします。

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

`up()` は現在のノードのチェーン元を返すため、各ノードを変数に保持せずに、ワークフロー全体を1つの式で記述できます。単一のチェーン内では渡す変数がないため、ノードIDを指定して `go_to()` / `goTo()` を使用します。

```python

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())

```

```typescript
const wf = new Workflow();
wf.converse("triage", "Find out why the customer is calling")
  .when("Caller gave an order number")
  .tool("lookup_order", ["tool_get_order"])
  .onResult({
    success: (ok) =>
      ok.converse("resolve", "Explain the order status").when("Customer is satisfied").end(),
    failure: (failed) => failed.goTo("triage"),
  })
  .up()
  .when(Expr.variable("tier").eq("gold"))
  .transfer("vip", Destination.agent("agent_vip_desk"));

await elevenlabs.conversationalAi.agents.update("agent_id", { workflow: wf.build() });
```

`wf.node(id)` から取得したノードハンドルでは、`up()` はそのノードに最初に入るエッジの接続元ノードを返します。開始ノードでは、開始ノード自身を返します。

### ノードのオプション

オプション設定は、Pythonではキーワード引数、JavaScriptでは末尾のoptionsオブジェクトに指定します。名前はAPIのノードフィールドと一致します。

| ノード        | Python                                                                                         | JavaScript                                                                              |
| ---------- | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `converse` | `label`, `tool_ids`, `knowledge_base`, `conversation_config`, `entry_behavior`                 | `label`, `toolIds`, `knowledgeBase`, `conversationConfig`, `entryBehavior`              |
| `tool`     | `schema_overrides`                                                                             | `schemaOverrides`                                                                       |
| `transfer` | `transfer_type`, `custom_sip_headers`, `post_dial_digits`, `delay_ms`, `transfer_message`, ... | `transferType`, `customSipHeaders`, `postDialDigits`, `delayMs`, `transferMessage`, ... |
| `end`      | `id`                                                                                           | `id`                                                                                    |

ノードを自分で配置したい場合は、すべての動詞メソッドで `position` も指定できます。Pythonの `Workflow(prevent_subagent_loops=True)`、またはJavaScriptの `new Workflow({ preventSubagentLoops: true })` を使用すると、会話が一度訪れたサブエージェントに再び入るのを防止できます。

```python
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")
```

```typescript
const triage = wf.converse("triage", "Greet the caller and find out why they're calling", {
  label: "Triage",
  toolIds: ["tool_crm_search"],
  entryBehavior: "generate_immediately",
});

triage.when("Caller is a VIP").transfer("vip_desk", Destination.agent("agent_vip_concierge"), {
  transferMessage: "Connecting you to our VIP team",
  delayMs: 500,
});

identify.when("Caller refuses to share details").end({ id: "abandoned" });
```

### 転送先

| Python                             | JavaScript                        | 転送先               |
| ---------------------------------- | --------------------------------- | ----------------- |
| `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を呼び出さずに、[動的変数](/docs/ja/eleven-agents/customization/personalization/dynamic-variables)に対して評価されます。

式は `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` を渡します。

```python
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")
```

```typescript
import { Condition, Expr } from "@elevenlabs/elevenlabs-js";

const isVip = Expr.anyOf(
  Expr.variable("tier").eq("gold"),
  Expr.allOf(Expr.variable("orders").gte(10), Expr.variable("churn_risk").eq(true))
);

triage
  .when(Condition.expr(isVip, { label: "VIP" }))
  .transfer("vip_desk", Destination.agent("agent_vip"));

const bigRefund = Expr.variable("unit_price").mul(Expr.variable("quantity")).gt(500);
resolve.when(bigRefund).transfer("finance", Destination.phone("+15551234567"));

const 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` でエッジに後方条件を指定します。

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

```typescript
const 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、エディター上の位置、エッジの順序は維持されるため、再反映しても変更した箇所だけが更新されます。

| Python                                  | JavaScript                             | 処理内容                                   |
| --------------------------------------- | -------------------------------------- | -------------------------------------- |
| `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` を返します。

```python

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())
```

```typescript
const agent = await elevenlabs.conversationalAi.agents.get("agent_id");
const wf = Workflow.fromAgent(agent);

wf.node("resolve").update({ prompt: "Explain the status, then offer a discount code" });

const verify = wf.insertBetween("identify", "lookup_order", (start) =>
  start.converse("verify", "Verify the caller's identity with a one-time code")
);

verify?.when("Caller can't verify").end({ id: "unverified" });

wf.remove("vip");

await elevenlabs.conversationalAi.agents.update("agent_id", { workflow: wf.build() });
```

## ワークフローをバージョン管理に保存する

`to_json_string()` / `toJSONString()` は、APIのワイヤー形式でワークフローを書き出します。キーはsnake_caseで、すべての階層で並べ替えられ、2スペースでインデントされます。REST APIおよび[CLI](/docs/ja/eleven-agents/operate/cli)が使用するものと同じ構造です。両方のSDKは同じワークフローに対してバイト単位で同一のファイルを書き出すため、差分を小さく保てます。`Workflow.from_json()` / `Workflow.fromJSON()` で再度読み込めます。また、CLIの `agent_configs/` ディレクトリ内のファイルなど、`workflow` キーを持つ完全なエージェント設定も読み込めます。

```python
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())
```

```typescript
import { Workflow } from "@elevenlabs/elevenlabs-js";
import { readFile, writeFile } from "node:fs/promises";

await writeFile("workflows/support.json", wf.toJSONString());

const stored = Workflow.fromJSON(await readFile("workflows/support.json", "utf8"));
const fromCli = Workflow.fromJSON(await readFile("agent_configs/support.json", "utf8"));
```

一般的な設定手順は次のとおりです。

#### ファイルを初期作成する

`from_agent()` / `fromAgent()` でエージェントを読み込んで書き出すか、`elevenlabs
    agents pull` で取得します。

#### 編集する

JSONを直接変更するか、`from_json()` / `fromJSON()` で読み込み、スクリプト内の
ビルダーで編集します。

#### CIで検証する

すべてのプルリクエストで `Workflow.from_json(file).build()` / `Workflow.fromJSON(file).build()` を
実行し、`elevenlabs agents workflow diagram <file> --format mermaid-md` をコメントとして投稿して、
レビュアーがグラフを確認できるようにします。

#### マージ時にデプロイする

構築したワークフローを `agents.update()` で反映します。

## ワークフローを可視化する

[CLI](/docs/ja/eleven-agents/operate/cli)はワークフローファイルをフローチャートとして描画するため、ダッシュボードを開かずにコードが構築した内容を確認できます。`to_json_string()` / `toJSONString()` で書き出したワークフロー単体のファイル、または `workflow` キーを持つエージェント設定を読み込めます。

```bash
elevenlabs agents workflow diagram workflows/support.json
```

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

```bash
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` が含まれます。

```python
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)
```

```typescript
import { WorkflowValidationError } from "@elevenlabs/elevenlabs-js";

try {
  const workflow = wf.build({ onWarning: (issue) => console.warn(issue.message) });
} catch (err) {
  if (err instanceof WorkflowValidationError) {
    for (const issue of err.issues) console.error(issue.code, issue.message);
  } else {
    throw err;
  }
}
```

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

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

開始ノードから到達できないノードは、`on_warning` / `onWarning` を通じて警告として報告されますが、ビルドは停止しません。

## SDKより新しいノードタイプ

APIまたはファイルから読み込んだワークフローには、使用中のSDKバージョンがまだ認識していないノードタイプが含まれる場合があります。これらは `from_json()` / `fromJSON()` と `to_json_string()` / `toJSONString()` を通じて変更されずにラウンドトリップされ、`build()` はそれぞれを警告として報告します。型付きリクエストモデルではこれらを保持できないため、SDKをアップグレードするか、ワイヤー形式を直接送信してください。

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

```typescript
await elevenlabs.conversationalAi.agents.update(
  "agent_id",
  {},
  { additionalBodyParameters: { workflow: wf.toJSON() } }
);
```