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

# Workflow come codice

## Panoramica

Un [workflow](/docs/it/eleven-agents/customization/agent-workflows) viene archiviato come un grafo di nodi e archi. Scrivere manualmente quel grafo significa dover mantenere coerenti gli ID degli archi, gli elenchi `edge_order` e gli oggetti delle condizioni. Il builder `Workflow` negli SDK Python e JavaScript ti consente di descrivere lo stesso grafo con chiamate a metodi concatenate. Produce quindi il valore `workflow` già accettato da `agents.create()` e `agents.update()`.

Usalo per:

* Creare un workflow nel codice e revisionarlo come qualsiasi altra modifica al codice.
* Caricare il workflow di un agente, modificare un nodo e inviarlo di nuovo senza alterare il resto del grafo.
* Archiviare i workflow come JSON nel controllo di versione e convalidarli in CI.

> **Note**
>
> Il builder viene eseguito interamente nell'SDK. Non chiama nuovi endpoint API e tutto ciò che crea può essere
> aperto e modificato nell'editor visivo della dashboard.

## Requisiti

* Python: il pacchetto `elevenlabs`. Il builder viene importato da `elevenlabs.agent_workflows`.
* JavaScript: il pacchetto `@elevenlabs/elevenlabs-js`. Il builder viene esportato dalla radice del pacchetto.

## Crea un workflow

L'oggetto `Workflow` rappresenta il nodo iniziale. Ogni verbo aggiunge un nodo e lo restituisce, così puoi concatenare il passaggio successivo a partire da esso:

| Python                                 | JavaScript                            | Aggiunge                                                                                         |
| -------------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `converse(id, prompt, **options)`      | `converse(id, prompt, options?)`      | Un nodo sottoagente. L'agente tiene una conversazione con `prompt` aggiunto alle sue istruzioni. |
| `tool(id, tool_ids, **options)`        | `tool(id, toolIds, options?)`         | Un nodo strumento di instradamento che esegue gli strumenti specificati.                         |
| `transfer(id, destination, **options)` | `transfer(id, destination, options?)` | Un trasferimento a un numero di telefono, un URI SIP o un altro agente.                          |
| `end(id="end")`                        | `end(options?)`                       | Un nodo finale. Ogni `end()` senza ID condivide un unico nodo `"end"`.                           |

I metodi di instradamento stabiliscono quale arco porta al nodo successivo:

| Python                                | JavaScript                       | Crea                                                                                                                                                                                                   |
| ------------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `when(condition)`                     | `when(condition)`                | Un arco condizionale. Una stringa è una condizione LLM; un `Expr` è un'espressione.                                                                                                                    |
| `on_success()` / `on_failure()`       | `onSuccess()` / `onFailure()`    | Un arco da un nodo strumento, seguito quando la chiamata allo strumento riesce o fallisce.                                                                                                             |
| `on_result(success=..., failure=...)` | `onResult({ success, failure })` | Entrambi gli archi del risultato dello strumento contemporaneamente.                                                                                                                                   |
| `go_to(node)`                         | `goTo(node)`                     | Un arco verso un nodo già esistente, per unioni e cicli.                                                                                                                                               |
| `up()`                                | `up()`                           | Nessun arco. Restituisce il nodo da cui è stato concatenato quello corrente, per continuare la concatenazione da lì. Vedi [Scrivere un workflow come un'unica catena](#write-a-workflow-as-one-chain). |

I verbi chiamati direttamente sul workflow si diramano dal nodo iniziale con un arco incondizionato. Questo esempio indirizza i chiamanti a una ricerca dell'ordine, riprova l'identificazione se la ricerca fallisce e trasferisce i chiamanti VIP o frustrati:

```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()` convalida l'intero grafo e restituisce il modello della richiesta. Inoltre:

* Assegna al nodo iniziale l'ID `start_node`, richiesto dall'API.
* Genera gli ID degli archi nel formato `source__target`.
* Compila `edge_order` di ogni nodo nell'ordine in cui hai aggiunto gli archi.
* Posiziona ogni nodo senza posizione, in modo che il grafo sia disposto da sinistra a destra nell'editor visivo.

### Scrivere un workflow come un'unica catena

`up()` restituisce il nodo da cui è stato concatenato quello corrente, così puoi descrivere un intero workflow in un'unica espressione invece di conservare ogni nodo in una variabile. All'interno di una singola catena, usa `go_to()` / `goTo()` con un ID nodo, poiché non c'è una variabile da passare:

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

Per un handle di nodo da `wf.node(id)`, `up()` restituisce il nodo da cui proviene il primo arco in entrata. Per il nodo iniziale, restituisce il nodo iniziale.

### Opzioni dei nodi

Le impostazioni facoltative vanno inserite negli argomenti con nome in Python e in un oggetto opzioni finale in JavaScript. I loro nomi corrispondono ai campi del nodo nell'API:

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

Ogni verbo accetta anche `position` se vuoi posizionare tu stesso il nodo. `Workflow(prevent_subagent_loops=True)` in Python, oppure `new Workflow({ preventSubagentLoops: true })` in JavaScript, impedisce alla conversazione di rientrare in un sottoagente già visitato.

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

### Destinazioni di trasferimento

| Python                             | JavaScript                        | Trasferisce a                                             |
| ---------------------------------- | --------------------------------- | --------------------------------------------------------- |
| `Destination.phone(number)`        | `Destination.phone(number)`       | Un numero di telefono                                     |
| `Destination.phone_variable(name)` | `Destination.phoneVariable(name)` | Un numero di telefono contenuto in una variabile dinamica |
| `Destination.sip(uri)`             | `Destination.sip(uri)`            | Un URI SIP                                                |
| `Destination.sip_variable(name)`   | `Destination.sipVariable(name)`   | Un URI SIP contenuto in una variabile dinamica            |
| `Destination.agent(agent_id)`      | `Destination.agent(agentId)`      | Un altro agente                                           |

## Condizioni

`when()` accetta una stringa semplice, una `Condition` o un `Expr`:

* Una stringa è una condizione LLM: l'LLM decide se è soddisfatta.
* `Condition.llm(text, label=...)` è equivalente, con un'etichetta visualizzata sull'arco nell'editor visivo. Usa `Condition.expr(expr, label=...)` per etichettare un'espressione.
* Un `Expr` viene valutato rispetto alle [variabili dinamiche](/docs/it/eleven-agents/customization/personalization/dynamic-variables), senza chiamare l'LLM.

Crea espressioni con `Expr.variable(name)` e `Expr.literal(value)`. Confrontale con `eq`, `neq`, `gt`, `gte`, `lt` e `lte`, ed esegui operazioni aritmetiche con `add`, `sub`, `mul` e `div`. Combinale con `all_of` / `allOf`, `any_of` / `anyOf` e `if_else` / `ifElse`. I valori semplici vengono racchiusi automaticamente in `Expr.literal()`. `Expr.llm(description)` chiede all'LLM di estrarre un valore dalla conversazione; passa `type` ed `enum` per ottenere una stringa, un numero o uno di un insieme fisso invece di un booleano.

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

## Cicli e archi all'indietro

L'API consente un arco tra due nodi qualsiasi, in entrambe le direzioni. Per consentire alla conversazione di tornare indietro, assegna all'arco una condizione all'indietro con `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()` gestisce automaticamente il caso più comune. Quando il nodo di destinazione ha già un arco che porta qui, il nuovo arco diventa la condizione all'indietro di quell'arco. Nel primo esempio, `failed.go_to(identify)` viene inviato come condizione all'indietro dell'arco `identify` → `lookup_order`. Qualsiasi altro secondo arco tra gli stessi due nodi, e qualsiasi arco da un nodo a sé stesso, non supera la convalida.

## Modifica un agente esistente

`Workflow.from_agent()` / `Workflow.fromAgent()` carica il workflow di un agente restituito da `agents.get()`. Gli ID dei nodi, gli ID degli archi, le posizioni nell'editor e l'ordine degli archi vengono mantenuti, quindi reinviandolo cambierà solo ciò che hai modificato.

| Python                                  | JavaScript                             | Funzione                                                                          |
| --------------------------------------- | -------------------------------------- | --------------------------------------------------------------------------------- |
| `wf.node(id)`                           | `wf.node(id)`                          | Restituisce un handle a un nodo esistente, per modificarlo o concatenare da esso. |
| `node.update(**fields)`                 | `node.update(fields)`                  | Modifica il prompt, gli strumenti, la destinazione o le opzioni di un nodo.       |
| `wf.insert_between(source, target, fn)` | `wf.insertBetween(source, target, fn)` | Inserisce nuovi nodi in un arco esistente, mantenendone la condizione.            |
| `wf.remove(id)`                         | `wf.remove(id)`                        | Rimuove un nodo e tutti gli archi in entrata o in uscita.                         |

`insert_between()` / `insertBetween()` passa alla tua funzione un ramo che conserva la condizione dell'arco sostituito. La funzione restituisce l'ultimo nodo aggiunto, che viene quindi collegato alla destinazione originale. Il metodo restituisce quel nodo, oppure `None` / `undefined` se non esiste alcun arco tra i due nodi.

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

## Archivia i workflow nel controllo di versione

`to_json_string()` / `toJSONString()` scrive il workflow nel formato wire dell'API: chiavi snake_case, ordinate a ogni livello, con rientro di 2 spazi. Ha la stessa struttura usata dall'API REST e dalla [CLI](/docs/it/eleven-agents/operate/cli), ed entrambi gli SDK scrivono file identici byte per byte per lo stesso workflow, così le differenze restano minime. `Workflow.from_json()` / `Workflow.fromJSON()` lo legge nuovamente e accetta anche una configurazione completa dell'agente con una chiave `workflow`, come un file nella directory `agent_configs/` della 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"));
```

Una configurazione tipica:

#### Inizializza il file

Carica l'agente con `from_agent()` / `fromAgent()` e scrivilo nel file, oppure recuperalo con `elevenlabs
    agents pull`.

#### Modifica

Modifica direttamente il JSON oppure caricalo con `from_json()` / `fromJSON()` e modificalo con il
builder in uno script.

#### Convalida in CI

Esegui `Workflow.from_json(file).build()` / `Workflow.fromJSON(file).build()` su ogni pull
request e pubblica `elevenlabs agents workflow diagram <file> --format mermaid-md` come commento, così i
revisori possono vedere il grafo.

#### Distribuisci al merge

Invia il workflow creato con `agents.update()`.

## Visualizza un workflow

La [CLI](/docs/it/eleven-agents/operate/cli) disegna un file di workflow come diagramma di flusso, così puoi verificare ciò che ha creato il tuo codice senza aprire la dashboard. Accetta un file di workflow semplice scritto da `to_json_string()` / `toJSONString()`, oppure una configurazione dell'agente con una chiave `workflow`:

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

Per impostazione predefinita, disegna riquadri e frecce nel terminale, con ogni condizione dell'arco numerata sul relativo arco e riportata in una legenda sottostante. Usa invece `--format mermaid` per stampare il sorgente [Mermaid](https://mermaid.js.org/), oppure `--format mermaid-md` per racchiuderlo in un blocco di codice delimitato che GitHub visualizza, ad esempio in un commento a una pull request:

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

Il comando legge solo file locali, quindi non richiede una chiave API e può essere eseguito in CI.

## Convalida

Le chiamate concatenate non falliscono mai da sole. `build()`, `to_json()` / `toJSON()` e `to_json_string()` / `toJSONString()` verificano l'intero grafo e sollevano o generano un singolo `WorkflowValidationError`, il cui elenco `issues` riporta ogni problema, ciascuno con un `code`, un `message` e il `node_id` / `nodeId` a cui si riferisce.

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

Gli errori includono:

* Due archi tra la stessa coppia di nodi, oppure un arco da un nodo a sé stesso.
* Un arco verso il nodo iniziale o in uscita da un nodo finale.
* Un arco da un nodo strumento che non sia un arco di successo o fallimento, oppure un arco di successo o fallimento da qualsiasi altro tipo di nodo.
* Due nodi con lo stesso ID, oppure un arco verso un nodo inesistente.

I nodi che non possono essere raggiunti dal nodo iniziale vengono segnalati come avvisi, tramite `on_warning` / `onWarning`, e non interrompono la creazione.

## Tipi di nodo più recenti del tuo SDK

Un workflow caricato dall'API o da un file può contenere tipi di nodo che la tua versione dell'SDK non conosce ancora. Vengono mantenuti invariati tramite `from_json()` / `fromJSON()` e `to_json_string()` / `toJSONString()`, e `build()` segnala ciascuno di essi come avviso. Il modello di richiesta tipizzato non può includerli, quindi aggiorna l'SDK oppure invia direttamente il formato wire:

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