Naar navigatie springen

Workflows als code

Bouw, bewerk en versiebeheer agentworkflows met de Python- en JavaScript-SDK’s

Overzicht

Een workflow wordt opgeslagen als een graaf van nodes en edges. Als je die graaf met de hand schrijft, moet je zelf zorgen dat edge-ID’s, lijsten met edge_order en voorwaardeobjecten consistent blijven. Met de Workflow-builder in de Python- en JavaScript-SDK’s kun je dezelfde graaf beschrijven met gekoppelde methodeaanroepen. Vervolgens produceert deze de waarde workflow die agents.create() en agents.update() al accepteren.

Gebruik dit om:

  • Een workflow in code te bouwen en deze te beoordelen zoals elke andere codewijziging.
  • De workflow van een agent te laden, één node te wijzigen en deze terug te sturen zonder de rest van de graaf te verstoren.
  • Workflows als JSON op te slaan in versiebeheer en ze te valideren in CI.

De builder wordt volledig in de SDK uitgevoerd. Hij roept geen nieuwe API-endpoints aan en alles wat hij bouwt kan worden geopend en bewerkt in de visuele editor van het dashboard.

Vereisten

  • Python: het pakket elevenlabs. De builder wordt geïmporteerd vanuit elevenlabs.agent_workflows.
  • JavaScript: het pakket @elevenlabs/elevenlabs-js. De builder wordt geëxporteerd vanuit de hoofdmap van het pakket.

Een workflow bouwen

Het object Workflow staat voor de startnode. Elk werkwoord voegt een node toe en retourneert deze, zodat je vanaf daar de volgende stap kunt koppelen:

PythonJavaScriptVoegt toe
converse(id, prompt, **options)converse(id, prompt, options?)Een subagentnode. De agent voert een gesprek met prompt toegevoegd aan de instructies.
tool(id, tool_ids, **options)tool(id, toolIds, options?)Een dispatch-toolnode die de opgegeven tools uitvoert.
transfer(id, destination, **options)transfer(id, destination, options?)Een transfer naar een telefoonnummer, een SIP-URI of een andere agent.
end(id="end")end(options?)Een eindnode. Elke end() zonder ID deelt één node "end".

De routeringsmethoden bepalen welke edge naar de volgende node leidt:

PythonJavaScriptMaakt
when(condition)when(condition)Een voorwaardelijke edge. Een string is een LLM-voorwaarde; een Expr is een expressie.
on_success() / on_failure()onSuccess() / onFailure()Een edge vanuit een toolnode, die wordt gevolgd wanneer de toolaanroep slaagt of mislukt.
on_result(success=..., failure=...)onResult({ success, failure })Beide toolresultaat-edges tegelijk.
go_to(node)goTo(node)Een edge naar een node die al bestaat, voor samenvoegingen en loops.
up()up()Geen edge. Retourneert de node waarvan de huidige node is gekoppeld, om daarvandaan verder te koppelen. Zie Een workflow als één keten schrijven.

Werkwoorden die rechtstreeks op de workflow worden aangeroepen, vertakken vanaf de startnode met een onvoorwaardelijke edge. Dit voorbeeld routeert bellers naar het opzoeken van een bestelling, probeert identificatie opnieuw als het opzoeken mislukt en transfereert VIP-bellers of gefrustreerde bellers:

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() valideert de hele graaf en retourneert het requestmodel. Het doet ook het volgende:

  • Geeft de startnode de ID start_node, die de API vereist.
  • Genereert edge-ID’s in de vorm source__target.
  • Vult de edge_order van elke node in de volgorde waarin je de edges hebt toegevoegd.
  • Positioneert elke node zonder positie, zodat de graaf van links naar rechts wordt weergegeven in de visuele editor.

Een workflow als één keten schrijven

up() retourneert de node waarvan de huidige node is gekoppeld, zodat je een hele workflow in één expressie kunt beschrijven in plaats van elke node in een variabele te bewaren. Gebruik binnen één keten go_to() / goTo() met een node-ID, omdat er geen variabele is om door te geven:

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

Voor een node-handle van wf.node(id) retourneert up() de node waar de eerste inkomende edge vandaan komt. Voor de startnode retourneert deze de startnode.

Nodeopties

Optionele instellingen geef je in Python door als keyword-argumenten en in JavaScript in een afsluitend options-object. De namen komen overeen met de velden van de node in de API:

NodePythonJavaScript
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

Elk werkwoord accepteert ook position als je de node zelf wilt plaatsen. Workflow(prevent_subagent_loops=True) in Python, of new Workflow({ preventSubagentLoops: true }) in JavaScript, voorkomt dat het gesprek opnieuw een subagent binnenkomt die het al heeft bezocht.

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

Transferbestemmingen

PythonJavaScriptTransfereert naar
Destination.phone(number)Destination.phone(number)Een telefoonnummer
Destination.phone_variable(name)Destination.phoneVariable(name)Een telefoonnummer in een dynamische variabele
Destination.sip(uri)Destination.sip(uri)Een SIP-URI
Destination.sip_variable(name)Destination.sipVariable(name)Een SIP-URI in een dynamische variabele
Destination.agent(agent_id)Destination.agent(agentId)Een andere agent

Voorwaarden

when() accepteert een gewone string, een Condition of een Expr:

  • Een string is een LLM-voorwaarde: de LLM bepaalt of deze geldt.
  • Condition.llm(text, label=...) is hetzelfde, met een label dat op de edge in de visuele editor wordt weergegeven. Gebruik Condition.expr(expr, label=...) om een expressie van een label te voorzien.
  • Een Expr wordt geëvalueerd aan de hand van dynamische variabelen, zonder de LLM aan te roepen.

Bouw expressies op uit Expr.variable(name) en Expr.literal(value). Vergelijk ze met eq, neq, gt, gte, lt en lte, en voer rekenkundige bewerkingen uit met add, sub, mul en div. Combineer ze met all_of / allOf, any_of / anyOf en if_else / ifElse. Gewone waarden worden automatisch verpakt in Expr.literal(). Expr.llm(description) vraagt de LLM een waarde uit het gesprek te halen; geef type en enum door om een string, een getal of een van een vaste reeks waarden te krijgen in plaats van een boolean.

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

Loops en achterwaartse edges

De API staat één edge tussen twee willekeurige nodes toe, in beide richtingen. Om het gesprek langs dezelfde weg terug te laten gaan, geef je de edge met back een achterwaartse voorwaarde:

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

go_to() / goTo() handelt het veelvoorkomende geval voor je af. Wanneer de doelnode al een edge heeft die hierheen leidt, wordt de nieuwe edge de achterwaartse voorwaarde van die edge. In het eerste voorbeeld wordt failed.go_to(identify) verzonden als de achterwaartse voorwaarde van de edge identify → lookup_order. Elke andere tweede edge tussen dezelfde twee nodes, en elke edge van een node naar zichzelf, faalt bij validatie.

Een bestaande agent bewerken

Workflow.from_agent() / Workflow.fromAgent() laadt de workflow van een agent die is geretourneerd door agents.get(). Node-ID’s, edge-ID’s, editorposities en edgevolgorde blijven behouden, dus door deze terug te sturen wijzig je alleen wat je hebt gewijzigd.

PythonJavaScriptDoet
wf.node(id)wf.node(id)Retourneert een handle naar een bestaande node, om deze te bewerken of ervan uit te koppelen.
node.update(**fields)node.update(fields)Wijzigt de prompt, tools, bestemming of opties van een node.
wf.insert_between(source, target, fn)wf.insertBetween(source, target, fn)Voegt nieuwe nodes in een bestaande edge in, met behoud van de voorwaarde.
wf.remove(id)wf.remove(id)Verwijdert een node en elke edge ernaartoe of ervan vandaan.

insert_between() / insertBetween() geeft aan je functie een vertakking door die de voorwaarde van de vervangen edge bevat. De functie retourneert de laatste node die deze heeft toegevoegd, die vervolgens met het oorspronkelijke doel wordt verbonden. De methode retourneert die node, of None / undefined als er geen edge tussen de twee nodes is.

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 opslaan in versiebeheer

to_json_string() / toJSONString() schrijft de workflow in het wire format van de API: snake_case-sleutels, op elk niveau gesorteerd, met inspringing van 2 spaties. Het heeft dezelfde vorm als de REST API en de CLI gebruiken, en beide SDK’s schrijven voor dezelfde workflow byte voor byte identieke bestanden, zodat verschillen klein blijven. Workflow.from_json() / Workflow.fromJSON() leest het weer in en accepteert ook een volledige agentconfiguratie met een sleutel workflow, zoals een bestand in de map agent_configs/ van de 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())

Een gebruikelijke opzet:

1

Het bestand voorbereiden

Laad de agent met from_agent() / fromAgent() en schrijf deze weg, of haal deze op met elevenlabs agents pull.

2

Bewerken

Wijzig de JSON rechtstreeks, of laad deze met from_json() / fromJSON() en bewerk deze met de builder in een script.

3

Valideren in CI

Voer Workflow.from_json(file).build() / Workflow.fromJSON(file).build() uit bij elke pull request, en plaats elevenlabs agents workflow diagram <file> --format mermaid-md als opmerking zodat reviewers de graaf kunnen zien.

4

Implementeren bij merge

Push de gebouwde workflow met agents.update().

Een workflow visualiseren

De CLI tekent een workflowbestand als stroomdiagram, zodat je kunt controleren wat je code heeft gebouwd zonder het dashboard te openen. De CLI accepteert een los workflowbestand dat is geschreven door to_json_string() / toJSONString(), of een agentconfiguratie met een sleutel workflow:

elevenlabs agents workflow diagram workflows/support.json

Standaard tekent de CLI vakken en pijlen in de terminal, waarbij elke edgevoorwaarde op de edge wordt genummerd en in een legenda eronder wordt uitgeschreven. Gebruik --format mermaid om in plaats daarvan Mermaid-broncode af te drukken, of --format mermaid-md om deze in een fenced code block te plaatsen dat GitHub rendert, bijvoorbeeld in een opmerking bij een pull request:

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

De opdracht leest alleen lokale bestanden, heeft dus geen API-sleutel nodig en kan in CI worden uitgevoerd.

Validatie

Gekoppelde aanroepen falen nooit op zichzelf. build(), to_json() / toJSON() en to_json_string() / toJSONString() controleren de hele graaf en veroorzaken of gooien één WorkflowValidationError waarvan de lijst issues elk probleem vermeldt, elk met een code, een message en de relevante 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)

Fouten zijn onder meer:

  • Twee edges tussen hetzelfde paar nodes, of een edge van een node naar zichzelf.
  • Een edge naar de startnode, of vanuit een eindnode.
  • Een edge vanuit een toolnode die geen succes- of foutedge is, of een succes- of foutedge vanuit een ander type node.
  • Twee nodes met dezelfde ID, of een edge naar een node die niet bestaat.

Nodes die niet bereikbaar zijn vanaf de startnode worden als waarschuwingen gemeld via on_warning / onWarning en stoppen de build niet.

Nodetypen nieuwer dan je SDK

Een workflow die vanuit de API of uit een bestand wordt geladen, kan nodetypen bevatten die je SDK-versie nog niet kent. Deze gaan ongewijzigd heen en weer via from_json() / fromJSON() en to_json_string() / toJSONString(), en build() meldt elk type als waarschuwing. Het getypeerde requestmodel kan ze niet meenemen, dus upgrade de SDK of verzend het wire format rechtstreeks:

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