Ir para a navegação

Workflows como código

Crie, edite e versione workflows de agentes com os SDKs de Python e JavaScript

Visão geral

Um workflow é 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.

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:

PythonJavaScriptAdiciona
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ó:

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

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:

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

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

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óPythonJavaScript
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

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.

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

Destinos de transferência

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

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

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.

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

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

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

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

Uma configuração típica:

1

Crie o arquivo inicial

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

2

Edite

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

3

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.

4

Implante ao mesclar

Envie o workflow criado com agents.update().

Visualize um workflow

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

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

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.

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)

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:

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