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

教程:使用 Azure 应用服务和 Azure OpenAI 构建聊天机器人(Spring Boot)

在本教程中,你将通过将 Azure OpenAI 与 Java Spring Boot 应用程序集成并将其部署到 Azure 应用服务来构建智能 AI 应用程序。 你将创建一个 Spring Boot 控制器,用于向 Azure OpenAI 发送查询并将响应发送到浏览器。

小窍门

虽然本教程使用 Spring Boot,但使用 Azure OpenAI 构建聊天应用程序的核心概念适用于任何 Java Web 应用程序。 如果在应用服务(如 Tomcat 或 JBoss EAP)上使用其他托管选项,则可以将此处显示的身份验证模式和 Azure SDK 用法调整为首选框架。

显示 Azure 应用服务中运行的聊天机器人的屏幕截图。

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

  • 创建 Azure OpenAI 资源并部署语言模型。
  • 生成连接到 Azure OpenAI 的 Spring Boot 应用程序。
  • 使用依赖项注入配置 Azure OpenAI 客户端。
  • 将应用程序部署到 Azure 应用服务。
  • 在开发环境和 Azure 中实现无密码安全身份验证。

先决条件

1.创建 Azure OpenAI 资源

在本部分中,你将使用 GitHub Codespaces 通过 Azure CLI 创建 Azure OpenAI 资源。

  1. 转到 GitHub Codespaces 并使用 GitHub 帐户登录。

  2. 通过 GitHub 查找 空白 模板,然后选择“ 使用此模板 创建新的空白 Codespace”。

  3. 在 Codespace 终端中,安装 Azure CLI:

    curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
    
  4. 请登录到 Azure 帐户:

    az login
    

    按照终端中的说明进行身份验证。

  5. 为资源组名称、Azure OpenAI 服务名称和位置设置环境变量:

    export RESOURCE_GROUP="<group-name>"
    export OPENAI_SERVICE_NAME="<azure-openai-name>"
    export APPSERVICE_NAME="<app-name>"
    export LOCATION="eastus2"
    

    重要

    该区域至关重要,因为它与所选模型的区域可用性相关联。 模型可用性和 部署类型可用性 因区域而异。 本教程使用的 gpt-4o-mini,在标准部署类型下的 eastus2 中可用。 如果部署到其他区域,则此模型可能不可用,或者可能需要不同的层。 在更改区域之前,请参阅 “模型摘要”表和区域可用性 ,以验证首选区域中的模型支持。

  6. 使用自定义域创建资源组和 Azure OpenAI 资源,然后添加 gpt-4o-mini 模型:

    # Resource group
    az group create --name $RESOURCE_GROUP --___location $LOCATION
    # Azure OpenAI resource
    az cognitiveservices account create \
      --name $OPENAI_SERVICE_NAME \
      --resource-group $RESOURCE_GROUP \
      --___location $LOCATION \
      --custom-___domain $OPENAI_SERVICE_NAME \
      --kind OpenAI \
      --sku s0
    # gpt-4o-mini model
    az cognitiveservices account deployment create \
      --name $OPENAI_SERVICE_NAME \
      --resource-group $RESOURCE_GROUP \
      --deployment-name gpt-4o-mini \
      --model-name gpt-4o-mini \
      --model-version 2024-07-18 \
      --model-format OpenAI \
      --sku-name Standard \
      --sku-capacity 1
    # Cognitive Services OpenAI User role that lets the signed in Azure user to read models from Azure OpenAI
    az role assignment create \
      --assignee $(az ad signed-in-user show --query id -o tsv) \
      --role "Cognitive Services OpenAI User" \
      --scope /subscriptions/$(az account show --query id -o tsv)/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.CognitiveServices/accounts/$OPENAI_SERVICE_NAME
    

有了 Azure OpenAI 资源后,你将创建一个 Web 应用程序以与之交互。

2.创建并设置 Spring Boot Web 应用

  1. 在 Codespace 终端中,将 Spring Boot REST 示例克隆到工作区,并尝试首次运行它。

    git clone https://github.com/rd-1-2022/rest-service .
    mvn spring-boot:run
    

    GitHub Codespaces 中应会显示一条通知,指示该应用在特定端口上可用。 选择“ 在浏览器中打开 ”以在新浏览器选项卡中启动应用。看到白色标签错误页时,Spring Boot 应用正在运行。

  2. 返回 Codespace 终端,使用 Ctrl+C 停止应用。

  3. 打开 pom.xml 并添加以下依赖项:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-ai-openai</artifactId>
        <version>1.0.0-beta.16</version>
    </dependency>
    <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-core</artifactId>
        <version>1.55.3</version>
    </dependency>
    <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-identity</artifactId>
        <version>1.16.0</version>
        <scope>compile</scope>
    </dependency>
    
  4. 在与 Application.java(src/main/java/com/example/restservice)相同的目录中,添加名为 ChatController.java 的 Java 文件,并将以下内容复制到其中:

    package com.example.restservice;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import com.azure.ai.openai.OpenAIAsyncClient;
    import com.azure.ai.openai.models.ChatChoice;
    import com.azure.ai.openai.models.ChatCompletionsOptions;
    import com.azure.ai.openai.models.ChatRequestMessage;
    import com.azure.ai.openai.models.ChatRequestUserMessage;
    import com.azure.ai.openai.models.ChatResponseMessage;
    import com.azure.core.credential.TokenCredential;
    import com.azure.identity.DefaultAzureCredentialBuilder;
    
    @Configuration
    class AzureConfig {
        // Reads the endpoint from environment variable AZURE_OPENAI_ENDPOINT
        @Value("${azure.openai.endpoint}")
        private String openAiEndpoint;
    
        // Provides a credential for local dev and production
        @Bean
        public TokenCredential tokenCredential() {
            return new DefaultAzureCredentialBuilder().build();
        }
    
        // Configures the OpenAIAsyncClient bean
        @Bean
        public OpenAIAsyncClient openAIClient(TokenCredential tokenCredential) {
            return new com.azure.ai.openai.OpenAIClientBuilder()
                    .endpoint(openAiEndpoint)
                    .credential(tokenCredential)
                    .buildAsyncClient();
        }
    }
    
    @Controller
    public class ChatController {
        private final OpenAIAsyncClient openAIClient;
    
        // Inject the OpenAIAsyncClient bean
        public ChatController(OpenAIAsyncClient openAIClient) {
            this.openAIClient = openAIClient;
        }
    
        @RequestMapping(value = "/", method = RequestMethod.GET)
        public String chatFormOrWithMessage(Model model, @RequestParam(value = "userMessage", required = false) String userMessage) {
            String aiResponse = null;
            if (userMessage != null && !userMessage.isBlank()) {
    
                // Create a list of chat messages
                List<ChatRequestMessage> chatMessages = new ArrayList<>();
                chatMessages.add(new ChatRequestUserMessage(userMessage));
    
                // Send the chat completion request
                String deploymentName = "gpt-4o-mini";
                StringBuilder serverResponse = new StringBuilder();
                var chatCompletions = openAIClient.getChatCompletions(
                    deploymentName, 
                    new ChatCompletionsOptions(chatMessages)
                ).block();
                if (chatCompletions != null) {
                    for (ChatChoice choice : chatCompletions.getChoices()) {
                        ChatResponseMessage message = choice.getMessage();
                        serverResponse.append(message.getContent());
                    }
                }
                aiResponse = serverResponse.toString();
            }
            model.addAttribute("aiResponse", aiResponse);
            return "chat";
        }
    }
    

    小窍门

    为了最大程度地减少本教程中的文件,该代码将 Spring @Configuration@Controller 类组合在一个文件中。 在生产中,通常会将配置和业务逻辑分开,以便保持可维护性。

  5. src/main/resources 下,创建 模板 目录,并为聊天界面添加包含以下内容的 chat.html

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Azure OpenAI Chat</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
    <div class="container py-4">
        <h2 class="mb-4">Azure OpenAI Chat</h2>
        <form action="/" method="get" class="d-flex mb-3">
            <input name="userMessage" class="form-control me-2" type="text" placeholder="Type your message..." autocomplete="off" required />
            <button class="btn btn-primary" type="submit">Send</button>
        </form>
        <div class="mb-3">
            <div th:if="${aiResponse}" class="alert alert-info">AI: <span th:text="${aiResponse}"></span></div>
        </div>
    </div>
    </body>
    </html>
    
  6. 在终端中,获取 OpenAI 端点:

    az cognitiveservices account show \
      --name $OPENAI_SERVICE_NAME \
      --resource-group $RESOURCE_GROUP \
      --query properties.endpoint \
      --output tsv
    
  7. 再次运行应用,并在其中添加 AZURE_OPENAI_ENDPOINT 及其在 CLI 输出中的值:

    AZURE_OPENAI_ENDPOINT=<output-from-previous-cli-command> mvn spring-boot:run
    
  8. 选择“ 在浏览器中打开 ”以在新浏览器选项卡中启动应用。

  9. 在文本框中键入一条消息,然后选择“发送”,并为应用提供几秒钟以使用 Azure OpenAI 的消息进行答复。

应用程序使用 DefaultAzureCredential,它会自动使用您通过 Azure CLI 登录的用户来获取身份验证令牌。 本教程稍后将 Web 应用部署到 Azure 应用服务,并将其配置为使用托管标识安全地连接到 Azure OpenAI 资源。 代码中的相同的DefaultAzureCredential 能够检测托管标识并将其用于身份验证。 无需额外的代码。

3.部署到 Azure 应用服务并配置 OpenAI 连接

应用在本地工作后,让我们将其部署到 Azure 应用服务,并使用托管标识设置到 Azure OpenAI 的服务连接。

  1. 使用 Maven 创建部署包。

    mvn clean package
    
  2. 首先,使用 Azure CLI 命令 az webapp up将应用部署到 Azure 应用服务。 此命令将创建新的 Web 应用并将代码部署到其中:

    az webapp up \
      --resource-group $RESOURCE_GROUP \
      --___location $LOCATION \
      --name $APPSERVICE_NAME \
      --plan $APPSERVICE_NAME \
      --sku B1 \
      --runtime "JAVA:21" \
      --os-type Linux \
      --track-status false
    

    该命令可能需要几分钟时间才能完成。 它将在 OpenAI 资源所在的同一资源组中创建新的 Web 应用。

  3. 部署应用后,使用托管标识在 Web 应用与 Azure OpenAI 资源之间创建服务连接:

    az webapp connection create cognitiveservices \
      --resource-group $RESOURCE_GROUP \
      --name $APPSERVICE_NAME \
      --target-resource-group $RESOURCE_GROUP \
      --account $OPENAI_SERVICE_NAME \
      --system-identity
    

    此命令通过以下方式在 Web 应用与 Azure OpenAI 资源之间创建连接:

    • 为 Web 应用生成系统分配的托管标识。
    • 将认知服务 OpenAI 参与者角色添加到 Azure OpenAI 资源的托管标识。
    • AZURE_OPENAI_ENDPOINT 应用设置添加到 Web 应用。
  4. 在浏览器中打开已部署的 Web 应用。

    az webapp browse
    
  5. 在文本框中键入一条消息,然后选择“发送”,并为应用提供几秒钟以使用 Azure OpenAI 的消息进行答复。

    显示 Azure 应用服务中运行的聊天机器人的屏幕截图。

应用现已部署并连接到具有托管标识的 Azure OpenAI。 请注意,它通过 @Configuration 注入访问 AZURE_OPENAI_ENDPOINT 应用设置。

常见问题

为什么示例对 OpenAI 客户端使用 @Configuration 和 Spring Bean?

OpenAIAsyncClient 使用 Spring Bean 可确保:

  • Spring 加载并注入所有配置属性(如终结点)。
  • 在完全初始化应用程序上下文后,将创建凭据和客户端。
  • 使用依赖关系注入,这是 Spring 应用程序中的标准和最可靠的模式。

异步客户端更可靠,尤其是在使用 DefaultAzureCredential Azure CLI 身份验证时。 在某些本地开发方案中,同步 OpenAIClient 可能会遇到令牌获取问题。 使用异步客户端可避免这些问题,建议使用此方法。


如果想要连接到 OpenAI 而不是 Azure OpenAI,该怎么办?

若要改为连接到 OpenAI,请使用以下代码:

OpenAIClient client = new OpenAIClientBuilder()
    .credential(new KeyCredential(<openai-api-key>))
    .buildClient();

有关详细信息,请参阅 OpenAI API 身份验证

在应用服务中使用连接机密时,应使用 Key Vault 引用 ,而不是直接在代码库中存储机密。 这可确保敏感信息保持安全,并集中管理。


是否可以改为使用 API 密钥连接到 Azure OpenAI?

是的,可以使用 API 密钥而不是托管标识连接到 Azure OpenAI。 Azure OpenAI SDK 和语义内核支持此方法。

在应用服务中使用连接机密时,应使用 Key Vault 引用 ,而不是直接在代码库中存储机密。 这可确保敏感信息保持安全,并集中管理。


DefaultAzureCredential 如何在本教程中工作?

通过 DefaultAzureCredential 自动选择最佳可用身份验证方法来简化身份验证:

  • 在本地开发期间:运行 az login后,它将使用本地 Azure CLI 凭据。
  • 部署到 Azure 应用服务时:它使用应用的托管标识进行安全、无密码身份验证。

此方法允许代码在本地和云环境中安全无缝地运行,而无需修改。

后续步骤