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

# Workflowi jako kod

## Omówienie

[Workflow](/docs/pl/eleven-agents/customization/agent-workflows) jest przechowywany jako graf węzłów i krawędzi. Ręczne tworzenie takiego grafu oznacza, że musisz sam dbać o spójność identyfikatorów krawędzi, list `edge_order` i obiektów warunków. Konstruktor `Workflow` w SDK Python i JavaScript pozwala opisać ten sam graf za pomocą łańcuchowych wywołań metod. Następnie tworzy wartość `workflow`, którą akceptują już `agents.create()` i `agents.update()`.

Użyj go, aby:

* Tworzyć workflow w kodzie i sprawdzać je jak każdą inną zmianę w kodzie.
* Wczytać workflow agenta, zmienić jeden węzeł i wysłać go z powrotem bez naruszania reszty grafu.
* Przechowywać workflowi jako JSON w kontroli wersji i sprawdzać je w CI.

> **Note**
>
> Konstruktor działa wyłącznie w SDK. Nie wywołuje nowych endpointów API, a wszystko, co utworzy,
> można otworzyć i edytować w edytorze wizualnym w dashboardzie.

## Wymagania

* Python: pakiet `elevenlabs`. Konstruktor importujesz z `elevenlabs.agent_workflows`.
* JavaScript: pakiet `@elevenlabs/elevenlabs-js`. Konstruktor jest eksportowany z głównego modułu pakietu.

## Tworzenie workflow

Obiekt `Workflow` reprezentuje węzeł początkowy. Każdy czasownik dodaje węzeł i go zwraca, więc możesz od niego połączyć kolejne kroki:

| Python                                 | JavaScript                            | Dodaje                                                                    |
| -------------------------------------- | ------------------------------------- | ------------------------------------------------------------------------- |
| `converse(id, prompt, **options)`      | `converse(id, prompt, options?)`      | Węzeł podagenta. Agent prowadzi rozmowę z `prompt` dodanym do instrukcji. |
| `tool(id, tool_ids, **options)`        | `tool(id, toolIds, options?)`         | Węzeł narzędzia dyspozytorskiego, który uruchamia podane narzędzia.       |
| `transfer(id, destination, **options)` | `transfer(id, destination, options?)` | Przekazanie do numeru telefonu, identyfikatora URI SIP lub innego agenta. |
| `end(id="end")`                        | `end(options?)`                       | Węzeł końcowy. Każde `end()` bez ID używa wspólnego węzła `"end"`.        |

Metody routingu określają, która krawędź prowadzi do następnego węzła:

| Python                                | JavaScript                       | Tworzy                                                                                                                                                                 |
| ------------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `when(condition)`                     | `when(condition)`                | Krawędź warunkową. Ciąg znaków to warunek LLM, a `Expr` to wyrażenie.                                                                                                  |
| `on_success()` / `on_failure()`       | `onSuccess()` / `onFailure()`    | Krawędź z węzła narzędzia, wybieraną po powodzeniu lub niepowodzeniu wywołania narzędzia.                                                                              |
| `on_result(success=..., failure=...)` | `onResult({ success, failure })` | Obie krawędzie wyniku narzędzia naraz.                                                                                                                                 |
| `go_to(node)`                         | `goTo(node)`                     | Krawędź do istniejącego już węzła, dla połączeń i pętli.                                                                                                               |
| `up()`                                | `up()`                           | Bez krawędzi. Zwraca węzeł, z którego połączono bieżący, by kontynuować łańcuch od niego. Zobacz [Zapisz workflow jako jeden łańcuch](#write-a-workflow-as-one-chain). |

Czasowniki wywołane bezpośrednio na workflow tworzą gałęzie z węzła początkowego przez bezwarunkową krawędź. Ten przykład kieruje dzwoniących do wyszukiwania zamówienia, ponawia identyfikację, jeśli wyszukiwanie się nie uda, oraz przekazuje klientów VIP lub zdenerwowanych:

```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()` sprawdza cały graf i zwraca model żądania. Dodatkowo:

* Nadaje węzłowi początkowemu wymagany przez API identyfikator `start_node`.
* Generuje identyfikatory krawędzi w formacie `source__target`.
* Uzupełnia `edge_order` każdego węzła w kolejności dodawania krawędzi.
* Pozycjonuje każdy węzeł bez pozycji, aby graf był ułożony od lewej do prawej w edytorze wizualnym.

### Zapisz workflow jako jeden łańcuch

`up()` zwraca węzeł, z którego połączono bieżący, więc możesz opisać cały workflow jednym wyrażeniem zamiast przechowywać każdy węzeł w zmiennej. W pojedynczym łańcuchu użyj `go_to()` / `goTo()` z ID węzła, ponieważ nie masz zmiennej do przekazania:

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

Dla uchwytu węzła z `wf.node(id)` metoda `up()` zwraca węzeł, z którego prowadzi pierwsza krawędź do tego węzła. Dla węzła początkowego zwraca węzeł początkowy.

### Opcje węzłów

Opcjonalne ustawienia przekazujesz jako argumenty nazwane w Pythonie i w końcowym obiekcie opcji w JavaScript. Ich nazwy odpowiadają polom węzła w API:

| Węzeł      | 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`                                                                                    |

Każdy czasownik przyjmuje też `position`, jeśli chcesz samodzielnie ustawić pozycję węzła. `Workflow(prevent_subagent_loops=True)` w Pythonie lub `new Workflow({ preventSubagentLoops: true })` w JavaScript zapobiega ponownemu wejściu rozmowy do odwiedzonego już podagenta.

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

### Cele przekazywania

| Python                             | JavaScript                        | Przekazuje do                                 |
| ---------------------------------- | --------------------------------- | --------------------------------------------- |
| `Destination.phone(number)`        | `Destination.phone(number)`       | Numeru telefonu                               |
| `Destination.phone_variable(name)` | `Destination.phoneVariable(name)` | Numeru telefonu w zmiennej dynamicznej        |
| `Destination.sip(uri)`             | `Destination.sip(uri)`            | Identyfikatora URI SIP                        |
| `Destination.sip_variable(name)`   | `Destination.sipVariable(name)`   | Identyfikatora URI SIP w zmiennej dynamicznej |
| `Destination.agent(agent_id)`      | `Destination.agent(agentId)`      | Innego agenta                                 |

## Warunki

`when()` przyjmuje zwykły ciąg znaków, `Condition` lub `Expr`:

* Ciąg znaków to warunek LLM: LLM decyduje, czy jest spełniony.
* `Condition.llm(text, label=...)` działa tak samo, z etykietą wyświetlaną na krawędzi w edytorze wizualnym. Użyj `Condition.expr(expr, label=...)`, aby oznaczyć wyrażenie etykietą.
* `Expr` jest obliczany na podstawie [zmiennych dynamicznych](/docs/pl/eleven-agents/customization/personalization/dynamic-variables), bez wywoływania LLM.

Twórz wyrażenia za pomocą `Expr.variable(name)` i `Expr.literal(value)`. Porównuj je z `eq`, `neq`, `gt`, `gte`, `lt` i `lte`, a działania arytmetyczne wykonuj przez `add`, `sub`, `mul` i `div`. Łącz je przez `all_of` / `allOf`, `any_of` / `anyOf` i `if_else` / `ifElse`. Zwykłe wartości są automatycznie opakowywane w `Expr.literal()`. `Expr.llm(description)` prosi LLM o wyodrębnienie wartości z rozmowy; przekaż `type` i `enum`, aby otrzymać ciąg znaków, liczbę lub jedną z ustalonych wartości zamiast wartości logicznej.

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

## Pętle i krawędzie wsteczne

API pozwala na jedną krawędź między dowolnymi dwoma węzłami, w każdym kierunku. Aby rozmowa mogła wrócić tą samą drogą, nadaj krawędzi warunek wsteczny za pomocą `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()` obsługuje typowy przypadek automatycznie. Gdy docelowy węzeł ma już krawędź prowadzącą tutaj, nowa krawędź staje się warunkiem wstecznym tamtej krawędzi. W pierwszym przykładzie `failed.go_to(identify)` jest wysyłane jako warunek wsteczny krawędzi `identify` → `lookup_order`. Każda inna druga krawędź między tymi samymi dwoma węzłami oraz każda krawędź z węzła do niego samego nie przejdzie walidacji.

## Edycja istniejącego agenta

`Workflow.from_agent()` / `Workflow.fromAgent()` wczytuje workflow agenta zwróconego przez `agents.get()`. Identyfikatory węzłów i krawędzi, pozycje w edytorze oraz kolejność krawędzi zostają zachowane, więc wysłanie go z powrotem zmienia tylko to, co zmienisz.

| Python                                  | JavaScript                             | Działanie                                                             |
| --------------------------------------- | -------------------------------------- | --------------------------------------------------------------------- |
| `wf.node(id)`                           | `wf.node(id)`                          | Zwraca uchwyt istniejącego węzła, aby go edytować lub z niego łączyć. |
| `node.update(**fields)`                 | `node.update(fields)`                  | Zmienia prompt, narzędzia, cel lub opcje węzła.                       |
| `wf.insert_between(source, target, fn)` | `wf.insertBetween(source, target, fn)` | Wstawia nowe węzły w istniejącą krawędź, zachowując jej warunek.      |
| `wf.remove(id)`                         | `wf.remove(id)`                        | Usuwa węzeł oraz wszystkie krawędzie do niego i od niego.             |

`insert_between()` / `insertBetween()` przekazuje twojej funkcji gałąź zawierającą warunek zastępowanej krawędzi. Funkcja zwraca ostatni dodany węzeł, który zostaje następnie połączony z pierwotnym celem. Metoda zwraca ten węzeł albo `None` / `undefined`, jeśli między tymi dwoma węzłami nie ma krawędzi.

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

## Przechowywanie workflowi w kontroli wersji

`to_json_string()` / `toJSONString()` zapisuje workflow w formacie przesyłanym przez API: klucze snake_case, posortowane na każdym poziomie, z wcięciem 2 spacji. To ten sam format, którego używają REST API i [CLI](/docs/pl/eleven-agents/operate/cli), a oba SDK zapisują dla tego samego workflow pliki identyczne bajt po bajcie, więc różnice pozostają niewielkie. `Workflow.from_json()` / `Workflow.fromJSON()` odczytuje go z powrotem i przyjmuje też pełną konfigurację agenta z kluczem `workflow`, na przykład plik z katalogu `agent_configs/` 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"));
```

Typowa konfiguracja:

#### Utwórz plik

Wczytaj agenta za pomocą `from_agent()` / `fromAgent()` i zapisz go albo pobierz przez `elevenlabs
    agents pull`.

#### Edytuj

Zmień JSON bezpośrednio albo wczytaj go przez `from_json()` / `fromJSON()` i edytuj konstruktorem
w skrypcie.

#### Waliduj w CI

Uruchom `Workflow.from_json(file).build()` / `Workflow.fromJSON(file).build()` dla każdego pull
requesta i dodaj jako komentarz `elevenlabs agents workflow diagram <file> --format mermaid-md`, aby
osoby sprawdzające mogły zobaczyć graf.

#### Wdrażaj po scaleniu

Wyślij utworzony workflow przez `agents.update()`.

## Wizualizacja workflow

[CLI](/docs/pl/eleven-agents/operate/cli) rysuje plik workflow jako schemat blokowy, dzięki czemu możesz sprawdzić, co utworzył kod, bez otwierania dashboardu. Przyjmuje sam plik workflow zapisany przez `to_json_string()` / `toJSONString()` lub konfigurację agenta z kluczem `workflow`:

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

Domyślnie rysuje w terminalu pola i strzałki, a każdy warunek krawędzi numeruje na krawędzi i opisuje w legendzie poniżej. Użyj `--format mermaid`, aby zamiast tego wyświetlić kod źródłowy [Mermaid](https://mermaid.js.org/), lub `--format mermaid-md`, aby umieścić go w ogrodzonym bloku kodu renderowanym przez GitHub, na przykład w komentarzu do pull requesta:

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

Polecenie odczytuje tylko pliki lokalne, więc nie potrzebuje klucza API i może działać w CI.

## Walidacja

Wywołania łańcuchowe same w sobie nigdy nie kończą się błędem. `build()`, `to_json()` / `toJSON()` i `to_json_string()` / `toJSONString()` sprawdzają cały graf i zgłaszają lub wyrzucają pojedynczy `WorkflowValidationError`, którego lista `issues` zawiera każdy problem wraz z jego `code`, `message` oraz dotyczącym go `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;
  }
}
```

Błędy obejmują:

* Dwie krawędzie między tą samą parą węzłów lub krawędź z węzła do niego samego.
* Krawędź do węzła początkowego lub z węzła końcowego.
* Krawędź z węzła narzędzia, która nie jest krawędzią sukcesu ani porażki, albo krawędź sukcesu lub porażki z węzła innego typu.
* Dwa węzły z tym samym ID lub krawędź do nieistniejącego węzła.

Węzły, do których nie można dotrzeć z węzła początkowego, są zgłaszane jako ostrzeżenia przez `on_warning` / `onWarning` i nie zatrzymują tworzenia.

## Typy węzłów nowsze niż twoje SDK

Workflow wczytany z API lub z pliku może zawierać typy węzłów, których twoja wersja SDK jeszcze nie zna. Przechodzą bez zmian przez `from_json()` / `fromJSON()` i `to_json_string()` / `toJSONString()`, a `build()` zgłasza każdy z nich jako ostrzeżenie. Typowany model żądania nie może ich obsłużyć, więc zaktualizuj SDK albo wyślij format przesyłany przez sieć bezpośrednio:

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