共用方式為


教學:在 Azure App Service 中使用 LangGraph 或 Foundry Agent Service(Node.js) 建立代理型網頁應用

本教學課程示範如何將代理程式功能新增至現有的數據驅動 Express.js CRUD 應用程式。 它採用兩種不同的方法:LangGraph 與 Foundry Agent Service。

如果你的網頁應用程式已有實用功能,例如購物、飯店預訂或資料管理,透過外掛(LangGraph)或 OpenAPI 端點(Foundry Agent Service)來加入代理功能相對簡單。 在本教學課程中,您會從簡單的 to-do 清單應用程式開始。 最後,您將能夠使用 App Service 應用程式中的代理程式來建立、更新及管理工作。

LangGraph 與 Foundry Agent Service 皆能讓你打造具備 AI 驅動功能的代理型網頁應用程式。 LangGraph 與 Microsoft Semantic Kernel 類似,而且是 SDK,但 Semantic Kernel 目前不支援 JavaScript。 下表顯示一些考慮和取捨:

Consideration LangGraph 鑄造代理服務
Performance 快速 (在本機執行) 速度較慢(受控、遠端服務)
Development 完整程式代碼,最大控制 低程式代碼,快速整合
Testing 程序代碼中的手動/單元測試 用於快速測試的內建遊樂場
Scalability 應用程式受控 Azure 受控、自動調整
安全護欄 需要自訂實作 內建內容安全與管理
身份 需要自訂實作 內建代理識別碼與認證
Enterprise 自訂整合需求 內建 Microsoft 365/Teams 部署及 Microsoft 365 整合工具的呼叫功能。

在本教學課程中,您將瞭解如何:

  • 將現有的應用程式功能轉換成 LangGraph 的外掛程式。
  • 將外掛程式新增至 LangGraph 代理程式,並在 Web 應用程式中使用它。
  • 將現有應用程式功能轉換為 Foundry Agent Service 的 OpenAPI 端點。
  • 在網頁應用程式中打電話給 Foundry 的代理人。
  • 指派受控識別連線所需的許可權。

Prerequisites

使用 Codespaces 開啟範例

最簡單的開始使用方式是使用 GitHub Codespaces,其提供預安裝所有必要的工具的完整開發環境。

  1. 流覽至 位於 https://github.com/Azure-Samples/app-service-agentic-langgraph-foundry-node的 GitHub 存放庫。

  2. 選取 [ 程序代碼] 按鈕,選取 [ Codespaces ] 索引標籤,然後選取 [在 main 上建立程式代碼空間]。

  3. 請稍候片刻,讓 Codespace 初始化。 準備好時,您會在瀏覽器中看到完整設定的開發環境。

  4. 在本機執行應用程式:

    npm install
    npm run build
    npm start
    
  5. 當您看到 您的應用程式已可在埠 3000 上使用時,請選取「在瀏覽器中開啟」,然後新增一些工作。

    代理程式尚未完全設定,因此還無法運作。 您稍後會設定它們。

檢閱代理程序代碼

這兩種方法都會使用相同的實作模式,其中代理程式會在應用程式啟動時初始化,並透過POST要求回應使用者訊息。

LangGraphTaskAgent會在 src/agents/LangGraphTaskAgent.ts 的建構函式中初始化 。 初始化程式代碼會執行下列動作:

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

在處理使用者訊息時,代理會根據 invoke() 使用者的訊息與會話設定來呼叫,以確保對話連續性:

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

部署範例應用程式

範例存放庫包含 Azure 開發人員 CLI (AZD) 範本,此範本會建立具有受控識別的 App Service 應用程式,並部署範例應用程式。

  1. 在終端機中,使用 Azure 開發人員 CLI 登入 Azure:

    azd auth login
    

    請遵循指示來完成驗證程式。

  2. 使用 AZD 樣本部署 Azure App Service 應用程式:

    azd up
    
  3. 出現提示時,請提供下列答案:

    Question Answer
    輸入新的環境名稱: 輸入唯一名稱。
    選取要使用的 Azure 訂用帳戶: 選取訂用帳戶。
    挑選要使用的資源群組: 選取 [建立新的資源群組]
    選取要用來建立資源群組的位置: 選取 [瑞典中部]。
    輸入新資源群組的名稱: 輸入 Enter
  4. 在 AZD 輸出中,尋找您應用程式的 URL,然後使用瀏覽器開啟該網址。 AZD 輸出中 URL 看起來像這樣:

     Deploying services (azd deploy)
    
       (✓) Done: Deploying service web
       - Endpoint: <URL>
     
  5. https://....azurewebsites.net/api/schema 路徑上開啟自動生成的 OpenAPI 模式。 您稍後需要此架構。

    您現在有具有系統指派受控識別的 App Service 應用程式。

建立並設定 Microsoft Foundry 資源

  1. Foundry 入口網站中,確保頂部的 New Foundry 單選按鈕設為啟用,並建立專案。

  2. 部署你選擇的模型(參見 Microsoft Foundry 快速入門:建立資源)。

  3. 從模型遊樂場頂端複製模型名稱。

  4. 取得 Azure OpenAI 端點最簡單的方式仍是從經典入口網站。 選擇 New Foundry 單選單選按鈕,然後選擇 Azure OpenAI,然後將 URL 複製到 Azure OpenAI 端點 以備後用。

    顯示如何在 Foundry 入口網站中複製 OpenAI 端點和 Foundry 專案端點的螢幕快照。

指派必要的許可權

  1. 在新 Foundry 入口網站的頂端選單中,選擇 操作,然後選擇 管理員。在你的 Foundry 專案那一列,你應該會看到兩個連結。 Name 欄位中的資源是 Foundry 專案資源,Parent 資源 欄位中的資源是 Foundry 資源。

    顯示如何快速切換至 Foundry 資源或 Foundry 專案資源的螢幕擷取畫面。

  2. 父資源 中選擇 Foundry 資源,然後 在 Azure 入口網站選擇管理此資源。 從 Azure 入口網站,你可以將該資源的角色存取權分配給已部署的網頁應用程式。

  3. 為 App Service 應用程式的管理身份新增以下角色:

    目標資源 必要角色 所需
    鑄造廠 認知服務 OpenAI 使用者 Microsoft Agent Framework 中的聊天完成服務。

    若需指示,請參閱使用 Azure 入口網站指派 Azure 角色

在範例應用程式中設定連線變數

  1. 開啟 .env。 利用你之前從 Foundry 入口複製的數值,設定以下變數:

    Variable Description
    AZURE_OPENAI_ENDPOINT Azure OpenAI 端點(從經典 Foundry 入口複製)。
    AZURE_OPENAI_DEPLOYMENT_NAME 部署中的模型名稱 (從新 Foundry 入口網站的模型遊樂場複製)。

    Note

    若要讓教學課程保持簡單,您將在 .env 中使用這些變數,而不用 App Service 中的應用程式設定覆寫這些變數。

    Note

    若要讓教學課程保持簡單,您將在 .env 中使用這些變數,而不用 App Service 中的應用程式設定覆寫這些變數。

  2. 使用 Azure CLI 登入 Azure:

    az login
    

    這可讓範例程式代碼中的 Azure 身分識別客戶端連結庫接收已登入使用者的驗證令牌。 請記住,您稍早已新增此使用者的必要角色。

  3. 在本機執行應用程式:

    npm run build
    npm start
    
  4. 當您的應用程式在埠 3000 上執行並可用時,請選取 在瀏覽器中開啟

  5. 選取 [LangGraph 代理程式 ] 連結和 [Foundry 代理程式 ] 連結,以試用聊天介面。 如果你收到回應,代表你的應用程式正在成功連接到 Microsoft Foundry 資源。

  6. 回到 GitHub Codespace,部署您的應用程式變更。

    azd up
    
  7. 再次流覽至已部署的應用程式,並測試聊天代理程式。

清理資源

使用應用程式完成時,您可以刪除 App Service 資源,以避免產生進一步的成本:

azd down --purge

由於 AZD 範本沒有包含 Microsoft Foundry 資源,如果你想刪除它們,就必須手動刪除。

更多資源