04-Azure OpenAI集成
04-Azure OpenAI集成
学习目标
- 掌握 Azure OpenAI 服务的特点和优势
- 理解 Azure OpenAI 与 OpenAI 的区别
- 能够在 Spring AI 中集成 Azure OpenAI
- 掌握 Azure OpenAI 的企业级配置
- 了解 Azure OpenAI 的安全和合规特性
知识结构
一、项目场景引入
1.1 实际项目需求
场景一:金融企业AI应用
某银行需要构建智能客服系统,要求:
- 数据不能离开中国境内
- 符合金融行业合规要求
- 需要企业级SLA保障
- 支持私有网络部署
- 需要详细的审计日志
场景二:医疗行业AI助手
医疗机构需要AI辅助诊断系统:
- 患者数据高度敏感
- 需要符合HIPAA合规
- 要求数据主权保护
- 需要高可用性保障
- 支持本地化部署
场景三:政府部门应用
政府机构需要文档处理系统:
- 数据安全等级要求高
- 需要完全可控的部署
- 要求审计和追溯能力
- 支持离线运行
- 符合政府采购要求
1.2 为什么选择 Azure OpenAI
Azure OpenAI 相比直接使用 OpenAI API 有以下优势:
1. 数据主权和合规性
- 数据存储在指定的 Azure 区域
- 符合各国数据保护法规
- 支持 GDPR、HIPAA 等合规要求
- 提供详细的审计日志
2. 企业级安全
- 集成 Azure Active Directory
- 支持虚拟网络和私有端点
- 提供托管身份认证
- 支持客户管理的密钥
3. 高可用性和 SLA
- 99.9% 的可用性保证
- 多区域部署支持
- 自动故障转移
- 企业级技术支持
4. 成本可控
- 预留容量定价
- 批量折扣
- 成本管理工具
- 详细的使用报告
5. 集成生态
- 与 Azure 服务无缝集成
- 统一的身份和访问管理
- 集中的监控和日志
- DevOps 工具链支持
1.3 Azure OpenAI vs OpenAI 对比
| 特性 | Azure OpenAI | OpenAI |
|---|---|---|
| 数据位置 | 可选择区域 | 美国 |
| 合规认证 | 多种企业认证 | 基础认证 |
| SLA保障 | 99.9% | 无正式SLA |
| 私有部署 | 支持 | 不支持 |
| 身份认证 | Azure AD集成 | API密钥 |
| 网络隔离 | 支持VNet | 不支持 |
| 审计日志 | 完整审计 | 基础日志 |
| 技术支持 | 企业级支持 | 社区支持 |
| 定价模式 | 灵活定价 | 按量付费 |
| 适用场景 | 企业应用 | 个人/小团队 |
二、Azure OpenAI 服务准备
2.1 创建 Azure 账号
步骤一:注册 Azure 账号
- 访问 https://azure.microsoft.com/
- 点击"免费开始"
- 使用 Microsoft 账号登录或创建新账号
- 完成身份验证(需要信用卡验证)
- 获得 $200 免费额度(30天有效)
步骤二:申请 Azure OpenAI 访问权限
注意:Azure OpenAI 需要申请才能使用
申请流程:
1. 访问 https://aka.ms/oai/access
2. 填写申请表单
3. 说明使用场景和目的
4. 等待审批(通常1-2个工作日)
5. 收到批准邮件后即可使用
2.2 创建 Azure OpenAI 资源
步骤一:登录 Azure 门户
- 访问 https://portal.azure.com/
- 使用 Azure 账号登录
步骤二:创建资源
-
点击"创建资源"
-
搜索"Azure OpenAI"
-
点击"创建"
-
填写基本信息:
- 订阅:选择你的订阅
- 资源组:创建新的或选择现有的
- 区域:选择离你最近的区域(如 East US、West Europe)
- 名称:输入唯一的资源名称
- 定价层:选择 Standard S0
-
点击"审阅 + 创建"
-
等待部署完成
步骤三:部署模型
- 进入创建的 Azure OpenAI 资源
- 点击"模型部署"
- 点击"创建新部署"
- 选择模型:
- gpt-35-turbo(推荐用于开发)
- gpt-4(用于生产环境)
- 输入部署名称(如:gpt-35-turbo-deployment)
- 点击"创建"
2.3 获取连接信息
获取端点和密钥:
- 在 Azure OpenAI 资源页面
- 点击"密钥和终结点"
- 复制以下信息:
- 终结点(Endpoint):https://your-resource.openai.azure.com/
- 密钥1(Key 1):your-api-key
- 区域(Location):eastus
记录部署名称:
- 点击"模型部署"
- 记录你创建的部署名称
- 这个名称将在代码中使用
重要信息记录:
├─ Endpoint: https://your-resource.openai.azure.com/
├─ API Key: your-api-key-here
├─ Deployment Name: gpt-35-turbo-deployment
└─ API Version: 2023-05-15
三、Spring AI 集成 Azure OpenAI
3.1 项目依赖配置
完整的 pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>spring-ai-azure-demo</artifactId>
<version>1.0.0</version>
<name>Spring AI Azure OpenAI Integration Demo</name>
<description>Spring AI Azure OpenAI 集成示例项目</description>
<properties>
<java.version>17</java.version>
<spring-ai.version>0.8.1</spring-ai.version>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI Azure OpenAI -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-azure-openai-spring-boot-starter</artifactId>
</dependency>
<!-- Azure Identity(用于托管身份认证) -->
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>1.11.0</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Spring Boot Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
3.2 配置文件设置
application.yml 完整配置:
spring:
application:
name: spring-ai-azure-demo
# Spring AI Azure OpenAI 配置
ai:
azure:
openai:
# Azure OpenAI 端点
endpoint: ${AZURE_OPENAI_ENDPOINT}
# API 密钥
api-key: ${AZURE_OPENAI_API_KEY}
# Chat 模型配置
chat:
options:
# 部署名称(在 Azure 门户中创建的部署名称)
deployment-name: gpt-35-turbo-deployment
# API 版本
api-version: 2023-05-15
# 温度参数
temperature: 0.7
# 最大生成token数
max-tokens: 1000
# Top P 采样参数
top-p: 1.0
# 频率惩罚
frequency-penalty: 0.0
# 存在惩罚
presence-penalty: 0.0
# Embedding 模型配置
embedding:
options:
deployment-name: text-embedding-ada-002-deployment
api-version: 2023-05-15
# 服务器配置
server:
port: 8080
# 日志配置
logging:
level:
root: INFO
com.example: DEBUG
org.springframework.ai: DEBUG
com.azure: DEBUG
环境变量配置(.env 文件):
# Azure OpenAI 端点
AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com/
# Azure OpenAI API 密钥
AZURE_OPENAI_API_KEY=your-api-key-here
# 可选:Azure 租户ID(用于托管身份)
# AZURE_TENANT_ID=your-tenant-id
# 可选:Azure 客户端ID(用于托管身份)
# AZURE_CLIENT_ID=your-client-id
3.3 基础集成代码
项目结构:
spring-ai-azure-demo/
├── src/main/java/com/example/azureai/
│ ├── SpringAiAzureDemoApplication.java
│ ├── controller/
│ │ └── AzureChatController.java
│ ├── service/
│ │ ├── AzureChatService.java
│ │ └── impl/
│ │ └── AzureChatServiceImpl.java
│ ├── config/
│ │ ├── AzureOpenAIConfig.java
│ │ └── SecurityConfig.java
│ └── dto/
│ ├── ChatRequest.java
│ └── ChatResponse.java
├── src/main/resources/
│ ├── application.yml
│ └── application-prod.yml
└── pom.xml
1. 启动类:
package com.example.azureai;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Spring AI Azure OpenAI 集成示例应用
*
* @author Your Name
* @since 1.0.0
*/
@SpringBootApplication
public class SpringAiAzureDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAiAzureDemoApplication.class, args);
}
}
2. 配置类:
package com.example.azureai.config;
import com.azure.ai.openai.OpenAIClient;
import com.azure.ai.openai.OpenAIClientBuilder;
import com.azure.core.credential.AzureKeyCredential;
import com.azure.core.credential.TokenCredential;
import com.azure.identity.DefaultAzureCredentialBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
/**
* Azure OpenAI 配置类
* 支持 API 密钥和托管身份两种认证方式
*
* @author Your Name
* @since 1.0.0
*/
@Slf4j
@Configuration
public class AzureOpenAIConfig {
@Value("${spring.ai.azure.openai.endpoint}")
private String endpoint;
@Value("${spring.ai.azure.openai.api-key:}")
private String apiKey;
/**
* 使用 API 密钥认证(开发环境)
*/
@Bean
@Profile("!prod")
public OpenAIClient openAIClientWithApiKey() {
log.info("使用 API 密钥认证连接 Azure OpenAI");
return new OpenAIClientBuilder()
.endpoint(endpoint)
.credential(new AzureKeyCredential(apiKey))
.buildClient();
}
/**
* 使用托管身份认证(生产环境)
*/
@Bean
@Profile("prod")
public OpenAIClient openAIClientWithManagedIdentity() {
log.info("使用托管身份认证连接 Azure OpenAI");
TokenCredential credential = new DefaultAzureCredentialBuilder()
.build();
return new OpenAIClientBuilder()
.endpoint(endpoint)
.credential(credential)
.buildClient();
}
}
3. 服务接口:
package com.example.azureai.service;
import com.example.azureai.dto.ChatRequest;
import com.example.azureai.dto.ChatResponse;
import reactor.core.publisher.Flux;
/**
* Azure Chat 服务接口
*
* @author Your Name
* @since 1.0.0
*/
public interface AzureChatService {
/**
* 简单对话
*/
String simpleChat(String message);
/**
* 高级对话
*/
ChatResponse advancedChat(ChatRequest request);
/**
* 流式对话
*/
Flux<String> streamChat(String message);
/**
* 批量处理
*/
List<String> batchProcess(List<String> messages);
}
4. 服务实现类:
package com.example.azureai.service.impl;
import com.example.azureai.dto.ChatRequest;
import com.example.azureai.dto.ChatResponse;
import com.example.azureai.service.AzureChatService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.azure.openai.AzureOpenAiChatClient;
import org.springframework.ai.azure.openai.AzureOpenAiChatOptions;
import org.springframework.ai.chat.ChatResponse as AiChatResponse;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import java.util.ArrayList;
import java.util.List;
/**
* Azure Chat 服务实现
* 项目场景:企业级智能客服系统
* 功能:处理客户咨询,提供智能回复
*
* @author Your Name
* @since 1.0.0
*/
@Slf4j
@Service
public class AzureChatServiceImpl implements AzureChatService {
private final AzureOpenAiChatClient chatClient;
@Value("${spring.ai.azure.openai.chat.options.deployment-name}")
private String deploymentName;
public AzureChatServiceImpl(AzureOpenAiChatClient chatClient) {
this.chatClient = chatClient;
log.info("AzureChatService 初始化完成");
}
@Override
public String simpleChat(String message) {
log.info("收到简单对话请求: {}", message);
try {
Message userMessage = new UserMessage(message);
Prompt prompt = new Prompt(userMessage);
AiChatResponse response = chatClient.call(prompt);
String content = response.getResult().getOutput().getContent();
log.info("AI 回复: {}", content);
return content;
} catch (Exception e) {
log.error("简单对话失败", e);
return "抱歉,服务暂时不可用,请稍后再试。";
}
}
@Override
public ChatResponse advancedChat(ChatRequest request) {
log.info("收到高级对话请求: {}", request);
long startTime = System.currentTimeMillis();
try {
// 构建 Azure OpenAI 选项
AzureOpenAiChatOptions.Builder optionsBuilder =
AzureOpenAiChatOptions.builder()
.withDeploymentName(deploymentName);
if (request.getTemperature() != null) {
optionsBuilder.withTemperature(request.getTemperature());
}
if (request.getMaxTokens() != null) {
optionsBuilder.withMaxTokens(request.getMaxTokens());
}
AzureOpenAiChatOptions options = optionsBuilder.build();
// 创建提示词
Message userMessage = new UserMessage(request.getMessage());
Prompt prompt = new Prompt(userMessage, options);
// 调用 Azure OpenAI API
AiChatResponse aiResponse = chatClient.call(prompt);
long responseTime = System.currentTimeMillis() - startTime;
// 构建响应
return ChatResponse.builder()
.content(aiResponse.getResult().getOutput().getContent())
.model(deploymentName)
.totalTokens(aiResponse.getMetadata().getUsage().getTotalTokens())
.responseTime(responseTime)
.success(true)
.build();
} catch (Exception e) {
log.error("高级对话失败", e);
long responseTime = System.currentTimeMillis() - startTime;
return ChatResponse.builder()
.success(false)
.errorMessage(e.getMessage())
.responseTime(responseTime)
.build();
}
}
@Override
public Flux<String> streamChat(String message) {
log.info("收到流式对话请求: {}", message);
try {
Prompt prompt = new Prompt(new UserMessage(message));
Flux<AiChatResponse> responseFlux = chatClient.stream(prompt);
return responseFlux
.map(response -> response.getResult().getOutput().getContent())
.doOnNext(content -> log.debug("流式内容: {}", content))
.doOnComplete(() -> log.info("流式对话完成"))
.doOnError(error -> log.error("流式对话失败", error));
} catch (Exception e) {
log.error("流式对话启动失败", e);
return Flux.error(e);
}
}
@Override
public List<String> batchProcess(List<String> messages) {
log.info("批量处理 {} 条消息", messages.size());
List<String> results = new ArrayList<>();
for (String message : messages) {
try {
String result = simpleChat(message);
results.add(result);
} catch (Exception e) {
log.error("处理消息失败: {}", message, e);
results.add("处理失败");
}
}
return results;
}
}
5. 控制器:
package com.example.azureai.controller;
import com.example.azureai.dto.ChatRequest;
import com.example.azureai.dto.ChatResponse;
import com.example.azureai.service.AzureChatService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import java.util.List;
/**
* Azure Chat 控制器
*
* @author Your Name
* @since 1.0.0
*/
@Slf4j
@RestController
@RequestMapping("/api/azure/chat")
public class AzureChatController {
private final AzureChatService chatService;
public AzureChatController(AzureChatService chatService) {
this.chatService = chatService;
}
@GetMapping("/simple")
public String simpleChat(@RequestParam String message) {
log.info("简单对话请求: {}", message);
return chatService.simpleChat(message);
}
@PostMapping("/advanced")
public ChatResponse advancedChat(@RequestBody ChatRequest request) {
log.info("高级对话请求: {}", request);
return chatService.advancedChat(request);
}
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat(@RequestParam String message) {
log.info("流式对话请求: {}", message);
return chatService.streamChat(message);
}
@PostMapping("/batch")
public List<String> batchProcess(@RequestBody List<String> messages) {
log.info("批量处理请求: {} 条消息", messages.size());
return chatService.batchProcess(messages);
}
@GetMapping("/health")
public String health() {
return "Azure OpenAI Service OK";
}
}
四、企业级特性
4.1 虚拟网络集成
Azure OpenAI 支持通过虚拟网络进行私有访问,提高安全性。
配置虚拟网络:
# application-prod.yml
spring:
ai:
azure:
openai:
endpoint: ${AZURE_OPENAI_ENDPOINT}
# 使用私有端点
use-private-endpoint: true
# 虚拟网络配置
vnet:
enabled: true
subnet-id: /subscriptions/{subscription-id}/resourceGroups/{rg}/providers/Microsoft.Network/virtualNetworks/{vnet}/subnets/{subnet}
Java 配置:
@Configuration
public class VNetConfig {
/**
* 配置私有端点连接
*/
@Bean
@Profile("prod")
public OpenAIClient secureOpenAIClient(
@Value("${spring.ai.azure.openai.endpoint}") String endpoint) {
// 使用托管身份
TokenCredential credential = new DefaultAzureCredentialBuilder()
.build();
// 配置网络选项
HttpClientOptions clientOptions = new HttpClientOptions()
.setConnectTimeout(Duration.ofSeconds(30))
.setReadTimeout(Duration.ofSeconds(60));
return new OpenAIClientBuilder()
.endpoint(endpoint)
.credential(credential)
.clientOptions(clientOptions)
.buildClient();
}
}
4.2 托管身份认证
托管身份提供了更安全的认证方式,无需管理密钥。
启用托管身份:
# 在 Azure 门户中为应用启用系统分配的托管身份
# 或使用 Azure CLI
az webapp identity assign --name <app-name> --resource-group <resource-group>
# 为托管身份分配权限
az role assignment create \
--assignee <managed-identity-principal-id> \
--role "Cognitive Services OpenAI User" \
--scope /subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.CognitiveServices/accounts/<openai-resource>
代码配置:
@Configuration
@Profile("prod")
public class ManagedIdentityConfig {
@Bean
public OpenAIClient managedIdentityClient(
@Value("${spring.ai.azure.openai.endpoint}") String endpoint) {
log.info("使用托管身份连接 Azure OpenAI");
// 使用默认 Azure 凭据链
// 会自动尝试:托管身份 -> 环境变量 -> Azure CLI
TokenCredential credential = new DefaultAzureCredentialBuilder()
.build();
return new OpenAIClientBuilder()
.endpoint(endpoint)
.credential(credential)
.buildClient();
}
}
4.3 内容过滤
Azure OpenAI 提供内置的内容过滤功能,符合企业合规要求。
配置内容过滤:
@Service
public class ContentFilterService {
private final AzureOpenAiChatClient chatClient;
/**
* 带内容过滤的对话
*/
public ChatResponse chatWithContentFilter(String message) {
try {
// Azure OpenAI 会自动应用内容过滤
AzureOpenAiChatOptions options = AzureOpenAiChatOptions.builder()
.withDeploymentName("gpt-35-turbo-deployment")
// 内容过滤级别(low, medium, high)
.withContentFilterLevel("medium")
.build();
Prompt prompt = new Prompt(new UserMessage(message), options);
AiChatResponse response = chatClient.call(prompt);
// 检查内容过滤结果
if (response.getMetadata().getContentFilterResults() != null) {
log.info("内容过滤结果: {}",
response.getMetadata().getContentFilterResults());
}
return ChatResponse.builder()
.content(response.getResult().getOutput().getContent())
.success(true)
.build();
} catch (ContentFilterException e) {
log.warn("内容被过滤: {}", e.getMessage());
return ChatResponse.builder()
.success(false)
.errorMessage("内容不符合安全策略")
.build();
}
}
}
4.4 审计和监控
Azure OpenAI 提供完整的审计日志和监控功能。
启用诊断日志:
@Configuration
public class MonitoringConfig {
@Bean
public ApplicationInsights applicationInsights() {
// 配置 Application Insights
TelemetryConfiguration config = TelemetryConfiguration.createDefault();
config.setInstrumentationKey(System.getenv("APPINSIGHTS_INSTRUMENTATIONKEY"));
return new ApplicationInsights(config);
}
}
@Service
public class AuditService {
private final TelemetryClient telemetryClient;
public void logApiCall(String userId, String operation,
Map<String, String> properties) {
// 记录自定义事件
telemetryClient.trackEvent("AzureOpenAI_APICall", properties, null);
// 记录指标
telemetryClient.trackMetric("API_Calls", 1.0);
}
public void logError(String operation, Exception e) {
telemetryClient.trackException(e);
}
}
五、实战应用场景
5.1 场景一:金融企业客服系统
业务需求:
- 符合金融行业合规要求
- 数据不能离开中国境内
- 需要完整的审计追踪
- 高可用性要求
完整实现:
@Service
public class FinancialCustomerService {
private final AzureOpenAiChatClient chatClient;
private final AuditService auditService;
private final ComplianceService complianceService;
/**
* 处理金融咨询
*/
public ChatResponse handleFinancialQuery(FinancialQueryRequest request) {
// 1. 合规检查
if (!complianceService.checkCompliance(request)) {
return ChatResponse.builder()
.success(false)
.errorMessage("请求不符合合规要求")
.build();
}
// 2. 审计日志
auditService.logApiCall(
request.getUserId(),
"financial_query",
Map.of(
"query_type", request.getQueryType(),
"timestamp", String.valueOf(System.currentTimeMillis())
)
);
// 3. 构建系统提示词(金融专业)
String systemPrompt = """
你是一个专业的金融客服助手。
职责:
1. 回答客户关于银行产品和服务的问题
2. 提供专业、准确的金融建议
3. 遵守金融行业规范和法律法规
限制:
1. 不提供具体的投资建议
2. 不泄露客户隐私信息
3. 对于复杂问题,建议联系专业理财顾问
4. 所有建议需符合监管要求
""";
try {
// 4. 调用 Azure OpenAI
AzureOpenAiChatOptions options = AzureOpenAiChatOptions.builder()
.withDeploymentName("gpt-4-deployment")
.withTemperature(0.3) // 较低温度,更准确
.withMaxTokens(800)
.build();
List<Message> messages = List.of(
new SystemMessage(systemPrompt),
new UserMessage(request.getQuery())
);
Prompt prompt = new Prompt(messages, options);
AiChatResponse response = chatClient.call(prompt);
String content = response.getResult().getOutput().getContent();
// 5. 内容审核
if (!complianceService.validateResponse(content)) {
log.warn("响应内容不符合合规要求");
content = "抱歉,我无法回答这个问题。请联系人工客服。";
}
// 6. 记录响应
auditService.logApiCall(
request.getUserId(),
"financial_response",
Map.of(
"tokens", String.valueOf(response.getMetadata().getUsage().getTotalTokens()),
"model", "gpt-4"
)
);
return ChatResponse.builder()
.content(content)
.success(true)
.build();
} catch (Exception e) {
auditService.logError("financial_query", e);
return ChatResponse.builder()
.success(false)
.errorMessage("服务暂时不可用")
.build();
}
}
}
5.2 场景二:医疗文档处理
业务需求:
- 处理医疗记录和报告
- 符合 HIPAA 合规要求
- 数据加密和隐私保护
- 支持多语言
实现代码:
@Service
public class MedicalDocumentService {
private final AzureOpenAiChatClient chatClient;
private final EncryptionService encryptionService;
/**
* 处理医疗文档
*/
public DocumentProcessingResult processDocument(MedicalDocument document) {
// 1. 数据脱敏
String sanitizedContent = sanitizePatientInfo(document.getContent());
// 2. 加密敏感信息
String encryptedContent = encryptionService.encrypt(sanitizedContent);
// 3. 文档分析
String systemPrompt = """
你是一个医疗文档分析助手。
任务:
1. 分析医疗文档内容
2. 提取关键医疗信息
3. 生成结构化摘要
注意:
1. 保护患者隐私
2. 使用专业医疗术语
3. 标注不确定的信息
""";
try {
AzureOpenAiChatOptions options = AzureOpenAiChatOptions.builder()
.withDeploymentName("gpt-4-deployment")
.withTemperature(0.2)
.withMaxTokens(2000)
.build();
String userPrompt = String.format("""
请分析以下医疗文档并提取关键信息:
%s
请按以下格式输出:
1. 主要诊断
2. 症状描述
3. 治疗方案
4. 注意事项
""", encryptedContent);
Prompt prompt = new Prompt(
List.of(new SystemMessage(systemPrompt),
new UserMessage(userPrompt)),
options
);
AiChatResponse response = chatClient.call(prompt);
String analysis = response.getResult().getOutput().getContent();
return DocumentProcessingResult.builder()
.analysis(analysis)
.success(true)
.build();
} catch (Exception e) {
log.error("文档处理失败", e);
return DocumentProcessingResult.builder()
.success(false)
.errorMessage("文档处理失败")
.build();
}
}
private String sanitizePatientInfo(String content) {
// 移除或替换患者姓名、身份证号等敏感信息
return content
.replaceAll("\\b[A-Z][a-z]+ [A-Z][a-z]+\\b", "[PATIENT_NAME]")
.replaceAll("\\b\\d{18}\\b", "[ID_NUMBER]")
.replaceAll("\\b\\d{11}\\b", "[PHONE_NUMBER]");
}
}
六、成本优化
6.1 预留容量定价
Azure OpenAI 支持预留容量,可以显著降低成本。
配置预留容量:
@Configuration
public class CapacityConfig {
/**
* 配置预留容量
*/
@Bean
public CapacityReservation capacityReservation() {
return CapacityReservation.builder()
.deploymentName("gpt-35-turbo-deployment")
.reservedCapacity(100) // 预留100个TPM
.duration(Duration.ofDays(30))
.build();
}
}
6.2 成本监控
@Service
public class CostMonitoringService {
private final AtomicLong totalTokens = new AtomicLong(0);
private final AtomicInteger totalRequests = new AtomicInteger(0);
public void recordUsage(int tokens) {
totalTokens.addAndGet(tokens);
totalRequests.incrementAndGet();
}
public CostReport generateReport() {
long tokens = totalTokens.get();
int requests = totalRequests.get();
// Azure OpenAI 定价(示例)
// GPT-3.5-turbo: $0.002 / 1K tokens
// GPT-4: $0.06 / 1K tokens
double cost = (tokens / 1000.0) * 0.002;
return CostReport.builder()
.totalTokens(tokens)
.totalRequests(requests)
.estimatedCost(cost)
.build();
}
}
七、常见问题
Q1: Azure OpenAI 和 OpenAI 有什么区别?
A: 主要区别:
- 数据位置:Azure OpenAI 数据存储在指定区域
- 合规性:Azure OpenAI 提供更多企业合规认证
- 安全性:支持虚拟网络、托管身份等企业级安全特性
- SLA:Azure OpenAI 提供 99.9% 可用性保证
- 定价:Azure OpenAI 支持预留容量等灵活定价
Q2: 如何申请 Azure OpenAI 访问权限?
A: 申请步骤:
- 访问 https://aka.ms/oai/access
- 填写申请表单,说明使用场景
- 等待审批(通常1-2个工作日)
- 收到批准邮件后即可使用
Q3: 如何选择部署区域?
A: 选择建议:
- 选择离用户最近的区域(降低延迟)
- 考虑数据合规要求(如数据主权)
- 检查区域的模型可用性
- 考虑成本差异(不同区域价格可能不同)
Q4: 托管身份和 API 密钥哪个更好?
A: 推荐使用托管身份:
- 优点:无需管理密钥、更安全、自动轮换
- 缺点:配置稍复杂、仅适用于 Azure 环境
- 建议:生产环境使用托管身份,开发环境使用 API 密钥
八、练习题
基础练习
练习1: 创建 Azure OpenAI 资源并部署模型
练习2: 实现基本的对话功能
练习3: 配置虚拟网络访问
进阶练习
练习4: 实现托管身份认证
练习5: 集成 Application Insights 监控
练习6: 实现内容过滤和合规检查
综合练习
练习7: 开发企业级客服系统
练习8: 实现医疗文档处理系统
练习9: 构建成本监控和优化方案
九、学习检查清单
- 理解 Azure OpenAI 的特点和优势
- 能够创建和配置 Azure OpenAI 资源
- 掌握 API 密钥和托管身份认证
- 能够配置虚拟网络和私有端点
- 理解内容过滤和合规要求
- 能够实现审计和监控
- 掌握成本优化策略
- 能够开发企业级应用
十、扩展阅读
-
Azure OpenAI 官方文档
- https://learn.microsoft.com/azure/cognitive-services/openai/
-
Azure 安全最佳实践
- https://learn.microsoft.com/azure/security/fundamentals/best-practices-and-patterns
-
托管身份文档
- https://learn.microsoft.com/azure/active-directory/managed-identities-azure-resources/
十一、总结
Azure OpenAI 为企业提供了安全、合规、可控的 AI 解决方案。通过本章学习,你已经掌握了:
- ✅ Azure OpenAI 的企业级特性
- ✅ 安全认证和网络配置
- ✅ 实战应用场景开发
- ✅ 成本优化和监控
继续学习下一章节:Ollama 本地模型集成,探索本地部署方案!
十二、高级配置
12.1 多区域部署
为了提高可用性,可以配置多区域部署。
@Configuration
public class MultiRegionConfig {
@Bean
public List<AzureOpenAiChatClient> multiRegionClients() {
List<AzureOpenAiChatClient> clients = new ArrayList<>();
// 主区域
clients.add(createClient(
"https://primary.openai.azure.com/",
System.getenv("PRIMARY_API_KEY")
));
// 备用区域
clients.add(createClient(
"https://secondary.openai.azure.com/",
System.getenv("SECONDARY_API_KEY")
));
return clients;
}
private AzureOpenAiChatClient createClient(String endpoint, String apiKey) {
OpenAIClient client = new OpenAIClientBuilder()
.endpoint(endpoint)
.credential(new AzureKeyCredential(apiKey))
.buildClient();
return new AzureOpenAiChatClient(client);
}
}
12.2 负载均衡
@Service
public class LoadBalancerService {
private final List<AzureOpenAiChatClient> clients;
private final AtomicInteger counter = new AtomicInteger(0);
public LoadBalancerService(List<AzureOpenAiChatClient> clients) {
this.clients = clients;
}
/**
* 轮询选择客户端
*/
public AzureOpenAiChatClient getClient() {
int index = counter.getAndIncrement() % clients.size();
return clients.get(index);
}
/**
* 带故障转移的调用
*/
public ChatResponse callWithFailover(Prompt prompt) {
for (AzureOpenAiChatClient client : clients) {
try {
AiChatResponse response = client.call(prompt);
return ChatResponse.builder()
.content(response.getResult().getOutput().getContent())
.success(true)
.build();
} catch (Exception e) {
log.warn("客户端调用失败,尝试下一个", e);
}
}
return ChatResponse.builder()
.success(false)
.errorMessage("所有区域都不可用")
.build();
}
}
12.3 批量处理优化
@Service
public class BatchProcessingService {
private final AzureOpenAiChatClient chatClient;
private final ExecutorService executor;
public BatchProcessingService(AzureOpenAiChatClient chatClient) {
this.chatClient = chatClient;
this.executor = Executors.newFixedThreadPool(10);
}
/**
* 并行批量处理
*/
public List<CompletableFuture<String>> batchProcessAsync(
List<String> messages) {
return messages.stream()
.map(message -> CompletableFuture.supplyAsync(
() -> processMessage(message),
executor))
.collect(Collectors.toList());
}
private String processMessage(String message) {
try {
Prompt prompt = new Prompt(new UserMessage(message));
AiChatResponse response = chatClient.call(prompt);
return response.getResult().getOutput().getContent();
} catch (Exception e) {
log.error("处理消息失败: {}", message, e);
return "处理失败";
}
}
/**
* 等待所有任务完成
*/
public List<String> waitForAll(
List<CompletableFuture<String>> futures) {
return futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
}
十三、性能优化
13.1 连接池配置
@Configuration
public class ConnectionPoolConfig {
@Bean
public HttpClient httpClient() {
ConnectionPoolManager poolManager = new ConnectionPoolManager();
poolManager.setMaxConnections(100);
poolManager.setMaxConnectionsPerRoute(20);
poolManager.setConnectionTimeout(Duration.ofSeconds(30));
return HttpClient.newBuilder()
.connectionPoolManager(poolManager)
.build();
}
}
13.2 请求超时配置
spring:
ai:
azure:
openai:
# 连接超时(毫秒)
connect-timeout: 30000
# 读取超时(毫秒)
read-timeout: 60000
# 写入超时(毫秒)
write-timeout: 60000
13.3 响应缓存策略
@Service
public class CacheStrategyService {
private final CacheManager cacheManager;
/**
* 智能缓存策略
*/
@Cacheable(
value = "azure-chat-responses",
key = "#message",
condition = "#message.length() < 500",
unless = "#result == null"
)
public String chatWithSmartCache(String message) {
// 短消息才缓存
// 避免缓存过大的响应
return callAzureOpenAI(message);
}
/**
* 分层缓存
*/
public String chatWithTieredCache(String message) {
// L1: 本地缓存(快速)
String cached = localCache.get(message);
if (cached != null) {
return cached;
}
// L2: Redis 缓存(共享)
cached = redisCache.get(message);
if (cached != null) {
localCache.put(message, cached);
return cached;
}
// L3: 调用 API
String result = callAzureOpenAI(message);
// 写入缓存
redisCache.put(message, result);
localCache.put(message, result);
return result;
}
}
十四、安全最佳实践
14.1 密钥轮换
@Service
public class KeyRotationService {
private volatile String currentApiKey;
private volatile String nextApiKey;
@Scheduled(cron = "0 0 0 * * ?") // 每天凌晨
public void rotateKeys() {
log.info("开始密钥轮换");
try {
// 1. 从 Key Vault 获取新密钥
String newKey = keyVaultClient.getSecret("azure-openai-key-new");
// 2. 更新下一个密钥
nextApiKey = newKey;
// 3. 等待一段时间,确保所有请求使用新密钥
Thread.sleep(60000);
// 4. 切换到新密钥
currentApiKey = nextApiKey;
// 5. 删除旧密钥
keyVaultClient.deleteSecret("azure-openai-key-old");
log.info("密钥轮换完成");
} catch (Exception e) {
log.error("密钥轮换失败", e);
}
}
public String getCurrentKey() {
return currentApiKey;
}
}
14.2 请求签名
@Component
public class RequestSignatureInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(
HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution) throws IOException {
// 添加时间戳
String timestamp = String.valueOf(System.currentTimeMillis());
request.getHeaders().add("X-Timestamp", timestamp);
// 生成签名
String signature = generateSignature(body, timestamp);
request.getHeaders().add("X-Signature", signature);
return execution.execute(request, body);
}
private String generateSignature(byte[] body, String timestamp) {
// 使用 HMAC-SHA256 生成签名
String data = new String(body) + timestamp;
return HmacUtils.hmacSha256Hex(secretKey, data);
}
}
14.3 IP 白名单
@Configuration
public class IpWhitelistConfig {
@Bean
public FilterRegistrationBean<IpWhitelistFilter> ipFilter() {
FilterRegistrationBean<IpWhitelistFilter> registration =
new FilterRegistrationBean<>();
registration.setFilter(new IpWhitelistFilter());
registration.addUrlPatterns("/api/*");
registration.setOrder(1);
return registration;
}
}
public class IpWhitelistFilter implements Filter {
private static final Set<String> WHITELIST = Set.of(
"192.168.1.0/24",
"10.0.0.0/8"
);
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String clientIp = getClientIp(httpRequest);
if (!isWhitelisted(clientIp)) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
httpResponse.getWriter().write("Access denied");
return;
}
chain.doFilter(request, response);
}
private boolean isWhitelisted(String ip) {
// 检查 IP 是否在白名单中
return WHITELIST.stream()
.anyMatch(range -> isInRange(ip, range));
}
}
十五、故障排查
15.1 常见错误码
@Service
public class ErrorHandlingService {
public ChatResponse handleError(Exception e) {
if (e instanceof ResourceNotFoundException) {
return ChatResponse.builder()
.success(false)
.errorCode("RESOURCE_NOT_FOUND")
.errorMessage("Azure OpenAI 资源未找到")
.build();
}
if (e instanceof AuthenticationException) {
return ChatResponse.builder()
.success(false)
.errorCode("AUTHENTICATION_FAILED")
.errorMessage("认证失败,请检查密钥或托管身份配置")
.build();
}
if (e instanceof QuotaExceededException) {
return ChatResponse.builder()
.success(false)
.errorCode("QUOTA_EXCEEDED")
.errorMessage("配额已用完,请升级套餐或等待重置")
.build();
}
if (e instanceof RateLimitException) {
return ChatResponse.builder()
.success(false)
.errorCode("RATE_LIMIT")
.errorMessage("请求过于频繁,请稍后再试")
.build();
}
return ChatResponse.builder()
.success(false)
.errorCode("UNKNOWN_ERROR")
.errorMessage("未知错误:" + e.getMessage())
.build();
}
}
15.2 诊断日志
@Aspect
@Component
public class DiagnosticLoggingAspect {
@Around("@annotation(com.example.azureai.annotation.Diagnostic)")
public Object logDiagnostics(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
log.info("=== 诊断开始 ===");
log.info("方法: {}", methodName);
log.info("参数: {}", Arrays.toString(args));
long startTime = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - startTime;
log.info("执行成功,耗时: {}ms", duration);
log.info("返回值: {}", result);
return result;
} catch (Exception e) {
long duration = System.currentTimeMillis() - startTime;
log.error("执行失败,耗时: {}ms", duration);
log.error("异常信息: ", e);
throw e;
} finally {
log.info("=== 诊断结束 ===");
}
}
}
15.3 健康检查
@Component
public class AzureOpenAIHealthIndicator implements HealthIndicator {
private final AzureOpenAiChatClient chatClient;
@Override
public Health health() {
try {
// 发送测试请求
Prompt testPrompt = new Prompt(new UserMessage("test"));
AiChatResponse response = chatClient.call(testPrompt);
if (response != null) {
return Health.up()
.withDetail("status", "Azure OpenAI 服务正常")
.withDetail("model", "gpt-35-turbo")
.build();
}
return Health.down()
.withDetail("status", "Azure OpenAI 服务异常")
.build();
} catch (Exception e) {
return Health.down()
.withDetail("status", "Azure OpenAI 服务不可用")
.withDetail("error", e.getMessage())
.build();
}
}
}
十六、生产部署
16.1 Docker 部署
Dockerfile:
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/spring-ai-azure-demo-1.0.0.jar app.jar
# 使用托管身份,不需要在镜像中包含密钥
ENV AZURE_CLIENT_ID=${AZURE_CLIENT_ID}
ENV AZURE_TENANT_ID=${AZURE_TENANT_ID}
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
docker-compose.yml:
version: '3.8'
services:
azure-ai-app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- AZURE_OPENAI_ENDPOINT=${AZURE_OPENAI_ENDPOINT}
- AZURE_CLIENT_ID=${AZURE_CLIENT_ID}
- AZURE_TENANT_ID=${AZURE_TENANT_ID}
networks:
- ai-network
restart: unless-stopped
networks:
ai-network:
driver: bridge
16.2 Kubernetes 部署
deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: azure-ai-app
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: azure-ai-app
template:
metadata:
labels:
app: azure-ai-app
aadpodidbinding: azure-openai-identity
spec:
containers:
- name: app
image: myregistry.azurecr.io/azure-ai-app:latest
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: AZURE_OPENAI_ENDPOINT
valueFrom:
configMapKeyRef:
name: azure-config
key: endpoint
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 20
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: azure-ai-service
namespace: production
spec:
selector:
app: azure-ai-app
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
16.3 CI/CD 配置
Azure DevOps Pipeline:
trigger:
branches:
include:
- main
pool:
vmImage: 'ubuntu-latest'
variables:
imageName: 'azure-ai-app'
containerRegistry: 'myregistry.azurecr.io'
stages:
- stage: Build
jobs:
- job: BuildJob
steps:
- task: Maven@3
inputs:
mavenPomFile: 'pom.xml'
goals: 'clean package'
options: '-DskipTests'
- task: Docker@2
inputs:
command: 'buildAndPush'
repository: '$(imageName)'
dockerfile: 'Dockerfile'
containerRegistry: '$(containerRegistry)'
tags: |
$(Build.BuildId)
latest
- stage: Deploy
dependsOn: Build
jobs:
- deployment: DeployJob
environment: 'production'
strategy:
runOnce:
deploy:
steps:
- task: Kubernetes@1
inputs:
connectionType: 'Azure Resource Manager'
azureSubscription: 'MyAzureSubscription'
azureResourceGroup: 'my-resource-group'
kubernetesCluster: 'my-aks-cluster'
command: 'apply'
arguments: '-f deployment.yaml'
十七、面试准备
17.1 常见面试题
Q1: Azure OpenAI 和 OpenAI 的主要区别是什么?
A: 主要区别包括:
- 数据位置:Azure OpenAI 可以选择数据存储区域
- 合规性:Azure OpenAI 提供更多企业合规认证
- 安全性:支持虚拟网络、托管身份等企业级安全特性
- SLA:Azure OpenAI 提供 99.9% 可用性保证
- 集成:与 Azure 生态系统无缝集成
Q2: 如何在项目中实现托管身份认证?
A: 实现步骤:
- 在 Azure 门户为应用启用托管身份
- 为托管身份分配 “Cognitive Services OpenAI User” 角色
- 在代码中使用 DefaultAzureCredentialBuilder
- 配置 OpenAIClientBuilder 使用托管身份
- 部署到 Azure 环境(App Service、AKS 等)
Q3: 如何优化 Azure OpenAI 的成本?
A: 优化策略:
- 选择合适的模型(GPT-3.5 vs GPT-4)
- 使用预留容量定价
- 实施响应缓存
- 优化提示词长度
- 实施配额管理
- 监控和分析使用情况
Q4: 如何保证 Azure OpenAI 应用的高可用性?
A: 高可用性方案:
- 多区域部署
- 实现故障转移机制
- 配置负载均衡
- 实施重试策略
- 监控和告警
- 定期健康检查
17.2 项目经验描述模板
模板:
在[项目名称]项目中,我负责集成 Azure OpenAI 服务。
技术选型:
- 选择 Azure OpenAI 而非 OpenAI API,因为:
1. 数据合规要求(数据不能离境)
2. 企业级安全需求(虚拟网络、托管身份)
3. SLA 保证(99.9% 可用性)
实现方案:
1. 使用 Spring AI 框架集成 Azure OpenAI
2. 配置托管身份认证,避免密钥管理
3. 实现多区域部署,提高可用性
4. 使用 Redis 缓存,降低成本 30%
5. 集成 Application Insights 监控
遇到的挑战:
1. 初期 API 调用延迟较高
- 解决:配置连接池,启用 HTTP/2
2. 成本超出预算
- 解决:实施缓存策略,优化提示词
3. 偶尔出现超时
- 解决:实现重试机制,配置故障转移
项目成果:
- 响应时间从 3s 降低到 1s
- 成本降低 40%
- 可用性达到 99.95%
- 支持 1000+ 并发用户
十八、总结与展望
18.1 核心要点
通过本章学习,你已经掌握了:
-
Azure OpenAI 基础
- 服务特点和优势
- 资源创建和配置
- 模型部署和管理
-
Spring AI 集成
- 依赖配置
- 认证方式(API 密钥、托管身份)
- 基本使用方法
-
企业级特性
- 虚拟网络集成
- 内容过滤
- 审计和监控
-
生产实践
- 多区域部署
- 负载均衡
- 故障转移
- 成本优化
18.2 最佳实践总结
-
安全性
- 使用托管身份而非 API 密钥
- 配置虚拟网络和私有端点
- 实施 IP 白名单
- 定期轮换密钥
-
可靠性
- 多区域部署
- 实现故障转移
- 配置重试机制
- 监控和告警
-
性能
- 使用连接池
- 实施缓存策略
- 优化提示词
- 批量处理
-
成本
- 选择合适的模型
- 使用预留容量
- 实施配额管理
- 监控使用情况
18.3 下一步学习
完成本章后,建议:
-
实践项目
- 开发一个企业级应用
- 部署到 Azure 环境
- 实施监控和优化
-
深入学习
- Azure 安全最佳实践
- Kubernetes 部署
- DevOps 流程
-
持续关注
- Azure OpenAI 新功能
- 定价变化
- 最佳实践更新
恭喜你完成了 Azure OpenAI 集成的学习!
你已经掌握了企业级 AI 应用开发的核心技能。
继续学习下一章节:Ollama 本地模型集成,探索本地部署方案!
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)