Zelfstudie: Een agentische web-app bouwen in Azure App Service met LangGraph of Foundry Agent Service (Node.js)

In deze zelfstudie ziet u hoe u agentische mogelijkheden toevoegt aan een bestaande gegevensgestuurde Express.js CRUD-toepassing. Dit doet u met behulp van twee verschillende benaderingen: LangGraph en Foundry Agent Service.

Als uw webtoepassing al nuttige functies heeft, zoals winkelen, hotelreservering of gegevensbeheer, is het relatief eenvoudig om agentfunctionaliteit toe te voegen aan uw webtoepassing door deze functies in een invoegtoepassing (voor LangGraph) of als een OpenAPI-eindpunt (voor Foundry Agent Service) te verpakken. In deze handleiding begint u met een eenvoudige to-do lijstapp. Aan het einde kunt u taken maken, bijwerken en beheren met een agent in een App Service-app.

Met zowel LangGraph als Foundry Agent Service kunt u agentische webtoepassingen bouwen met AI-gestuurde mogelijkheden. LangGraph is vergelijkbaar met Microsoft Semantic Kernel en is een SDK, maar Semantische kernel biedt momenteel geen ondersteuning voor JavaScript. In de volgende tabel ziet u enkele overwegingen en afwegingen:

Consideration LangGraph Foundry Agentendienst
Performance Snel (lokaal uitgevoerd) Trager (beheerde, externe service)
Development Volledige code, maximaal beheer Low-code, snelle integratie
Testing Handmatige/eenheidstests in code Ingebouwde speeltuin voor snel testen
Scalability App-managed Door Azure beheerd, automatisch geschaald
Veiligheidsrails Aangepaste implementatie vereist Ingebouwde inhoudsveiligheid en toezicht
Identiteit Aangepaste implementatie vereist Ingebouwde agent-id en verificatie
Enterprise Aangepaste integratie vereist Ingebouwde microsoft 365/Teams-implementatie en geïntegreerde hulpprogramma-aanroepen van Microsoft 365.

In de geïmplementeerde app vereist App Service-authenticatie Microsoft Entra-aanmelding voor zowel de browserinterface als de API's. LangGraph draait binnen App Service en roept de taakservice direct aan. Foundry Agent Service draait op afstand en roept de beschermde taak-API aan via zijn OpenAPI-tool.

In deze handleiding leer je hoe je:

  • Bestaande app-functionaliteit converteren naar een invoegtoepassing voor LangGraph.
  • Voeg de invoegtoepassing toe aan een LangGraph-agent en gebruik deze in een web-app.
  • Bestaande app-functionaliteit converteren naar een OpenAPI-eindpunt voor Foundry Agent Service.
  • Roep een Foundry-agent aan in een web-app.
  • Wijs de vereiste machtigingen toe voor connectiviteit met beheerde identiteiten.
  • Bescherm een App Service webapp en haar API's met Microsoft Entra ID.
  • Configureer een Foundry OpenAPI-tool om beschermde App Service-API's met beheerde identiteit aan te roepen.

Prerequisites

Het voorbeeld openen met Codespaces

De eenvoudigste manier om aan de slag te gaan is door GitHub Codespaces te gebruiken. Dit biedt een volledige ontwikkelomgeving met alle vereiste hulpprogramma's die vooraf zijn geïnstalleerd.

  1. Navigeer naar de GitHub-opslagplaats op https://github.com/Azure-Samples/app-service-agentic-langgraph-foundry-node.

  2. Klik op de knop Code, selecteer het tabblad Codespaces en selecteer Codespace op main maken.

  3. Wacht even totdat uw Codespace is geïnitialiseerd. Wanneer u klaar bent, ziet u een volledig geconfigureerde ontwikkelomgeving in uw browser.

  4. Voer de toepassing lokaal uit:

    npm install
    npm run build
    npm start
    
  5. Wanneer u ziet dat uw toepassing wordt uitgevoerd op poort 3000 beschikbaar is, selecteert u Openen in browser en voegt u enkele taken toe.

    De agents zijn niet volledig geconfigureerd, zodat ze nog niet werken. U gaat ze later configureren.

De agentcode controleren

Beide benaderingen gebruiken hetzelfde implementatiepatroon, waarbij de agent wordt geïnitialiseerd bij het starten van de toepassing en reageert op gebruikersberichten door POST-aanvragen.

De LangGraphTaskAgent wordt geïnitialiseerd in de constructor in src/agents/LangGraphTaskAgent.ts. De initialisatiecode doet het volgende:

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

De geïmplementeerde sample is beschermd door App Service-authenticatie en gebruikt één door de server geselecteerde LangGraph-thread. Wanneer je gebruikersberichten verwerkt, roept de agent invoke() aan met het bericht van de gebruiker en de door de server beheerde thread-ID:

private readonly conversationThreadId = 'authenticated-conversation';

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

De voorbeeldtoepassing implementeren

De voorbeeldrepository bevat een Azure Developer CLI (AZD) sjabloon, die een App Service-app maakt en je voorbeeldapplicatie uitrolt. Het sjabloon maakt een door het systeem toegewezen beheerde identiteit mogelijk voor uitgaande Azure AI-aanroepen en configureert App Service-authenticatie met Microsoft Entra ID. Voor meer informatie over de onderliggende authenticatieconfiguratie, zie Secure OpenAPI-eindpunten voor Foundry Agent Service.

  1. Log in de terminal in bij Azure met behulp van Azure Developer CLI:

    azd auth login
    

    Volg de instructies om het verificatieproces te voltooien.

  2. Deploy de Azure App Service app met behulp van de AZD-template:

    azd up
    
  3. Geef de volgende antwoorden wanneer u hierom wordt gevraagd:

    Question Answer
    Voer een nieuwe omgevingsnaam in: Voer een unieke naam in.
    Selecteer een Azure-abonnement dat u wilt gebruiken: Selecteer het abonnement.
    Kies een resourcegroep die u wilt gebruiken: Selecteer Een nieuwe resourcegroep maken.
    Selecteer een locatie waarin u de resourcegroep wilt maken in: Selecteer Zweden - centraal.
    Voer een naam in voor de nieuwe resourcegroep: Typ Enter.
  4. Zoek in de AZD-uitvoer de URL van uw app. Kopieer ook de Foundry OpenAPI managed identity audience waarde voor later. De uitvoer ziet er als volgt uit:

     Deploying services (azd deploy)
    
       (✓) Done: Deploying service web
       - Endpoint: <URL>
    
     Foundry OpenAPI managed identity audience:
     api://<generated-client-id>
     
  5. Open het App Service-eindpunt vanuit de AZD-uitvoer.

  6. Wanneer Microsoft je ernaar vraagt, log dan in met een account in de deployment tenant en controleer of de takenlijst geladen is.

  7. Open in dezelfde geauthenticeerde browser het automatisch gegenereerde OpenAPI-schema op https://<app-name>.azurewebsites.net/api/schema.

  8. Kopieer of sla het gegenereerde OpenAPI-schema op. Je gebruikt het in de Foundry Agent Service-pivot.

    Note

    App Service-authenticatie geeft een HTTP 302-redirect terug voor niet-geauthenticeerde browserverzoeken. Dit voorbeeld bevat zowel een browserinterface als API's, waardoor de redirect een bruikbare aanmeldervaring biedt. API-only apps gebruiken vaak HTTP 401.

    Je hebt nu een geauthenticeerde App Service-app. De systeem-toegewezen beheerde identiteit wordt gebruikt voor uitgaande Foundry-oproepen. Een aparte, door de gebruiker toegewezen beheerde identiteit biedt inloggegevens zonder geheimen voor App Service-authenticatie.

De Microsoft Foundry-resource maken en configureren

  1. Maak in het Foundry-portaal een project.

  2. Een model van uw keuze implementeren (zie Snelstartgids voor Microsoft Foundry: Resources maken).

  3. Kopieer de naam van het model vanaf de bovenkant van de modelspeelplaats.

  4. Kopieer op de startpagina het Azure OpenAI-eindpunt voor later.

Vereiste machtigingen toewijzen

  1. Selecteer in het Foundry-portaal Beheren in het bovenste menu.

  2. Selecteer in Project details de Ouderresource van je project en selecteer vervolgens Open in Azure portal.

    Vanuit het Azure-portaal kun je rolgebaseerde toegang voor de resource toewijzen.

  3. Voeg de volgende rol toe voor zowel de beheerde identiteit van de App Service-app als de gebruiker waarmee je werkt az login:

    Doelresource Vereiste rol Vereist voor
    Gieterij Cognitive Services OpenAI-gebruiker De voltooiingsservice voor chats in Microsoft Agent Framework.

    Zie Azure-rollen toewijzen via Azure Portal voor instructies.

Verbindingsvariabelen configureren in uw voorbeeldtoepassing

  1. Open .env. Configureer de volgende variabelen met behulp van de waarden die u eerder hebt gekopieerd uit de Foundry-portal:

    Variable Description
    AZURE_OPENAI_ENDPOINT Azure OpenAI endpoint (gekopieerd van de Foundry portal homepage).
    AZURE_OPENAI_DEPLOYMENT_NAME Modelnaam in de implementatie (gekopieerd uit de modelspeeltuin in de nieuwe Foundry-portal).

    Note

    Als u de zelfstudie eenvoudig wilt houden, gebruikt u deze variabelen in .env in plaats van ze te overschrijven met app-instellingen in App Service.

    Note

    Als u de zelfstudie eenvoudig wilt houden, gebruikt u deze variabelen in .env in plaats van ze te overschrijven met app-instellingen in App Service.

    De waarden in .env configureren de uitgaande verbinding van de app met Foundry. AZURE_AI_FOUNDRY_ACCOUNT_CLIENT_ID configureert de afzonderlijke inkomende Foundry-to-App-Service OpenAPI-verbinding en wordt opgeslagen in de AZD-omgeving.

App Service-authenticatie draait in Azure, niet in het lokale Express-proces, dus de lokale testworkflow blijft ongewijzigd.

  1. Meld u aan bij Azure met de Azure CLI:

    az login
    

    Hierdoor kan de Azure Identity-clientbibliotheek in de voorbeeldcode een verificatietoken ontvangen voor de aangemelde gebruiker. Houd er rekening mee dat u de vereiste rol voor deze gebruiker eerder hebt toegevoegd.

  2. Voer de toepassing lokaal uit:

    npm run build
    npm start
    
  3. Wanneer uw toepassing draait op poort 3000 en beschikbaar is, selecteert u Openen in de browser.

  4. Controleer beide draaipunten afzonderlijk:

    • LangGraph: Selecteer LangGraph Agent en vraag de agent om een taak aan te maken. LangGraph roept de in-process tasktool aan.
    • Foundry Agent Service: Selecteer Foundry Agent en vraag de agent om een taak te maken. De externe Foundry-agent roept het uitgerolde, beschermde /api/tasks endpoint met beheerde identiteit aan.

    De taak die de Foundry-agent aanmaakt, verschijnt in de gedeployeerde App Service-instantie, niet in de lokale geheugendatabase. De Foundry OpenAPI-tool gebruikt altijd de server-URL die is ingebed in het OpenAPI-schema.

  5. Implementeer uw app-wijzigingen in de GitHub-codespace.

    azd up
    
  6. Navigeer naar de geïmplementeerde applicatie, log in en test beide pivots. Maak taken aan en lijst met de LangGraph Agent, en maak vervolgens taken aan en list ze met de Foundry Agent. Controleer of beide pivots de takenlijst bijwerken.

Veelgestelde vragen

Hoe voeg ik retrieval augmented generation (RAG) toe aan de Foundry-agent?

Deze richtlijn geldt voor het Foundry Agent Service-pad in deze tutorial. Het verandert niet de implementaties van LangGraph, Semantic Kernel of Microsoft Agent Framework die in het andere tabblad worden getoond.

Maak of selecteer een kennisbank van Foundry IQ, en verbind deze vervolgens met de Foundry Agent Service-agent. De verbinding wordt voor de agent beschikbaar gesteld als een beheerde MCP-kennistool.

De App Service-code blijft dezelfde agent bij naam aanroepen via zijn bestaande Foundry-client en agent_reference. De webapp heeft geen directe integratie met Azure AI Zoeken of een eigen MCP-client nodig. Als de gebruikersinterface bronnen toont, verwerk dan de citatie-annotaties die door de agent zijn teruggegeven.

Welke beheerde identiteit gebruikt elke verbinding?

Richting Identiteit
App Service roept Foundry aan App Service door het systeem toegewezen identiteit
Foundry OpenAPI hulpprogramma-aanroepen /api/tasks Bovenliggende Foundry-resource met door het systeem toegewezen identiteit

Het project-eindpunt selecteert het project en de agent. Het bepaalt niet de identiteit die de gehoste OpenAPI-tool gebruikt.

De hulpbronnen opschonen

Wanneer u klaar bent met de toepassing, kunt u de App Service-resources verwijderen om verdere kosten te voorkomen:

azd down --purge

Verwijder vervolgens de Foundry-bron als je die apart hebt aangemaakt.

Meer middelen