Quem coloca um agente de IA em produção dentro de um CRM ou de um ERP descobre rápido que a maior parte do trabalho fica fora do modelo. O nome que o mercado deu para essa parte é harness: tudo que fica em volta da LLM e faz ela funcionar dentro de um sistema de verdade.

Este post explica o conceito peça por peça e depois mostra cada uma no código de um agente SDR de imobiliária, o mesmo de um vídeo anterior do canal. A ideia é que, entendendo o que é harness, colocar um agente em produção vire um projeto de dias em vez de meses.

A LLM sozinha só troca texto

Tudo começa pelo modelo. A LLM está cada vez melhor e cada vez mais commodity, mas sozinha ela faz uma coisa só: recebe texto, processa texto e devolve texto.

O modelo
A LLM recebe texto e devolve texto
Todo o resto que o agente faz depende do sistema em volta dela.
"Lead: quero um apê de 2 quartos na Vila Mariana até R$ 650 mil. Ferramentas disponíveis: obter_lead, buscar_imoveis…"
LLM
modelo
"Intenção: buscar_imoveis. Chamar buscar_imoveis com bairro=Vila Mariana, quartos=2, preco_max=650000."
Não executa
Ela escreve que quer chamar buscar_imoveis. Quem chama a API é o seu sistema.
Não lembra
Cada chamada começa do zero. O histórico da conversa vai junto no texto, toda vez.
Não acessa nada
Banco, CRM e agenda do corretor só chegam até ela se o sistema transformar em texto.
A LLM recebe um texto com a mensagem do lead e as ferramentas disponíveis e devolve um texto dizendo qual ferramenta quer chamar. Ela não executa, não lembra e não acessa nada.

Quando um lead escreve “quero um apê de 2 quartos na Vila Mariana até R$ 650 mil”, o modelo pode responder que a intenção é buscar imóveis e que quer chamar uma ferramenta de busca com esses filtros. Ele escreve isso. Quem chama a API de imóveis é o seu sistema. O modelo também não lembra da conversa anterior, porque cada chamada começa do zero, e não acessa banco, CRM ou agenda do corretor por conta própria.

É ali dentro que está a rede neural e todo o trabalho de deep learning. A LLM sozinha, porém, não é o seu agente nem o seu harness.

As peças do harness

Para usar o modelo num sistema de gestão, você precisa de oito peças em volta dele. Todas são código seu.

Harness
Harness é tudo que fica em volta do modelo
O modelo é uma peça. As outras oito são código do seu sistema.
1
Contexto
Prompt, instruções, dados do sistema, RAG e memória da conversa.
2
Ferramentas
APIs, consultas e comandos que o modelo pode pedir para executar.
3
Loop
Modelo decide, sistema executa, resultado volta. Repete até terminar.
4
Orquestração
Roteamento por intenção para fluxos e especialistas que você controla.
LLM
texto entra, texto sai
5
Estado durável
Se o pod cair ou a LLM ficar fora do ar, retoma de onde parou.
6
Guardrails
O que o modelo nunca pode fazer. Validação de entrada e de saída.
7
Observabilidade
Trace, custo e latência. O que decidiu, o que chamou, o que voltou.
8
Evals
Medir se a resposta melhorou ou piorou antes de chegar ao usuário.
O harness é formado por oito peças em volta da LLM: contexto, ferramentas, loop, orquestração, estado durável, guardrails, observabilidade e evals.

Contexto. São os prompts, as instruções, os dados do seu sistema e a memória da conversa. Muita gente chama a parte de dados de RAG. O harness junta tudo isso, transforma em texto e manda para a LLM.

Ferramentas. É a lista do que o modelo pode pedir para fazer: qual API chamar, qual tabela consultar, quais comandos estão disponíveis. O modelo recebe essa lista em texto e decide quando usar cada item.

Loop. Depois que o modelo decide, alguém precisa avançar. O harness tem um laço de execução: a LLM escolhe uma ferramenta e manda essa escolha para o sistema. O sistema executa, registra o que aconteceu e devolve o resultado. A LLM decide se já tem o suficiente ou se precisa de mais uma chamada.

Loop
A LLM decide, o harness executa
Cada volta do laço é uma chamada ao modelo e, se ele pedir, uma execução no seu sistema.
LLM decide
chamar obter_lead(4711)
Guardrail
tool permitida? args válidos?
Sistema executa
GET /api/leads/4711
Registra
trace, custo, latência
Resultado em texto
JSON do lead volta no contexto
LLM avalia
precisa de mais uma chamada?
↺ Volta ao começo
se o modelo pedir outra ferramenta
Responde ao lead
quando o modelo diz que terminou

No exemplo, o laço é um while (true) que só termina quando a política de encerramento manda sair.

Loop do harness: a LLM decide qual ferramenta chamar, o guardrail valida, o sistema executa, registra, devolve o resultado em texto e a LLM avalia se precisa de mais uma chamada ou se responde ao lead.

O modelo não executa nada nesse laço. Quem executa é o harness, que no caso é o seu próprio sistema.

Orquestração. Quem atende cada pedido e cada troca de mensagem com o usuário? Num CRM ou ERP, o ideal é você orquestrar com base no que a IA devolve, usando especialistas e roteamento. Não precisa confiar cegamente no modelo. Cada lado faz o que faz melhor: a LLM interpreta linguagem, e o seu código determinístico, que você já controla, executa as regras de negócio.

Estado durável. A conexão cai, o provedor da LLM fica indisponível, a rede falha, a máquina reinicia, o pod crasha. O harness precisa guardar o estado de cada atendimento e conseguir voltar de onde parou. A série sobre execução durável aqui no blog detalha esse assunto.

Guardrails. Definem o que a LLM nunca pode fazer e como o sistema valida o que entra e o que sai do modelo.

Observabilidade. Trace, custo e latência de cada chamada. O que a LLM decidiu, qual ferramenta chamou, o que voltou e por quê.

Evals. Como você sabe que uma mudança no prompt melhorou ou piorou o agente? Precisa medir antes de a ação ser executada ou de a resposta chegar ao usuário.

O harness no código: um agente SDR de imobiliária

O agente do exemplo atende leads de uma imobiliária pelo WhatsApp. Ele qualifica o lead, busca imóveis e agenda visitas com os corretores. O código dele é o harness: é ele que conversa com a IA. Os trechos abaixo são simplificados para mostrar cada peça.

[SkailFunction]
public async SkailTask Atender(string sessaoId, MensagemLead mensagem)
{
    var config = await ObterConfiguracoesDaSessao(sessaoId);   // regras da imobiliária, horários, limites
    var anexos = await ProcessarAnexos(mensagem.Anexos);       // PDF, áudio e foto viram texto
    while (true)
    {
        var intencao  = await DetectarIntencao(sessaoId, mensagem.Texto, anexos, config);
        var resultado = await ExecutarFluxo(sessaoId, intencao, config);
        var motivo = PoliticaDeEncerramento.Avaliar(intencao, resultado, config);
        if (motivo is not null)
        {
            await EncerrarSessao(sessaoId, motivo);
            break;
        }
        mensagem = await AguardarProximaMensagem(sessaoId);
    }
}

O atendimento começa pelas configurações da sessão. É um conjunto de regras que o agente precisa obedecer, porque no fim ele é um sistema: horário de atendimento dos corretores, regiões que a imobiliária atende, quantas mensagens sem resposta encerram a conversa. Isso é carregado de forma determinística, com código seu, sem passar pelo modelo.

Anexos viram texto antes de entrar no contexto

Com a mensagem do usuário em mãos, o harness processa os anexos. Aqui também entra IA, mas do jeito que a gente viu no conceito: o sistema transforma tudo em texto e manda esse texto para o modelo.

Contexto
Anexo só entra no contexto depois de virar texto
O lead manda arquivo pelo WhatsApp. O harness extrai o conteúdo antes de chamar o modelo do atendimento.
holerite.pdf
2 páginas
áudio de 48 s
"tenho FGTS pra entrada…"
foto da CNH
JPEG
Extração
OCR, transcrição ou modelo de visão
// texto que entra no contexto renda_mensal: R$ 14.200 entrada: FGTS, valor a confirmar documento: CNH válida

A extração também usa IA, mas o harness decide o formato do texto, o que fica de fora e o que vai para o modelo do atendimento.

O lead manda holerite em PDF, um áudio de 48 segundos e a foto da CNH. A extração transforma tudo em texto com renda, entrada e documento, que entra no contexto.

Um holerite em PDF passa por OCR, um áudio de 48 segundos dizendo “tenho FGTS pra entrada” passa por transcrição e a foto da CNH passa por um modelo de visão. O que chega ao modelo do atendimento são três linhas de texto com renda mensal, forma de entrada e documento. O harness decide o formato desse texto e o que fica de fora.

Até aqui, se o seu sistema já interage com IA, você provavelmente já tem um harness básico. Pegar um prompt, mandar para a LLM e usar o resultado para conversar com o usuário é harness. É um harness simples, com contexto e nada mais. As próximas peças são o que separa esse uso de um agente em produção.

A IA detecta a intenção, o código executa o fluxo

Dentro do loop, o harness manda para o modelo o texto do lead, as configurações e o contexto. O modelo devolve qual é a intenção daquela mensagem naquele contexto. Com a intenção em mãos, o harness executa um fluxo, que é código determinístico seu.

[SkailFunction]
public async SkailTask<ResultadoFluxo> ExecutarFluxo(string sessaoId, Intencao intencao, ConfigSessao config)
{
    return intencao.Chave switch
    {
        "qualificar_lead"       => await QualificarLead(sessaoId, intencao),
        "buscar_imoveis"        => await BuscarImoveis(sessaoId, intencao),
        "agendar_visita"        => await AgendarVisita(sessaoId, intencao, config),
        "encerrar_atendimento"  => await EncerrarAtendimento(sessaoId, intencao),
        _                       => await PedirEsclarecimento(sessaoId)
    };
}

Pegue o fluxo de encerrar atendimento como exemplo. Ele está ligado à intenção encerrar_atendimento. Quem decide que a intenção é essa é a IA, com base no contexto que recebeu. Quem decide o que fazer com essa intenção é o seu código.

Orquestração
A IA detecta a intenção, o código decide o que fazer
O modelo classifica a mensagem. O fluxo que roda depois é código determinístico do seu sistema.
"Pode marcar a visita do apê da Rua Domingos de Morais no sábado às 10h?"
LLM
intenção: agendar_visita
SEU CÓDIGO
ResolverFluxo
intenção → fluxo
AgendarVisita
agenda do corretor
Política
encerrar?
Continua
Resultado do fluxo volta para o modelo, que escreve a resposta ao lead. O loop segue.
Encerra
Intenção encerrar_atendimento e configuração da sessão permitem fechar: grava o motivo, encerra a sessão e sai do loop.
O lead pede para marcar visita no sábado às 10h. A LLM detecta a intenção agendar_visita, o código resolve o fluxo, agenda com o corretor e passa pela política de encerramento.

Depois do fluxo, entra a política de encerramento. Ela olha a intenção, o resultado do fluxo e a configuração carregada no início do atendimento. É lógica do sistema, sem modelo envolvido. Se a política entende que a sessão deve fechar, o harness grava o motivo, encerra a sessão e sai do loop. Se não, o resultado volta para o modelo escrever a resposta ao lead e o loop segue. O loop é um while (true): enquanto a política não manda sair, ele continua.

Esse é o ciclo completo do harness num exemplo concreto. O sistema trouxe informações do mundo externo para o modelo, o modelo decidiu a intenção, o código executou o que precisava e devolveu o resultado para o modelo.

Anatomia de uma tool

A outra forma de o modelo agir é chamando ferramentas diretamente. Uma tool no exemplo tem quatro partes:

  1. Nome, que o modelo usa para pedir a ferramenta.
  2. Descrição, um prompt curto que explica o que a ferramenta faz e quando usar.
  3. Contrato, com os parâmetros e os tipos que o modelo precisa preencher.
  4. Executar, o método que roda quando o modelo pede a ferramenta.
public sealed class ObterLeadTool(IApiInterna api) : ITool
{
    public string Nome => "obter_lead";
    public string Descricao => "Busca os dados atuais do lead: nome, telefone, faixa de preço, " +
                               "bairros de interesse e estágio da qualificação.";
    public string Contrato => """{ "type": "object", "properties": { "leadId": { "type": "integer" } }, "required": ["leadId"] }""";
    public Task<string> ExecutarAsync(JsonElement args, CancellationToken ct)
        => api.EnviarAsync(HttpMethod.Get, $"/api/leads/{args.GetProperty("leadId").GetInt32()}", ct);
}

As três primeiras partes vão para o modelo em forma de texto, porque é só isso que ele entende. Se o modelo decide usar a ferramenta, o harness chama o ExecutarAsync. Ele é uma chamada a uma API do próprio sistema, /api/leads, com os argumentos que o modelo pediu. O EnviarAsync concentra o tratamento de injeção, controle de acesso e segurança da chamada.

Ferramentas
Uma tool é nome, descrição, contrato e um executar
As três primeiras partes vão para o modelo em texto. A quarta é uma chamada à API do seu sistema.
// o que o modelo recebe { “name”: “atualizar_lead”, “description”: “Atualiza a qualificação do lead. Use obter_lead antes e preserve os dados existentes.”, “parameters”: { “leadId”: { “type”: “integer” }, “faixaPreco”: { “type”: “number” }, “bairros”: { “type”: “array” } } }
1 · Nome
Como o modelo se refere à ferramenta quando pede para usá-la.
2 · Descrição
Prompt de quando usar. Aqui ela manda chamar obter_lead primeiro.
3 · Contrato
Parâmetros e tipos que o modelo precisa preencher.
4 · Executar
PUT /api/leads/4711 com os argumentos que o modelo devolveu.
Anatomia da tool atualizar_lead: o JSON que o modelo recebe com nome, descrição e parâmetros, e o executar que faz PUT em /api/leads/4711.

A tool atualizar_lead mostra um detalhe útil. A descrição dela diz “Atualiza a qualificação do lead. Use obter_lead antes e preserve os dados existentes.” O próprio contrato instrui o modelo a chamar uma ferramenta antes da outra. É mais uma forma de montar o loop de ferramentas: o modelo decide, o sistema executa e devolve, o modelo decide de novo. A chamada pode ir para os endpoints do seu sistema, como no exemplo, ou para qualquer API REST externa.

Com isso o código já tem contexto, ferramentas, loop e orquestração. Faltam estado durável e observabilidade.

Estado durável e observabilidade com o skail

Este post sai no blog da skail, e o skail é a plataforma de execução durável que a gente constrói aqui. O agente do exemplo roda sobre ele, e é nele que estão as duas peças que faltam.

O estado durável está nos atributos que aparecem no código. O Atender e o ExecutarFluxo são [SkailFunction], os métodos que orquestram o atendimento. Cada chamada ao modelo e cada ferramenta fica num [SkailCommand], o método que faz uma operação de I/O:

[SkailCommand]
public async SkailTask<Intencao> DetectarIntencao(string sessaoId, string texto, Anexos anexos, ConfigSessao config)
    => await _llm.ClassificarAsync(texto, anexos, config.Intencoes);

Com esses atributos, a execução ganha estado durável sem código extra. Se o atendimento para no meio por um problema de infraestrutura, ele volta exatamente de onde parou. Se precisa esperar dias ou semanas por uma resposta do lead ou de um corretor, a execução hiberna e libera a thread e o host enquanto espera. Isso vale para o código rodando em contêiner, na nuvem ou no servidor onde o seu sistema já roda.

A observabilidade segue o mesmo caminho. Tudo que o agente faz, decide e cada passo por onde passa fica registrado e aparece no Monitor do skail, numa linha do tempo por execução.

Somando tudo, o harness do agente fica com execução durável, loop, execução de ferramentas, detecção de intenção e orquestração baseada nos fluxos. Tem código determinístico no atendimento e dentro dos fluxos, e as integrações com o sistema atual passam pelas interfaces que o sistema já usa.

Alternativas de stack

O código do exemplo é curto porque usa o skail, mas nada disso depende dele. Para chamar ferramentas e manter a conversa, você pode usar o function calling do SDK da OpenAI, o tool use da Anthropic ou o equivalente do Google. Para o estado durável, precisaria de uma plataforma de execução durável ao lado, como Azure Durable Functions ou Cloudflare Workflows.

Alternativas
Onde cada peça do harness pode morar
Dá para montar com peças separadas ou concentrar estado e observabilidade numa plataforma.
MONTANDO COM PEÇAS SEPARADAS

Tool calling: SDK da OpenAI (function calling), da Anthropic (tool use) ou do Google.

Estado durável: Azure Durable Functions, Cloudflare Workflows ou outra plataforma de execução durável.

Observabilidade: integrar traces e custos de cada chamada por conta própria.

COM SKAIL

[SkailFunction] no fluxo do agente e [SkailCommand] em cada chamada ao modelo e a cada tool.

Retomada, hibernação e linha do tempo no Monitor vêm junto.

O código roda no seu host, ao lado do CRM ou do ERP.

Onde cada peça pode morar: montando com SDKs de tool calling e uma plataforma de execução durável separada, ou com skail concentrando estado, hibernação e Monitor.

No exemplo, o skail entregou essas peças num lugar só, então não foi preciso combinar o SDK de um provedor com a plataforma de execução durável de outro.

Resumo para consulta

PeçaPergunta que ela respondeNo agente da imobiliária
ContextoO que o modelo precisa saber agora?Configurações da sessão, histórico e anexos em texto
FerramentasO que o modelo pode pedir para fazer?obter_lead, atualizar_lead, busca de imóveis
LoopComo o agente avança até terminar?while (true) até a política de encerramento
OrquestraçãoQuem decide o que fazer com a resposta do modelo?Fluxo por intenção em código determinístico
Estado durávelE se cair no meio?[SkailFunction] e [SkailCommand]
GuardrailsO que o modelo nunca pode fazer?Validação de entrada e de saída
ObservabilidadeO que aconteceu e quanto custou?Linha do tempo no Monitor
EvalsMudou para melhor ou para pior?Medição antes de a resposta chegar ao lead

Perguntas frequentes

Se eu só mando um prompt para a LLM e mostro a resposta, isso já é harness? É um harness básico, com contexto e nada mais. Ele funciona para uma pergunta e uma resposta. Para um agente que consulta o CRM, agenda visita e espera o lead responder, faltam ferramentas, loop, orquestração e estado durável.

Por que deixar a decisão do fluxo no código em vez de deixar o modelo decidir tudo? Porque num sistema de gestão as regras de negócio já existem e já funcionam. O modelo é bom para interpretar a mensagem do lead. O código é previsível, testável e fica sob o seu controle.

A ferramenta precisa chamar uma API do meu sistema? Pode chamar a API do seu sistema, um endpoint interno ou qualquer API REST externa. O que importa é a chamada passar pelo harness, onde ficam os guardrails e o registro.

Por onde começar

Olhe para o agente que você já tem, mesmo que seja um prompt e uma resposta. Liste as oito peças e marque quais existem hoje. Quase sempre o contexto está lá e o estado durável não. Comece pelo que mais dói em produção. Para ver o agente da imobiliária construído do zero, assista ao vídeo sobre criar um agente do zero no canal.