# Construindo agentes de IA com skail

Este guia descreve um conjunto de padrões para construir agentes de IA sobre o skail — desde a classificação do que o usuário quer até a execução de ações no mundo real, passando por conversas longas, integrações que falham e pontos onde uma pessoa precisa intervir.

A ideia central é simples: **um agente é um método durável**. Você escreve a lógica como código normal; o skail garante que esse código sobreviva a falhas, reinícios e esperas de horas sem você construir filas, máquinas de estado ou serviços auxiliares. As seções a seguir mostram como montar cada peça e como o skail sustenta tudo por baixo.

***

### 1. O modelo mental

Um agente bem estruturado quase sempre tem as mesmas camadas, independente do domínio (atendimento, cobrança, agendamento, back-office, etc.):

1. **Gatilho** — algo inicia o agente (uma mensagem, um evento, uma rota de API).
2. **Compreensão** — o agente classifica a *intenção*: o que o usuário quer.
3. **Roteamento** — a intenção seleciona um *agente especializado* para aquela tarefa.
4. **Coleta** — o agente extrai os dados necessários, usando *ferramentas* para se completar sozinho e conversando com o usuário quando falta algo.
5. **Ação** — o agente executa a operação e **aguarda** o resultado de sistemas externos.
6. **Resolução** — sucesso, ou *human-in-the-loop* quando precisa de uma decisão humana.

O skail entra como a **camada de durabilidade** que conecta tudo isso: ele preserva o estado entre cada passo, repete o que falhou e deixa o agente esperar por sinais externos sem consumir recursos.

```
gatilho ─▶ [ compreensão ] ─▶ [ roteamento ] ─▶ [ agente especializado ]
                                                      │
                          coleta (ferramentas + conversa) ◀─┘
                                                      │
                                  ação ─▶ aguarda externo ─▶ resolução / revisão humana
```

***

### 2. Os primitivos do skail

Você constrói agentes com poucos conceitos. Dominá-los é o suficiente para implementar qualquer um dos padrões deste guia.

#### Função durável vs. passo atômico

| Conceito                               | Quando usar                                                                                                                                                                                                                            |
| -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Função durável** (`[SkailFunction]`) | O ponto de entrada de um agente ou sub-agente. É onde vivem as esperas, os atrasos e as corridas. O estado é preservado entre cada `await`, mesmo que o processo reinicie.                                                             |
| **Passo atômico** (`[SkailCommand]`)   | Uma operação pontual e idempotente: uma chamada HTTP, uma escrita no banco, uma chamada ao LLM, o envio de uma mensagem. Se falhar, o skail repete **apenas o passo** — sem refazer o fluxo inteiro nem repetir trabalho já concluído. |

> **Regra de ouro:** isole cada ação não determinística (I/O, chamada de LLM, integração) em seu próprio passo atômico. É isso que torna o agente retomável e barato de repetir quando algo falha no meio.

#### Esperar pelo mundo externo, sem segurar recursos

| Primitivo                             | Para quê                                                                                                                                              |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `WaitForEvent<T>(nome, id)`           | Pausa o agente até alguém disparar o evento `(nome, id)` com um payload do tipo `T`. Consome **0 CPU** enquanto espera — pode durar segundos ou dias. |
| `DelayMinutes(n)` / `DelaySeconds(n)` | Um atraso durável: timeout, polling, backoff.                                                                                                         |
| `WhenAny(a, b)`                       | Retorna o que terminar primeiro. É o padrão **"evento ou timeout"**.                                                                                  |

O par `WaitForEvent` + `WhenAny(Delay...)` é o idioma mais importante: **espere por um sinal externo, mas nunca para sempre.**

```csharp
// Aguarda um retorno externo por até 1 minuto; se não vier, segue por outro caminho.
var retorno = WaitForEvent<RespostaExterna>("EVENTO_RETORNO", correlacaoId);
var timeout = DelayMinutes(1);

var resultado = await WhenAny(retorno, timeout);
if (resultado == retorno)
    processar(await retorno);           // o sinal chegou
else
    await consultarPorPolling(correlacaoId);  // timeout → plano B
```

#### Entrar e sair: disparar e desbloquear

Um agente não roda sozinho. Dois movimentos o conectam ao resto do sistema:

* **Disparar uma instância** — uma chamada que inicia uma nova execução da função durável, passando um identificador de correlação. Tipicamente vem de uma rota de API, de um webhook de entrada ou de um agendador.
* **Disparar um evento** — uma chamada que entrega um payload e **destrava** o `WaitForEvent` correspondente. Tipicamente vem de um callback externo, da chegada de uma nova mensagem do usuário, ou de uma aprovação numa interface.

O **identificador de correlação** (o `id` do evento) é o que liga o sinal externo à execução certa. Escolha uma chave estável e única por fluxo — o id da conversa, o id da transação, o id do pedido.

***

### 3. Compreensão: classificação de intenção

Antes de agir, o agente precisa entender **o que o usuário quer**. Trate isso como um passo dedicado, com saída estruturada — não deixe a interpretação espalhada pela lógica.

**Recomendações:**

* **Declare as intenções como dados**, não como código. Uma lista de `(chave, descrição)` é fácil de manter, revisar e expandir. A descrição ensina o modelo a reconhecer cada caso.
* **Force saída estruturada** (JSON Schema estrito) e `temperature = 0`. A classificação deve ser determinística e validável, não texto livre.
* **Devolva sempre três coisas:** a intenção, um sinal de *incompleto* (o agente ainda não tem clareza?) e uma *mensagem para o usuário* (o que perguntar quando estiver incompleto).
* **Mantenha o classificador genérico.** Ele recebe o histórico, a lista de opções e um contexto ("você é um assistente de…"); o mesmo classificador serve a qualquer agente — você só troca as opções e o contexto.

```csharp
// Intenções declaradas como dados — específicas do seu domínio.
var intencoes = new[]
{
    ("executar_acao",    "usuário quer realizar a operação principal. Sinais: ..."),
    ("consultar_status", "usuário quer saber o andamento de algo já iniciado."),
    ("cancelar",         "usuário quer desfazer ou cancelar uma operação."),
    ("duvida",           "mensagem vaga ou saudação. Marque incompleto e pergunte o que precisa."),
    ("fora_de_escopo",   "assunto não relacionado. Responda educadamente e encerre."),
};

var resultado = await classificarIntencao(historico, intencoes, contexto);
// resultado: { intencao, incompleto, mensagemParaUsuario }
```

A classificação roda como um **passo atômico** (`[SkailCommand]`): é uma chamada de LLM isolada e retomável.

***

### 4. Roteamento: escolhendo o agente especializado

Um único agente "que faz tudo" fica frágil e difícil de evoluir. Prefira um **roteador fino** que, a partir da intenção, delega para um **agente especializado** — cada um focado em uma tarefa, com seu próprio prompt, suas ferramentas e suas regras.

```csharp
[SkailFunction]
public async SkailTask Atender(string conversaId)
{
    var conversa = await ColetarContexto(conversaId);
    var intencao = await ClassificarIntencao(conversa);   // passo atômico

    // Loop de esclarecimento enquanto não há clareza (ver seção 6).
    intencao = await EsclarecerAteEntender(conversaId, intencao);

    switch (intencao.Chave)
    {
        case "executar_acao":    await AgenteDeExecucao(conversaId);   break;
        case "consultar_status": await AgenteDeConsulta(conversaId);   break;
        case "cancelar":         await AgenteDeCancelamento(conversaId); break;
        default:                 await ResponderForaDeEscopo(conversaId); break;
    }
}
```

**Por que isso funciona bem no skail:**

* Cada agente especializado é também uma `[SkailFunction]` — durável e retomável por si só. Um fluxo de execução longo não trava nem complica os fluxos curtos.
* O roteador permanece simples: classifica e delega. A complexidade fica contida dentro de cada especialista.
* Adicionar uma capacidade nova é adicionar uma intenção e um agente — sem mexer no resto.

***

### 5. Coleta: ferramentas (tool use)

O agente especializado precisa reunir os dados para agir. Em vez de interrogar o usuário campo a campo, dê a ele **ferramentas** para se completar sozinho — buscar um cadastro, validar um documento, consultar uma referência, reaproveitar um registro anterior.

**Recomendações:**

* **Modele cada ferramenta como uma função com schema claro** (nome, descrição, parâmetros). A descrição diz ao modelo *quando* usar a ferramenta.
* **Prefira completar a perguntar.** Se um dado pode ser derivado de outro (endereço a partir de um CEP, cadastro a partir de um documento), exponha isso como ferramenta — a conversa fica mais curta e natural.
* **Cada ferramenta é um passo atômico.** A execução de uma ferramenta deve ser um `[SkailCommand]`: isolada, idempotente e retomável.
* **Devolva resultado estruturado**, indicando o que está *completo* e o que ainda *falta*, mais uma mensagem para pedir ao usuário só o que realmente não dá para descobrir sozinho.
* **Inclua rastreabilidade.** Registrar de onde veio cada dado (qual mensagem, qual ferramenta) facilita auditoria e depuração.

```csharp
// Ferramentas oferecidas ao modelo — ele decide quando chamar cada uma.
var ferramentas = new[]
{
    Ferramenta("buscar_cadastro",   "Recupera dados já conhecidos por um identificador."),
    Ferramenta("validar_documento", "Valida um documento antes de prosseguir."),
    Ferramenta("completar_por_referencia", "Completa campos a partir de um dado-chave."),
    Ferramenta("reusar_registro",   "Usa um registro anterior como base e aplica só as mudanças pedidas."),
};

var extracao = await extrairDados(conversa, ferramentas);
// extracao: { dados, incompleto, mensagemParaUsuario, evidencias }
```

> **Multimodal:** se o usuário envia PDFs, imagens, planilhas ou áudio, faça um pré-processamento que extraia o conteúdo (texto, visão, transcrição) **antes** da etapa de raciocínio, entregando tudo como contexto. Para o agente, "ler um documento" vira parte natural da coleta.
>
> **Custo:** use o modelo certo para cada passo. Classificação e textos simples pedem um modelo rápido e barato; raciocínio com ferramentas e anexos pede um modelo mais capaz. Decida isso na camada de IA, não no workflow.

***

### 6. Conversas longas: o loop de esclarecimento

Raramente o usuário entrega tudo de uma vez. O padrão é **responder → esperar a próxima mensagem → reavaliar**, repetindo até ter o necessário — com um limite de inatividade para não esperar indefinidamente.

```csharp
while (resultado.Incompleto)
{
    await responderAoUsuario(conversaId, resultado.MensagemParaUsuario);

    var timeout      = DelayMinutes(15);
    var novaMensagem = WaitForEvent<string>("EVENTO_NOVA_MENSAGEM", conversaId);

    if (await WhenAny(timeout, novaMensagem) == timeout)
    {
        await encerrarPorInatividade(conversaId);
        return;
    }

    resultado = await reavaliar(conversaId);   // reclassifica ou re-extrai
}
```

O ponto-chave: durante a espera, o agente **não consome CPU nem memória**. Ele pode ficar pausado por minutos ou horas aguardando a resposta do usuário e retoma exatamente de onde parou quando a mensagem chega — porque o estado é durável.

***

### 7. Ação e espera por sistemas externos

Quando o agente executa a operação principal, o resultado muitas vezes não é imediato: depende de um provedor que responde por webhook, de uma fila de processamento, de uma confirmação que pode levar minutos.

O padrão recomendado combina **webhook com fallback para polling** — assim você não fica refém de o callback chegar:

```csharp
[SkailFunction]
public async SkailTask ExecutarOperacao(Pedido pedido)
{
    await enviarParaProvedor(pedido);                         // passo atômico

    var retorno = WaitForEvent<Resultado>("EVENTO_RETORNO", pedido.Id);
    var timeout = DelayMinutes(1);

    var resultado = await WhenAny(retorno, timeout) == retorno
        ? await retorno                                       // o webhook chegou
        : await consultarPorPolling(pedido.Id);               // timeout → polling durável

    await persistir(resultado);                               // passo atômico

    if (!resultado.Concluido)
        await AguardarRevisaoHumana(resultado);               // ver seção 8
}
```

O **polling durável** é só um laço com `DelaySeconds`/`DelayMinutes` e uma condição de parada — cada iteração é retomável, então um reinício no meio não recomeça do zero nem perde a contagem do timeout.

***

### 8. Human-in-the-loop

Alguns fluxos precisam de uma decisão humana: aprovar um valor, corrigir um dado, liberar uma operação sensível. No skail, isso é apenas mais uma espera por evento.

```csharp
[SkailFunction]
public async SkailTask AguardarRevisaoHumana(Resultado pendente)
{
    await registrarPedidoDeRevisao(pendente);   // notifica quem decide

    // O agente pausa aqui — 0 CPU — até alguém aprovar/corrigir na interface.
    var decisao = await WaitForEvent<Decisao>("EVENTO_REVISAO", pendente.Id);

    await aplicarEReprocessar(decisao);
}
```

Não há thread parada, nem job rodando em loop, nem timeout artificial te obrigando a decidir rápido. O agente fica genuinamente suspenso até a interface (ou outro sistema) disparar o evento de revisão. Isso torna aprovações de horas — ou dias — triviais de implementar.

***

### 9. Como o skail facilita tudo isso

Recapitulando o que você **não** precisa construir, porque o skail entrega por padrão:

| Necessidade do agente                 | O que o skail oferece                                                 |
| ------------------------------------- | --------------------------------------------------------------------- |
| Não perder o que estava fazendo       | **Estado durável** preservado entre cada `await`, mesmo após reinício |
| Sobreviver a falhas transitórias      | **Retries automáticos** por passo atômico, sem refazer o fluxo        |
| Esperar por sistemas externos         | `WaitForEvent` com **0 CPU** durante a espera — segundos ou dias      |
| Aprovações e revisões humanas         | Human-in-the-loop nativo, sem jobs nem polling artificial             |
| "Evento ou timeout", polling, backoff | `WhenAny` + `Delay...` como primitivos de primeira classe             |
| Entender o que aconteceu em produção  | Trace completo de cada execução, com reprodução determinística        |
| Coordenar tudo isso                   | Um **método normal** — sem filas, brokers ou microsserviços novos     |

O resultado: a sua equipe escreve a **lógica do agente** (intenções, ferramentas, regras do domínio) e ganha de graça a infraestrutura que normalmente separa um protótipo de um agente confiável em produção.

***

### 10. Montando o seu agente

Um roteiro prático, reunindo os padrões acima:

1. **Mapeie as intenções** do seu domínio como uma lista de `(chave, descrição)` e defina o contexto do assistente.
2. **Escreva o roteador** como uma `[SkailFunction]`: classifica a intenção, faz o loop de esclarecimento e delega para o agente especializado.
3. **Para cada intenção, crie um agente especializado** (`[SkailFunction]`) com o seu prompt, suas ferramentas e seu resultado estruturado (`completo? / falta o quê? / o que perguntar`).
4. **Modele as ferramentas** que permitem ao agente se completar sozinho; implemente cada uma como um `[SkailCommand]`.
5. **Defina seus eventos e a chave de correlação.** Use `WaitForEvent` no workflow e dispare os eventos a partir de webhooks, novas mensagens e aprovações.
6. **Trate a ação principal** com o padrão webhook-ou-polling e adicione human-in-the-loop onde uma decisão humana for necessária.
7. **Conecte os gatilhos**: uma rota/serviço que dispara a instância do agente e os pontos que disparam os eventos de desbloqueio.

Comece pequeno — uma intenção, um agente especializado, uma ferramenta — e expanda. Como cada peça é um método durável independente, o agente cresce sem virar um emaranhado: novas capacidades são novas intenções e novos especialistas.

***

### 11. Esqueleto completo

O exemplo abaixo conecta todos os padrões num único agente. É um **modelo genérico** — troque o domínio, as intenções, as ferramentas e as integrações pelas suas. A estrutura (roteador → especialistas, loop de conversa, ação com espera, human-in-the-loop) permanece a mesma para qualquer agente.

#### O agente

```csharp
using Skail.Platform.Runtime;
using static Skail.Platform.Runtime.Threading.SkailTask;

public sealed class Agente(
    IIntencaoExtractor intencao,   // classificador genérico (seção 3)
    IDadosExtractor    dados,      // extração com ferramentas (seção 5)
    IProvedorExterno   provedor,   // integração que executa a ação (seção 7)
    IConversaStore     store,      // histórico, estado e persistência
    ICanalSaida        canal)      // como responder ao usuário (WhatsApp, e-mail, chat...)
{
    private const ulong TimeoutInatividadeMin = 15;
    private const string Contexto = "assistente que ajuda o usuário a realizar e acompanhar operações";

    // As capacidades do agente, declaradas como dados (seção 3).
    private static readonly (string Chave, string Descricao)[] Intencoes =
    [
        ("executar_acao",    "usuário quer realizar a operação principal. Sinais: ..."),
        ("consultar_status", "usuário quer saber o andamento de algo já iniciado."),
        ("cancelar",         "usuário quer desfazer ou cancelar uma operação."),
        ("duvida",           "mensagem vaga ou saudação. Marque incompleto e pergunte o que precisa."),
        ("fora_de_escopo",   "assunto não relacionado. Responda educadamente e encerre."),
    ];

    // ── Roteador: ponto de entrada do agente (disparado por um trigger) ──────────
    [SkailFunction]
    public async SkailTask Atender(string conversaId)
    {
        var historico = await CarregarHistorico(conversaId);
        var intent    = await ClassificarIntencao(historico);

        // Loop de esclarecimento: conversa até entender o que o usuário quer (seção 6).
        while (intent.Incompleto)
        {
            if (!await AguardarResposta(conversaId, intent.MensagemParaUsuario)) return;
            historico = await CarregarHistorico(conversaId);
            intent    = await ClassificarIntencao(historico);
        }

        // Roteamento para o agente especializado (seção 4).
        switch (intent.Chave)
        {
            case "executar_acao":    await AgenteDeExecucao(conversaId);   break;
            case "consultar_status": await AgenteDeConsulta(conversaId);   break;
            case "cancelar":         await AgenteDeCancelamento(conversaId); break;
            default:
                await Responder(conversaId, "Posso ajudar a executar, consultar ou cancelar uma operação.");
                await EncerrarConversa(conversaId);
                break;
        }
    }

    // ── Agente especializado: coleta os dados e dispara a ação ───────────────────
    [SkailFunction]
    public async SkailTask AgenteDeExecucao(string conversaId)
    {
        var extracao = await ExtrairDados(conversaId);

        // Loop de coleta: pede só o que não dá para descobrir sozinho (seções 5 e 6).
        while (extracao.Incompleto)
        {
            if (!await AguardarResposta(conversaId, extracao.MensagemParaUsuario)) return;
            extracao = await ExtrairDados(conversaId);
        }

        await Responder(conversaId, "Recebi tudo. Já estou processando, um instante.");
        await ExecutarOperacao(extracao.Pedido);
        await EncerrarConversa(conversaId);
    }

    // ── Ação principal: webhook com fallback para polling (seção 7) ──────────────
    [SkailFunction]
    public async SkailTask ExecutarOperacao(Pedido pedido)
    {
        await EnviarParaProvedor(pedido);

        var retorno = WaitForEvent<Resultado>("EVENTO_RETORNO", pedido.Id);
        var timeout = DelayMinutes(1);

        var resultado = await WhenAny(retorno, timeout) == retorno
            ? await retorno                              // o webhook chegou
            : await ConsultarPorPolling(pedido.Id);      // timeout → polling durável

        await Persistir(resultado);

        if (!resultado.Concluido)
            await AguardarRevisaoHumana(resultado);      // seção 8
    }

    // ── Polling durável: cada iteração é retomável ───────────────────────────────
    [SkailFunction]
    public async SkailTask<Resultado> ConsultarPorPolling(string pedidoId)
    {
        var timeout = DelayMinutes(10);
        var atual   = new Resultado();

        while (!timeout.IsCompleted)
        {
            atual = await ConsultarProvedor(pedidoId);
            if (atual.EhFinal) return atual;
            await DelaySeconds(30);
        }
        return atual;
    }

    // ── Human-in-the-loop: pausa até uma decisão humana (seção 8) ────────────────
    [SkailFunction]
    public async SkailTask AguardarRevisaoHumana(Resultado pendente)
    {
        await RegistrarPedidoDeRevisao(pendente);

        var decisao = await WaitForEvent<Decisao>("EVENTO_REVISAO", pendente.Id);

        await EnviarParaProvedor(decisao.PedidoCorrigido);
        await DelayMinutes(1);
        await Persistir(await ConsultarProvedor(pendente.Id));
    }

    // ── Helper de conversa: responde e espera a próxima mensagem (ou desiste) ────
    // Retorna false quando a conversa deve terminar por inatividade.
    private async SkailTask<bool> AguardarResposta(string conversaId, string? mensagem)
    {
        await Responder(conversaId, mensagem ?? "Me conta um pouco mais sobre o que você precisa.");

        var timeout = DelayMinutes(TimeoutInatividadeMin);
        var msg     = WaitForEvent<string>("EVENTO_NOVA_MENSAGEM", conversaId);

        if (await WhenAny(timeout, msg) == timeout)
        {
            await Responder(conversaId, "Vou encerrar por inatividade. É só me chamar quando quiser continuar.");
            await EncerrarConversa(conversaId);
            return false;
        }
        return true;
    }

    // ── Passos atômicos: cada efeito colateral isolado e retomável (seção 2) ─────
    [SkailCommand] public async SkailTask<List<Mensagem>> CarregarHistorico(string id) => await store.Historico(id);
    [SkailCommand] public async SkailTask<IntencaoResult> ClassificarIntencao(List<Mensagem> h) => await intencao.Classificar(h, Intencoes, Contexto);
    [SkailCommand] public async SkailTask<DadosResult>    ExtrairDados(string id) => await dados.Extrair(await store.Historico(id));
    [SkailCommand] public async SkailTask EnviarParaProvedor(Pedido p) => await provedor.Enviar(p);
    [SkailCommand] public async SkailTask<Resultado> ConsultarProvedor(string id) => await provedor.Consultar(id);
    [SkailCommand] public async SkailTask Persistir(Resultado r) => await store.Salvar(r);
    [SkailCommand] public async SkailTask RegistrarPedidoDeRevisao(Resultado r) => await store.MarcarParaRevisao(r);
    [SkailCommand] public async SkailTask Responder(string id, string msg) { await store.SalvarMensagem(id, msg); await canal.Enviar(id, msg); }
    [SkailCommand] public async SkailTask EncerrarConversa(string id) => await store.Encerrar(id);

    // Sub-agentes de consulta/cancelamento seguem o mesmo padrão do AgenteDeExecucao.
    [SkailFunction] public async SkailTask AgenteDeConsulta(string conversaId)     { /* extrai identificador, consulta, responde */ }
    [SkailFunction] public async SkailTask AgenteDeCancelamento(string conversaId) { /* extrai identificador, cancela, confirma */ }
}
```

#### As pontas: gatilho e desbloqueio

O agente acima é dirigido por **eventos**. Faltam as duas pontas que o conectam ao mundo — normalmente fora do worker, na sua API/BFF e nos seus webhooks:

```csharp
// 1) GATILHO — inicia uma instância do agente (ex.: ao chegar a primeira mensagem).
//    Dispara a SkailFunction, passando a chave de correlação.
await skail.Trigger(functionName: "Atender", instanceId: conversaId, payload: conversaId);

// 2) NOVA MENSAGEM — destrava o loop de conversa quando o usuário responde.
await skail.Fire("EVENTO_NOVA_MENSAGEM", eventId: conversaId, payload: textoDaMensagem);

// 3) RETORNO DO PROVEDOR — destrava a ação quando o webhook externo chega.
await skail.Fire("EVENTO_RETORNO", eventId: pedidoId, payload: resultado);

// 4) REVISÃO HUMANA — destrava o agente quando alguém aprova/corrige na interface.
await skail.Fire("EVENTO_REVISAO", eventId: pedidoId, payload: decisao);
```

Com isso o ciclo se fecha: um gatilho inicia o agente, ele conversa e age usando LLM e ferramentas, e os eventos o reativam exatamente de onde parou — sem você gerenciar estado, filas ou threads em espera.

***

### Referência rápida

| Quero…                                    | Use                                                  |
| ----------------------------------------- | ---------------------------------------------------- |
| Definir um agente / sub-agente durável    | `[SkailFunction]`                                    |
| Isolar um I/O ou chamada de LLM retomável | `[SkailCommand]`                                     |
| Esperar por um sinal externo              | `WaitForEvent<T>(nome, id)`                          |
| Timeout / polling / backoff               | `DelayMinutes(n)` / `DelaySeconds(n)`                |
| "Evento ou timeout"                       | `WhenAny(evento, timeout)`                           |
| Iniciar uma instância de agente           | disparar a função durável (trigger)                  |
| Desbloquear um `WaitForEvent`             | disparar o evento (fire) com a chave de correlação   |
| Entender o que o usuário quer             | classificação de intenção com saída estruturada      |
| Escolher o fluxo certo                    | roteamento por intenção para um agente especializado |
| Reunir dados sem interrogar o usuário     | ferramentas (tool use) que autocompletam             |
| Aprovação ou correção humana              | `WaitForEvent` (human-in-the-loop)                   |


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.skailhq.com/quickstart/agentes-de-ia/construindo-agentes-de-ia-com-skail.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
