在大语言模型(LLM)技术快速发展的今天,将 ChatGPT4 等先进模型集成到企业应用中已成为提升产品竞争力的关键。微软 Azure 提供的 ChatGPT4 服务凭借稳定的性能、全面的 API 支持和企业级安全保障,成为企业集成 LLM 的优选方案。本文将以程序员视角,详细讲解如何在 Java 应用中高效调用 Azure ChatGPT4 模型,从开发环境配置到核心交互功能实现,再到性能优化与场景适配,通过完整代码示例展示企业级 LLM 集成的最佳实践。

开发环境配置与认证管理:安全连接 Azure LLM 服务

集成 Azure ChatGPT4 的首要任务是搭建安全可靠的开发环境,正确配置认证信息是确保应用能安全访问 Azure 服务的基础。合理的环境配置不仅能提高开发效率,更能保障 API 调用的安全性和合规性。

开发依赖与项目配置需要引入 Azure 官方 SDK 并配置必要的项目依赖:


<!-- Maven依赖配置 -->

<dependencies>

<!-- Azure OpenAI服务客户端 -->

<dependency>

<groupId>com.azure</groupId>

<artifactId>azure-ai-openai</artifactId>

<version>1.0.0-beta.11</version>

</dependency>

<!-- Azure身份认证 -->

<dependency>

<groupId>com.azure</groupId>

<artifactId>azure-identity</artifactId>

<version>1.10.4</version>

</dependency>

<!-- JSON处理 -->

<dependency>

<groupId>com.fasterxml.jackson.core</groupId>

<artifactId>jackson-databind</artifactId>

<version>2.15.2</version>

</dependency>

<!-- 日志框架 -->

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-api</artifactId>

<version>2.0.9</version>

</dependency>

<dependency>

<groupId>ch.qos.logback</groupId>

<artifactId>logback-classic</artifactId>

<version>1.4.8</version>

</dependency>

<!-- 单元测试 -->

<dependency>

<groupId>org.junit.jupiter</groupId>

<artifactId>junit-jupiter-api</artifactId>

<version>5.9.3</version>

<scope>test</scope>

</dependency>

</dependencies>

服务认证配置是连接 Azure ChatGPT4 服务的关键环节,需要妥善管理访问凭证:


@Configuration

public class AzureOpenAiConfig {

// 从配置文件读取Azure OpenAI服务信息

@Value("${azure.openai.endpoint}")

private String endpoint;

@Value("${azure.openai.key}")

private String apiKey;

@Value("${azure.openai.deployment-id}")

private String deploymentId;

@Value("${azure.openai.timeout:30000}")

private int timeout;

// 配置OpenAI客户端

@Bean

public OpenAIClient openAIClient() {

// 验证必要配置

if (StringUtils.isEmpty(endpoint) || StringUtils.isEmpty(apiKey) ||

StringUtils.isEmpty(deploymentId)) {

throw new IllegalArgumentException("Azure OpenAI配置不完整,需指定endpoint、key和deployment-id");

}

// 创建客户端配置

OpenAIClientBuilder clientBuilder = new OpenAIClientBuilder();

// 配置API密钥认证

clientBuilder.credential(new AzureKeyCredential(apiKey));

clientBuilder.endpoint(endpoint);

// 配置客户端选项(超时设置等)

clientBuilder.clientOptions(

new ClientOptions()

.setConnectTimeout(Duration.ofMillis(timeout))

.setResponseTimeout(Duration.ofMillis(timeout))

);

return clientBuilder.buildClient();

}

// 配置聊天服务

@Bean

public AzureChatService azureChatService(OpenAIClient openAIClient) {

return new AzureChatService(

openAIClient,

deploymentId,

Duration.ofMinutes(30) // 对话上下文过期时间

);

}

}

认证配置最佳实践:

  • 生产环境中务必使用 Azure Key Vault 管理 API 密钥,避免硬编码
  • 为不同环境(开发、测试、生产)配置不同的部署实例
  • 设置合理的超时时间,平衡用户体验和资源消耗
  • 定期轮换 API 密钥,提高系统安全性

核心交互功能开发:实现与 ChatGPT4 的智能对话

完成环境配置后,核心工作是开发与 ChatGPT4 模型的交互功能。这一过程包括构建对话请求、处理模型响应、管理对话上下文等关键环节,每个环节都需要针对 LLM 的特性进行优化设计。

对话模型封装提供简洁易用的接口,隐藏底层 API 调用细节:


@Service

@Slf4j

public class AzureChatService {

private final OpenAIClient openAIClient;

private final String deploymentId;

private final Duration conversationExpiration;

// 存储对话上下文,使用ConcurrentHashMap确保线程安全

private final ConcurrentHashMap<String, ConversationContext> conversationContexts = new ConcurrentHashMap<>();

public AzureChatService(OpenAIClient openAIClient,

String deploymentId,

Duration conversationExpiration) {

this.openAIClient = openAIClient;

this.deploymentId = deploymentId;

this.conversationExpiration = conversationExpiration;

// 启动定时任务清理过期对话

scheduleConversationCleanup();

}

/**

* 发送消息并获取响应

* @param sessionId 会话ID,用于维护对话上下文

* @param userMessage 用户消息

* @param systemPrompt 系统提示词(可选)

* @return 模型响应结果

*/

public ChatResponse sendMessage(String sessionId, String userMessage, String systemPrompt) {

// 1. 获取或创建对话上下文

ConversationContext context = getOrCreateConversationContext(sessionId, systemPrompt);

// 2. 添加用户消息到对话历史

context.addMessage(ChatRole.USER, userMessage);

// 3. 构建请求参数

ChatCompletionsOptions options = new ChatCompletionsOptions(context.getMessages());

// 4. 配置生成参数

options.setTemperature(0.7); // 控制随机性,0-1之间

options.setMaxTokens(1024); // 最大生成token数

options.setN(1); // 生成回复数量

options.setStop(List.of("\nUser:", "\nSystem:")); // 停止序列

try {

// 5. 调用Azure OpenAI服务

long startTime = System.currentTimeMillis();

ChatCompletions response= openAIClient.getChatCompletions(deploymentId, options);

long duration = System.currentTimeMillis() - startTime;

log.info("LLM调用耗时: {}ms, sessionId: {}", duration, sessionId);

// 6. 处理响应结果

if (response.getChoices().isEmpty()) {

throw new AzureChatException("未获取到模型响应");

}

// 7. 获取助手回复并添加到对话历史

ChatChoice choice = response.getChoices().get(0);

String assistantMessage = choice.getMessage().getContent();

context.addMessage(ChatRole.ASSISTANT, assistantMessage);

// 8. 更新最后活动时间

context.updateLastActiveTime();

// 9. 返回响应结果

return ChatResponse.builder()

.success(true)

.responseText(assistantMessage)

.conversationId(sessionId)

.tokenUsage(response.getUsage())

.requestTime(duration)

.build();

} catch (Exception e) {

log.error("LLM调用失败, sessionId: {}", sessionId, e);

throw new AzureChatException("调用ChatGPT4服务失败: " + e.getMessage(), e);

}

}

/**

* 获取或创建对话上下文

*/

private ConversationContext getOrCreateConversationContext(String sessionId, String systemPrompt) {

return conversationContexts.compute(sessionId, (id, existingContext) -> {

if (existingContext != null) {

// 更新最后活动时间

existingContext.updateLastActiveTime();

// 如果提供了新的系统提示词,更新它

if (StringUtils.hasText(systemPrompt) &&

!systemPrompt.equals(existingContext.getSystemPrompt())) {

existingContext.setSystemPrompt(systemPrompt);

existingContext.resetMessages();

}

return existingContext;

} else {

// 创建新的对话上下文

log.info("创建新对话上下文, sessionId: {}", sessionId);

return new ConversationContext(systemPrompt);

}

});

}

/**

* 清理会话上下文

*/

public void clearConversation(String sessionId) {

conversationContexts.remove(sessionId);

log.info("清理对话上下文, sessionId: {}", sessionId);

}

/**

* 定时清理过期对话

*/

private void scheduleConversationCleanup() {

ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

// 每小时执行一次清理

scheduler.scheduleAtFixedRate(() -> {

long expiredTime = System.currentTimeMillis() - conversationExpiration.toMillis();

int removedCount = 0;

// 遍历并清理过期对话

for (Iterator<Map.Entry<String, ConversationContext>> iterator = conversationContexts.entrySet().iterator();

iterator.hasNext(); ) {

Map.Entry<String, ConversationContext> entry = iterator.next();

if (entry.getValue().getLastActiveTime() < expiredTime) {

iterator.remove();

removedCount++;

}

}

if (removedCount > 0) {

log.info("清理过期对话上下文: {} 个", removedCount);

}

}, 0, 1, TimeUnit.HOURS);

}

/**

* 对话上下文内部类

*/

private static class ConversationContext {

private final String systemPrompt;

private final List<ChatMessage> messages = new ArrayList<>();

private long lastActiveTime;

public ConversationContext(String systemPrompt) {

this.systemPrompt = StringUtils.defaultIfEmpty(systemPrompt, "你是一个 helpful 的助手");

this.lastActiveTime = System.currentTimeMillis();

// 添加系统提示词到对话历史

if (StringUtils.hasText(this.systemPrompt)) {

messages.add(new ChatMessage(ChatRole.SYSTEM, this.systemPrompt));

}

}

public void addMessage(ChatRole role, String content) {

messages.add(new ChatMessage(role, content));

// 限制对话历史长度,防止token溢出

if (messages.size() > 20) {

// 保留系统消息,移除最旧的用户和助手消息对

messages.remove(1); // 移除第一个用户消息

messages.remove(1); // 移除对应的助手消息

}

}

public List<ChatMessage> getMessages() {

return new ArrayList<>(messages); // 返回副本防止外部修改

}

public void resetMessages() {

messages.clear();

if (StringUtils.hasText(systemPrompt)) {

messages.add(new ChatMessage(ChatRole.SYSTEM, systemPrompt));

}

}

public void updateLastActiveTime() {

this.lastActiveTime = System.currentTimeMillis();

}

public long getLastActiveTime() {

return lastActiveTime;

}

public String getSystemPrompt() {

return systemPrompt;

}

}

}

请求与响应模型定义清晰的数据结构,规范交互格式:


// 聊天响应模型

@Data

@Builder

public class ChatResponse {

private boolean success;

private String responseText;

private String conversationId;

private ChatCompletionsUsage tokenUsage;

private long requestTime; // 请求耗时(ms)

private String errorMessage; // 错误信息,成功时为null

}

// 控制器层实现

@RestController

@RequestMapping("/api/chat")

@RequiredArgsConstructor

public class ChatController {

private final AzureChatService chatService;

/**

* 处理聊天请求

*/

@PostMapping

public ResponseEntity<ChatResponse> chat(@RequestBody ChatRequest request) {

try {

// 生成或使用客户端提供的sessionId

String sessionId = StringUtils.hasText(request.getSessionId())

? request.getSessionId()

: generateSessionId();

// 调用聊天服务

ChatResponse response = chatService.sendMessage(

sessionId,

request.getMessage(),

request.getSystemPrompt()

);

return ResponseEntity.ok(response);

} catch (Exception e) {

ChatResponse errorResponse = ChatResponse.builder()

.success(false)

.errorMessage(e.getMessage())

.build();

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);

}

}

/**

* 清理对话上下文

*/

@DeleteMapping("/{sessionId}")

public ResponseEntity<Void> clearConversation(@PathVariable String sessionId) {

chatService.clearConversation(sessionId);

return ResponseEntity.noContent().build();

}

// 生成唯一会话ID

private String generateSessionId() {

return UUID.randomUUID().toString().replace("-", "");

}

}

// 请求模型

@Data

public class ChatRequest {

private String sessionId; // 可选,用于维持对话上下文

@NotBlank(message = "消息内容不能为空")

private String message; // 用户消息

private String systemPrompt; // 系统提示词,可选

}

性能优化与场景适配:构建企业级 LLM 应用

基础交互功能实现后,还需要针对企业级应用的需求进行性能优化和场景适配。这包括请求缓存、异步处理、错误重试、多场景适配等关键技术点,确保 LLM 服务在实际生产环境中稳定高效运行。

请求缓存与优化减少重复请求,提升响应速度:


@Service

@Slf4j

public class ChatOptimizationService {

private final AzureChatService chatService;

// Caffeine缓存配置,设置过期时间和最大缓存数量

private final LoadingCache<String, String> requestCache = Caffeine.newBuilder()

.maximumSize(1000) // 最大缓存条目

.expireAfterWrite(1, TimeUnit.HOURS) // 写入后1小时过期

.recordStats() // 开启统计

.build(this::computeCacheKey);

public ChatOptimizationService(AzureChatService chatService) {

this.chatService = chatService;

}

/**

* 带缓存的消息发送方法

* 适用于无状态查询类请求,不适合需要上下文的对话

*/

public ChatResponse sendCachedMessage(String sessionId, String userMessage,

String systemPrompt, boolean useCache) {

// 对于简单查询且启用缓存的请求,先检查缓存

if (useCache && isCacheableRequest(userMessage)) {

try {

// 生成缓存键

String cacheKey = generateCacheKey(userMessage, systemPrompt);

// 从缓存获取或计算

String cachedResponse = requestCache.get(cacheKey);

// 返回缓存结果

return ChatResponse.builder()

.success(true)

.responseText(cachedResponse)

.conversationId(sessionId)

.requestTime(0)

.build();

} catch (Exception e) {

log.warn("缓存查询失败,将直接调用LLM服务", e);

// 缓存失败时直接调用原始服务

}

}

// 不使用缓存或缓存未命中时,直接调用LLM服务

return chatService.sendMessage(sessionId, userMessage, systemPrompt);

}

/**

* 判断请求是否适合缓存

*/

private boolean isCacheableRequest(String message) {

// 简单规则:长度适中且不包含动态内容的查询

return message.length() < 500 &&

!message.contains("今天") &&

!message.contains("现在") &&

!message.contains("最新");

}

/**

* 生成缓存键

*/

private String generateCacheKey(String message, String systemPrompt) {

// 结合消息内容和系统提示词生成唯一键

String baseKey = message + "|" + StringUtils.defaultIfEmpty(systemPrompt, "");

// 使用MD5哈希缩短键长度

return DigestUtils.md5DigestAsHex(baseKey.getBytes(StandardCharsets.UTF_8));

}

/**

* 缓存加载函数,当缓存未命中时调用

*/

private String computeCacheKey(String cacheKey) {

// 这里需要反向解析缓存键获取原始消息,实际实现需存储键与消息的映射

// 简化实现:创建临时会话处理缓存计算

String tempSessionId = "cache-" + UUID.randomUUID();

try {

// 从缓存键反向解析消息(实际应用需完善此逻辑)

String message = resolveMessageFromCacheKey(cacheKey);

</doubaocanvas>

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐