在 Foundry Agent Service (Node.js) 中新增 App Service 應用程式作為工具

在這個教學中,你將學習如何透過 OpenAPI 揭露 Express.js 應用程式的功能,將其作為工具加入 Foundry Agent Service,並在代理的遊樂場中用自然語言與你的應用程式互動。

如果您的網頁應用程式已有實用功能,如購物、飯店預訂或資料管理,您可以輕鬆透過 Foundry Agent Service 讓 AI 代理使用這些功能。 只要將 OpenAPI 架構新增至您的應用程式,您即可讓代理程式在回應使用者的提示時瞭解並使用應用程式的功能。 這表示您的應用程式可以執行的任何動作,您的 AI 代理程式也可以執行,除了為您的應用程式建立 OpenAPI 端點之外,也不需要付出最少的努力。 在本教學課程中,您會從簡單的 to-do 清單應用程式開始。 最後,您將能夠透過對話式 AI 建立、更新及管理使用代理程式的工作。

此螢幕擷取畫面顯示使用 OpenAPI 工具採取動作之交談中間的代理程式遊樂場。

  • 將 OpenAPI 功能新增至 Web 應用程式。
  • 確保 OpenAPI 架構與 Foundry Agent Service 相容。
  • 在 Foundry Agent Service 中註冊你的應用程式為 OpenAPI 工具。
  • 在代理程式遊樂場中測試您的代理程式。

Prerequisites

本教學課程假設您使用教學 課程:將 Node.js + MongoDB Web 應用程式部署至 Azure 中使用的範例。

至少,在 GitHub Codespaces 中開啟 範例應用程式 ,並執行 azd up來部署應用程式。

在 GitHub Codespaces 中開啟 中開啟

將 OpenAPI 功能新增至 Web 應用程式

  1. 在 codespace 終端機中,將 NuGet swagger-jsdoc NPM 套件新增至您的專案:

    npm install swagger-jsdoc
    
  2. 開啟 routes/index.js。 在檔案底部,上述 module.exports = router; 新增下列 API 函式。 為了讓它們與 Foundry Agent Service 相容,你必須在註解中指定operationId屬性(參見@swagger》)。

    router.get('/schema', function(req, res, next) {
      try {
        const swaggerJsdoc = require('swagger-jsdoc');
    
        res.json(
          swaggerJsdoc(
            {
              definition: {
                openapi: '3.0.0',
                servers: [
                  {
                    url: `${req.protocol}://${req.get('host')}`,
                    description: 'Task API',
                  },
                ],
              },
              apis: ['./routes/*.js'],
            }
          )
        );
      } catch (error) {
        res.status(500).json({ error: error.message });
      }
    });
    
    /**
     * @swagger
     * /api/tasks:
     *   get:
     *     summary: Get all tasks
     *     operationId: getAllTasks
     *     responses:
     *       200:
     *         description: List of tasks
     */
    router.get('/api/tasks', async function(req, res, next) {
      try {
        const tasks = await Task.find();
        res.json(tasks);
      } catch (error) {
        res.status(500).json({ error: error.message });
      }
    });
    
    /**
     * @swagger
     * /api/tasks/{id}:
     *   get:
     *     summary: Get task by ID
     *     operationId: getTaskById
     *     parameters:
     *       - in: path
     *         name: id
     *         required: true
     *         schema:
     *           type: string
     *     responses:
     *       200:
     *         description: Task details
     */
    router.get('/api/tasks/:id', async function(req, res, next) {
      try {
        const task = await Task.findById(req.params.id);
        res.json(task);
      } catch (error) {
        res.status(404).json({ error: error.message });
      }
    });
    
    /**
     * @swagger
     * /api/tasks:
     *   post:
     *     summary: Create a new task
     *     operationId: createTask
     *     requestBody:
     *       required: true
     *       content:
     *         application/json:
     *           schema:
     *             type: object
     *             properties:
     *               taskName:
     *                 type: string
     *     responses:
     *       201:
     *         description: Task created
     */
    router.post('/api/tasks', async function(req, res, next) {
      try {
        // Set createDate to current timestamp when creating a task
        const taskData = {
          ...req.body,
          createDate: new Date()
        };
    
        const task = new Task(taskData);
        await task.save();
        res.status(201).json(task);
      } catch (error) {
        res.status(400).json({ error: error.message });
      }
    });
    
    /**
     * @swagger
     * /api/tasks/{id}:
     *   put:
     *     summary: Update a task
     *     operationId: updateTask
     *     parameters:
     *       - in: path
     *         name: id
     *         required: true
     *         schema:
     *           type: string
     *     requestBody:
     *       required: true
     *       content:
     *         application/json:
     *           schema:
     *             type: object
     *             properties:
     *               taskName:
     *                 type: string
     *               completed:
     *                 type: boolean
     *     responses:
     *       200:
     *         description: Task updated
     */
    router.put('/api/tasks/:id', async function(req, res, next) {
      try {
        // If completed is being set to true, also set completedDate
        if (req.body.completed === true) {
          req.body.completedDate = new Date();
        }
    
        const task = await Task.findByIdAndUpdate(req.params.id, req.body, { new: true });
        res.json(task);
      } catch (error) {
        res.status(400).json({ error: error.message });
      }
    });
    
    /**
     * @swagger
     * /api/tasks/{id}:
     *   delete:
     *     summary: Delete a task
     *     operationId: deleteTask
     *     parameters:
     *       - in: path
     *         name: id
     *         required: true
     *         schema:
     *           type: string
     *     responses:
     *       200:
     *         description: Task deleted
     */
    router.delete('/api/tasks/:id', async function(req, res, next) {
      try {
        const task = await Task.findByIdAndDelete(req.params.id);
        res.json({ message: 'Task deleted successfully', task });
      } catch (error) {
        res.status(404).json({ error: error.message });
      }
    });
    

    此程式代碼會複製現有路由的功能,這是不必要的,但為了簡單起見,您將保留它。 最佳做法是將應用程式邏輯移至共用函式,然後從MVC路由和OpenAPI路由呼叫它們。

  3. 在codespace終端機中,使用 npm start執行應用程式。

  4. 選取 [在瀏覽器中開啟]。

  5. 將 新增 /schema 至 URL 以檢視 OpenAPI 架構。

  6. 回到 Codespace 終端機,藉由提交變更(GitHub Actions 方法)或執行 azd up (Azure Developer CLI 方法)來部署變更。

  7. 部署變更之後,請流覽至 https://<your-app's-url>/schema 並複製架構以供稍後使用。

在 Microsoft Foundry 建立代理程式

備註

這些步驟使用了新的 Foundry 入口網站。

  1. 在 Foundry 入口網站,右上角選單中選擇 「New Foundry」。

  2. 如果你是第一次進入新的 Foundry 入口網站,請選擇專案名稱並選擇 建立新專案。

  3. 給你的專案取個名字,然後選擇 「創建」。

  4. 選擇 開始建置,然後 建立代理。

  5. 給你的經紀人一個名字,然後選擇 「建立」。 當代理程式準備好後,您應該會看到代理程式遊樂場。

    請注意 您可以使用的模型和可用的區域。

  6. 在代理遊玩場中,展開 「工具 」並選擇 「新增>自訂>OpenAPI 工具>」「建立」。

  7. 給工具一個名稱和描述。 在 OpenAPI 3.0+ 的架構 框裡,貼上你之前複製的架構。

  8. 選擇 建立工具。

  9. 選取 [儲存]。

小提示

在本教學課程中,OpenAPI 工具會設定為匿名呼叫您的應用程式,無需驗證。 針對生產案例,您應該使用受控識別驗證來保護工具。 如需逐步操作說明,請參閱 Foundry Agent Service 的安全 OpenAPI 端點。

測試代理程式

  1. 在 指示中,提供一些簡單的指示,例如 「請使用todosApp工具來協助管理工作」。

  2. 使用下列提示建議與客服代理聊天:

    • 顯示所有任務。
    • 建立一個名為“想出三個生菜笑話”的任務。
    • 將其改為「想出三個敲門笑話」。

    此螢幕擷取畫面顯示使用 OpenAPI 工具採取動作之交談中間的代理程式遊樂場。

安全性最佳做法

透過 Azure App Service 中的 OpenAPI 公開 API 時,請遵循下列安全性最佳做法:

  • 驗證和授權:使用 Microsoft Entra 驗證保護您的 OpenAPI 端點。 如需逐步操作說明,請參閱 Foundry Agent Service 的安全 OpenAPI 端點。 您也可以 使用 Microsoft Entra ID 保護 Azure API 管理 後方的端點,並確保只有授權的使用者或代理程式才能存取工具。
  • 驗證輸入數據: 請一律驗證傳入數據,以防止無效或惡意輸入。 針對 Node.js 應用程式,請使用 express-validator 之類的連結庫來強制執行數據驗證規則。 如需最佳做法和實作詳細數據,請參閱其檔。
  • 使用 HTTPS: 此範例依賴 Azure App Service,其預設會強制執行 HTTPS,並提供免費的 TLS/SSL 憑證來加密傳輸中的數據。
  • 限制 CORS: 僅限將跨原始來源資源分享 (CORS) 限制為受信任的網域。 如需詳細資訊,請參閱 啟用CORS。
  • 套用速率限制: 使用 API 管理 或自定義中間件來防止濫用和阻斷服務攻擊。
  • 隱藏敏感性端點: 請避免在您的 OpenAPI 架構中公開內部或系統管理員 API。
  • 檢閱 OpenAPI 架構: 請確定您的 OpenAPI 架構不會洩漏敏感性資訊(例如內部 URL、秘密或實作詳細數據)。
  • 保持相依性更新: 定期更新 NuGet 套件並監視安全性公告。
  • 監視和記錄活動: 啟用記錄和監視存取,以偵測可疑的活動。
  • 使用受控識別: 呼叫其他 Azure 服務時,請使用受控識別,而不是硬式編碼認證。

如需詳細資訊,請參閱 保護 App Service 應用程式和REST API 安全性的最佳做法。

後續步驟

您至此已經啟用 App Service 應用程式,可讓 Foundry Agent Service 當作工具使用,並透過自然語言在代理程式的環境與應用程式的 API 互動。 接著,你可以繼續在 Foundry 入口網站為代理程式新增功能,或使用 Microsoft Foundry SDK 或 REST API 將其整合到自己的應用程式中,或將其部署為更大解決方案的一部分。 在 Microsoft Foundry 中建立的代理可以在雲端執行、整合進聊天機器人,或嵌入網頁和行動應用程式中。

若要採取下一個步驟,並瞭解如何直接在 Azure App Service 內執行代理程式,請參閱下列教學課程:

更多資源