Entendendo a Biblioteca Azure AI Persistent Agents

Carlos MachelCarlos Machel
8 min read

Durante meus estudos para a AI-102 e a natureza do meu trabalho, eu trabalhei com a Assistant API da OpenAi e com os Agentes do Azure AI Foundry. Percebi que muita coisa tava sendo abstraída para fazer os meus agentes de uma maneira rápida e eficiente. A ideia da biblioteca Azure.AI.Agents.Persistent é parecido com a funcionalidade do Assistant Api, onde um é focado apenas na OpenAi e o outro é focado em vários modelos disponíveis no Azure AI Foundry.

Neste artigo, vou apresentar a biblioteca que facilita a criação de agentes inteligentes, abstraindo boa parte da lógica que outras frameworks exigem implementações manuais e explicar um pouco o que ela está abstraindo e dando alguns conceitos que vamos explorar em outros artigos usando Semantic Kernel.

Qual?? A que está no título obviamente. Estou falando do Persistent Agent, uma biblioteca da Microsoft que integra com modelos LLMs do Azure AI Foundry. Um diferencial importante: alguns desses modelos suportam memória de longo prazo de conversas, por meio do conceito de Threads e é ai que o negócio fica interessante.

Mas o que é uma Thread?

Uma Thread não é apenas um banco de dados onde o histórico da conversa é salvo. Embora tecnicamente ela armazene esse histórico, eu entendo melhor como um serviço que:

  • Armazena tudo que passa pelo agente: mensagens do usuário, respostas do modelo e uso de ferramentas.

  • Reconstrói o contexto da conversa com base nesse histórico, alimentando o modelo com as informações relevantes no momento da resposta.

Isso permite que a conversa evolua de forma mais natural, como se o agente "lembrasse" do que foi dito anteriormente.

Como esse contexto é montado?

A estratégia exata usada pela thread para selecionar e injetar partes do histórico no prompt não é documentada, mas algumas abordagens são possíveis:

  • Sliding Window: mantém uma janela deslizante das últimas interações e inclui essas mensagens diretamente no prompt.

  • VectorStore (RAG): transforma as mensagens em embeddings vetoriais e armazena em uma base vetorial. Quando o usuário envia uma nova mensagem, o sistema busca no vetorstore as mensagens mais próximas semanticamente e as usa como contexto.

Diagrama de como é gerado o contexto

Qual a vantagem da Thread?

Na prática, quando você usa PersistentAgent com suporte a threads, não precisa implementar esses mecanismos manualmente. A biblioteca se encarrega de:

  • Criar e manter a thread automaticamente.

  • Armazenar interações.

  • Decidir como e o que trazer de volta para o prompt do modelo com base no histórico.

  • Rapidez na hora de implementar um agente com memória de longo prazo.

Qual a desvantagem?

Você não tem controle de muita coisa. no Azure Ai Foundry (basic) a Thread é armazenada em algum local no seu tenant onde você não tem acesso (bom, você consegue consultar o contéudo, mas não sabe onde está armazenado), no Azure Ai Foundry (standard) a Thread é armazenada num banco CosmosDb.

Você não consegue definir quais estratégias vão ser usadas.

Outras técnicas para montar o contexto

1. Entity Tracking (Memória de Entidades)

  • Como funciona: o sistema extrai e rastreia entidades importantes (nomes, datas, locais, intenções) ao longo da conversa e reusa essas informações nos prompts. (Pra quem já viu Document Intelligence e outras ferramentas de IA, a Entidade Nomeada).

  • Exemplo: se o usuário menciona "minha esposa Ana" e depois disser "ela quer ir ao médico", o sistema sabe que "ela" = "Ana".


2. Summarization Memory (Memória por Resumo)

  • Como funciona: a cada N interações, o sistema gera um resumo condensado da conversa até aquele ponto. Esse resumo é usado nos próximos prompts como contexto. (Um snapshopt da conversa até aquele momento.)

  • Vantagem: reduz consumo de tokens mantendo o “contexto" da conversa.

  • Desvantagem: pode perder detalhes finos importantes.

🔁 Pode ser feito de forma incremental (soma novos eventos ao resumo) ou periódica.


3. Hierarchical Memory (Memória Hierárquica)

  • Como funciona: mantém diferentes camadas de memória:

    • Curto prazo: últimas interações (Sliding Window)

    • Médio prazo: contexto condensado (Summarization)

    • Longo prazo: base vetorial ou banco de conhecimento


4. Topic-based Memory

  • Como funciona: armazena trechos da conversa agrupados por tópicos detectados automaticamente (ex: “viagem”, “projeto”, “problema técnico”) e resgata o que for mais relevante.

  • Técnica: geralmente via clustering de embeddings ou classificação supervisionada.

🔍 Útil em sessões longas onde o usuário muda de assunto com frequência.


5. FSM / Workflow-driven Context

  • Como funciona: a memória é controlada por um fluxo de estado (ex: máquina de estados finita), onde cada etapa tem dados associados ao contexto.

  • Vantagem: previsível e mais interpretável.

  • Usado em: bots de atendimento, agentes com objetivo claro.


6. Tool-calling with Memory Storage

  • Como funciona: o agente chama uma ferramenta (plugin/API interna) que lê/escreve em um repositório de memória estruturada (ex: banco SQL, Redis, MongoDB).

  • Uso comum: recuperação de fatos ou preferências salvas do usuário.

Show me the code

Requisitos para rodar o código

  • Azure Subscription

  • Recurso: Azure AI Foundry

  • Um modelo criado no Azure AI Foundry (usei o gpt-4.1-mini


A estrutura que eu sigo nessas samples é uma minimal api com um Register para os módulos e o serviço com os métodos que vamos utilizar. (vou fazer um template disso logo logo). Dessa maneira se você quiser expandir você pode criar novos registros e novos módulos.

O código completo está no meu repositório do GitHub

Vou mostrar alguns pontos importantes, mas o código funcional está no meu github (Deixa uma estrelinha lá por favor? Me ajuda muito).

  1. Vamos criar a nossa Solution
mkdir azure-ai-agents-basic && cd azure-ai-agents-basic && dotnet new sln
  1. Vamos criar o projeto em WebApi e associar com a nossa Solution
dotnet new webapi -n AzureAiAgent.Api && dotnet sln azure-ai-agents-basic.sln add AzureAiAgent.Api/AzureAiAgent.Api.csproj

(Se não quiser fazer assim, crie pela sua IDE preferida)

  1. Vamos precisar da URI e do Modelo LLM que vamos usar. (Aqui lembrando do requisito para rodar, precisa desse recurso do Azure AI Foundry)
"AzureAiSettings" : {
  "Uri": "",
  "Model": ""
},

E no Program.cs adicionar para utilizar mais tarde no serviço.

builder.Services.Configure<AzureAiSettings>(builder.Configuration.GetSection("AzureAiSettings"));
  1. Caminhe até a pasta onde está AzureAiAgent.Api e rode adicione esses pacotes (da para utilizar o Azure.AI.Agents.Persistent sozinho, mas vou mostrar a criação de Thread usando o Azure.AI.Projects.
dotnet add package Azure.AI.Agents.Persistent --version 1.1.0-beta.1
dotnet add package Azure.AI.Projects --version 1.0.0-beta.9
  1. A implementação das rotas que vamos utilizar
        app.MapPost("/ai-agent", async (
                [FromServices] Service service,
                [FromQuery] string name, [FromQuery] string instructions) =>
            {
                var agentId = await service.CreateAgentAsync(name, instructions);
                return Results.Ok(agentId);
            })
            .WithTags("Ai Agents");

        app.MapGet("/ai-agent/create-thread", async (
                [FromServices] Service service) =>
            {
                var agentId = await service.CreateThreadAsync();
                return Results.Ok(agentId);
            })
            .WithTags("Ai Agents");

        app.MapGet("/ai-agent/run", async (
                [FromServices] Service service,
                [FromQuery] string agentId,
                [FromQuery] string threadId,
                [FromQuery] string userInput) =>
            {
                var result = await service.RunAsync(agentId, threadId, userInput);
                return Results.Ok(result);
            })
            .WithTags("Ai Agents");

E por fim o nosso exemplo de serviço de implementação:

  1. Primeiro, criaremos o agente usando a biblioteca Azure.AI.Agents.Persistent onde tem que passar a URI e estou usando a DefaultAzureCredential (então tem que usar o az login para logar na sua subscription do Azure).
public async Task<string> CreateAgentAsync(string agentName, string instructions)
    {
        var agentClient = new PersistentAgentsClient(options.Value.Uri, new DefaultAzureCredential());
        PersistentAgent agent = await agentClient.Administration.CreateAgentAsync(options.Value.Model,
            name: agentName,
            instructions: instructions);
        return agent.Id;
    }

Repare que nós temos que criar o PersistentAgentsClient e a partir dai criar o agente passando o nome do Agente que vc quer dar e a instruções (exemplo: agentName = Agente Básico, instructions = “Você é um agente útil”).

Com isso a gente tem o nosso Agente, mas ainda não tem a nossa Thread!

  1. Método para criar a Thread.
public async Task<string> CreateThreadAsync()
{
    var projectClient = new AIProjectClient(new Uri(options.Value.Uri), new DefaultAzureCredential());
    var agentClient = projectClient.GetPersistentAgentsClient();

    PersistentAgentThread thread = await agentClient.Threads.CreateThreadAsync();
    return thread.Id;
}

Estou mostrando um exemplo usando o AIProjectClient que com ele vc pega o cliente do PersistentAgentClient. Percebe que a gente chega no mesmo client no final? A biblioteca do Azure.AI.Projects dd é uma abstração maior ao Projeto no Azure Ai Foundry. Você consegue com ele criar outros clients.

  1. Por fim, criar um serviço para fazer um Run que é quando o usuário interage com o Agente.
public async Task<IEnumerable<string>> RunAsync(string assistantId, string threadId, string userInput)
{
    var projectClient = new AIProjectClient(new Uri(options.Value.Uri), new DefaultAzureCredential());
    var agentClient = projectClient.GetPersistentAgentsClient();

    PersistentAgent agent = await agentClient.Administration.GetAgentAsync(assistantId);
    PersistentAgentThread thread = await agentClient.Threads.GetThreadAsync(threadId);

    await agentClient.Messages.CreateMessageAsync( threadId, role: MessageRole.User, content: userInput);

    ThreadRun run = await agentClient.Runs.CreateRunAsync(thread, agent);

    do
    {
        await Task.Delay(TimeSpan.FromMilliseconds(500));
        run = await agentClient.Runs.GetRunAsync(thread.Id, run.Id);
    }
    while (run.Status == RunStatus.Queued
           || run.Status == RunStatus.InProgress
           || run.Status == RunStatus.RequiresAction);

    var messagesResponse = agentClient.Messages.GetMessagesAsync(thread.Id, order: ListSortOrder.Descending);
    var result = new List<string>();

    await foreach (var threadMessage in messagesResponse)
    {
        if (threadMessage.Role == MessageRole.Agent &&
            threadMessage.ContentItems.FirstOrDefault() is MessageTextContent messageTextContent)
        {
            result.Add(messageTextContent.Text);
        }
    }

    return result;
}

A princípio parece que é muita coisa ocorrendo nesse código.

O objetivo desse método é usar as informações obtidas nos últimos dois métodos e rodar a pergunta de um usuário

Obter o Agente pelo assistantId, pegar a thread pelo threadId.

Observem que aqui começa a mágica: a criação da mensagem não tem nada haver com o Agente e sim com a Thread. Adiciona a mensagem do Usuário na Thread.

await agentClient.Messages.CreateMessageAsync( threadId, role: MessageRole.User, content: userInput);

Desse ponto do método pra desse ponto pra baixo é a conexão do Agente com a Thread a execução do Agente e recuperando o retorno do agente.

Esse ponto tem uma sequencia de coisas acontecendo.

  • A Thread seleciona o que vai pro contexto do Agente. (O ponto central desse post)

  • e o agente gera a mensagem e a resposta é salva na Thread

  • A busca da resposta do agente é feita pela Thread novamente. Você pode ver que não tem mais relação nenhuma agora com o agente. O papel ele ja fez.

Nos próximos artigos eu vou trazer um pouco mais da flexibilidade do Semantic Kernel para implementar essas estratégias e dar o total controle sobre o agente. E sinceramente aprender como implementar junto com vocês.

Referências

Azure.AI.Agents.Persistent

Azure.AI.Projects

Repositório do Github

5
Subscribe to my newsletter

Read articles from Carlos Machel directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Carlos Machel
Carlos Machel