Lernprogramm: Erstellen einer agentischen Web-App in Azure App Service mit LangGraph oder Foundry Agent Service (Node.js)

In diesem Lernprogramm wird veranschaulicht, wie Sie einer vorhandenen datengesteuerten Express.js CRUD-Anwendung agentische Funktionen hinzufügen. Dazu werden zwei verschiedene Ansätze verwendet: LangGraph und Foundry Agent Service.

Wenn Ihre Webanwendung bereits nützliche Funktionen wie Shopping, Hotelbuchung oder Datenverwaltung aufweist, ist es relativ einfach, Ihrer Webanwendung Agent-Funktionen hinzuzufügen, indem diese Funktionen in ein Plug-In (für LangGraph) oder als OpenAPI-Endpunkt (für Foundry Agent Service) eingeschlossen werden. In diesem Lernprogramm beginnen Sie mit einer einfachen To-Do-Listen-App. Am Ende können Sie Aufgaben mit einem Agent in einer App Service-App erstellen, aktualisieren und verwalten.

Sowohl der LangGraph- als auch der Foundry-Agent-Dienst ermöglichen es Ihnen, agentische Webanwendungen mit KI-gesteuerten Funktionen zu erstellen. LangGraph ähnelt dem Microsoft Semantischer Kernel und ist ein SDK, aber Der semantische Kernel unterstützt derzeit kein JavaScript. In der folgenden Tabelle sind einige der Überlegungen und Kompromisse aufgeführt:

Consideration LangGraph Gießerei-Agentendienst
Performance Schnell (wird lokal ausgeführt) Langsamer (verwalteter, Remotedienst)
Development Vollständiger Code, maximale Steuerung Geringer Code, schnelle Integration
Testing Manuelle Tests/Komponententests im Code Integrierter Playground für schnelle Tests
Scalability App-verwaltet Von Azure verwaltet, automatisch skaliert
Sicherheitsschutzschienen Benutzerdefinierte Implementierung erforderlich Integrierte Sicherheit und Moderation von Inhalten
Identität Benutzerdefinierte Implementierung erforderlich Integrierte Agent-ID und Authentifizierung
Enterprise Benutzerdefinierte Integration erforderlich Integrierte Microsoft 365/Teams-Bereitstellung und Integrierte Microsoft 365-Toolaufrufe.

In der bereitgestellten App erfordert die App Service-Authentifizierung eine Microsoft Entra-Anmeldung sowohl für die Browser-UI als auch für die APIs. LangGraph läuft innerhalb des App Service und ruft den Task-Service direkt auf. Foundry Agent Service läuft remote und ruft die geschützte Aufgaben-API über sein OpenAPI-Tool auf.

In diesem Tutorial lernen Sie Folgendes:

  • Konvertieren Sie vorhandene App-Funktionen in ein Plug-In für LangGraph.
  • Fügen Sie das Plug-In einem LangGraph-Agent hinzu, und verwenden Sie es in einer Web-App.
  • Konvertieren Sie vorhandene App-Funktionen in einen OpenAPI-Endpunkt für den Foundry Agent Service.
  • Rufen Sie einen Foundry-Agent in einer Web-App auf.
  • Weisen Sie die erforderlichen Berechtigungen für verwaltete Identitätskonnektivität zu.
  • Schützen Sie eine App-Service-Webanwendung und ihre APIs mit Microsoft Entra ID.
  • Konfigurieren Sie ein Foundry OpenAPI-Tool, das geschützte App Service APIs mit verwalteter Identität aufruft.

Prerequisites

Öffnen des Beispiels mit Codespaces

Die einfachste Möglichkeit für die ersten Schritte ist die Verwendung von GitHub Codespaces, die eine vollständige Entwicklungsumgebung mit allen erforderlichen Tools vorinstalliert bietet.

  1. Navigieren Sie zum GitHub-Repository unter https://github.com/Azure-Samples/app-service-agentic-langgraph-foundry-node.

  2. Wählen Sie die Schaltfläche "Code ", dann die Registerkarte " Codespaces " und dann " Codespace erstellen" im Hauptfeld aus.

  3. Warten Sie einige Augenblicke, bis Der Codespace initialisiert wird. Wenn Sie bereit sind, wird eine vollständig konfigurierte Entwicklungsumgebung in Ihrem Browser angezeigt.

  4. Führen Sie die Anwendung lokal aus:

    npm install
    npm run build
    npm start
    
  5. Wenn Sie sehen, dass Ihre Anwendung auf Port 3000 ausgeführt wird, wählen Sie "Im Browser öffnen" aus, und fügen Sie einige Aufgaben hinzu.

    Die Agents sind nicht vollständig konfiguriert, sodass sie noch nicht funktionieren. Sie konfigurieren sie später.

Überprüfen des Agenten-Codes

Beide Ansätze verwenden dasselbe Implementierungsmuster, bei dem der Agent beim Starten der Anwendung initialisiert wird, und antwortet von POST-Anforderungen auf Benutzernachrichten.

Die LangGraphTaskAgent Initialisierung erfolgt im Konstruktor in "src/agents/LangGraphTaskAgent.ts". Der Initialisierungscode führt folgendes aus:

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

Das bereitgestellte Sample ist durch App-Service-Authentifizierung geschützt und verwendet einen vom Server ausgewählten LangGraph-Thread. Wenn Sie Nachrichten von Benutzern verarbeiten, ruft der Agent invoke() mit der Nachricht des Benutzers und der vom Server verwalteten Thread-ID auf:

private readonly conversationThreadId = 'authenticated-conversation';

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

Bereitstellen der Beispielanwendung

Das Beispiel-Repository enthält eine Azure Developer CLI (AZD)-Vorlage, die eine App Service-App erstellt und deine Beispielanwendung bereitstellt. Die Vorlage ermöglicht eine systemzugewiesene verwaltete Identität für ausgehende Azure-AI-Aufrufe und konfiguriert die App Service-Authentifizierung mit der Microsoft Entra ID. Weitere Informationen zur zugrunde liegenden Authentifizierungskonfiguration finden Sie unter Secure OpenAPI endpoints for Foundry Agent Service.

  1. Im Terminal melden Sie sich mit Azure Developer CLI bei Azure an:

    azd auth login
    

    Folgen Sie den Anweisungen, um den Authentifizierungsprozess abzuschließen.

  2. Deploye die Azure App Service App mit der AZD-Vorlage:

    azd up
    
  3. Wenn Sie dazu aufgefordert werden, geben Sie die folgenden Antworten:

    Question Answer
    Geben Sie einen neuen Umgebungsnamen ein: Geben Sie einen eindeutigen Namen ein.
    Wählen Sie ein Azure-Abonnement aus, das Sie verwenden möchten: Wählen Sie das Abonnement aus.
    Wählen Sie eine zu verwendende Ressourcengruppe aus: Wählen Sie Eine neue Ressourcengruppe erstellen aus.
    Wählen Sie einen Speicherort aus, in dem die Ressourcengruppe erstellt werden soll: Wählen Sie "Schweden Zentral" aus.
    Geben Sie einen Namen für die neue Ressourcengruppe ein: Geben Sie Eingeben ein.
  4. Suchen Sie in der AZD-Ausgabe die URL Ihrer App. Kopiere außerdem den Wert für die Foundry OpenAPI-Zielgruppe der verwalteten Identität zur späteren Verwendung. Die Ausgabe sieht wie folgt aus:

     Deploying services (azd deploy)
    
       (✓) Done: Deploying service web
       - Endpoint: <URL>
    
     Foundry OpenAPI managed identity audience:
     api://<generated-client-id>
     
  5. Öffne den App Service-Endpunkt aus dem AZD-Output.

  6. Wenn Microsoft Sie auffordert, melden Sie sich mit einem Konto im Deployment-Tenant an und überprüfen Sie, ob die Aufgabenliste geladen ist.

  7. Im selben authentifizierten Browser öffnet man das automatisch generierte OpenAPI-Schema unter https://<app-name>.azurewebsites.net/api/schema.

  8. Kopiere oder speichere das generierte OpenAPI-Schema. Du nutzt es im Foundry Agent Service Pivot.

    Note

    Die App Service-Authentifizierung gibt eine HTTP-302-Weiterleitung für nicht authentifizierte Browseranfragen zurück. Dieses Beispiel enthält sowohl eine Browser-Benutzeroberfläche als auch APIs, sodass die Weiterleitung ein brauchbares Anmeldeerlebnis bietet. API-only-Apps verwenden stattdessen häufig HTTP 401.

    Du hast jetzt eine authentifizierte App Service App. Seine systemzugewiesene verwaltete Identität wird für ausgehende Foundry-Aufrufe verwendet. Eine separate, vom Benutzer zugewiesene verwaltete Identität bietet geheimnislose Zugangsdaten für die App Service-Authentifizierung.

Erstellen und Konfigurieren der Microsoft Foundry-Ressource

  1. Im Foundry-Portal erstellen Sie ein Projekt.

  2. Stellen Sie ein Modell Ihrer Wahl bereit (siehe Schnellstart von Microsoft Foundry: Erstellen von Ressourcen).

  3. Kopieren Sie den Namen des Modells aus dem oberen Bereich des Modell-Playrounds.

  4. Auf der Startseite kopieren Sie den Azure OpenAI-Endpunkt für später.

Zuweisen erforderlicher Berechtigungen

  1. Im Foundry-Portal wählen Sie im oberen Menü "Verwalten " aus.

  2. In Project Details wählen Sie die Elternressource Ihres Projekts aus und wählen Sie dann im Azure-Portal öffnen.

    Vom Azure-Portal aus können Sie rollenbasierten Zugriff für die Ressource zuweisen.

  3. Fügen Sie die folgende Rolle sowohl für die verwaltete Identität der App Service-App als auch für den Benutzer hinzu, den Sie mit az login verwenden:

    Zielressource Erforderliche Rolle Erforderlich für
    Gießerei Kognitive Dienste OpenAI-Nutzer Der Chatabschlussdienst in Microsoft Agent Framework.

    Anweisungen hierzu finden Sie unter Zuweisen von Azure-Rollen über das Azure-Portal.

Konfigurieren von Verbindungsvariablen in Ihrer Beispielanwendung

  1. Öffnen Sie .env. Konfigurieren Sie mithilfe der Werte, die Sie zuvor aus dem Foundry-Portal kopiert haben, die folgenden Variablen:

    Variable Description
    AZURE_OPENAI_ENDPOINT Azure OpenAI-Endpunkt (kopiert von der Startseite des Foundry-Portals).
    AZURE_OPENAI_DEPLOYMENT_NAME Modellname in der Bereitstellung (kopiert aus dem Modell-Playground im New Foundry Portal).

    Note

    Um das Lernprogramm einfach zu halten, verwenden Sie diese Variablen in env , anstatt sie mit App-Einstellungen in App Service zu überschreiben.

    Note

    Um das Lernprogramm einfach zu halten, verwenden Sie diese Variablen in env , anstatt sie mit App-Einstellungen in App Service zu überschreiben.

    Die Werte in .env konfigurieren die ausgehende Verbindung der App zu Foundry. AZURE_AI_FOUNDRY_ACCOUNT_CLIENT_ID konfiguriert die separate eingehende Foundry-to-App-Service OpenAPI-Verbindung und wird in der AZD-Umgebung gespeichert.

Die App Service-Authentifizierung läuft in Azure, nicht im lokalen Express-Prozess, sodass der lokale Testablauf unverändert bleibt.

  1. Melden Sie sich mit der Azure CLI bei Azure an:

    az login
    

    Dadurch kann die Azure Identity-Clientbibliothek im Beispielcode ein Authentifizierungstoken für den angemeldeten Benutzer empfangen. Denken Sie daran, dass Sie die erforderliche Rolle für diesen Benutzer zuvor hinzugefügt haben.

  2. Führen Sie die Anwendung lokal aus:

    npm run build
    npm start
    
  3. Wenn Sie sehen, dass Ihre Anwendung auf Port 3000 ausgeführt wird, wählen Sie "Im Browser öffnen" aus.

  4. Validiere beide Pivots separat:

    • LangGraph: Wählen Sie LangGraph Agent und bitten Sie den Agenten, eine Aufgabe zu erstellen. LangGraph ruft das In-Process-Aufgabentool auf.
    • Foundry Agent Service: Wählen Sie Foundry Agent aus und bitten Sie den Agenten, einen Task zu erstellen. Der entfernte Foundry-Agent ruft den bereitgestellten, geschützten /api/tasks Endpunkt mit verwalteter Identität auf.

    Die Aufgabe, die der Foundry-Agent erstellt, erscheint in der bereitgestellten App Service-Instanz, nicht in der lokalen In-Memory-Datenbank. Das Foundry OpenAPI-Tool verwendet immer die Server-URL, die im OpenAPI-Schema eingebettet ist.

  5. Stellen Sie ihre App-Änderungen wieder im GitHub-Codespace bereit.

    azd up
    
  6. Navigiere zur bereitgestellten Anwendung, melde dich an und teste beide Pivots. Erstellen und listen Sie Aufgaben mit dem LangGraph Agent auf und erstellen und listen Sie dann Aufgaben mit dem Foundry Agent. Überprüfen Sie, dass beide Pivots die Aufgabenliste aktualisieren.

Häufig gestellte Fragen

Wie füge ich dem Foundry-Agenten Retrieval-Augmented Generation (RAG) hinzu?

Diese Anleitung gilt für den Foundry Agent Service-Weg in diesem Tutorial. Es ändert nicht die im anderen Tab gezeigten Implementierungen von LangGraph, Semantischer Kernel oder Microsoft Agent Framework.

Erstellen oder wählen Sie eine Foundry IQ Wissensdatenbank und verbinden Sie diese dann mit dem Foundry Agent Service Agenten. Die Verbindung wird dem Agenten als verwaltetes MCP-Wissenswerkzeug zur Verfügung gestellt.

Der App Service-Code ruft denselben Agenten weiterhin namentlich über seinen bestehenden Foundry-Client und agent_referenceauf. Die Webanwendung benötigt keine direkte Azure KI-Suche-Integration oder einen eigenen MCP-Client. Wenn die Benutzeroberfläche Quellen anzeigt, verarbeiten Sie die vom Agenten zurückgegebenen Zitationsanmerkungen.

Welche verwaltete Identität verwendet jede Verbindung?

Richtung Identität
App Service ruft Foundry App Service systemseitig zugewiesene Identität
Foundry OpenAPI-Toolaufrufe /api/tasks Parent-Foundry-Ressource: vom System zugewiesene Identität

Der Projektendpunkt wählt das Projekt und den Agenten aus. Es bestimmt nicht die Identität, die das gehostete OpenAPI-Tool verwendet.

Bereinigen von Ressourcen

Wenn Sie mit der Anwendung fertig sind, können Sie die App Service-Ressourcen löschen, um weitere Kosten zu vermeiden:

azd down --purge

Dann lösche die Foundry-Ressource, falls du sie separat erstellt hast.

Weitere Ressourcen