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

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

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

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

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

  • 将 OpenAPI 功能添加到 Web 应用。
  • 确保 OpenAPI 架构与 Foundry 代理服务兼容。
  • 在 Foundry 代理服务中将应用注册为 OpenAPI 工具。
  • 在智能体操场中测试智能体。

先决条件

本教程假定你使用的是教程中使用的示例 :将 ASP.NET Core 和 Azure SQL 数据库应用部署到 Azure 应用服务

至少,在 GitHub Codespaces 中打开 示例应用程序 ,并通过运行 azd up来部署应用。

将 OpenAPI 功能添加到 Web 应用

小窍门

可以通过在代理模式下告诉 GitHub Copilot 进行以下所有更改:

I'd like to add OpenAPI functionality to all the methods in Controllers/TodosController.cs, but I don't want to change the existing functionality with MVC. Please also generate the server URL and operation ID in the schema.

  1. 在 codespace 终端中,将 NuGet Swashbuckle 包添加到项目:

    dotnet add package Swashbuckle.AspNetCore --version 6.5.0
    dotnet add package Swashbuckle.AspNetCore.Annotations --version 6.5.0
    
  2. Controllers/TodosController.cs中,添加以下 API 方法。 若要使其与 Foundry 代理服务兼容,必须在属性中OperationId指定SwaggerOperation属性(请参阅如何将 Foundry 代理服务与 OpenAPI 指定工具:先决条件配合使用)。

        // GET: api/todos
        [HttpGet("api/todos")]
        [SwaggerOperation(Summary = "Gets all Todo items", OperationId = "GetTodos")]
        [ProducesResponseType(typeof(IEnumerable<Todo>), 200)]
        public async Task<ActionResult<IEnumerable<Todo>>> GetTodos()
        {
            return await _context.Todo.ToListAsync();
        }
    
        // GET: api/todos/5
        [HttpGet("api/todos/{id}")]
        [SwaggerOperation(Summary = "Gets a Todo item by ID", OperationId = "GetTodoById")]
        [ProducesResponseType(typeof(Todo), 200)]
        [ProducesResponseType(404)]
        public async Task<ActionResult<Todo>> GetTodo(int id)
        {
            var todo = await _context.Todo.FindAsync(id);
    
            if (todo == null)
            {
                return NotFound();
            }
    
            return todo;
        }
    
    
    // POST: api/todos
    [HttpPost("api/todos")]
    [ProducesResponseType(StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [SwaggerOperation(Summary = "Creates a new todo item.", Description = "Creates a new todo item and returns it.", OperationId = "CreateTodo")]
    public async Task<IActionResult> CreateTodo([FromBody] Todo todo)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        _context.Add(todo);
        await _context.SaveChangesAsync();
        return CreatedAtAction(nameof(GetTodo), new { id = todo.ID }, todo);
    }
    
    // PUT: api/todos/{id}
    [HttpPut("api/todos/{id}")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [SwaggerOperation(Summary = "Updates a todo item.", Description = "Updates an existing todo item by ID.", OperationId = "UpdateTodo")]
    public async Task<IActionResult> UpdateTodo(int id, [FromBody] Todo todo)
    {
        // Use the id from the URL fragment only, ignore mismatching check
        if (!TodoExists(id))
        {
            return NotFound();
        }
        todo.ID = id;
        _context.Entry(todo).State = EntityState.Modified;
        await _context.SaveChangesAsync();
        return NoContent();
    }
    
    // DELETE: api/todos/{id}
    [HttpDelete("api/todos/{id}")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [SwaggerOperation(Summary = "Deletes a todo item.", Description = "Deletes a todo item by ID.", OperationId = "DeleteTodo")]
    public async Task<IActionResult> DeleteTodo(int id)
    {
        var todo = await _context.Todo.FindAsync(id);
        if (todo == null)
        {
            return NotFound();
        }
        _context.Todo.Remove(todo);
        await _context.SaveChangesAsync();
        return NoContent();
    }
    
  3. Controllers/TodosController.cs 顶部,使用以下命令添加以下内容:

    using Swashbuckle.AspNetCore.Annotations;
    

    此代码复制了现有控制器的功能,这是不必要的,但为了简单起见,你将保留它。 最佳实践是将应用程序逻辑移动到服务类中,然后从 MVC动作和 API 方法中调用这些服务方法。

  4. Program.cs中,注册 Swagger 生成器服务。 这将为 API 启用 OpenAPI 文档,这使 Foundry 代理服务能够了解 API。 请务必指定服务器 URL。 Foundry 代理服务需要包含服务器 URL 的架构。

    builder.Services.AddSwaggerGen(c =>
    {
        c.EnableAnnotations();
        var websiteHostname = Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME");
        if (!string.IsNullOrEmpty(websiteHostname))
        {
            c.AddServer(new Microsoft.OpenApi.Models.OpenApiServer { Url = $"https://{websiteHostname}" });
        }
    });
    
  5. Program.cs中,启用 Swagger 中间件。 此中间件在运行时提供生成的 OpenAPI 文档,使其可通过浏览器访问。

    app.UseSwagger();
    app.UseSwaggerUI();
    
  6. 在 codespace 终端中,使用 dotnet run.. 运行应用程序。

  7. 选择“在浏览器中打开”。

  8. 通过将 /swagger/index.html 添加到 URL 导航到 Swagger UI。

  9. 通过在 Swagger UI 中试用 API 操作,确认 API 操作是否正常工作。

  10. 返回 codespace 终端,通过提交更改(GitHub Actions 方法)或运行 azd up (Azure 开发人员 CLI 方法)来部署更改。

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

在 Microsoft Foundry 中创建代理

注释

这些步骤使用新的 Foundry 门户。

  1. Foundry 门户的右上角菜单中,选择 “新建 Foundry”。

  2. 如果这是你第一次在新 Foundry 门户中,请选择项目名称,然后选择“ 创建新项目”。

  3. 为项目命名并选择“ 创建”。

  4. 选择 “开始生成”,然后选择 “创建代理”。

  5. 为代理指定一个名称,然后选择“ 创建”。 代理准备就绪后,您将看到代理操作环境。

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

  6. 在代理游乐场中,展开工具并选择添加>自定义>OpenAPI 工具>创建

  7. 为工具提供名称和说明。 在 OpenAPI 3.0+ 架构 框中,粘贴之前复制的架构。

  8. 选择“ 创建工具”。

  9. 选择“保存”

小窍门

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

测试代理

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

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

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

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

安全最佳做法

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

  • 身份验证和授权:使用 Microsoft Entra 身份验证保护 OpenAPI 终结点。 有关分步说明,请参阅 为 Foundry Agent Service 配置安全的 OpenAPI 终结点。 还可以 使用 Microsoft Entra ID 保护 Azure API 管理 背后的终结点,并确保只有经过授权的用户或代理才能访问这些工具。
  • 验证输入数据:示例代码在方法中ModelState.IsValid检查CreateTodo,这可确保传入的数据与模型的验证属性匹配。 有关详细信息,请参阅 ASP.NET Core 中的模型验证
  • 使用 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 应用服务中运行代理,请参阅以下教程:

更多资源