> 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.

# Workflows als Code

## Überblick

Ein [Workflow](/docs/de/eleven-agents/customization/agent-workflows) wird als Graph aus Knoten und Kanten gespeichert. Wenn Sie diesen Graphen manuell schreiben, müssen Sie Kanten-IDs, `edge_order`-Listen und Bedingungsobjekte selbst konsistent halten. Der `Workflow`-Builder in den Python- und JavaScript-SDKs ermöglicht es Ihnen, denselben Graphen mit verketteten Methodenaufrufen zu beschreiben. Anschließend erstellt er den `workflow`-Wert, den `agents.create()` und `agents.update()` bereits akzeptieren.

Verwenden Sie ihn, um:

* einen Workflow in Code zu erstellen und ihn wie jede andere Codeänderung zu prüfen.
* den Workflow eines Agents zu laden, einen Knoten zu ändern und ihn zurückzuspielen, ohne den Rest des Graphen zu beeinträchtigen.
* Workflows als JSON in der Versionskontrolle zu speichern und sie in CI zu validieren.

> **Note**
>
> Der Builder läuft vollständig im SDK. Er ruft keine neuen API-Endpunkte auf, und alles, was er erstellt, kann
> im visuellen Editor des Dashboards geöffnet und bearbeitet werden.

## Voraussetzungen

* Python: das Paket `elevenlabs`. Der Builder wird aus `elevenlabs.agent_workflows` importiert.
* JavaScript: das Paket `@elevenlabs/elevenlabs-js`. Der Builder wird aus dem Paketstamm exportiert.

## Einen Workflow erstellen

Das `Workflow`-Objekt steht für den Startknoten. Jedes Verb fügt einen Knoten hinzu und gibt ihn zurück, sodass Sie den nächsten Schritt daran verketten können:

| Python                                 | JavaScript                            | Fügt hinzu                                                                              |
| -------------------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------- |
| `converse(id, prompt, **options)`      | `converse(id, prompt, options?)`      | Einen Subagent-Knoten. Der Agent führt ein Gespräch mit `prompt` in seinen Anweisungen. |
| `tool(id, tool_ids, **options)`        | `tool(id, toolIds, options?)`         | Einen Dispatch-Tool-Knoten, der die angegebenen Tools ausführt.                         |
| `transfer(id, destination, **options)` | `transfer(id, destination, options?)` | Eine Weiterleitung an eine Telefonnummer, eine SIP-URI oder einen anderen Agent.        |
| `end(id="end")`                        | `end(options?)`                       | Einen Endknoten. Jedes `end()` ohne ID verwendet denselben Knoten `"end"`.              |

Die Routing-Methoden bestimmen, welche Kante zum nächsten Knoten führt:

| Python                                | JavaScript                       | Erstellt                                                                                                                                                                                |
| ------------------------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `when(condition)`                     | `when(condition)`                | Eine bedingte Kante. Ein String ist eine LLM-Bedingung, ein `Expr` ist ein Ausdruck.                                                                                                    |
| `on_success()` / `on_failure()`       | `onSuccess()` / `onFailure()`    | Eine Kante von einem Tool-Knoten, die verwendet wird, wenn der Tool-Aufruf erfolgreich ist oder fehlschlägt.                                                                            |
| `on_result(success=..., failure=...)` | `onResult({ success, failure })` | Beide Tool-Ergebniskanten gleichzeitig.                                                                                                                                                 |
| `go_to(node)`                         | `goTo(node)`                     | Eine Kante zu einem bereits vorhandenen Knoten, für Zusammenführungen und Schleifen.                                                                                                    |
| `up()`                                | `up()`                           | Keine Kante. Gibt den Knoten zurück, von dem der aktuelle verkettet wurde, um daran weiterzuverketten. Siehe [Einen Workflow als eine Kette schreiben](#write-a-workflow-as-one-chain). |

Direkt auf dem Workflow aufgerufene Verben verzweigen vom Startknoten über eine unbedingte Kante. Dieses Beispiel leitet Anrufende zu einer Bestellabfrage, wiederholt die Identifizierung, wenn die Abfrage fehlschlägt, und leitet VIP- oder frustrierte Anrufende weiter:

```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()` validiert den gesamten Graphen und gibt das Request-Modell zurück. Außerdem:

* erhält der Startknoten die von der API erforderliche ID `start_node`.
* werden Kanten-IDs im Format `source__target` generiert.
* wird das `edge_order` jedes Knotens in der Reihenfolge gefüllt, in der Sie die Kanten hinzugefügt haben.
* werden Knoten ohne Position positioniert, sodass der Graph im visuellen Editor von links nach rechts angeordnet ist.

### Einen Workflow als eine Kette schreiben

`up()` gibt den Knoten zurück, von dem der aktuelle verkettet wurde. So können Sie einen gesamten Workflow in einem Ausdruck beschreiben, statt jeden Knoten in einer Variablen zu speichern. Verwenden Sie innerhalb einer einzelnen Kette `go_to()` / `goTo()` mit einer Knoten-ID, da keine Variable zum Übergeben vorhanden ist:

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

Bei einem Knoten-Handle von `wf.node(id)` gibt `up()` den Knoten zurück, von dem die erste eingehende Kante stammt. Beim Startknoten gibt es den Startknoten zurück.

### Knotenoptionen

Optionale Einstellungen werden in Python als Schlüsselwortargumente und in JavaScript als abschließendes Optionsobjekt übergeben. Ihre Namen entsprechen den Feldern des Knotens in der API:

| Knoten     | 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`                                                                                    |

Jedes Verb akzeptiert außerdem `position`, wenn Sie den Knoten selbst platzieren möchten. `Workflow(prevent_subagent_loops=True)` in Python oder `new Workflow({ preventSubagentLoops: true })` in JavaScript verhindert, dass das Gespräch erneut einen bereits besuchten Subagent betritt.

```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" });
```

### Weiterleitungsziele

| Python                             | JavaScript                        | Leitet weiter an                                              |
| ---------------------------------- | --------------------------------- | ------------------------------------------------------------- |
| `Destination.phone(number)`        | `Destination.phone(number)`       | Eine Telefonnummer                                            |
| `Destination.phone_variable(name)` | `Destination.phoneVariable(name)` | Eine in einer dynamischen Variable gespeicherte Telefonnummer |
| `Destination.sip(uri)`             | `Destination.sip(uri)`            | Eine SIP-URI                                                  |
| `Destination.sip_variable(name)`   | `Destination.sipVariable(name)`   | Eine in einer dynamischen Variable gespeicherte SIP-URI       |
| `Destination.agent(agent_id)`      | `Destination.agent(agentId)`      | Einen anderen Agent                                           |

## Bedingungen

`when()` akzeptiert einen einfachen String, eine `Condition` oder einen `Expr`:

* Ein String ist eine LLM-Bedingung: Das LLM entscheidet, ob sie zutrifft.
* `Condition.llm(text, label=...)` ist dasselbe, mit einem Label, das im visuellen Editor auf der Kante angezeigt wird. Verwenden Sie `Condition.expr(expr, label=...)`, um einen Ausdruck zu beschriften.
* Ein `Expr` wird anhand [dynamischer Variablen](/docs/de/eleven-agents/customization/personalization/dynamic-variables) ausgewertet, ohne das LLM aufzurufen.

Erstellen Sie Ausdrücke mit `Expr.variable(name)` und `Expr.literal(value)`. Vergleichen Sie sie mit `eq`, `neq`, `gt`, `gte`, `lt` und `lte` und führen Sie mit `add`, `sub`, `mul` und `div` Rechenoperationen aus. Kombinieren Sie sie mit `all_of` / `allOf`, `any_of` / `anyOf` und `if_else` / `ifElse`. Einfache Werte werden automatisch in `Expr.literal()` eingeschlossen. `Expr.llm(description)` fordert das LLM auf, einen Wert aus dem Gespräch zu extrahieren; übergeben Sie `type` und `enum`, um statt eines Booleschen Werts einen String, eine Zahl oder einen Wert aus einer festen Menge zu erhalten.

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

## Schleifen und rückwärts gerichtete Kanten

Die API erlaubt eine Kante zwischen zwei beliebigen Knoten, in beide Richtungen. Damit das Gespräch auf dem bisherigen Weg zurückkehren kann, geben Sie der Kante mit `back` eine Rückwärtsbedingung:

```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()` übernimmt den häufigen Fall für Sie. Wenn der Zielknoten bereits eine hierher führende Kante hat, wird die neue Kante zur Rückwärtsbedingung dieser Kante. Im ersten Beispiel wird `failed.go_to(identify)` als Rückwärtsbedingung der Kante `identify` → `lookup_order` gesendet. Jede andere zweite Kante zwischen denselben zwei Knoten sowie jede Kante von einem Knoten zu sich selbst schlägt bei der Validierung fehl.

## Einen vorhandenen Agent bearbeiten

`Workflow.from_agent()` / `Workflow.fromAgent()` lädt den Workflow eines von `agents.get()` zurückgegebenen Agents. Knoten-IDs, Kanten-IDs, Editor-Positionen und Kantenreihenfolge bleiben erhalten. Beim Zurückspielen wird daher nur geändert, was Sie geändert haben.

| Python                                  | JavaScript                             | Funktion                                                                                           |
| --------------------------------------- | -------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `wf.node(id)`                           | `wf.node(id)`                          | Gibt ein Handle für einen vorhandenen Knoten zurück, um ihn zu bearbeiten oder daran zu verketten. |
| `node.update(**fields)`                 | `node.update(fields)`                  | Ändert Prompt, Tools, Ziel oder Optionen eines Knotens.                                            |
| `wf.insert_between(source, target, fn)` | `wf.insertBetween(source, target, fn)` | Fügt neue Knoten in eine vorhandene Kante ein und behält deren Bedingung bei.                      |
| `wf.remove(id)`                         | `wf.remove(id)`                        | Entfernt einen Knoten und jede Kante zu oder von ihm.                                              |

`insert_between()` / `insertBetween()` übergibt Ihrer Funktion einen Branch, der die Bedingung der ersetzten Kante enthält. Die Funktion gibt den letzten hinzugefügten Knoten zurück, der dann mit dem ursprünglichen Ziel verbunden wird. Die Methode gibt diesen Knoten zurück oder `None` / `undefined`, wenn zwischen den beiden Knoten keine Kante vorhanden ist.

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

## Workflows in der Versionskontrolle speichern

`to_json_string()` / `toJSONString()` schreibt den Workflow im Wire-Format der API: snake_case-Schlüssel, auf jeder Ebene sortiert, mit Einrückung von 2 Leerzeichen. Es entspricht dem Format der REST API und der [CLI](/docs/de/eleven-agents/operate/cli). Beide SDKs schreiben für denselben Workflow bytegenau identische Dateien, sodass Diffs klein bleiben. `Workflow.from_json()` / `Workflow.fromJSON()` liest es wieder ein und akzeptiert auch eine vollständige Agent-Konfiguration mit einem `workflow`-Schlüssel, beispielsweise eine Datei im Verzeichnis `agent_configs/` der CLI.

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

Ein typisches Setup:

#### Datei erstellen

Laden Sie den Agent mit `from_agent()` / `fromAgent()` und schreiben Sie ihn in eine Datei, oder rufen Sie ihn mit `elevenlabs
    agents pull` ab.

#### Bearbeiten

Ändern Sie das JSON direkt oder laden Sie es mit `from_json()` / `fromJSON()` und bearbeiten Sie es mit dem
Builder in einem Skript.

#### In CI validieren

Führen Sie bei jedem Pull Request `Workflow.from_json(file).build()` / `Workflow.fromJSON(file).build()` aus
und posten Sie `elevenlabs agents workflow diagram <file> --format mermaid-md` als Kommentar, damit
Reviewer den Graphen sehen können.

#### Beim Merge bereitstellen

Übertragen Sie den erstellten Workflow mit `agents.update()`.

## Einen Workflow visualisieren

Die [CLI](/docs/de/eleven-agents/operate/cli) zeichnet eine Workflow-Datei als Flussdiagramm. So können Sie prüfen, was Ihr Code erstellt hat, ohne das Dashboard zu öffnen. Sie akzeptiert eine einfache Workflow-Datei, die mit `to_json_string()` / `toJSONString()` geschrieben wurde, oder eine Agent-Konfiguration mit einem `workflow`-Schlüssel:

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

Standardmäßig zeichnet sie Kästen und Pfeile im Terminal. Jede Kantenbedingung wird an ihrer Kante nummeriert und in einer Legende darunter ausgeschrieben. Verwenden Sie stattdessen `--format mermaid`, um [Mermaid](https://mermaid.js.org/)-Quellcode auszugeben, oder `--format mermaid-md`, um ihn in einen Codeblock einzuschließen, den GitHub rendert, etwa in einem Pull-Request-Kommentar:

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

Der Befehl liest nur lokale Dateien. Daher benötigt er keinen API-Schlüssel und kann in CI ausgeführt werden.

## Validierung

Verkettete Aufrufe schlagen nie eigenständig fehl. `build()`, `to_json()` / `toJSON()` und `to_json_string()` / `toJSONString()` prüfen den gesamten Graphen und lösen einen einzelnen `WorkflowValidationError` aus. Dessen Liste `issues` enthält jedes Problem, jeweils mit einem `code`, einer `message` und der betroffenen `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;
  }
}
```

Fehler umfassen:

* Zwei Kanten zwischen demselben Knotenpaar oder eine Kante von einem Knoten zu sich selbst.
* Eine Kante in den Startknoten oder aus einem Endknoten.
* Eine Kante von einem Tool-Knoten, die keine Erfolgs- oder Fehlerkante ist, oder eine Erfolgs- oder Fehlerkante von einem anderen Knotentyp.
* Zwei Knoten mit derselben ID oder eine Kante zu einem nicht vorhandenen Knoten.

Knoten, die vom Startknoten aus nicht erreichbar sind, werden über `on_warning` / `onWarning` als Warnungen gemeldet und verhindern den Build nicht.

## Knotentypen, die neuer sind als Ihr SDK

Ein aus der API oder einer Datei geladener Workflow kann Knotentypen enthalten, die Ihre SDK-Version noch nicht kennt. Sie werden unverändert über `from_json()` / `fromJSON()` und `to_json_string()` / `toJSONString()` verarbeitet, und `build()` meldet jeden davon als Warnung. Das typisierte Request-Modell kann sie nicht enthalten. Aktualisieren Sie daher das SDK oder senden Sie das Wire-Format direkt:

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