Självstudie: Skapa en agentisk webbapp i Azure App Service med LangGraph eller Foundry Agent Service (Node.js)

Den här tutorialen visar hur du lägger till agent-kapacitet i en befintlig datadriven Express.js CRUD-applikation. Det gör detta med två olika metoder: LangGraph och Foundry Agent Service.

Om ditt webbprogram redan har användbara funktioner, till exempel shopping, hotellbokning eller datahantering, är det relativt enkelt att lägga till agentfunktioner i webbappen genom att omsluta funktionerna i ett plugin-program (för LangGraph) eller som en OpenAPI-slutpunkt (för Foundry Agent Service). I den här handledningen börjar du med en enkel to-do-listapp. I slutet kan du skapa, uppdatera och hantera uppgifter med en agent i en App Service-app.

Med både LangGraph och Foundry Agent Service kan du skapa agentiska webbprogram med AI-drivna funktioner. LangGraph liknar Microsoft Semantic Kernel och är ett SDK, men semantisk kernel stöder för närvarande inte JavaScript. Följande tabell visar några av övervägandena och kompromisserna:

Consideration LangGraph Gjuteritjänstagent
Performance Snabb (körs lokalt) Långsammare (hanterad fjärrtjänst)
Development Fullständig kod, maximal kontroll Låg kod, snabb integrering
Testing Manuella tester och enhetstester i kod Inbyggd lekplats för snabb testning
Scalability App-managed Azure-hanterad, skalad automatiskt
Skyddsräcken Anpassad implementation krävs Inbyggd innehållssäkerhet och moderering
Identitet Anpassad implementation krävs Inbyggt agent-ID och autentisering
Enterprise Anpassad integrering krävs Inbyggd Microsoft 365/Teams-distribution och integrerade Verktygsanrop för Microsoft 365.

I den distribuerade appen kräver App Service-autentisering Microsoft Entra-inloggning för både webbläsarens gränssnitt och API:erna. LangGraph körs i App Service och anropar aktivitetstjänsten direkt. Foundry Agent Service körs på distans och anropar det skyddade uppgifts-API:et via sitt OpenAPI-verktyg.

I den här tutorialen lär du dig följande:

  • Konvertera befintliga appfunktioner till ett plugin-program för LangGraph.
  • Lägg till plugin-programmet i en LangGraph-agent och använd det i en webbapp.
  • Konvertera befintliga appfunktioner till en OpenAPI-slutpunkt för Foundry Agent Service.
  • Anropa en Foundry-agent i en webbapplikation.
  • Tilldela nödvändiga behörigheter för hanterad identitetsanslutning.
  • Skydda en App Service-webbapp och dess API:er med Microsoft Entra ID.
  • Konfigurera ett Foundry OpenAPI-verktyg för att anropa skyddade App Service-API:er med hanterad identitet.

Prerequisites

Öppna exemplet med Codespaces

Det enklaste sättet att komma igång är att använda GitHub Codespaces, som ger en fullständig utvecklingsmiljö med alla nödvändiga verktyg förinstallerade.

  1. Navigera till GitHub-lagringsplatsen på https://github.com/Azure-Samples/app-service-agentic-langgraph-foundry-node.

  2. Välj knappen Kod , välj fliken Codespaces och välj Skapa kodområde på main.

  3. Vänta en stund tills kodområdet initieras. När du är klar visas en fullständigt konfigurerad utvecklingsmiljö i webbläsaren.

  4. Kör programmet lokalt:

    npm install
    npm run build
    npm start
    
  5. När du ser Din applikation körs på port 3000 och är tillgänglig, väljer du Öppna i webbläsare och lägger till några uppgifter.

    Agenterna är inte helt konfigurerade så de fungerar inte än. Du konfigurerar dem senare.

Granska agentkoden

Båda metoderna använder samma implementeringsmönster, där agenten initieras vid programstart, och svarar på användarmeddelanden via POST-begäranden.

LangGraphTaskAgent Initieras i konstruktorn i src/agents/LangGraphTaskAgent.ts. Initieringskoden gör följande:

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

Det distribuerade exemplet skyddas av App Service-autentisering och använder en servervald LangGraph-tråd. När du bearbetar användarmeddelanden anropar invoke() agenten med användarens meddelande och serverhanterad tråd-ID:

private readonly conversationThreadId = 'authenticated-conversation';

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

Distribuera exempelprogrammet

Exempelarkivet innehåller en Azure Developer CLI (AZD)-mall, som skapar en App Service-app och distribuerar din exempelapplikation. Mallen möjliggör en systemtilldelad hanterad identitet för utgående Azure AI-anrop och konfigurerar App Service-autentisering med Microsoft Entra ID. För mer information om den underliggande autentiseringskonfigurationen, se Secure OpenAPI-endpoints för Foundry Agent Service.

  1. I terminalen, logga in på Azure genom att använda Azure Developer CLI:

    azd auth login
    

    Följ anvisningarna för att slutföra autentiseringsprocessen.

  2. Distribuera Azure App Service-appen genom att använda AZD-mallen:

    azd up
    
  3. När du uppmanas, ge följande svar:

    Question Answer
    Ange ett nytt miljönamn: Skriv ett unikt namn.
    Välj en Azure-prenumeration som ska användas: Välj prenumerationen.
    Välj en resursgrupp som ska användas: Välj Skapa en ny resursgrupp.
    Välj en plats för att skapa resursgruppen i: Välj Sweden Central.
    Ange ett namn för den nya resursgruppen: Skriv Retur.
  4. Leta reda på appens URL i AZD-utdata. Kopiera också värdet för Foundry OpenAPI managed identity audience för att använda senare. Utdata ser ut så här:

     Deploying services (azd deploy)
    
       (✓) Done: Deploying service web
       - Endpoint: <URL>
    
     Foundry OpenAPI managed identity audience:
     api://<generated-client-id>
     
  5. Öppna App Service-endpointen från AZD-utgången.

  6. När Microsoft uppmanar dig loggar du in med ett konto i distributionsklientorganisationen och verifierar att uppgiftslistan läses in.

  7. I samma autentiserade webbläsare, öppna det automatiskt genererade OpenAPI-schemat vid https://<app-name>.azurewebsites.net/api/schema.

  8. Kopiera eller spara det genererade OpenAPI-schemat. Du använder den i Foundry Agent Service-pivoten.

    Note

    App Service-autentisering returnerar en HTTP 302-omdirigering för oautentiserade webbläsarförfrågningar. Detta exempel innehåller både ett webbläsargränssnitt och API:er, så omdirigeringen ger en användbar inloggningsupplevelse. API-endast appar använder vanligtvis HTTP 401 istället.

    Du har nu en autentiserad App Service-app. Dess systemtilldelade hanterade identitet används för utgående Foundry-anrop. En separat användartilldelad hanterad identitet möjliggör autentiseringsuppgifter utan hemligheter för autentisering för App Service.

Skapa och konfigurera Microsoft Foundry-resursen

  1. I Foundry-portalen, skapa ett projekt.

  2. Distribuera en modell (se Microsoft Foundry Snabbstart: Skapa resurser).

  3. Kopiera modellnamnet överst på modelllekplatsen.

  4. På startsidan, kopiera Azure OpenAI-endpointen för senare.

Tilldela nödvändiga behörigheter

  1. I Foundry-portalen, välj Hantera i toppmenyn.

  2. I Project-detaljer, välj Parent-resursen för ditt projekt och välj sedan Open i Azure-portalen.

    Från Azure-portalen kan du tilldela rollbaserad åtkomst till resursen.

  3. Lägg till följande roll både för App Service-appens hanterade identitet och för användaren du använder med az login:

    Målresurs Obligatorisk roll Behövs för
    Gjuteri Cognitive Services OpenAI-användare Tjänsten för chattens slutförande i Microsoft Agent Framework.

    Instruktioner finns i Tilldela Azure-roller med hjälp av Azure-portalen.

Konfigurera anslutningsvariabler i exempelprogrammet

  1. Öppna .env. Med de värden som du kopierade tidigare från Foundry-portalen konfigurerar du följande variabler:

    Variable Description
    AZURE_OPENAI_ENDPOINT Azure OpenAI endpoint (kopierat från Foundry-portalens startsida).
    AZURE_OPENAI_DEPLOYMENT_NAME Modellnamn i distributionen (kopieras från modelllekplatsen i den nya Foundry-portalen).

    Note

    För att förenkla handledningen använder du dessa variabler i .env istället för att skriva över dem med appinställningar i App Service.

    Note

    För att förenkla handledningen använder du dessa variabler i .env istället för att skriva över dem med appinställningar i App Service.

    Värdena i .env konfigurerar appens utgående anslutning till Foundry. AZURE_AI_FOUNDRY_ACCOUNT_CLIENT_ID konfigurerar den separata inkommande Foundry-to-App-Service OpenAPI-anslutningen och lagras i AZD-miljön.

App Service-autentisering körs i Azure, inte i den lokala Express-processen, så det lokala testarbetsflödet förblir oförändrat.

  1. Logga in på Azure med Azure CLI:

    az login
    

    På så sätt kan Azure Identity-klientbiblioteket i exempelkoden ta emot en autentiseringstoken för den inloggade användaren. Kom ihåg att du lade till den roll som krävs för den här användaren tidigare.

  2. Kör programmet lokalt:

    npm run build
    npm start
    
  3. När du ser Ditt program som körs på port 3000 är tillgängligt väljer du Öppna i webbläsaren.

  4. Validera båda pivoterna separat:

    • LangGraph: Välj LangGraph Agent och be agenten skapa en uppgift. LangGraph anropar det pågående uppgiftsverktyget.
    • Gjuteriagenttjänst: Välj Foundry Agent och be agenten skapa en uppgift. Den fjärrstyrda Foundry-agenten anropar den distribuerade, skyddade /api/tasks endpointen med hanterad identitet.

    Uppgiften som Foundry-agenten skapar visas i den distribuerade App Service-instansen, inte i den lokala minnesdatabasen. Foundry OpenAPI-verktyget använder alltid serverns URL inbäddad i OpenAPI-schemat.

  5. Tillbaka i GitHub-kodområdet distribuerar du dina appändringar.

    azd up
    
  6. Gå till den distribuerade applikationen, logga in och testa båda pivoterna. Skapa och lista uppgifter med LangGraph Agent, och skapa och lista sedan uppgifter med Foundry Agent. Kontrollera att båda pivoterna uppdaterar uppgiftslistan.

Vanliga frågor och svar

Hur lägger jag till retrieval-augmented generation (RAG) i Foundry-agenten?

Denna vägledning gäller för Foundry Agent Service-vägen i denna handledning. Det ändrar inte de implementationer av LangGraph, Semantic Kernel eller Microsoft Agent Framework som visas i den andra fliken.

Skapa eller välj en kunskapsbas för Foundry IQ, och koppla sedan kunskapsbasen till Foundry Agent Service-agenten. Kopplingen exponeras för agenten som ett hanterat MCP-kunskapsverktyg.

App Service-koden fortsätter att anropa samma agent vid namn via sin befintliga Foundry-klient och agent_reference. Webbappen behöver inte en direkt Azure AI-sökning-integration eller en egen MCP-klient. Om användargränssnittet visar källor, bearbeta citeringsanteckningarna som agenten returnerat.

Vilken managed identity använder varje anslutning?

Riktning Identitet
App Service anropar Foundry App Service-systemtilldelad identitet
Foundry OpenAPI-verktygsanrop /api/tasks Identitet tilldelad av Parent Foundry resurssystem

Projektets endpoint väljer projektet och agenten. Det bestämmer inte identiteten som det hostade OpenAPI-verktyget använder.

Rensa resurser

När du är klar med programmet kan du ta bort App Service-resurserna för att undvika ytterligare kostnader:

azd down --purge

Ta sedan bort Foundry-resursen om du skapade den separat.

Fler resurser