Zur Navigation springen

Workflows als Code

Agent-Workflows mit den Python- und JavaScript-SDKs erstellen, bearbeiten und versionieren

Überblick

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

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:

PythonJavaScriptFü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:

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

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:

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

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

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:

KnotenPythonJavaScript
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

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.

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

Weiterleitungsziele

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

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

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:

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.

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

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

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

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

Ein typisches Setup:

1

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.

2

Bearbeiten

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

3

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.

4

Beim Merge bereitstellen

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

Einen Workflow visualisieren

Die 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:

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-Quellcode auszugeben, oder --format mermaid-md, um ihn in einen Codeblock einzuschließen, den GitHub rendert, etwa in einem Pull-Request-Kommentar:

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.

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)

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:

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