Símbolo da AmivuSímbolo da AmivuDevelopers

Construir um agente

Receber, pensar e responder — sem dar ao agente mais do que ele precisa.

Um agente externo que recebe o que o cliente escreveu, decide o que responder e devolve pela Amivu. Tudo pela API — o modelo é seu, a conversa é da Amivu.

O fluxo

webhook message.received
        ↓
seu agente lê o histórico (opcional)     GET /v1/conversations/{id}/messages
        ↓
seu modelo decide
        ↓
POST /v1/messages                        ou nada, se for caso humano

Os escopos, e só eles

Crie uma chave dedicada ao agente, com o mínimo:

  • conversations:read — para ver o contexto.
  • messages:read — para ler o histórico.
  • messages:write — para responder.
  • notes:write — opcional, para registrar por que ele decidiu o que decidiu.
Não dê `contacts:write` a um agente
Um modelo que pode editar cadastro vai editar cadastro — com um nome mal interpretado, um telefone alucinado. O agente responde; quem muda dado é código determinístico.

O código

JavaScript
app.post("/webhooks/amivu", async (req, res) => {
  if (!assinaturaValida(req)) return res.status(401).end();
  res.status(200).end();

  const { type, data, id: eventoId } = req.body;
  if (type !== "message.received") return;

  // Entrega é pelo menos uma vez: sem isto, o agente responde duas vezes.
  if (await jaProcessado(eventoId)) return;

  await enfileirar(async () => {
    const historico = await amivu(
      `/v1/conversations/${data.conversation.id}/messages?limit=20`,
    );

    const resposta = await seuModelo({
      // Mande o MÍNIMO. O agente não precisa do telefone nem do CPF para
      // responder sobre um prazo de entrega.
      mensagens: historico.data.map((m) => ({
        papel: m.direction === "inbound" ? "cliente" : "atendimento",
        texto: m.text,
      })),
    });

    if (!resposta.deveResponder) {
      // Sem certeza, quem assume é gente. Uma nota diz por quê.
      await amivu(`/v1/conversations/${data.conversation.id}/notes`, {
        method: "POST",
        body: { body: `Agente não respondeu: ${resposta.motivo}` },
      });
      return;
    }

    await amivu("/v1/messages", {
      method: "POST",
      // Uma resposta por mensagem recebida — a chave é o id do evento.
      headers: { "Idempotency-Key": `agente-${eventoId}` },
      body: {
        conversation_id: data.conversation.id,
        type: "text",
        text: resposta.texto,
      },
    });
  });
});

Quatro cuidados

  1. Deduplique pelo id do evento. A entrega é pelo menos uma vez, e um agente que responde duas vezes é pior que um que não responde.
  2. Responda o webhook em milissegundos. Chamar o modelo dentro do ciclo da requisição estoura os 10 segundos e faz a Amivu reentregar — e o agente processa de novo.
  3. Não deixe o agente responder o próprio agente. Só message.received dispara o ciclo; message.sent, nunca.
  4. Tenha uma saída para gente. Fora do assunto, cliente irritado, dúvida jurídica: não responda e atribua a conversa a um time. POST /v1/conversations/{id}/assign resolve.
A Amivu já tem uma assistente
A Ami responde, sugere e aprende com o acervo da própria organização, sem integração nenhuma. Este guia é para quem quer o seu modelo, com o seu conhecimento — os dois caminhos coexistem.

Veja também Assinaturas.

Escrevemos sobre operação de atendimento, canais e IA no blog da Amivu.