Pular para o conteúdo
Construindo Agentes de IA em Tempo Real com Astro e Cloudflare
Inteligência Artificial Última atualização em 🎯 Intermediate Astro 6.x, Cloudflare Workers AI Verificado

Construindo Agentes de IA em Tempo Real com Astro e Cloudflare

Aprenda a arquitetar um agente de IA focado em performance usando Astro Actions e a rede global da Cloudflare.


Principais pontos
  1. Astro Actions fornecem uma interface type-safe para invocar LLMs a partir do cliente.
  2. Cloudflare Workers AI permite executar modelos como Llama 3 no edge com latência mínima.
  3. Streaming de respostas via ReadableStream é essencial para uma UX de IA responsiva.
  4. O uso de bancos de dados edge como Cloudflare D1 permite o gerenciamento de estado em tempo real para execução de agentes multi-etapa.

Direto ao Ponto (BLUF): Construir agentes de IA em tempo real exige mover a inferência para o edge. Combinando Astro Actions para lógica de servidor type-safe com Cloudflare Workers AI, você pode implantar experiências de IA com streaming e baixa latência que escalam globalmente sem a sobrecarga de gerenciamento de servidores tradicionais.

O que é Inferência de IA no Edge?

A Inferência de IA no Edge é o processo de execução de modelos de aprendizado de máquina (como LLMs, motores de embedding ou geradores de imagem) em servidores físicos situados próximos ao usuário final, em vez de rotear o tráfego para um centro de dados centralizado (ex: US-East-1). Executar a inferência de modelos na rede global da Cloudflare (que abrange mais de 300 cidades) minimiza o tempo de ida e volta (RTT), resultando em uma experiência de usuário altamente responsiva.


Por que o Edge é o Futuro dos Agentes de IA

Arquiteturas de IA tradicionais geralmente envolvem o envio de dados para um servidor centralizado, que então chama um provedor de LLM (como a OpenAI), aguarda uma resposta e a envia de volta. Esse modelo “hub-and-spoke” introduz uma latência significativa.

main
src/ index.text
--:--
PADRÃO CENTRALIZADO:
Usuário ---> Servidor da Aplicação (US-East) ---> API da OpenAI (West) ---> Servidor App ---> Usuário
RTT Total: 800ms - 1500ms

PADRÃO DE INFRAESTRUTURA EDGE:
Usuário ---> Servidor Cloudflare Edge (Data Center mais próximo) ---> Workers AI (GPU no mesmo Data Center) ---> Usuário
RTT Total: 150ms - 300ms

Implantar na Cloudflare permite que a lógica do seu “agente” seja executada no data center mais próximo do seu usuário. Quando combinado com a arquitetura focada em performance do Astro, você elimina totalmente o servidor intermediário. Essa mudança é crítica porque a importância dos agentes de IA em 2026 depende fortemente de sua capacidade de executar loops sequenciais de chamada de ferramentas sem causar atrasos frustrantes para o usuário.

Comparação de Métricas de Latência

| Modelo | Provedor | Localização | TTFT (Média) | Tokens/Seg (Média) | | :----------------------- | :-------------------- | :-------------- | :----------- | :----------------- | | Llama-3-8B-Instruct | Cloudflare Workers AI | Edge (Qualquer) | 180ms | 45 | | GPT-4o-mini | OpenAI API | US-West | 620ms | 80 | | Llama-3-70B-Instruct | Nuvem Centralizada | US-East | 890ms | 25 |


Configurando o Ambiente

Para seguir este tutorial, você precisa de um conhecimento básico de TypeScript e uma conta de desenvolvedor ativa na Cloudflare.

Pré-requisitos

  • Node.js versão 20 ou superior.
  • pnpm ou npm instalado.
  • Uma conta Cloudflare com faturamento ativado (o Workers AI inclui um nível gratuito generoso de 10.000 tokens gratuitos por dia).

Instalação e Dependências

Crie um novo projeto Astro e adicione o adaptador Cloudflare usando o auxiliar de linha de comando:

main
src/ setup-astro-ai.sh
--:--

npx astro add cloudflare

Este comando instala automaticamente o pacote do adaptador @astrojs/cloudflare, configura o Astro para rodar no modo SSR (Server-Side Rendering) e atualiza as configurações do seu astro.config.mjs.

Em seguida, abra o diretório do seu projeto e configure seus bindings da Cloudflare. Certifique-se de que seu arquivo wrangler.jsonc (ou wrangler.toml) na raiz do projeto contenha a definição do binding de AI:

main
src/ index.json
--:--
{
  "name": "astro-ai-agent",
  "compatibility_date": "2026-06-01",
  "main": "dist/server/entry.mjs",
  "ai": {
    "binding": "AI"
  }
}

Este binding injeta o SDK do Cloudflare Workers AI no runtime do Astro, expondo-o sob context.locals.runtime.env.AI.


Implementando a Action de IA

Astro Actions nos permitem definir lógica de servidor que nosso frontend pode chamar como uma função regular. Veja como definimos uma action de inferência de IA:

main
src/ index.typescript
--:--
// src/actions/index.ts
import { defineAction } from "astro:actions";
import { z } from "astro:schema";

export const server = {
  askAgent: defineAction({
    input: z.object({
      prompt: z.string().min(1, "O prompt não pode estar vazio"),
      systemPrompt: z.string().optional(),
    }),
    handler: async ({ prompt, systemPrompt }, context) => {
      // 1. Localizar o binding do Cloudflare Workers AI
      const env = context.locals.runtime?.env;
      if (!env || !env.AI) {
        throw new Error(
          "O binding do Cloudflare Workers AI não foi encontrado.",
        );
      }

      const ai = env.AI;

      // 2. Configurar limites de segurança e padrões do modelo
      const systemMessage = systemPrompt || "Você é um assistente útil.";

      try {
        // 3. Invocar a execução do modelo em GPUs próximas ao usuário
        const response = await ai.run("@cf/meta/llama-3-8b-instruct", {
          messages: [
            { role: "system", content: systemMessage },
            { role: "user", content: prompt },
          ],
          stream: true,
        });

        // 4. Retornar o ReadableStream bruto diretamente para o cliente
        return response;
      } catch (error) {
        console.error("Erro de inferência no Workers AI:", error);
        throw new Error(
          "Falha ao processar a requisição através do modelo de IA no edge.",
        );
      }
    },
  }),
};

Streaming da Resposta para a UI

Um dos maiores erros na UX de IA é fazer o usuário esperar pela resposta completa. Streaming é inegociável para agentes.

Ao usar a opção stream: true no Cloudflare Workers AI, a resposta é retornada como um ReadableStream. No seu componente Astro, você pode consumir esse stream e atualizar seu estado em tempo real. Veja uma implementação prática mostrando como vincular a action a uma interface React ou Vanilla JS:

main
src/ index.html
--:--
<!-- src/components/AgentChat.astro -->
<div class="chat-container mx-auto max-w-xl rounded-lg border bg-black/20 p-4">
  <div id="chat-output" class="mb-4 h-64 overflow-y-auto border-b p-2"></div>
  <form id="chat-form" class="flex gap-2">
    <input
      type="text"
      id="chat-input"
      placeholder="Pergunte ao seu agente..."
      class="flex-1 rounded-full border border-zinc-700 bg-zinc-800 px-4 py-2 text-white"
    />
    <button
      type="submit"
      class="rounded-full bg-purple-600 px-6 py-2 hover:bg-purple-700"
    >
      Enviar
    </button>
  </form>
</div>

<script>
  import { actions } from "astro:actions";

  const form = document.getElementById("chat-form") as HTMLFormElement;
  const input = document.getElementById("chat-input") as HTMLInputElement;
  const output = document.getElementById("chat-output") as HTMLDivElement;

  form?.addEventListener("submit", async (e) => {
    e.preventDefault();
    if (!input || !output || !input.value.trim()) return;

    const userPrompt = input.value;
    input.value = "";

    // Adicionar entrada do usuário à UI
    output.innerHTML += `<div class="user-message mb-2 text-right"><strong>Você:</strong> ${userPrompt}</div>`;

    // Adicionar placeholder para a resposta em streaming da IA
    const aiMessageContainer = document.createElement("div");
    aiMessageContainer.className = "ai-message mb-2 text-left";
    aiMessageContainer.innerHTML = `<strong>Agente:</strong> <span class="content">Pensando...</span>`;
    output.appendChild(aiMessageContainer);
    const contentSpan = aiMessageContainer.querySelector(".content") as HTMLSpanElement;

    try {
      // Invocar a action do Astro
      const { data, error } = await actions.askAgent({ prompt: userPrompt });
      if (error || !data) {
        contentSpan.innerText = "Erro: " + (error?.message || "Falha na inferência");
        return;
      }

      // Ler o stream de resposta
      const reader = (data as ReadableStream).getReader();
      const decoder = new TextDecoder();
      contentSpan.innerText = ""; // Limpar placeholder "Pensando..."

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        const chunkText = decoder.decode(value, { stream: true });
        contentSpan.innerText += chunkText;
        output.scrollTop = output.scrollHeight;
      }
    } catch (err) {
      console.error(err);
      contentSpan.innerText = "Falha ao se comunicar com o agente.";
    }
  });
</script>

Dica Pro: Use um componente de “Resumo” ou um bloco de “Principais Conclusões” no início de suas respostas longas de IA para melhorar a visibilidade de AEO para motores de resposta.


Persistência de Estado no Edge com Cloudflare D1

Em um fluxo de trabalho agêntico real, manter o estado e o histórico da sessão é crítico. Um agente sem estado não pode realizar raciocínios de múltiplos turnos ou lembrar as preferências do usuário. No entanto, conexões de banco de dados tradicionais (como pools PostgreSQL ou MySQL baseados em TCP) introduzem sobrecarga de conexão e latência significativas quando inicializadas a partir de workers edge de curta duração.

É aqui que o Cloudflare D1 — o banco de dados SQLite serverless da Cloudflare, construído diretamente no runtime do Workers — torna-se essencial. Armazenando o histórico do chat próximo ao ambiente de computação, podemos buscar e anexar contexto em milissegundos de um único dígito.

Fluxo de Dados para Agentes Edge com Estado

Aqui está a arquitetura de um agente edge com estado utilizando D1 para histórico persistente e Workers AI para inferência localizada:

main
src/ index.text
--:--
+--------+            +----------------------------+            +-------------+
|        |            |       Servidor Astro       |            |             |
|        | --(POST)-->| (Astro Action: askAgent)   |            |             |
|        |            |  +----------------------+  |            |             |
|        |            |  | 1. Consultar Hist D1 |<------------->| Cloudflare  |
| Cliente|            |  +----------------------+  |            | Banco D1    |
| (UI)   |            |  +----------------------+  |            |             |
|        |            |  | 2. Buscar Cache KV   |<------------->| Cloudflare  |
|        |            |  +----------------------+  |            | KV Store    |
|        |            |  +----------------------+  |            |             |
|        |            |  | 3. Executar Inferên. |<------------->| Workers AI  |
|        |<-(Stream)--|  +----------------------+  |            | Nodo GPU    |
|        |            |  +----------------------+  |            |             |
|        |            |  | 4. Salvar no D1 / KV |  |            |             |
+--------+            +----------------------------+            +-------------+

1. Configuração do Esquema do Banco de Dados

Primeiro, defina o esquema SQL para o histórico do seu chat. Crie um arquivo schema.sql no seu projeto:

main
src/ index.sql
--:--
-- db/schema.sql
CREATE TABLE IF NOT EXISTS chat_history (
  id TEXT PRIMARY KEY,
  session_id TEXT NOT NULL,
  role TEXT CHECK(role IN ('system', 'user', 'assistant')) NOT NULL,
  content TEXT NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_sessions ON chat_history(session_id);

Inicialize seu banco de dados D1 local e globalmente usando a CLI do Wrangler:

main
src/ setup-d1.sh
--:--

npx wrangler d1 create astro-agent-db

Adicione o binding ao seu arquivo wrangler.jsonc:

main
src/ index.json
--:--
{
  "name": "astro-ai-agent",
  "compatibility_date": "2026-06-01",
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "astro-agent-db",
      "database_id": "SEU_DATABASE_UUID_AQUI"
    }
  ]
}

2. Implementação da Action Astro com Estado

Podemos agora modificar nossa Action Astro para buscar mensagens anteriores do D1, injetá-las como contexto de conversação para o LLM e armazenar a consulta do usuário e a resposta do LLM.

Aqui está a implementação completa de uma action nativa de edge com estado:

main
src/ index.typescript
--:--
// src/actions/index.ts (Estendido com persistência D1)
import { defineAction } from "astro:actions";
import { z } from "astro:schema";

export const server = {
  askStatefulAgent: defineAction({
    input: z.object({
      sessionId: z.string().uuid("Identificador de sessão inválido"),
      prompt: z.string().min(1, "O prompt não pode estar vazio"),
    }),
    handler: async ({ sessionId, prompt }, context) => {
      const env = context.locals.runtime?.env;
      if (!env || !env.AI || !env.DB) {
        throw new Error(
          "Bindings do runtime Cloudflare ausentes (AI ou Banco D1).",
        );
      }

      const ai = env.AI;
      const db = env.DB;

      try {
        // 1. Buscar as últimas 10 mensagens do D1 para evitar inchaço da janela de contexto
        const { results } = await db
          .prepare(
            "SELECT role, content FROM chat_history WHERE session_id = ? ORDER BY created_at ASC LIMIT 10",
          )
          .bind(sessionId)
          .all<{ role: string; content: string }>();

        // 2. Formatar o histórico para o motor Llama 3
        const conversationHistory = results.map((row) => ({
          role: row.role as "system" | "user" | "assistant",
          content: row.content,
        }));

        // 3. Anexar a consulta atual do usuário
        const currentMessages = [
          {
            role: "system" as const,
            content: "Você é um agente de IA com estado rodando no edge.",
          },
          ...conversationHistory,
          { role: "user" as const, content: prompt },
        ];

        // 4. Salvar o prompt do usuário imediatamente no D1
        const userMessageId = crypto.randomUUID();
        await db
          .prepare(
            "INSERT INTO chat_history (id, session_id, role, content) VALUES (?, ?, 'user', ?)",
          )
          .bind(userMessageId, sessionId, prompt)
          .run();

        // 5. Consultar o Workers AI com stream ativado
        const responseStream = (await ai.run("@cf/meta/llama-3-8b-instruct", {
          messages: currentMessages,
          stream: true,
        })) as ReadableStream;

        // 6. Configurar um stream duplo: transmitir de volta ao usuário enquanto acumula a resposta completa para o D1
        const [clientStream, serverStream] = responseStream.tee();

        // Escrevemos no D1 de forma assíncrona sem bloquear o stream do cliente
        (async () => {
          const reader = serverStream.getReader();
          const decoder = new TextDecoder();
          let completeResponse = "";

          while (true) {
            const { done, value } = await reader.read();
            if (done) break;
            completeResponse += decoder.decode(value, { stream: true });
          }

          const assistantMessageId = crypto.randomUUID();
          await db
            .prepare(
              "INSERT INTO chat_history (id, session_id, role, content) VALUES (?, ?, 'assistant', ?)",
            )
            .bind(assistantMessageId, sessionId, completeResponse)
            .run();
        })().catch((err) =>
          console.error("Falha ao persistir a resposta do assistente:", err),
        );

        return clientStream;
      } catch (error) {
        console.error("Falha na execução com estado no edge:", error);
        throw new Error(
          "Incapaz de completar o processamento do agente com estado.",
        );
      }
    },
  }),
};

Fazendo Cache de Respostas do Agente com Cloudflare KV

Para reduzir os custos de computação e otimizar a latência para entradas repetitivas (ex: perguntas padrão como “Quais são seus horários?”), você pode introduzir uma camada de cache usando o Cloudflare KV (armazenamento Chave-Valor).

Ao fazer o hash do prompt (ou contexto da sessão), você pode verificar o KV primeiro. Se existir uma resposta em cache, você ignora totalmente a inferência do Workers AI:

main
src/ index.typescript
--:--
// Snippet de demonstração de cache
const promptHash = await crypto.subtle.digest(
  "SHA-256",
  new TextEncoder().encode(prompt),
);
const cacheKey = `agent-cache:${Array.from(new Uint8Array(promptHash))
  .map((b) => b.toString(16).padStart(2, "0"))
  .join("")}`;

// Verificar KV
const cachedResponse = await env.KV_STORE.get(cacheKey);
if (cachedResponse) {
  // Transmitir diretamente o valor do cache ou retornar imediatamente
  return new ReadableStream({
    start(controller) {
      controller.enqueue(new TextEncoder().encode(cachedResponse));
      controller.close();
    },
  });
}

Este padrão híbrido (D1 para memória, KV para cache de alta velocidade) resulta em arquiteturas altamente eficientes e de baixa sobrecarga, adequadas para implantações em escala empresarial.


Estimativas de Custo

Executar a inferência no edge é significativamente mais barato do que chamar provedores de LLM baseados em API. O Cloudflare Workers AI oferece preços competitivos baseados no tamanho do modelo e nos recursos de computação necessários.

| Tamanho do Modelo | Métrica | Custo (Nível Gratuito Incluído) | Custo Est. por 1M Tokens | | :----------------------------------- | :----------------------------- | :------------------------------ | :----------------------- | | 8B Parâmetros (ex: Llama 3) | Por 1.000 tokens entrada/saída | $0.000077 | $0.077 | | 70B Parâmetros (ex: Llama 3 70B) | Por 1.000 tokens entrada/saída | $0.000343 | $0.343 | | GPT-4o-mini | Por 1M entrada / saída | Preço padrão da OpenAI | $0.150 / $0.600 |


Conclusão e Próximos Passos

Combinar Astro e Cloudflare não é apenas sobre velocidade; é sobre simplicidade. Você tem a segurança de tipos do TypeScript, o poder do Llama 3 da Cloudflare e a experiência de desenvolvedor do Astro.

Para nosso próximo post, exploraremos como adicionar memória aos agentes usando o banco de dados Cloudflare D1 e Vectorize, permitindo estados de sessão persistentes no edge.

Fontes e Leitura Adicional

Henrique Bonfim

Autor

Henrique Bonfim

Senior Software Engineer

Senior Software Engineer with extensive experience building production web systems. Creator of ZettaBytes. Contributor to open-source Astro tooling. Specializes in edge-first architectures, AI integration, and web performance.

Artigos relacionados