> 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 como código

## Visão geral

Um [workflow](/docs/pt/eleven-agents/customization/agent-workflows) é armazenado como um grafo de nós e arestas. Escrever esse grafo manualmente significa que você precisa manter os IDs das arestas, as listas `edge_order` e os objetos de condição consistentes por conta própria. O construtor `Workflow` nos SDKs de Python e JavaScript permite descrever o mesmo grafo com chamadas de método encadeadas. Ele então produz o valor `workflow` que `agents.create()` e `agents.update()` já aceitam.

Use-o para:

* Criar um workflow em código e revisá-lo como qualquer outra alteração de código.
* Carregar o workflow de um agente, alterar um nó e enviá-lo de volta sem afetar o restante do grafo.
* Armazenar workflows como JSON no controle de versão e validá-los na CI.

> **Note**
>
> O construtor é executado inteiramente no SDK. Ele não chama novos endpoints da API, e tudo o que ele cria pode ser
> aberto e editado no editor visual do dashboard.

## Requisitos

* Python: o pacote `elevenlabs`. O construtor é importado de `elevenlabs.agent_workflows`.
* JavaScript: o pacote `@elevenlabs/elevenlabs-js`. O construtor é exportado da raiz do pacote.

## Crie um workflow

O objeto `Workflow` representa o nó inicial. Cada verbo adiciona um nó e o retorna, para que você possa encadear a próxima etapa a partir dele:

| Python                                 | JavaScript                            | Adiciona                                                                                |
| -------------------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------- |
| `converse(id, prompt, **options)`      | `converse(id, prompt, options?)`      | Um nó de subagente. O agente mantém uma conversa com `prompt` adicionado às instruções. |
| `tool(id, tool_ids, **options)`        | `tool(id, toolIds, options?)`         | Um nó de ferramenta de despacho que executa as ferramentas fornecidas.                  |
| `transfer(id, destination, **options)` | `transfer(id, destination, options?)` | Uma transferência para um número de telefone, um URI SIP ou outro agente.               |
| `end(id="end")`                        | `end(options?)`                       | Um nó final. Cada `end()` sem um ID compartilha um nó `"end"`.                          |

Os métodos de roteamento decidem qual aresta leva ao próximo nó:

| Python                                | JavaScript                       | Cria                                                                                                                                                                                                    |
| ------------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `when(condition)`                     | `when(condition)`                | Uma aresta condicional. Uma string é uma condição de LLM; um `Expr` é uma expressão.                                                                                                                    |
| `on_success()` / `on_failure()`       | `onSuccess()` / `onFailure()`    | Uma aresta de um nó de ferramenta, percorrida quando a chamada de ferramenta é bem-sucedida ou falha.                                                                                                   |
| `on_result(success=..., failure=...)` | `onResult({ success, failure })` | Ambas as arestas de resultado da ferramenta de uma vez.                                                                                                                                                 |
| `go_to(node)`                         | `goTo(node)`                     | Uma aresta para um nó que já existe, para mesclagens e loops.                                                                                                                                           |
| `up()`                                | `up()`                           | Nenhuma aresta. Retorna o nó a partir do qual o atual foi encadeado, para continuar o encadeamento a partir dele. Consulte [Escreva um workflow como uma única cadeia](#write-a-workflow-as-one-chain). |

Verbos chamados diretamente no workflow criam ramificações a partir do nó inicial com uma aresta incondicional. Este exemplo direciona quem liga para uma consulta de pedido, tenta novamente a identificação se a consulta falhar e transfere pessoas VIP ou frustradas:

```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()` valida todo o grafo e retorna o modelo de solicitação. Ele também:

* Atribui ao nó inicial o ID `start_node`, exigido pela API.
* Gera IDs de aresta no formato `source__target`.
* Preenche o `edge_order` de cada nó na ordem em que você adicionou as arestas.
* Posiciona qualquer nó sem posição, para que o grafo seja organizado da esquerda para a direita no editor visual.

### Escreva um workflow como uma única cadeia

`up()` retorna o nó a partir do qual o atual foi encadeado, para que você possa descrever um workflow inteiro em uma expressão, em vez de manter cada nó em uma variável. Em uma única cadeia, use `go_to()` / `goTo()` com um ID de nó, pois não há uma variável para passar:

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

Para um identificador de nó de `wf.node(id)`, `up()` retorna o nó de onde vem a primeira aresta que entra nele. Para o nó inicial, retorna o próprio nó inicial.

### Opções de nó

As configurações opcionais são passadas como argumentos nomeados em Python e em um objeto de opções final em JavaScript. Seus nomes correspondem aos campos do nó na API:

| Nó         | 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`                                                                                    |

Cada verbo também aceita `position` caso você queira posicionar o nó manualmente. `Workflow(prevent_subagent_loops=True)` em Python, ou `new Workflow({ preventSubagentLoops: true })` em JavaScript, impede que a conversa entre novamente em um subagente que já visitou.

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

### Destinos de transferência

| Python                             | JavaScript                        | Transfere para                                 |
| ---------------------------------- | --------------------------------- | ---------------------------------------------- |
| `Destination.phone(number)`        | `Destination.phone(number)`       | Um número de telefone                          |
| `Destination.phone_variable(name)` | `Destination.phoneVariable(name)` | Um número de telefone em uma variável dinâmica |
| `Destination.sip(uri)`             | `Destination.sip(uri)`            | Um URI SIP                                     |
| `Destination.sip_variable(name)`   | `Destination.sipVariable(name)`   | Um URI SIP em uma variável dinâmica            |
| `Destination.agent(agent_id)`      | `Destination.agent(agentId)`      | Outro agente                                   |

## Condições

`when()` aceita uma string simples, um `Condition` ou um `Expr`:

* Uma string é uma condição de LLM: a LLM decide se ela é atendida.
* `Condition.llm(text, label=...)` é o mesmo, com um rótulo exibido na aresta no editor visual. Use `Condition.expr(expr, label=...)` para rotular uma expressão.
* Um `Expr` é avaliado em relação às [variáveis dinâmicas](/docs/pt/eleven-agents/customization/personalization/dynamic-variables), sem chamar a LLM.

Crie expressões com `Expr.variable(name)` e `Expr.literal(value)`. Compare-as com `eq`, `neq`, `gt`, `gte`, `lt` e `lte`, e faça operações aritméticas com `add`, `sub`, `mul` e `div`. Combine-as com `all_of` / `allOf`, `any_of` / `anyOf` e `if_else` / `ifElse`. Os valores simples são encapsulados em `Expr.literal()` automaticamente. `Expr.llm(description)` solicita à LLM que extraia um valor da conversa; passe `type` e `enum` para obter uma string, um número ou um de um conjunto fixo, em vez de um 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");
```

## Loops e arestas de retorno

A API permite uma aresta entre dois nós quaisquer, em qualquer direção. Para permitir que a conversa retorne pelo caminho de onde veio, atribua à aresta uma condição de retorno com `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()` lida com o caso comum automaticamente. Quando o nó de destino já tem uma aresta que leva até aqui, a nova aresta se torna a condição de retorno dessa aresta. No primeiro exemplo, `failed.go_to(identify)` é enviado como a condição de retorno da aresta `identify` → `lookup_order`. Qualquer outra segunda aresta entre os mesmos dois nós, e qualquer aresta de um nó para ele mesmo, falha na validação.

## Edite um agente existente

`Workflow.from_agent()` / `Workflow.fromAgent()` carrega o workflow de um agente retornado por `agents.get()`. Os IDs dos nós, IDs das arestas, posições no editor e ordem das arestas são preservados, portanto enviá-lo de volta altera apenas o que você modificou.

| Python                                  | JavaScript                             | Faz                                                                                     |
| --------------------------------------- | -------------------------------------- | --------------------------------------------------------------------------------------- |
| `wf.node(id)`                           | `wf.node(id)`                          | Retorna um identificador para um nó existente, para editá-lo ou encadear a partir dele. |
| `node.update(**fields)`                 | `node.update(fields)`                  | Altera o prompt, as ferramentas, o destino ou as opções de um nó.                       |
| `wf.insert_between(source, target, fn)` | `wf.insertBetween(source, target, fn)` | Insere novos nós em uma aresta existente, mantendo sua condição.                        |
| `wf.remove(id)`                         | `wf.remove(id)`                        | Remove um nó e todas as arestas que chegam ou saem dele.                                |

`insert_between()` / `insertBetween()` passa à sua função uma ramificação que carrega a condição da aresta substituída. A função retorna o último nó adicionado, que então é conectado ao destino original. O método retorna esse nó, ou `None` / `undefined` se não houver uma aresta entre os dois nós.

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

## Armazene workflows no controle de versão

`to_json_string()` / `toJSONString()` grava o workflow no formato de transmissão da API: chaves em snake_case, ordenadas em todos os níveis, com indentação de 2 espaços. É o mesmo formato usado pela API REST e pela [CLI](/docs/pt/eleven-agents/operate/cli), e ambos os SDKs gravam arquivos idênticos byte a byte para o mesmo workflow, mantendo os diffs pequenos. `Workflow.from_json()` / `Workflow.fromJSON()` o lê de volta e também aceita uma configuração completa de agente com uma chave `workflow`, como um arquivo no diretório `agent_configs/` da 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"));
```

Uma configuração típica:

#### Crie o arquivo inicial

Carregue o agente com `from_agent()` / `fromAgent()` e grave-o, ou faça o pull com `elevenlabs
    agents pull`.

#### Edite

Altere o JSON diretamente ou carregue-o com `from_json()` / `fromJSON()` e edite-o com o
construtor em um script.

#### Valide na CI

Execute `Workflow.from_json(file).build()` / `Workflow.fromJSON(file).build()` em cada pull
request e publique `elevenlabs agents workflow diagram <file> --format mermaid-md` como um comentário para que
os revisores possam ver o grafo.

#### Implante ao mesclar

Envie o workflow criado com `agents.update()`.

## Visualize um workflow

A [CLI](/docs/pt/eleven-agents/operate/cli) desenha um arquivo de workflow como um fluxograma, para que você possa verificar o que seu código criou sem abrir o dashboard. Ela aceita um arquivo de workflow simples gravado por `to_json_string()` / `toJSONString()`, ou uma configuração de agente com uma chave `workflow`:

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

Por padrão, ela desenha caixas e setas no terminal, com cada condição de aresta numerada em sua aresta e descrita em uma legenda abaixo. Use `--format mermaid` para imprimir o código-fonte [Mermaid](https://mermaid.js.org/) ou `--format mermaid-md` para envolvê-lo em um bloco de código delimitado que o GitHub renderiza, por exemplo, em um comentário de pull request:

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

O comando lê apenas arquivos locais, portanto não precisa de chave de API e pode ser executado na CI.

## Validação

Chamadas encadeadas nunca falham por si só. `build()`, `to_json()` / `toJSON()` e `to_json_string()` / `toJSONString()` verificam todo o grafo e levantam ou lançam um único `WorkflowValidationError` cuja lista `issues` contém todos os problemas, cada um com um `code`, uma `message` e o `node_id` / `nodeId` a que se refere.

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

Os erros incluem:

* Duas arestas entre o mesmo par de nós, ou uma aresta de um nó para ele mesmo.
* Uma aresta que entra no nó inicial ou que sai de um nó final.
* Uma aresta de um nó de ferramenta que não é uma aresta de sucesso ou falha, ou uma aresta de sucesso ou falha de qualquer outro tipo de nó.
* Dois nós com o mesmo ID, ou uma aresta para um nó que não existe.

Nós que não podem ser alcançados a partir do nó inicial são relatados como avisos, por meio de `on_warning` / `onWarning`, e não interrompem a criação.

## Tipos de nó mais novos que seu SDK

Um workflow carregado da API ou de um arquivo pode conter tipos de nó que a versão do seu SDK ainda não conhece. Eles são preservados sem alterações ao passar por `from_json()` / `fromJSON()` e `to_json_string()` / `toJSONString()`, e `build()` relata cada um como um aviso. O modelo de solicitação tipado não consegue transportá-los, então atualize o SDK ou envie o formato de transmissão diretamente:

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