你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

在 Foundry 代理服务中将应用服务应用添加为工具(Node.js)

本教程介绍如何通过 OpenAPI 公开 Express.js 应用的功能,将其添加为 Foundry 代理服务的工具,并在代理场中使用自然语言与应用交互。

如果 Web 应用程序已有有用的功能(如购物、酒店预订或数据管理),则可以轻松地在 Foundry 代理服务中向 AI 代理提供这些功能。 只需将 OpenAPI 架构添加到应用,即可在代理响应用户的提示时了解和使用应用的功能。 这意味着你的应用可以执行的任何作,AI 代理也可以执行,除了为应用创建 OpenAPI 终结点之外,也只需付出最少的努力。 本教程从简单的 to-do 列表应用开始。 最后,你将能够通过对话式 AI 通过代理创建、更新和管理任务。

屏幕截图显示使用 OpenAPI 工具执行操作的对话中间的代理操场。

  • 将 OpenAPI 功能添加到 Web 应用。
  • 确保 OpenAPI 架构与 Foundry 代理服务兼容。
  • 在 Foundry 代理服务中将应用注册为 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 代理服务兼容,必须在批注中operationId指定@swagger属性(请参阅如何将 Foundry 代理服务与 OpenAPI 指定工具:先决条件配合使用)。

    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 开发人员 CLI 方法)来部署更改。

  7. 部署更改后,导航到 https://<your-app's-url>/schema 并复制架构以供以后使用。

在 Microsoft Foundry 中创建代理

  1. 按照以下步骤在 Foundry 门户中创建代理 :快速入门:创建新代理

    请注意 可以使用的模型和可用的区域

  2. 按照《如何使用 OpenAPI 规范工具》的步骤,选择新代理并使用 OpenAPI 3.0 指定的工具添加一个动作。

  3. “定义架构 ”页中,粘贴之前复制的架构。 检查并保存操作。

小窍门

在本教程中,OpenAPI 工具配置为在不进行身份验证的情况下匿名调用应用。 对于生产方案,应使用托管标识身份验证保护该工具。 有关分步说明,请参阅 Foundry 代理服务的安全 OpenAPI 端点

测试代理

  1. 如果代理测试区还没有在 Foundry 门户中打开,请选择代理,然后选择在测试区试用

  2. “说明”中,提供一些简单的说明,例如 “请使用 todosApp 工具来帮助管理任务”。

  3. 使用以下提示语与智能助手聊天:

    • 向我显示所有任务。
    • 创建一个名为“拿出三个生菜笑话”的任务。
    • 将其改为“想出三个敲门笑话。”

    屏幕截图显示使用 OpenAPI 工具执行操作的对话中间的代理操场。

安全最佳做法

在 Azure 应用服务中通过 OpenAPI 公开 API 时,请遵循以下安全最佳做法:

  • 身份验证和授权:使用 Microsoft Entra 身份验证保护 OpenAPI 终结点。 有关分步说明,请参阅 Foundry 代理服务的安全 OpenAPI 端点。 还可以 使用 Microsoft Entra ID 保护 Azure API 管理 背后的终结点,并确保只有经过授权的用户或代理才能访问这些工具。
  • 验证输入数据: 始终验证传入数据,以防止无效或恶意输入。 对于 Node.js 应用,请使用 快速验证程序 等库来强制实施数据验证规则。 有关最佳做法和实现详细信息,请参阅其文档。
  • 使用 HTTPS: 此示例依赖于 Azure 应用服务,该服务默认强制实施 HTTPS,并提供免费的 TLS/SSL 证书来加密传输中的数据。
  • 限制 CORS: 仅将跨域资源共享(CORS)限制为受信任的域。 有关详细信息,请参阅 “启用 CORS”。
  • 应用速率限制: 使用 API 管理 或自定义中间件防止滥用和拒绝服务攻击。
  • 隐藏敏感终结点: 避免在 OpenAPI 架构中公开内部或管理员 API。
  • 查看 OpenAPI 架构: 确保 OpenAPI 架构不会泄露敏感信息(例如内部 URL、机密或实现详细信息)。
  • 保持依赖项更新: 定期更新 NuGet 包并监视安全公告。
  • 监视和记录活动: 启用日志记录和监视访问以检测可疑活动。
  • 使用托管标识: 调用其他 Azure 服务时,请使用托管标识而不是硬编码凭据。

有关详细信息,请参阅 保护应用服务应用REST API 安全性的最佳做法

后续步骤

现在,你已启用应用服务应用,使其可以作为 Foundry 代理服务的工具,并在代理沙盒中通过自然语言与应用的 API 进行交互。 在这里,可以在 Foundry 门户中继续将功能添加到代理,使用 Microsoft Foundry SDK 或 REST API 将其集成到自己的应用程序中,或将其部署为更大的解决方案的一部分。 在 Microsoft Foundry 中创建的代理可以在云中运行、集成到聊天机器人中或嵌入在 Web 和移动应用中。

若要执行下一步,了解如何直接在 Azure 应用服务中运行代理,请参阅以下教程:

更多资源