Tutorial: Criar um aplicativo Web agente no Serviço de Aplicativo do Azure com o LangGraph ou o Serviço do Foundry Agent (Node.js)

Este tutorial demonstra como adicionar capacidade agente a um aplicativo CRUD Express.js controlado por dados existente. Ele faz isso usando duas abordagens diferentes: LangGraph e Foundry Agent Service.

Se seu aplicativo Web já tiver recursos úteis, como compras, reservas de hotéis ou gerenciamento de dados, é relativamente simples adicionar funcionalidade de agente ao seu aplicativo Web encapsulando essas funcionalidades em um plug-in (para LangGraph) ou como um ponto de extremidade OpenAPI (para o Serviço do Foundry Agent). Neste tutorial, você começará com um aplicativo simples de listas chamado to-do. Ao final, você poderá criar, atualizar e gerenciar tarefas com um agente em um aplicativo do Serviço de Aplicativo.

O LangGraph e o Foundry Agent Service permitem criar aplicativos Web agente com recursos controlados por IA. O LangGraph é semelhante ao Kernel Semântico da Microsoft e é um SDK, mas o Kernel Semântico não dá suporte ao JavaScript no momento. A tabela a seguir mostra algumas das considerações e compensações:

Consideration LangGraph Serviço de Agente da Fábrica
Performance Rápido (executado localmente) Mais lento (serviço gerenciado e remoto)
Development Código completo, controle máximo Código baixo, integração rápida
Testing Testes manuais/de unidade no código Playground integrado para testes rápidos
Scalability App-managed Gerenciado pelo Azure, dimensionado automaticamente
Proteções de segurança Implementação personalizada necessária Segurança e moderação de conteúdo embutidas
Identidade Implementação personalizada necessária ID de agente integrado e autenticação
Enterprise Integração personalizada necessária Implantação integrada do Microsoft 365/Teams e chamadas integradas de ferramentas no Microsoft 365.

No aplicativo implantado, a autenticação do App Service requer login Microsoft Entra tanto para a interface do navegador quanto para as APIs. O LangGraph roda dentro do App Service e chama diretamente o serviço de tarefas. O Foundry Agent Service roda remotamente e chama a API de tarefas protegidas por meio de sua ferramenta OpenAPI.

Neste tutorial, você aprenderá como:

  • Converta a funcionalidade de aplicativo existente em um plug-in para LangGraph.
  • Adicione o plug-in a um agente do LangGraph e use-o em um aplicativo Web.
  • Converta a funcionalidade de aplicativo existente em um endpoint OpenAPI para o Foundry Agent Service.
  • Chame um agente do Foundry em um aplicativo Web.
  • Atribua as permissões necessárias para conectividade de identidade gerenciada.
  • Proteja um aplicativo web de Serviço de Aplicativos e suas APIs com o Microsoft Entra ID.
  • Configure uma ferramenta Foundry OpenAPI para chamar APIs de Serviços de Aplicativos protegidas com identidade gerenciada.

Prerequisites

Abra o exemplo com codespaces

A maneira mais fácil de começar é usando o GitHub Codespaces, que fornece um ambiente de desenvolvimento completo com todas as ferramentas necessárias pré-instaladas.

  1. Navegue até o repositório GitHub em https://github.com/Azure-Samples/app-service-agentic-langgraph-foundry-node.

  2. Selecione o botão Código , selecione a guia Codespaces e selecione Criar codespace no principal.

  3. Aguarde alguns instantes para que o codespace seja inicializado. Quando estiver pronto, você verá um ambiente de desenvolvimento totalmente configurado no navegador.

  4. Execute o aplicativo localmente:

    npm install
    npm run build
    npm start
    
  5. Quando você vir que seu aplicativo em execução na porta 3000 está disponível, selecione Abrir no Navegador e adicione algumas tarefas.

    Os agentes não estão totalmente configurados, portanto, ainda não funcionam. Você os configurará mais tarde.

Examinar o código do agente

Ambas as abordagens usam o mesmo padrão de implementação, em que o agente é inicializado no início do aplicativo e responde às mensagens do usuário por solicitações POST.

O LangGraphTaskAgent é inicializado no construtor em src/agents/LangGraphTaskAgent.ts. O código de inicialização faz o seguinte:

    constructor(taskService: TaskService) {
        this.taskService = taskService;
        this.memory = new MemorySaver();
        try {
            const endpoint = process.env.AZURE_OPENAI_ENDPOINT;
            const deploymentName = process.env.AZURE_OPENAI_DEPLOYMENT_NAME;

            if (!endpoint || !deploymentName) {
                console.warn('Azure OpenAI configuration missing for LangGraph agent');
                return;
            }
            // Initialize Azure OpenAI client
            const credential = new DefaultAzureCredential();
            const azureADTokenProvider = getBearerTokenProvider(credential, "https://cognitiveservices.azure.com/.default");
            
            this.llm = new AzureChatOpenAI({
                azureOpenAIEndpoint: endpoint,
                azureOpenAIApiDeploymentName: deploymentName,
                azureADTokenProvider: azureADTokenProvider,
                azureOpenAIApiVersion: "2024-10-21"
            });
            // Define tools directly in the array
            const tools = [
                tool(
                    async ({ title, isComplete = false }) => {
                        const task = await this.taskService.addTask(title, isComplete);
                        return `Task created successfully: "${task.title}" (ID: ${task.id})`;
                    },
                    {
                        name: 'createTask',
                        description: 'Create a new task',
                        schema: z.object({
                            title: z.string(),
                            isComplete: z.boolean().optional()
                        }) as any
                    }
                ),
                tool(
                    async () => {
                        const tasks = await this.taskService.getAllTasks();
                        if (tasks.length === 0) {
                            return 'No tasks found.';
                        }
                        return `Found ${tasks.length} tasks:\n` + 
                               tasks.map(t => `- ${t.id}: ${t.title} (${t.isComplete ? 'Complete' : 'Incomplete'})`).join('\n');
                    },
                    {
                        name: 'getTasks',
                        description: 'Get all tasks',
                        schema: z.object({}) as any
                    }
                ),
                tool(
                    async ({ id }) => {
                        const task = await this.taskService.getTaskById(id);
                        if (!task) {
                            return `Task with ID ${id} not found.`;
                        }
                        return `Task ${task.id}: "${task.title}" - Status: ${task.isComplete ? 'Complete' : 'Incomplete'}`;
                    },
                    {
                        name: 'getTask',
                        description: 'Get a specific task by ID',
                        schema: z.object({
                            id: z.number()
                        }) as any
                    }
                ),
                tool(
                    async ({ id, title, isComplete }) => {
                        const updated = await this.taskService.updateTask(id, title, isComplete);
                        if (!updated) {
                            return `Task with ID ${id} not found.`;
                        }
                        return `Task ${id} updated successfully.`;
                    },
                    {
                        name: 'updateTask',
                        description: 'Update an existing task',
                        schema: z.object({
                            id: z.number(),
                            title: z.string().optional(),
                            isComplete: z.boolean().optional()
                        }) as any
                    }
                ),
                tool(
                    async ({ id }) => {
                        const deleted = await this.taskService.deleteTask(id);
                        if (!deleted) {
                            return `Task with ID ${id} not found.`;
                        }
                        return `Task ${id} deleted successfully.`;
                    },
                    {
                        name: 'deleteTask',
                        description: 'Delete a task',
                        schema: z.object({
                            id: z.number()
                        }) as any
                    }
                )
            ];

            // Create the ReAct agent with memory
            this.agent = createReactAgent({
                llm: this.llm,
                tools,
                checkpointSaver: this.memory,
                stateModifier: `You are an AI assistant that manages tasks using CRUD operations.
                
You have access to tools for creating, reading, updating, and deleting tasks.
Always use the appropriate tool for any task management request.
Be helpful and provide clear responses about the actions you take.

If you need more information to complete a request, ask the user for it.`
            });
        } catch (error) {
            console.error('Error initializing LangGraph agent:', error);
        }
    }

O exemplo implantado é protegido por autenticação de App Service e utiliza um único thread LangGraph selecionado pelo servidor. Quando você processa mensagens de usuário, o agente invoca invoke() a mensagem do usuário e o ID da thread gerenciada pelo servidor:

private readonly conversationThreadId = 'authenticated-conversation';

const result = await this.agent.invoke(
    {
        messages: [
            { role: 'user', content: message }
        ]
    },
    {
        configurable: {
            thread_id: this.conversationThreadId
        }
    }
);

Implantar o aplicativo de exemplo

O repositório de exemplo contém um template de CLI para Desenvolvedores do Azure (AZD), que cria um aplicativo de Serviço de Aplicativos e implanta sua aplicação de exemplo. O modelo permite uma identidade gerenciada atribuída ao sistema para chamadas de IA do Azure de saída e configura a autenticação de Serviços de Aplicativos com o Microsoft Entra ID. Para obter mais informações sobre a configuração de autenticação subjacente, consulte Pontos de extremidade OpenAPI seguros para o Serviço de Agente do Foundry.

  1. No terminal, faça login no Azure usando o Azure Developer CLI:

    azd auth login
    

    Siga as instruções para concluir o processo de autenticação.

  2. Implante o aplicativo Serviço de Aplicativo do Azure usando o modelo AZD:

    azd up
    
  3. Quando solicitado, dê as seguintes respostas:

    Question Answer
    Insira um novo nome de ambiente: Digite um nome exclusivo.
    Selecione uma Assinatura do Azure para usar: Selecione a assinatura.
    Escolha um grupo de recursos a ser usado: Selecione Criar um grupo de recursos.
    Selecione um local para criar o grupo de recursos em: Selecione Suécia Central.
    Insira um nome para o novo grupo de recursos: Digite ENTER.
  4. Na saída do AZD, localize a URL do seu aplicativo. Copie também o valor de Público-alvo da identidade gerenciada da OpenAPI do Foundry para uso posterior. A saída tem esta aparência:

     Deploying services (azd deploy)
    
       (✓) Done: Deploying service web
       - Endpoint: <URL>
    
     Foundry OpenAPI managed identity audience:
     api://<generated-client-id>
     
  5. Abra o ponto de extremidade do Serviço de Aplicativo na saída do AZD.

  6. Quando a Microsoft solicitar, faça login usando uma conta no tenant de implantação e verifique se a lista de tarefas está carregada.

  7. No mesmo navegador autenticado, abra o esquema OpenAPI gerado automaticamente em https://<app-name>.azurewebsites.net/api/schema.

  8. Copie ou salve o esquema gerado da OpenAPI. Você o utiliza no pivô do Serviço de Agente do Foundry.

    Note

    A autenticação por App Service retorna um redirecionamento HTTP 302 para requisições de navegador não autenticadas. Este exemplo contém tanto uma interface de navegador quanto APIs, então o redirecionamento oferece uma experiência de login utilizável. Aplicativos apenas de API geralmente usam HTTP 401.

    Agora você tem um aplicativo de App Service autenticado. Sua identidade gerenciada atribuída pelo sistema é usada para chamadas do Foundry de saída. Uma identidade gerenciada separada atribuída pelo usuário fornece credenciais sem segredo para a autenticação do App Service.

Criar e configurar o recurso microsoft foundry

  1. No portal da Foundry, crie um projeto.

  2. Implante um modelo de sua escolha (consulte o Início Rápido do Microsoft Foundry: Criar recursos).

  3. Na parte superior da área de testes do modelo, copie o nome dele.

  4. Na página inicial, copie o endpoint do Azure OpenAI para depois.

Atribuir permissões necessárias

  1. No portal Foundry, selecione Gerenciar no menu superior.

  2. Em Detalhes do projeto, selecione o recurso pai do seu projeto e selecione Abrir no portal do Azure.

    Pelo portal Azure, você pode atribuir acesso baseado em função para o recurso.

  3. Adicione a seguinte função tanto para a identidade gerenciada do aplicativo do App Service quanto para o usuário que você usa com az login:

    Recurso de destino Função necessária Necessário para
    Fundição Usuário dos Serviços Cognitivos OpenAI O serviço de conclusão de chat no Microsoft Agent Framework.

    Para obter instruções, confira Atribuir funções do Azure usando o portal do Azure.

Configurar variáveis de conexão em seu aplicativo de exemplo

  1. Abra .env. Usando os valores copiados anteriormente do portal do Foundry, configure as seguintes variáveis:

    Variable Description
    AZURE_OPENAI_ENDPOINT Azure OpenAI endpoint (copiado da página inicial do portal Foundry).
    AZURE_OPENAI_DEPLOYMENT_NAME Nome do modelo na implantação (copiado do ambiente de testes de modelos no novo portal Foundry).

    Note

    Para manter o tutorial simples, você usará essas variáveis em .env em vez de substituí-las com configurações de aplicativo no Serviço de Aplicativo.

    Note

    Para manter o tutorial simples, você usará essas variáveis em .env em vez de substituí-las com configurações de aplicativo no Serviço de Aplicativo.

    Os valores em .env configuram a conexão de saída do app para o Foundry. AZURE_AI_FOUNDRY_ACCOUNT_CLIENT_ID configura a conexão OpenAPI de entrada separada do Foundry para o Serviço de Aplicativos e é armazenada no ambiente AZD.

A autenticação do App Service roda no Azure, não no processo local Express, então o fluxo de trabalho de teste local permanece inalterado.

  1. Entre no Azure com a CLI do Azure:

    az login
    

    Isso permite que a biblioteca de clientes da Identidade do Azure no código de exemplo receba um token de autenticação para o usuário conectado. Lembre-se de que você adicionou a função necessária para esse usuário anteriormente.

  2. Execute o aplicativo localmente:

    npm run build
    npm start
    
  3. Quando você vir que seu aplicativo em execução na porta 3000 está disponível, selecione Abrir no Navegador.

  4. Validar ambos os pivôs separadamente:

    • LangGraph: Selecione LangGraph Agent e peça ao agente para criar uma tarefa. O LangGraph chama a ferramenta de tarefa em processo.
    • Serviço do Agente Foundry: Selecione Agente Foundry e peça ao agente para criar uma tarefa. O agente Foundry remoto chama o endpoint implantado e protegido /api/tasks usando identidade gerenciada.

    A tarefa criada pelo agente Foundry aparece na instância do App Service implantada, não no banco de dados local em memória. A ferramenta OpenAPI do Foundry sempre utiliza a URL do servidor embutida no esquema OpenAPI.

  5. De volta ao codespace do GitHub, implante as alterações do aplicativo.

    azd up
    
  6. Navegue até a aplicação implantada, faça login e teste ambos os pivôs. Crie e liste tarefas com o Agente LangGraph, e depois crie e liste tarefas com o Agente Foundry. Verifique se ambos os pivôs atualizam a lista de tarefas.

Perguntas frequentes

Como adiciono geração aumentada de recuperação (RAG) ao agente do Foundry?

Esta instrução se aplica ao caminho Foundry Agent Service neste tutorial. Ele não altera as implementações do LangGraph, Kernel semântico ou Microsoft Agent Framework mostradas na outra aba.

Crie ou selecione uma base de conhecimento do Foundry IQ e, em seguida, conecte a base de conhecimento ao agente do Serviço de Agente do Foundry. A conexão é exposta ao agente como uma ferramenta de conhecimento gerenciada do MCP.

O código do Serviço de Aplicativo continua a invocar o mesmo agente pelo nome por meio de seu cliente Foundry existente e agent_reference. O aplicativo web não precisa de uma integração direta com Pesquisa de IA do Azure  nem de um cliente MCP próprio. Se a interface exibir fontes, processe as anotações de citação retornadas pelo agente.

Qual identidade gerenciada cada conexão usa?

Direção Identidade
O App Service faz chamadas para o Foundry Identidade atribuída ao sistema de Serviços de Aplicativos
Chamadas de ferramenta OpenAPI do Foundry /api/tasks Identidade atribuída pelo sistema do recurso Foundry pai

O endpoint do projeto seleciona o projeto e o agente. Ele não determina a identidade que a ferramenta OpenAPI hospedada utiliza.

Limpar os recursos

Quando terminar de usar o aplicativo, você poderá excluir os recursos do Serviço de Aplicativo para evitar incorrer em custos adicionais:

azd down --purge

Depois, exclua o recurso do Foundry se você o criou separadamente.

Mais recursos