O blog da AWS

Agentes de IA com streaming de resposta no AgentCore harness e API Gateway

Por Rafael Werneck, Arquiteto de Prototipação Sênior na AWS Brasil e Rafael Ribeiro Martins, Gerente de Programas Sênior na AWS Brasil.

Construir um agente de IA vai muito além de chamar um modelo. É preciso desenvolver a orquestração: o loop que chama o modelo, seleciona ferramentas, gerencia o contexto da conversa e trata falhas. Também é preciso operar a infraestrutura que roda tudo isso.

Com o agente pronto, o passo seguinte é disponibilizá-lo aos usuários, normalmente por meio de uma API. E aqui surge outro ponto, o agente pode levar alguns segundos para produzir a resposta completa. Fazer o usuário esperar todo esse tempo olhando para uma tela parada compromete a experiência. Melhor exibir a resposta aos poucos, conforme ela é gerada.

Duas peças resolvem esses desafios. O AgentCore harness, uma funcionalidade do Amazon Bedrock, transforma a construção do agente em configuração: você declara o que o agente faz, e o serviço cuida da orquestração e da infraestrutura. Já o streaming de resposta em APIs REST do Amazon API Gateway entrega a resposta a quem chamou assim que ela começa a ser produzida.

Nesta publicação, você vai juntar as duas peças para construir um agente de IA e disponibilizá-lo por uma API. Em vez de deixar o usuário esperando, o agente começa a responder de imediato, com o texto aparecendo à medida que é gerado. O único código do backend é uma pequena função AWS Lambda que conecta as duas pontas.

Serviços utilizados

A solução combina os seguintes serviços:

  • Amazon Bedrock: com o AgentCore harness, uma das funcionalidades do serviço, você cria um agente gerenciado por configuração. Nesta publicação, o modelo de IA usado pelo agente também é provido pelo Amazon Bedrock.
  • Amazon API Gateway: usado para expor o agente em uma API REST com streaming de resposta, que encaminha os tokens à aplicação cliente conforme ficam disponíveis.
  • AWS Lambda: executa o código Node.js que conecta a API ao agente e repassa os tokens gerados por ele.

Como funciona

Para tornar o exemplo concreto, imagine um chat de perguntas e respostas. O usuário digita uma pergunta em linguagem natural e um agente responde com base em um conteúdo curado, como um catálogo de produtos ou uma base de conhecimento da empresa. É um caso propositalmente simples. A mesma solução atende agentes mais sofisticados, com tools e múltiplos passos.

Nesse cenário, o fluxo de uma pergunta é o seguinte:

  1. A aplicação cliente chama a API (POST /ask) enviando a pergunta e um identificador de sessão.
  2. O API Gateway invoca a função Lambda por meio de uma integração proxy com streaming.
  3. A função invoca o agente (InvokeHarness) no AgentCore harness, que executa o modelo de IA no Amazon Bedrock e devolve um stream de tokens.
  4. A função escreve cada token no stream de resposta, e o API Gateway os repassa à aplicação cliente conforme chegam.

O diagrama a seguir mostra a arquitetura da solução.

Diagrama da arquitetura: a aplicação cliente chama uma API REST no Amazon API Gateway, que invoca uma função AWS Lambda com streaming de resposta; a função invoca o Amazon Bedrock AgentCore harness, que executa um modelo de IA hospedado no Amazon Bedrock e devolve os tokens em stream

Nas próximas seções, você monta a solução em quatro passos, em ordem de execução. Primeiro você cria o agente, depois a função que o invoca, em seguida a API que expõe tudo e, por fim, a aplicação cliente que consome o stream.

Pré-requisitos

Antes de começar, você vai precisar do seguinte:

  • Uma conta AWS com permissões para criar recursos do Amazon Bedrock, do AWS Lambda e do Amazon API Gateway.
  • Acesso ao modelo de IA no Amazon Bedrock. Habilite o modelo desejado na sua conta seguindo as instruções em Solicite acesso aos modelos.
  • A AgentCore CLI, caso você opte por criar o agente pela linha de comando.

Passo 1: crie o agente com o AgentCore harness

Com o AgentCore harness, a construção do agente vira configuração. Você declara o que o agente faz (modelo, instruções e ferramentas), e o serviço cuida do ambiente, da computação, da memória, da identidade e da observabilidade que transformam essa configuração em um agente em execução. Trocar o modelo ou adicionar uma ferramenta é uma mudança de configuração, não uma reescrita do agente.

Você pode criar um harness com a AgentCore CLI. O passo a passo completo está em Conceitos básicos, na documentação do AgentCore:

# Instale a CLI e crie um projeto
npm install -g @aws/agentcore
agentcore create
 
# Adicione um harness ao projeto
agentcore add harness \
  --name assistant \
  --system-prompt "Você é um assistente que responde perguntas sobre o catálogo..."
 
# Implante na sua conta AWS
agentcore deploy

O prompt de sistema define como o agente responde e o conteúdo a que ele tem acesso. Para o nosso chat de perguntas e respostas, bastam instruções mais o conteúdo curado (o catálogo de produtos). Em um agente mais elaborado, esse prompt cresce ou dá lugar a ferramentas e recuperação de conteúdo sob demanda, sem mudar nada no restante da solução.

Como nenhum modelo foi especificado, o harness usa, no momento desta publicação, o modelo Claude Sonnet 4.6 da Anthropic no Amazon Bedrock. Você pode escolher outro modelo na configuração, e até trocá-lo no meio de uma sessão sem perder o contexto da conversa.

A implantação cria o harness na sua conta e informa o ARN dele. Anote esse ARN: a função Lambda vai usá-lo no próximo passo.

Vale saber que o harness é a opção gerenciada e de low-code do AgentCore. Quando você precisa de controle total sobre o loop, uma alternativa possível é o AgentCore Runtime, que hospeda o seu próprio código de agente. É o caso de um framework específico ou de uma orquestração customizada. Para uma comparação entre os dois, consulte AgentCore harness vs. Runtime.

Passo 2: repasse os tokens com streaming de resposta no Lambda

A função Lambda é a ponte entre a API e o agente, e é o único código do backend. Em funções Node.js, o streaming de resposta é nativo: em vez de retornar um objeto de resposta, a função é envolvida por awslambda.streamifyResponse e recebe um responseStream no qual escreve o corpo da resposta aos poucos. A função a seguir invoca o agente e repassa cada token assim que ele chega:

import {
  BedrockAgentCoreClient,
  InvokeHarnessCommand,
} from "@aws-sdk/client-bedrock-agentcore";
 
const client = new BedrockAgentCoreClient({});
 
export const handler = awslambda.streamifyResponse(async (event, responseStream) => {
  // A integração proxy com streaming espera os metadados da resposta em JSON,
  // seguidos de um delimitador de 8 bytes nulos, antes do corpo.
  responseStream.write(JSON.stringify({ statusCode: 200 }));
  responseStream.write("\x00".repeat(8));
 
  try {
    const { question, sessionId } = JSON.parse(event.body || "{}");
 
    const response = await client.send(
      new InvokeHarnessCommand({
        harnessArn: process.env.HARNESS_ARN,
        runtimeSessionId: sessionId,
        messages: [{ role: "user", content: [{ text: question }] }],
      }),
    );
 
    for await (const chunk of response.stream) {
      const text = chunk?.contentBlockDelta?.delta?.text;
      if (text) responseStream.write(text);
    }
  } finally {
    responseStream.end();
  }
});

Repare no que a função não faz: ela não carrega o prompt de sistema, não monta o histórico da conversa, não chama o modelo e não gerencia o loop do agente. Tudo isso fica com o harness. A função recebe o ARN do harness, o ID da sessão e a pergunta, e devolve os tokens. Dois detalhes valem atenção:

  • O preâmbulo de metadados. Em uma integração proxy com streaming, a função escreve primeiro os metadados da resposta (código de status e cabeçalhos) em JSON, seguidos de um delimitador de 8 bytes nulos, e só então o corpo. Sem esse preâmbulo, o API Gateway não sabe como montar a resposta HTTP.
  • A escolha da linguagem. O streamifyResponse existe apenas no runtime Node.js do Lambda. Outras linguagens, incluindo Python, exigem o Lambda Web Adapter ou um runtime customizado para fazer streaming. Quando o streaming é central para a experiência, o Node.js mantém a função enxuta e sem dependências extras.

O runtimeSessionId repassado ao agente é o que preserva o contexto da conversa. Reutilize o mesmo identificador entre as perguntas de uma sessão e o harness carrega o histórico sozinho; a função envia apenas a mensagem nova de cada turno. Para mais detalhes, consulte a documentação da Memória.

Passo 3: habilite o streaming de resposta no API Gateway

Com o agente criado e a função pronta, ainda é preciso definir a API que a aplicação cliente vai chamar. Há algumas formas de implementar streaming nessa camada. Uma opção é adotar uma API WebSocket, que muda o modelo de programação dos dois lados. Outra é expor a função diretamente à aplicação cliente por meio de URLs de função do Lambda, abrindo mão de recursos do API Gateway como autorizadores e controle de utilização. Em 19 de novembro de 2025, as APIs REST do API Gateway ganharam suporte nativo a streaming de resposta, e nenhum contorno é mais necessário. Veja o anúncio em Amazon API Gateway agora oferece suporte ao streaming de respostas para APIs REST.

Para habilitar o streaming de resposta em uma API REST, atualize a configuração da integração definindo o modo de transferência de resposta como STREAM. Você pode fazer isso pelo console, pela AWS Command Line Interface (AWS CLI) ou por infraestrutura como código. Em AWS CloudFormation, o método fica assim:

AskMethod:
  Type: AWS::ApiGateway::Method
  Properties:
    RestApiId: !Ref Api
    ResourceId: !Ref AskResource
    HttpMethod: POST
    AuthorizationType: NONE | AWS_IAM | CUSTOM | COGNITO_USER_POOLS  # selecione um
    Integration:
      Type: AWS_PROXY
      IntegrationHttpMethod: POST
      ResponseTransferMode: STREAM
      Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2021-11-15/functions/${ChatFunction.Arn}/response-streaming-invocations"

Em AuthorizationType, substitua pelo método de autorização da sua escolha. Um exemplo comum é COGNITO_USER_POOLS, com um user pool do Amazon Cognito validando o token enviado no cabeçalho Authorization. Nesse caso, informe também o identificador do autorizador na propriedade AuthorizerId. A autenticação não muda nada no padrão de streaming.

Repare também na URI da integração: ela usa o caminho response-streaming-invocations, e não o caminho de invocação tradicional. É a combinação dos dois (o modo de transferência e o endpoint de streaming) que faz os tokens fluírem até a aplicação cliente sem buffer. Os detalhes e as outras formas de configuração estão no post Construindo APIs responsivas com streaming de resposta do Amazon API Gateway.

Passo 4: consuma o stream na aplicação cliente

Na aplicação cliente, não é necessária nenhuma biblioteca nem protocolo especial. A resposta é um corpo HTTP comum que chega aos poucos. Clientes HTTP capazes de ler o corpo incrementalmente conseguem consumi-la: uma aplicação web, um app móvel ou até um script de linha de comando. Em uma aplicação web, por exemplo, a Streams API do próprio navegador é suficiente:

const res = await fetch(apiEndpoint, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ question, sessionId }),
});
 
const reader = res.body.getReader();
const decoder = new TextDecoder();
for (;;) {
  const { value, done } = await reader.read();
  if (done) break;
  appendToAnswer(decoder.decode(value, { stream: true }));
}

A opção { stream: true } do TextDecoder importa: um caractere multibyte (como um acento, comum em português) pode ser dividido entre dois fragmentos, e é ela que faz o decodificador guardar os bytes incompletos até o fragmento seguinte chegar.

Se você protegeu a API com um autorizador no passo anterior, a aplicação cliente também precisa se autenticar e enviar o token no cabeçalho Authorization da chamada. Para um user pool do Amazon Cognito, o AWS Amplify ajuda nessa parte: ele cuida do fluxo de login e da obtenção dos tokens na aplicação web ou móvel.

Limpeza

Para não gerar custos indesejados, remova os recursos criados quando terminar os testes. Se você definiu a função Lambda e a API por CloudFormation, exclua a pilha:

aws cloudformation delete-stack --stack-name <nome-da-stack>

Para o agente criado com a AgentCore CLI, remova os recursos do projeto e aplique a remoção com uma nova implantação:

agentcore remove all -y
agentcore deploy

Recursos criados manualmente (pelo console, por exemplo) devem ser excluídos da mesma forma: a função no console do Lambda e a API no console do API Gateway.

O caso de uso que motivou esta publicação

A solução apresentada neste post nasceu de um caso de uso real: a Marcopolo, multinacional brasileira fabricante de carrocerias de ônibus, desenvolveu um protótipo em conjunto com o time de prototipação da AWS chamado Prototyping and AI Customer Engineering (PACE).

O protótipo é um assistente para o time de vendas. O vendedor pergunta em linguagem natural sobre detalhes dos produtos e recebe respostas fundamentadas no material oficial de vendas, exibidas em streaming conforme são geradas. Com o assistente, a resposta aparece em segundos. Antes, encontrar a mesma informação na documentação podia levar minutos.

O projeto reflete o compromisso contínuo da Marcopolo com a inovação e o investimento em novas tecnologias, como a IA generativa.

A arquitetura enxuta fez diferença. Com o agente fornecido como configuração pelo AgentCore harness, o backend se resume a uma pequena função Lambda e uma API. É pouco código próprio para escrever, testar e manter. A arquitetura também facilita a evolução, pois ajustar o comportamento do assistente é editar o prompt, e ganhar novas capacidades (ferramentas, novas fontes de conteúdo) não exige rearquitetar a solução.

Conclusão

Esta publicação começou com dois desafios: construir um agente de IA vai muito além de chamar um modelo, e fazer o usuário esperar a resposta completa compromete a experiência. Você viu como resolver os dois quase sem código. O AgentCore harness transforma a construção do agente em configuração, e o streaming de resposta nas APIs REST do API Gateway entrega cada token ao usuário assim que ele é gerado. O resultado é um agente que responde de imediato, sem deixar o usuário à espera.

O que foi apresentado também atende casos de uso mais complexos, como agentes com múltiplas ferramentas e fluxos de vários passos. E as peças são independentes: você pode adotá-las em conjunto ou aproveitar apenas a que resolve o seu problema. Se você está construindo um agente, ou já tem um que deixa o usuário esperando pela resposta, este é um bom ponto de partida.

Próximos passos

Autores

Rafael Werneck Rafael Werneck é um Arquiteto de Prototipação Sênior na AWS Brasil. Anteriormente, trabalhou como Software Development Engineer na Amazon.com.br e no Amazon RDS Performance Insights.
Rafael Ribeiro Martins Rafael Ribeiro Martins é um Gerente de Programas Sênior na AWS Brasil. Ele ajuda clientes a vislumbrar a arte do possível na AWS, trabalhando com eles em engajamentos inovadores de prototipação. Com mais de 10 anos de experiência em gerenciamento de projetos para programas técnicos, ele atualmente foca em gerenciamento de projetos relacionados a tecnologias emergentes.