> 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 som kod

## Översikt

Ett [workflow](/docs/sv/eleven-agents/customization/agent-workflows) lagras som en graf med noder och kanter. Om du skriver grafen för hand måste du själv hålla kant-ID:n, `edge_order`-listor och villkorsobjekt konsekventa. `Workflow`-byggaren i Python- och JavaScript-SDK:erna låter dig beskriva samma graf med kedjade metodanrop. Den skapar sedan `workflow`-värdet som `agents.create()` och `agents.update()` redan accepterar.

Använd den för att:

* Bygga ett workflow i kod och granska det som vilken annan kodändring som helst.
* Läsa in en agents workflow, ändra en nod och skicka tillbaka det utan att påverka resten av grafen.
* Lagra workflows som JSON i versionshantering och validera dem i CI.

> **Note**
>
> Byggaren körs helt i SDK:n. Den anropar inga nya API-slutpunkter, och allt den bygger kan
> öppnas och redigeras i dashboardens visuella redigerare.

## Krav

* Python: paketet `elevenlabs`. Byggaren importeras från `elevenlabs.agent_workflows`.
* JavaScript: paketet `@elevenlabs/elevenlabs-js`. Byggaren exporteras från paketets rot.

## Bygg ett workflow

Objektet `Workflow` representerar startnoden. Varje verb lägger till en nod och returnerar den, så att du kan kedja nästa steg från den:

| Python                                 | JavaScript                            | Lägger till                                                                               |
| -------------------------------------- | ------------------------------------- | ----------------------------------------------------------------------------------------- |
| `converse(id, prompt, **options)`      | `converse(id, prompt, options?)`      | En subagentnod. Agenten håller en konversation med `prompt` tillagd i sina instruktioner. |
| `tool(id, tool_ids, **options)`        | `tool(id, toolIds, options?)`         | En dispatch tool-nod som kör de angivna verktygen.                                        |
| `transfer(id, destination, **options)` | `transfer(id, destination, options?)` | En överföring till ett telefonnummer, en SIP URI eller en annan agent.                    |
| `end(id="end")`                        | `end(options?)`                       | En slutnod. Varje `end()` utan ID delar en och samma `"end"`-nod.                         |

Routningsmetoderna avgör vilken kant som leder till nästa nod:

| Python                                | JavaScript                       | Skapar                                                                                                                                                                     |
| ------------------------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `when(condition)`                     | `when(condition)`                | En villkorlig kant. En sträng är ett LLM-villkor; en `Expr` är ett uttryck.                                                                                                |
| `on_success()` / `on_failure()`       | `onSuccess()` / `onFailure()`    | En kant från en tool-nod, som följs när verktygsanropet lyckas eller misslyckas.                                                                                           |
| `on_result(success=..., failure=...)` | `onResult({ success, failure })` | Båda kanterna för verktygsresultat på en gång.                                                                                                                             |
| `go_to(node)`                         | `goTo(node)`                     | En kant till en nod som redan finns, för sammanslagningar och loopar.                                                                                                      |
| `up()`                                | `up()`                           | Ingen kant. Returnerar noden som den aktuella kedjades från, så att du kan fortsätta kedja från den. Se [Skriv ett workflow som en kedja](#write-a-workflow-as-one-chain). |

Verb som anropas direkt på workflowet förgrenas från startnoden med en ovillkorlig kant. Det här exemplet dirigerar uppringare till en ordersökning, försöker identifieringen igen om sökningen misslyckas och överför VIP- eller frustrerade uppringare:

```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()` validerar hela grafen och returnerar request-modellen. Den:

* Ger startnoden ID:t `start_node`, som API:t kräver.
* Genererar kant-ID:n i formatet `source__target`.
* Fyller i varje nods `edge_order` i den ordning du lade till kanterna.
* Positionerar alla noder som saknar position, så att grafen visas från vänster till höger i den visuella redigeraren.

### Skriv ett workflow som en kedja

`up()` returnerar noden som den aktuella kedjades från, så att du kan beskriva ett helt workflow i ett uttryck i stället för att spara varje nod i en variabel. Inom en enda kedja använder du `go_to()` / `goTo()` med ett nod-ID, eftersom det inte finns någon variabel att skicka:

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

För ett nodhandtag från `wf.node(id)` returnerar `up()` noden som den första inkommande kanten kommer från. För startnoden returnerar den startnoden.

### Nodalternativ

Valfria inställningar anges som nyckelordsargument i Python och i ett avslutande alternativobjekt i JavaScript. Namnen motsvarar nodens fält i API:t:

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

Varje verb accepterar också `position` om du vill placera noden själv. `Workflow(prevent_subagent_loops=True)` i Python, eller `new Workflow({ preventSubagentLoops: true })` i JavaScript, förhindrar att konversationen återvänder till en subagent som den redan har besökt.

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

### Överföringsdestinationer

| Python                             | JavaScript                        | Överför till                             |
| ---------------------------------- | --------------------------------- | ---------------------------------------- |
| `Destination.phone(number)`        | `Destination.phone(number)`       | Ett telefonnummer                        |
| `Destination.phone_variable(name)` | `Destination.phoneVariable(name)` | Ett telefonnummer i en dynamisk variabel |
| `Destination.sip(uri)`             | `Destination.sip(uri)`            | En SIP URI                               |
| `Destination.sip_variable(name)`   | `Destination.sipVariable(name)`   | En SIP URI i en dynamisk variabel        |
| `Destination.agent(agent_id)`      | `Destination.agent(agentId)`      | En annan agent                           |

## Villkor

`when()` tar en vanlig sträng, ett `Condition` eller ett `Expr`:

* En sträng är ett LLM-villkor: LLM:en avgör om det uppfylls.
* `Condition.llm(text, label=...)` är samma sak, med en etikett som visas på kanten i den visuella redigeraren. Använd `Condition.expr(expr, label=...)` för att märka ett uttryck.
* Ett `Expr` utvärderas mot [dynamiska variabler](/docs/sv/eleven-agents/customization/personalization/dynamic-variables), utan att anropa LLM:en.

Bygg uttryck från `Expr.variable(name)` och `Expr.literal(value)`. Jämför dem med `eq`, `neq`, `gt`, `gte`, `lt` och `lte`, och utför aritmetik med `add`, `sub`, `mul` och `div`. Kombinera dem med `all_of` / `allOf`, `any_of` / `anyOf` och `if_else` / `ifElse`. Vanliga värden omsluts automatiskt av `Expr.literal()`. `Expr.llm(description)` ber LLM:en att extrahera ett värde ur konversationen; ange `type` och `enum` för att få en sträng, ett tal eller ett av en fast uppsättning värden i stället för ett booleskt värde.

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

## Loopar och bakåtriktade kanter

API:t tillåter en kant mellan två valfria noder, i båda riktningar. Om du vill att konversationen ska kunna återvända samma väg som den kom, ger du kanten ett bakåtriktat villkor med `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()` hanterar det vanliga fallet åt dig. När målnoden redan har en kant som leder hit blir den nya kanten den kantens bakåtriktade villkor. I det första exemplet skickas `failed.go_to(identify)` som det bakåtriktade villkoret för kanten `identify` → `lookup_order`. Alla andra kanter mellan samma två noder, och alla kanter från en nod till sig själv, misslyckas vid validering.

## Redigera en befintlig agent

`Workflow.from_agent()` / `Workflow.fromAgent()` läser in workflowet för en agent som returneras av `agents.get()`. Nod-ID:n, kant-ID:n, redigerarpositioner och kantordning behålls, så att endast det du ändrade ändras när du skickar tillbaka det.

| Python                                  | JavaScript                             | Funktion                                                                             |
| --------------------------------------- | -------------------------------------- | ------------------------------------------------------------------------------------ |
| `wf.node(id)`                           | `wf.node(id)`                          | Returnerar ett handtag till en befintlig nod, för att redigera eller kedja från den. |
| `node.update(**fields)`                 | `node.update(fields)`                  | Ändrar en nods prompt, verktyg, destination eller alternativ.                        |
| `wf.insert_between(source, target, fn)` | `wf.insertBetween(source, target, fn)` | Infogar nya noder i en befintlig kant och behåller dess villkor.                     |
| `wf.remove(id)`                         | `wf.remove(id)`                        | Tar bort en nod och alla kanter till eller från den.                                 |

`insert_between()` / `insertBetween()` skickar en gren till din funktion som bär det ersatta kantvillkoret. Funktionen returnerar den sista nod den lade till, som sedan ansluts till det ursprungliga målet. Metoden returnerar den noden, eller `None` / `undefined` om det inte finns någon kant mellan de två noderna.

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

## Lagra workflows i versionshantering

`to_json_string()` / `toJSONString()` skriver workflowet i API:ts wire-format: snake_case-nycklar, sorterade på varje nivå, med indrag på 2 mellanslag. Det har samma struktur som REST API:t och [CLI](/docs/sv/eleven-agents/operate/cli) använder, och båda SDK:erna skriver byte-identiska filer för samma workflow, så skillnaderna blir små. `Workflow.from_json()` / `Workflow.fromJSON()` läser in det igen och accepterar även en fullständig agentkonfiguration med en `workflow`-nyckel, till exempel en fil i CLI:ts katalog `agent_configs/`.

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

En typisk konfiguration:

#### Skapa filen

Läs in agenten med `from_agent()` / `fromAgent()` och skriv ut den, eller hämta den med `elevenlabs
    agents pull`.

#### Redigera

Ändra JSON direkt, eller läs in den med `from_json()` / `fromJSON()` och redigera den med
byggaren i ett skript.

#### Validera i CI

Kör `Workflow.from_json(file).build()` / `Workflow.fromJSON(file).build()` för varje pull
request och lägg upp `elevenlabs agents workflow diagram <file> --format mermaid-md` som en kommentar så att
granskare kan se grafen.

#### Distribuera vid sammanslagning

Skicka det byggda workflowet med `agents.update()`.

## Visualisera ett workflow

[CLI](/docs/sv/eleven-agents/operate/cli) ritar en workflowfil som ett flödesschema, så att du kan kontrollera vad din kod byggde utan att öppna dashboarden. Den accepterar en fristående workflowfil skriven av `to_json_string()` / `toJSONString()`, eller en agentkonfiguration med en `workflow`-nyckel:

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

Som standard ritar den rutor och pilar i terminalen, med varje kantvillkor numrerat på sin kant och utskrivet i en förklarande lista under. Använd `--format mermaid` för att skriva ut [Mermaid](https://mermaid.js.org/)-källkod i stället, eller `--format mermaid-md` för att omsluta den i ett inhägnat kodblock som GitHub återger, till exempel i en pull request-kommentar:

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

Kommandot läser endast lokala filer, så det behöver ingen API-nyckel och kan köras i CI.

## Validering

Kedjade anrop misslyckas aldrig på egen hand. `build()`, `to_json()` / `toJSON()` och `to_json_string()` / `toJSONString()` kontrollerar hela grafen och reser eller kastar ett enda `WorkflowValidationError`, vars `issues`-lista innehåller varje problem, med en `code`, ett `message` och det berörda `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;
  }
}
```

Fel omfattar:

* Två kanter mellan samma par av noder, eller en kant från en nod till sig själv.
* En kant in till startnoden, eller ut från en slutnod.
* En kant från en tool-nod som inte är en lyckad eller misslyckad kant, eller en lyckad eller misslyckad kant från någon annan typ av nod.
* Två noder med samma ID, eller en kant till en nod som inte finns.

Noder som inte kan nås från startnoden rapporteras som varningar via `on_warning` / `onWarning` och stoppar inte bygget.

## Nodtyper som är nyare än din SDK

Ett workflow som lästs in från API:t eller från en fil kan innehålla nodtyper som din SDK-version ännu inte känner till. De skickas oförändrade fram och tillbaka genom `from_json()` / `fromJSON()` och `to_json_string()` / `toJSONString()`, och `build()` rapporterar var och en som en varning. Den typade request-modellen kan inte bära dem, så uppgradera SDK:n eller skicka wire-formatet 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() } }
);
```