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

教程:使用 Microsoft 语义内核在 Azure 应用服务中生成代理 Web 应用(Spring Boot)

本教程演示如何将代理功能添加到现有数据驱动的 Spring Boot WebFlux CRUD 应用程序。 它使用Microsoft语义内核执行此作。

如果 Web 应用程序已具有有用的功能(如购物、酒店预订或数据管理),则通过将这些功能包装在插件(对于语义内核)中,将代理功能添加到 Web 应用程序相对简单。 本教程从简单的 to-do 列表应用开始。 最后,你将能够使用应用服务应用中的代理创建、更新和管理任务。

语义内核代理聊天完成会话的屏幕截图。

Note

Foundry 代理服务当前没有 Java SDK,因此本文未包含在其中。

本教程中,您将学习如何:

  • 将现有应用功能转换为语义内核的插件。
  • 将插件添加到语义内核代理并在 Web 应用中使用它。
  • 分配建立托管标识连接所需的权限。

Prerequisites

使用 Codespaces 打开示例

最简单的入门方法是使用 GitHub Codespaces,它提供一个完整的开发环境,并预装了所有必需的工具。

GitHub Codespaces 中打开。

  1. 请导航至位于https://github.com/Azure-Samples/app-service-agentic-semantic-kernel-java的 GitHub 存储库。

  2. 选择 “代码 ”按钮,选择 “Codespaces ”选项卡,然后选择 “在 main 上创建代码空间”。

  3. 请稍等片刻,让 Codespace 初始化。 准备就绪后,浏览器中会显示完全配置的开发环境。

  4. 在本地运行应用程序:

    mvn spring-boot:run
    
  5. 当看到 端口 8080 上运行的应用程序可用时,请选择“ 在浏览器中打开 ”并添加一些任务。

查看代理代码

当用户在新浏览器会话中输入第一个提示时,语义内核代理在 src/main/java/com/example/crudtaskswithagent/controller/AgentController.java 中初始化。

可以在构造函数中找到 SemanticKernelAgentService 初始化代码(在 src/main/java/com/example/crudtaskswithagent/service/SemanticKernelAgentService.java)。 初始化代码执行以下作:

  • 创建具有聊天完成功能的内核。
  • 添加一个内核插件,该插件封装 CRUD 应用程序的功能(在 src/main/java/com/example/crudtaskswithagent/plugin/TaskCrudPlugin.java)。 插件中有趣的部分是方法声明上的DefineKernelFunction注释,以及descriptionreturnType参数,这些参数帮助内核智能地调用插件。
  • 创建 聊天完成代理,并将其配置为让 AI 模型自动调用函数(FunctionChoiceBehavior.auto(true))。
  • 创建自动管理聊天历史记录的代理线程。
public SemanticKernelAgentService(
        @Value("${azure.openai.endpoint:}") String endpoint,
        @Value("${azure.openai.deployment:}") String deployment,
        TaskCrudPlugin taskCrudPlugin) {
    
    ChatCompletionAgent configuredAgent = null;
    
    if (endpoint != null && !endpoint.trim().isEmpty() && 
        deployment != null && !deployment.trim().isEmpty()) {
        try {
            // Create OpenAI client
            OpenAIAsyncClient openAIClient = new OpenAIClientBuilder()
                    .endpoint(endpoint)
                    .credential(new DefaultAzureCredentialBuilder().build())
                    .buildAsyncClient();
            
            // Create chat completion service
            OpenAIChatCompletion chatCompletion = OpenAIChatCompletion.builder()
                    .withOpenAIAsyncClient(openAIClient)
                    .withModelId(deployment)
                    .build();
            
            // Create kernel plugin from the task plugin
            KernelPlugin kernelPlugin = KernelPluginFactory.createFromObject(taskCrudPlugin, "TaskPlugin");
            
            // Create kernel with TaskCrudPlugin and chat completion service
            Kernel kernel = Kernel.builder()
                    .withAIService(OpenAIChatCompletion.class, chatCompletion)
                    .withPlugin(kernelPlugin)
                    .build();
            
            // Use automatic function calling
            InvocationContext invocationContext = InvocationContext.builder()
                .withFunctionChoiceBehavior(FunctionChoiceBehavior.auto(true))
                .build();

            // Create ChatCompletionAgent
            configuredAgent = ChatCompletionAgent.builder()
                    .withKernel(kernel)
                    .withName("TaskAgent")
                    .withInvocationContext(invocationContext)
                    .withInstructions(
                        "You are an agent that manages tasks using CRUD operations. " +
                        "Use the TaskCrudPlugin functions to create, read, update, and delete tasks. " +
                        "Always call the appropriate plugin function for any task management request. " +
                        "Don't try to handle any requests that are not related to task management."
                    )
                    .build();
            
        } catch (Exception e) {
            logger.error("Error initializing SemanticKernelAgentService: {}", e.getMessage(), e);
        }
    }

每次收到提示时,服务器代码会使用 ChatCompletionAgent.invokeAsync(),将用户提示和代理线程传入以调用代理。 代理线程跟踪聊天历史记录。

// Create chat message content for the user message
ChatMessageContent<?> userMessageContent = new ChatMessageContent<>(
        AuthorRole.USER,
        userMessage
);

// Use the agent to process the message with automatic function calling
return agent.invokeAsync(userMessageContent, thread)
        .<String>map(responses -> {
            
            if (responses != null && !responses.isEmpty()) {
                // Process all responses and concatenate them
                StringBuilder combinedResult = new StringBuilder();
                
                for (int i = 0; i < responses.size(); i++) {
                    var response = responses.get(i);
                    
                    // Update thread with the last response thread (as per Microsoft docs)
                    if (i == responses.size() - 1) {
                        var responseThread = response.getThread();
                        if (responseThread instanceof ChatHistoryAgentThread) {
                            this.thread = (ChatHistoryAgentThread) responseThread;
                        }
                    }
                    
                    // Get response content
                    ChatMessageContent<?> content = response.getMessage();
                    String responseContent = content != null ? content.getContent() : "";
                    
                    if (!responseContent.isEmpty()) {
                        if (combinedResult.length() > 0) {
                            combinedResult.append("\n\n"); // Separate multiple responses
                        }
                        combinedResult.append(responseContent);
                    }
                }
                
                String result = combinedResult.toString();
                if (result.isEmpty()) {
                    result = "No content returned from agent.";
                }
                return result;
            } else {
                return "I'm sorry, I couldn't process your request. Please try again.";
            }
        })
        .onErrorResume(throwable -> {
            logger.error("Error in processMessage: {}", throwable.getMessage(), throwable);
            return Mono.just("Error processing message: " + throwable.getMessage());
        });

部署示例应用程序

示例存储库包含一个 Azure 开发人员 CLI (AZD) 模板,该模板使用托管标识创建应用服务应用并部署示例应用程序。

  1. 在终端中,使用 Azure 开发人员 CLI 登录到 Azure:

    azd auth login
    

    按照说明完成身份验证过程。

  2. 使用 AZD 模板部署 Azure 应用服务应用:

    azd up
    
  3. 出现提示时,请提供以下答案:

    Question Answer
    输入新的环境名称: 键入唯一名称。
    选择要使用的 Azure 订阅: 选择订阅。
    选择要使用的资源组: 选择“新建资源组”
    选择要在其中创建资源组的位置: 选择任何区域。 实际上,资源将在 美国东部 2 中生成。
    输入新资源组的名称: 键入 Enter
  4. 在 AZD 输出中,找到应用的 URL 并在浏览器中导航到该 URL。 该 URL 在 AZD 输出中如下所示:

     Deploying services (azd deploy)
    
       (✓) Done: Deploying service web
       - Endpoint: <URL>
     
  5. 成功部署后,会看到已部署应用程序的 URL。

    现在已有了一个具有系统分配的托管标识的应用服务应用。

创建和配置 Microsoft Foundry 资源

  1. Foundry 门户中,部署所选的模型(请参阅 快速入门:Microsoft Foundry 入门)。 在此过程中会为你创建一个项目和模型部署。

  2. 从左侧菜单中选择“概述”。

  3. 选择 Azure OpenAI ,并在 Azure OpenAI 终结点 中复制 URL 供以后使用。

    显示如何在 Foundry 门户网站中复制 OpenAI 端点的屏幕截图。

  4. 选择 “模型 + 终结点 ”,并复制模型部署的名称供以后使用。

    显示如何在 Foundry 门户中复制模型部署名称的屏幕截图。

分配所需的权限

  1. 在 Foundry 门户的右上角,选择资源的名称,然后选择 资源组 以在 Azure 门户中打开它。

    显示如何在 Azure 门户中快速转到 Foundry 资源的资源组视图的屏幕截图。

  2. 使用下表为应用服务应用的管理标识的两个资源中的每一个添加一个角色:

    目标资源 所需角色 用于
    Microsoft Foundry 认知服务 OpenAI 用户 语义内核中的聊天完成服务。

    有关说明,请参阅使用 Azure 门户分配 Azure 角色

在示例应用程序中配置连接变量

  1. 打开 src/main/resources/application.properties。 使用前面从 Foundry 门户复制的值,配置以下变量:

    Variable Description
    azure.openai.endpoint Azure OpenAI 终结点(从“概述”页面复制)。 语义内核代理需要此项。
    azure.openai.deployment 部署中的模型名称(从“模型 + 终结点”页复制)。 语义内核代理需要此项。

    Note

    为了简化本教程,你将在 src/main/resources/application.properties 中使用这些变量,而不是在应用服务中使用应用设置覆盖这些变量。

  2. 使用 Azure CLI 登录到 Azure:

    az login
    

    这允许示例代码中的 Azure 标识客户端库接收已登录用户的身份验证令牌。 请记住,之前已为此用户添加了所需的角色。

  3. 在本地运行应用程序:

    mvn spring-boot:run
    
  4. 当看到 端口 8080 上运行的应用程序可用时,请在 浏览器中选择“打开”。

  5. 试用聊天界面。 如果收到响应,应用程序将成功连接到 Microsoft Foundry 资源。

  6. 返回 GitHub codespace,部署应用更改。

    azd up
    
  7. 再次导航到已部署的应用程序并测试聊天代理。

语义内核代理聊天完成会话的屏幕截图。

清理资源

完成应用程序后,可以删除应用服务资源,以避免产生进一步的成本:

azd down --purge

由于 AZD 模板不包含 Microsoft Foundry 资源,需要根据需要手动删除它们。

更多资源