Samouczek: Budowanie agentowej aplikacji internetowej w usłudze Azure App Service przy użyciu LangGraph lub Foundry Agent Service (Node.js)

W tym samouczku pokazano, jak dodać funkcję agenta do istniejącej aplikacji opartej na danych Express.js CRUD. Robi to przy użyciu dwóch różnych podejść: LangGraph i Foundry Agent Service.

Jeśli aplikacja internetowa ma już przydatne funkcje, takie jak zakupy, rezerwacja hotelowa lub zarządzanie danymi, stosunkowo proste jest dodanie funkcji agenta do aplikacji internetowej przez opakowywanie tych funkcji w wtyczki (dla LangGraph) lub jako punkt końcowy OpenAPI (dla usługi Agenta Foundry). W tym samouczku zaczniesz od prostej aplikacji typu lista to-do. Na koniec będziesz mieć możliwość tworzenia, aktualizowania i zarządzania zadaniami za pomocą agenta w aplikacji usługi App Service.

Zarówno LangGraph, jak i Foundry Agent Service umożliwiają tworzenie agentowych aplikacji internetowych z wykorzystaniem funkcji opartych na sztucznej inteligencji. Język LangGraph jest podobny do jądra semantycznego firmy Microsoft i jest zestawem SDK, ale semantyczne jądro nie obsługuje obecnie języka JavaScript. W poniższej tabeli przedstawiono niektóre zagadnienia i kompromisy:

Consideration LangGraph Usługa agenta programu Foundry
Performance Szybkie (działa lokalnie) Wolniejsze (zarządzana, zdalna usługa)
Development Pełny kod, maksymalna kontrolka Niski kod, szybka integracja
Testing Testy ręczne/jednostkowe w kodzie Wbudowany plac zabaw do szybkiego testowania
Scalability App-managed Zarządzane przez platformę Azure, autoskalowane
Wytyczne bezpieczeństwa Wymagana niestandardowa implementacja Wbudowane bezpieczeństwo i moderowanie zawartości
Tożsamość Wymagana niestandardowa implementacja Wbudowany identyfikator agenta i uwierzytelnianie
Enterprise Wymagana integracja niestandardowa Wbudowane wdrożenie platformy Microsoft 365/Teams i zintegrowane wywołania narzędzi platformy Microsoft 365.

We wdrożonej aplikacji uwierzytelnianie App Service wymaga logowania przy użyciu konta Microsoft Entra zarówno dla interfejsu użytkownika w przeglądarce, jak i interfejsów API. LangGraph działa w usłudze App Service i bezpośrednio wywołuje usługę zadań. Foundry Agent Service działa zdalnie i wywołuje chronione API zadań za pomocą narzędzia OpenAPI.

W tym poradniku nauczysz się, jak:

  • Przekonwertuj istniejącą funkcjonalność aplikacji na wtyczkę langgraph.
  • Dodaj wtyczkę do agenta LangGraph i użyj jej w aplikacji internetowej.
  • Przekonwertuj istniejące funkcje aplikacji na punkt końcowy interfejsu OpenAPI dla usługi Foundry Agent Service.
  • Wywołaj agenta Foundry w aplikacji internetowej.
  • Przyznaj wymagane uprawnienia dla łączności tożsamości zarządzanej.
  • Chroń aplikację webową App Service i jej API za pomocą Microsoft Entra ID.
  • Konfiguruj narzędzie Foundry OpenAPI do wywoływania chronionych API App Service z tożsamością zarządzaną.

Prerequisites

Otwieranie przykładu za pomocą usługi Codespaces

Najprostszym sposobem rozpoczęcia pracy jest użycie usługi GitHub Codespaces, która udostępnia kompletne środowisko programistyczne ze wszystkimi wymaganymi wstępnie zainstalowanymi narzędziami.

  1. Przejdź do repozytorium GitHub pod adresem https://github.com/Azure-Samples/app-service-agentic-langgraph-foundry-node.

  2. Wybierz przycisk Kod , wybierz kartę Codespaces i wybierz pozycję Utwórz przestrzeń kodu w obszarze głównym.

  3. Zaczekaj chwilę na zainicjowanie usługi Codespace. Gdy wszystko będzie gotowe, zobaczysz w przeglądarce w pełni skonfigurowane środowisko programistyczne.

  4. Uruchom aplikację lokalnie:

    npm install
    npm run build
    npm start
    
  5. Gdy zobaczysz, że aplikacja uruchomiona na porcie 3000 jest dostępna, wybierz pozycję Otwórz w przeglądarce i dodaj kilka zadań.

    Agenci nie są w pełni skonfigurowani, więc jeszcze nie działają. Skonfigurujesz je później.

Przeanalizować kod agenta

Oba podejścia używają tego samego wzorca implementacji, w którym agent jest inicjowany podczas uruchamiania aplikacji i odpowiada na komunikaty użytkowników według żądań POST.

Element LangGraphTaskAgent jest inicjowany w konstruktorze w pliku src/agents/LangGraphTaskAgent.ts. Kod inicjowania wykonuje następujące czynności:

    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);
        }
    }

Wdrożona przykładowa aplikacja jest chroniona za pomocą uwierzytelniania usługi App Service i korzysta z jednego wątku LangGraph wybranego przez serwer. Podczas przetwarzania wiadomości użytkownika agent wywołuje invoke(), przekazując wiadomość użytkownika oraz identyfikator wątku zarządzanego przez serwer:

private readonly conversationThreadId = 'authenticated-conversation';

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

Wdrażanie aplikacji przykładowej

Przykładowe repozytorium zawiera szablon Azure Developer CLI (AZD), który tworzy aplikację App Service i wdraża Twoją przykładową aplikację. Szablon umożliwia systemowo przypisaną tożsamość zarządzaną dla wychodzących wywołań Azure AI oraz konfiguruje uwierzytelnianie App Service za pomocą Microsoft Entra ID. Aby uzyskać więcej informacji o konfiguracji uwierzytelniania, zobacz Secure OpenAPI endpoints for Foundry Agent Service.

  1. W terminalu zaloguj się do Azure, używając Azure Developer CLI:

    azd auth login
    

    Postępuj zgodnie z instrukcjami, aby ukończyć proces uwierzytelniania.

  2. Wdrożenie aplikacji Azure App Service za pomocą szablonu AZD:

    azd up
    
  3. Po wyświetleniu monitu podaj następujące odpowiedzi:

    Question Answer
    Wprowadź nową nazwę środowiska: Wpisz unikatową nazwę.
    Wybierz subskrypcję platformy Azure do użycia: Wybierz subskrypcję.
    Wybierz grupę zasobów do użycia: Wybierz pozycję Utwórz nową grupę zasobów.
    Wybierz lokalizację, w ramach których chcesz utworzyć grupę zasobów: Wybierz pozycję Szwecja Środkowa.
    Wprowadź nazwę nowej grupy zasobów: Wpisz Enter.
  4. W danych wyjściowych usługi AZD znajdź adres URL aplikacji. Skopiuj też wartość „audience” tożsamości zarządzanej Foundry OpenAPI do późniejszego użycia. Dane wyjściowe wyglądają następująco:

     Deploying services (azd deploy)
    
       (✓) Done: Deploying service web
       - Endpoint: <URL>
    
     Foundry OpenAPI managed identity audience:
     api://<generated-client-id>
     
  5. Otwórz punkt końcowy App Service z wyjścia AZD.

  6. Gdy Microsoft cię o to poprosi, zaloguj się, używając konta w tenantzie wdrożenia i sprawdź, czy lista zadań się ładuje.

  7. W tej samej uwierzytelnionej przeglądarce otwórz automatycznie generowany schemat OpenAPI w .https://<app-name>.azurewebsites.net/api/schema

  8. Skopiuj lub zapisz wygenerowany schemat OpenAPI. Używasz go w obrócie Foundry Agent Service.

    Note

    Uwierzytelnianie usługi App Service zwraca przekierowanie HTTP 302 dla nieuwierzytelnionych żądań z przeglądarki. Ten przykład zawiera zarówno interfejs przeglądarki, jak i API, dzięki czemu przekierowanie zapewnia użyteczne doświadczenie logowania. Aplikacje oparte wyłącznie na API często korzystają z HTTP 401.

    Teraz masz uwierzytelnioną aplikację App Service. Do wychodzących wywołań do usługi Foundry jest używana tożsamość zarządzana przypisana przez system. Oddzielna zarządzana tożsamość przypisana przez użytkownika zapewnia poświadczenia niewymagające sekretów na potrzeby uwierzytelniania w usłudze App Service.

Tworzenie i konfigurowanie zasobu rozwiązania Microsoft Foundry

  1. W portalu Foundry stwórz projekt.

  2. Wdróż wybrany model (zobacz Przewodnik Szybki start firmy Microsoft Foundry: tworzenie zasobów).

  3. W górnej części placu zabaw modelu skopiuj nazwę modelu.

  4. Na stronie głównej skopiuj endpoint Azure OpenAI na później.

Przypisywanie wymaganych uprawnień

  1. W portalu Foundry wybierz Zarządzaj w górnym menu.

  2. W Szczegóły projektu wybierz zasób nadrzędny dla projektu, a następnie wybierz Otwórz w portalu Azure.

    Z portalu Azure możesz przypisać dostęp do zasobów opartych na rolach.

  3. Dodaj następującą rolę zarówno dla zarządzanej tożsamości aplikacji App Service, jak i dla użytkownika, którego używasz az login:

    Zasób docelowy Wymagana rola Wymagane do
    Odlewnia Użytkownik Usług Cognitive Services OpenAI Usługa uzupełniania czatu w programie Microsoft Agent Framework.

    Aby uzyskać instrukcje, zobacz temat Przypisywanie ról platformy Azure za pomocą witryny Azure Portal.

Konfigurowanie zmiennych połączenia w przykładowej aplikacji

  1. Otwórz plik env. Korzystając z wartości skopiowanych wcześniej z portalu Foundry, skonfiguruj następujące zmienne:

    Variable Description
    AZURE_OPENAI_ENDPOINT Azure OpenAI endpoint (skopiowany ze strony głównej portalu Foundry).
    AZURE_OPENAI_DEPLOYMENT_NAME Nazwa modelu we wdrożeniu (skopiowana z obszaru roboczego modelu w nowym portalu Foundry).

    Note

    Aby zachować prostotę samouczka, użyjesz tych zmiennych w pliku env zamiast zastępowania ich ustawieniami aplikacji w usłudze App Service.

    Note

    Aby zachować prostotę samouczka, użyjesz tych zmiennych w pliku env zamiast zastępowania ich ustawieniami aplikacji w usłudze App Service.

    Wartości w .env konfigurują połączenie wychodzące aplikacji z Foundry. AZURE_AI_FOUNDRY_ACCOUNT_CLIENT_ID konfiguruje oddzielne przychodzące połączenie OpenAPI z Foundry do App Service i jest przechowywane w środowisku AZD.

Uwierzytelnianie App Service działa w Azure, a nie w lokalnym procesie Express, więc lokalny workflow testowania pozostaje niezmieniony.

  1. Zaloguj się do platformy Azure przy użyciu interfejsu wiersza polecenia platformy Azure:

    az login
    

    Dzięki temu biblioteka klienta tożsamości platformy Azure w przykładowym kodzie może odbierać token uwierzytelniania dla zalogowanego użytkownika. Pamiętaj, że wcześniej dodano wymaganą rolę dla tego użytkownika.

  2. Uruchom aplikację lokalnie:

    npm run build
    npm start
    
  3. Gdy zobaczysz, że aplikacja uruchomiona na porcie 3000 jest dostępna, wybierz pozycję Otwórz w przeglądarce.

  4. Walidacja obu pivotów osobno:

    • LangGraph: Wybierz Agenta LangGraph i poproś agenta o utworzenie zadania. LangGraph wywołuje narzędzie zadań działające w tym samym procesie.
    • Usługa agenta Foundry: Wybierz Foundry Agent i poproś agenta o utworzenie zadania. Zdalny agent Foundry wywołuje wdrożony, chroniony /api/tasks punkt końcowy przy użyciu tożsamości zarządzanej.

    Zadanie tworzone przez agenta Foundry pojawia się w wdrożonej instancji App Service, a nie w lokalnej bazie danych w pamięci. Narzędzie Foundry OpenAPI zawsze korzysta z adresu URL serwera osadzonego w schemacie OpenAPI.

  5. W usłudze GitHub codespace wdróż zmiany aplikacji.

    azd up
    
  6. Przejdź do wdrożonej aplikacji, zaloguj się i przetestuj oba pivoty. Tworz i wystawiaj zadania za pomocą Agenta LangGraph, a następnie twórz i wystawiaj zadania za pomocą Foundry Agent. Sprawdź, czy oba pivoty aktualizują listę zadań.

Często zadawane pytania

Jak dodać generowanie wspomagane wyszukiwaniem (RAG) do agenta Foundry?

Te wskazówki dotyczą ścieżki Foundry Agent Service w tym samouczku. Nie zmienia to implementacji LangGraph, Semantic Kernel ani Microsoft Agent Framework pokazanych w drugiej karcie.

Stwórz lub wybierz bazę wiedzy Foundry IQ, a następnie połącz tę bazę z agentem Foundry Agent Service. Połączenie jest udostępniane agentowi jako zarządzane narzędzie wiedzy MCP.

Kod usługi App Service nadal wywołuje tego samego agenta po nazwie za pośrednictwem istniejącego klienta Foundry oraz agent_reference. Aplikacja webowa nie potrzebuje bezpośredniej integracji z Wyszukiwanie AI platformy Azure ani własnego klienta MCP. Jeśli interfejs wyświetla źródła, przetworz adnotacje cytowań zwrócone przez agenta.

Jakiej zarządzanej tożsamości używa każde połączenie?

Kierunek Tożsamość
Aplikacja Service wywołuje Foundry Tożsamość App Service przypisana przez system
Wywołania narzędzia Foundry OpenAPI /api/tasks Tożsamość przypisana przez system dla zasobu nadrzędnego Foundry

Endpoint projektu wybiera projekt i agenta. Nie określa tożsamości, jaką używa hostowane narzędzie OpenAPI.

Uprzątnij zasoby

Po zakończeniu pracy z aplikacją możesz usunąć zasoby usługi App Service, aby uniknąć ponoszenia dodatkowych kosztów:

azd down --purge

Następnie usuń zasób Foundry, jeśli utworzyłeś go osobno.

Więcej zasobów