LangChain4j是Java生态中强大的框架,帮助开发者快速构建基于大语言模型的应用。本文将带你从零开始,掌握LangChain4j的核心概念和Spring Boot集成技巧,打造属于自己的AI应用。

LangChain4j简介

LangChain4j是一个开源框架,专门为Java开发者设计,用于开发基于大语言模型的应用程序。它提供了一套完整的工具链,包括模型集成、提示工程、链式调用、记忆管理、代理系统等核心组件,并与Spring Boot等Java生态完美集成。

为什么选择LangChain4j?

  1. Java原生支持:专为Java生态设计,与Spring Boot完美集成
  2. 类型安全:利用Java的类型系统,提供更好的开发体验
  3. 简化开发流程:提供标准化组件,减少重复代码
  4. 模型无关性:支持多种LLM,如OpenAI、Anthropic、Hugging Face等
  5. 可扩展性:模块化设计,易于定制和扩展
  6. 生产就绪:内置错误处理、日志记录等企业级功能
  7. 社区活跃:拥有完善的文档和活跃的开源社区

核心组件详解

1. Models(模型)

LangChain4j支持多种类型的模型,包括大语言模型、聊天模型和文本嵌入模型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiEmbeddingModel;
import dev.langchain4j.model.openai.OpenAiTokenizer;

// 聊天模型
OpenAiChatModel chatModel = OpenAiChatModel.builder()
.apiKey("your-openai-api-key")
.modelName("gpt-3.5-turbo")
.temperature(0.3)
.maxTokens(1000)
.build();

// 嵌入模型
OpenAiEmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder()
.apiKey("your-openai-api-key")
.modelName("text-embedding-ada-002")
.build();

// 分词器
OpenAiTokenizer tokenizer = new OpenAiTokenizer("gpt-3.5-turbo");

2. Prompts(提示词)

提示词是与语言模型交互的核心,LangChain4j提供了丰富的提示词管理功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import dev.langchain4j.model.input.Prompt;
import dev.langchain4j.model.input.PromptTemplate;
import java.util.Map;

// 基础提示词模板
String template = """
你是一个专业的{{role}},请回答以下问题:
{{question}}

请用{{language}}回答。
""";

PromptTemplate promptTemplate = PromptTemplate.from(template);

// 使用提示词
Map<String, Object> variables = Map.of(
"role", "技术专家",
"question", "什么是微服务架构?",
"language", "中文"
);

Prompt prompt = promptTemplate.apply(variables);

// 直接创建提示词
Prompt directPrompt = Prompt.from("""
你是一个专业的技术专家,请详细解释什么是微服务架构。

请从以下几个方面进行说明:
1. 基本概念
2. 与单体架构的区别
3. 主要优势和挑战
4. 实际应用场景
""");

3. Chains(链)

在LangChain4j中,链式调用通过方法组合和管道模式来实现复杂的处理流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import dev.langchain4j.chain.ConversationalRetrievalChain;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.memory.chat.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.retriever.EmbeddingStoreRetriever;
import dev.langchain4j.store.embedding.EmbeddingStore;
import java.util.function.Function;

// 创建对话链
ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);

ConversationalRetrievalChain chain = ConversationalRetrievalChain.builder()
.chatLanguageModel(chatModel)
.retriever(retriever)
.chatMemory(chatMemory)
.build();

// 使用链进行对话
String question = "什么是微服务架构?";
AiMessage answer = chain.execute(question);

// 自定义链式处理
Function<String, String> processingChain = input -> {
// 第一步:预处理输入
String processedInput = preprocess(input);

// 第二步:调用AI模型
AiMessage response = chatModel.generate(
UserMessage.from(processedInput)
);

// 第三步:后处理输出
return postprocess(response.text());
};

// 执行链式处理
String result = processingChain.apply("原始输入");

4. Memory(记忆)

记忆组件让LLM能够记住之前的对话历史,提供更连贯的交互体验。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import dev.langchain4j.memory.chat.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.memory.chat.TokenWindowChatMemory;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.data.message.AiMessage;

// 基于消息窗口的记忆
ChatMemory messageWindowMemory = MessageWindowChatMemory.withMaxMessages(10);

// 基于Token窗口的记忆
ChatMemory tokenWindowMemory = TokenWindowChatMemory.withMaxTokens(1000, tokenizer);

// 对话服务类
public class ConversationalService {
private final OpenAiChatModel chatModel;
private final ChatMemory chatMemory;

public ConversationalService(OpenAiChatModel chatModel, ChatMemory chatMemory) {
this.chatModel = chatModel;
this.chatMemory = chatMemory;
}

public String chat(String userMessage) {
// 添加用户消息到记忆
chatMemory.add(UserMessage.from(userMessage));

// 获取对话历史
String conversationHistory = chatMemory.messages().stream()
.map(ChatMessage::text)
.collect(Collectors.joining("\n"));

// 构建完整提示词
Prompt prompt = Prompt.from(
"基于以下对话历史回答用户的问题:\n" +
conversationHistory + "\n" +
"用户问题:" + userMessage + "\n" +
"请提供准确、详细的回答:"
);

// 调用AI模型
AiMessage aiResponse = chatModel.generate(prompt.toUserMessage());

// 添加AI回复到记忆
chatMemory.add(aiResponse);

return aiResponse.text();
}
}

// 使用示例
MessageWindowChatMemory memory = MessageWindowChatMemory.withMaxMessages(20);
ConversationalService service = new ConversationalService(chatModel, memory);

String response1 = service.chat("你好,我叫小明");
String response2 = service.chat("我刚才说什么名字了?");

5. Agents(代理)

在LangChain4j中,代理功能通过Tool接口和AiServices来实现,让LLM能够自主选择工具并执行任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.agent.tool.ToolSpecification;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.Result;

// 定义工具类
public class SearchTools {

@Tool("搜索互联网信息")
public String searchWeb(String query) {
// 这里实现具体的搜索逻辑
// 例如调用搜索引擎API
return "搜索结果:根据查询 '" + query + "' 找到的相关信息...";
}

@Tool("计算数学表达式")
public double calculate(String expression) {
// 这里实现数学计算逻辑
// 可以使用JavaScript引擎或第三方库
try {
// 简化实现,实际应该使用更安全的计算方式
return Double.parseDouble(expression);
} catch (Exception e) {
return 0.0;
}
}

@Tool("获取当前时间")
public String getCurrentTime() {
return LocalDateTime.now().toString();
}
}

// 定义AI服务接口
interface IntelligentAssistant {
@dev.langchain4j.service.UserMessage("请帮我处理以下请求:{{request}}")
Result<String> processRequest(String request);
}

// 创建带工具的AI服务
IntelligentAssistant assistant = AiServices.builder(IntelligentAssistant.class)
.chatLanguageModel(chatModel)
.tools(new SearchTools())
.build();

// 使用代理
Result<String> result = assistant.processRequest("2024年最新的AI技术有哪些?");
String answer = result.content();

// 自定义工具执行器
public class CustomToolExecutor {
private final OpenAiChatModel chatModel;
private final List<ToolSpecification> tools;

public CustomToolExecutor(OpenAiChatModel chatModel, List<ToolSpecification> tools) {
this.chatModel = chatModel;
this.tools = tools;
}

public String executeWithTools(String userQuery) {
// 构建包含工具信息的提示词
StringBuilder prompt = new StringBuilder();
prompt.append("你是一个智能助手,可以使用以下工具来帮助用户:\n\n");

for (ToolSpecification tool : tools) {
prompt.append(String.format("- %s: %s\n",
tool.name(), tool.description()));
}

prompt.append(String.format("\n用户查询:%s\n", userQuery));
prompt.append("请根据用户查询选择合适的工具来回答问题。");

// 调用AI模型
AiMessage response = chatModel.generate(UserMessage.from(prompt.toString()));
return response.text();
}
}

实战案例:智能问答系统

让我们通过一个完整的Spring Boot项目来展示如何使用LangChain4j构建智能问答系统。

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
llm-qa-system/
├── src/main/java/com/example/qa/
│ ├── config/ # 配置类
│ │ ├── AiConfig.java # AI模型配置
│ │ └── AppConfig.java # 应用配置
│ ├── controller/ # 控制器
│ │ └── QaController.java # 问答接口
│ ├── service/ # 业务服务
│ │ ├── QaService.java # 问答服务
│ │ ├── PromptService.java # 提示词服务
│ │ └── MemoryService.java # 记忆服务
│ ├── model/ # 数据模型
│ │ ├── QaRequest.java # 请求模型
│ │ └── QaResponse.java # 响应模型
│ └── util/ # 工具类
│ └── PromptTemplates.java # 提示词模板
├── src/main/resources/
│ ├── application.yml # 应用配置
│ └── templates/ # 提示词模板
└── pom.xml # Maven依赖

Maven依赖配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!-- pom.xml -->
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- LangChain4j -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
<version>0.25.0</version>
</dependency>

<!-- 其他依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

核心配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// src/main/java/com/example/qa/config/AiConfig.java
@Configuration
public class AiConfig {

@Value("${openai.api-key}")
private String openAiApiKey;

@Bean
public OpenAiChatModel chatModel() {
return OpenAiChatModel.builder()
.apiKey(openAiApiKey)
.modelName("gpt-3.5-turbo")
.temperature(0.3)
.maxTokens(1000)
.build();
}

@Bean
public ChatMemory chatMemory() {
return MessageWindowChatMemory.withMaxMessages(20);
}
}

数据模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// src/main/java/com/example/qa/model/QaRequest.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class QaRequest {
@NotBlank(message = "问题不能为空")
private String question;

private String sessionId;

@Builder.Default
private String role = "技术专家";
}

// src/main/java/com/example/qa/model/QaResponse.java
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class QaResponse {
private String answer;
private String sessionId;
private long timestamp;
private boolean success;
private String errorMessage;
}

核心服务类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// src/main/java/com/example/qa/service/QaService.java
@Service
@Slf4j
public class QaService {

private final OpenAiChatModel chatModel;
private final PromptService promptService;
private final MemoryService memoryService;

public QaService(OpenAiChatModel chatModel,
PromptService promptService,
MemoryService memoryService) {
this.chatModel = chatModel;
this.promptService = promptService;
this.memoryService = memoryService;
}

public QaResponse askQuestion(QaRequest request) {
try {
// 获取或创建会话记忆
ChatMemory memory = memoryService.getMemory(request.getSessionId());

// 添加用户消息到记忆
memory.add(UserMessage.from(request.getQuestion()));

// 生成提示词
Prompt prompt = promptService.generateQaPrompt(request, memory);

// 调用AI模型
AiMessage aiResponse = chatModel.generate(prompt.toUserMessage());

// 添加AI回复到记忆
memory.add(aiResponse);

return QaResponse.builder()
.answer(aiResponse.text())
.sessionId(request.getSessionId())
.timestamp(System.currentTimeMillis())
.success(true)
.build();

} catch (Exception e) {
log.error("处理问答请求失败", e);
return QaResponse.builder()
.success(false)
.errorMessage("处理问题时出现错误:" + e.getMessage())
.timestamp(System.currentTimeMillis())
.build();
}
}
}

提示词服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// src/main/java/com/example/qa/service/PromptService.java
@Service
public class PromptService {

public Prompt generateQaPrompt(QaRequest request, ChatMemory memory) {
// 获取对话历史
String conversationHistory = memory.messages().stream()
.map(ChatMessage::text)
.collect(Collectors.joining("\n"));

String template = """
你是一个专业的{role},请基于以下对话历史回答用户的问题。

对话历史:
{history}

当前问题:{question}

回答要求:
1. 内容准确,基于可靠的技术知识
2. 结构清晰,逻辑性强
3. 适当使用代码示例说明
4. 语言简洁明了,避免废话
5. 如果是技术问题,提供具体可行的解决方案
""";

Map<String, Object> variables = Map.of(
"role", request.getRole(),
"history", conversationHistory,
"question", request.getQuestion()
);

PromptTemplate promptTemplate = PromptTemplate.from(template);
return promptTemplate.apply(variables);
}
}

记忆管理服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// src/main/java/com/example/qa/service/MemoryService.java
@Service
public class MemoryService {

private final ConcurrentHashMap<String, ChatMemory> memoryStore = new ConcurrentHashMap<>();

public ChatMemory getMemory(String sessionId) {
if (sessionId == null || sessionId.trim().isEmpty()) {
sessionId = generateSessionId();
}

return memoryStore.computeIfAbsent(sessionId, k ->
MessageWindowChatMemory.withMaxMessages(20));
}

public void clearMemory(String sessionId) {
if (sessionId != null) {
memoryStore.remove(sessionId);
}
}

public Set<String> getActiveSessions() {
return new HashSet<>(memoryStore.keySet());
}

private String generateSessionId() {
return UUID.randomUUID().toString();
}
}

REST控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// src/main/java/com/example/qa/controller/QaController.java
@RestController
@RequestMapping("/api/qa")
@RequiredArgsConstructor
@Slf4j
public class QaController {

private final QaService qaService;
private final MemoryService memoryService;

@PostMapping("/ask")
public ResponseEntity<QaResponse> askQuestion(@Valid @RequestBody QaRequest request) {
log.info("收到问答请求:{}", request.getQuestion());

QaResponse response = qaService.askQuestion(request);

return response.isSuccess()
? ResponseEntity.ok(response)
: ResponseEntity.badRequest().body(response);
}

@PostMapping("/sessions/{sessionId}/clear")
public ResponseEntity<Void> clearSession(@PathVariable String sessionId) {
memoryService.clearMemory(sessionId);
return ResponseEntity.ok().build();
}

@GetMapping("/sessions")
public ResponseEntity<Set<String>> getActiveSessions() {
return ResponseEntity.ok(memoryService.getActiveSessions());
}
}

高级功能扩展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// 添加文档问答功能
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import dev.langchain4j.retriever.EmbeddingStoreRetriever;

@Service
public class DocumentQaService {

private final OpenAiChatModel chatModel;
private final OpenAiEmbeddingModel embeddingModel;
private final EmbeddingStoreRetriever retriever;

public DocumentQaService(OpenAiChatModel chatModel,
OpenAiEmbeddingModel embeddingModel) {
this.chatModel = chatModel;
this.embeddingModel = embeddingModel;

// 初始化向量存储和检索器
this.retriever = initializeRetriever();
}

private EmbeddingStoreRetriever initializeRetriever() {
// 加载文档
List<Document> documents = FileSystemDocumentLoader.loadDocuments(
Paths.get("src/main/resources/docs"),
new TextDocumentParser()
);

// 分割文档
List<TextSegment> segments = DocumentSplitters.recursive(300, 0)
.split(documents);

// 创建嵌入并存储
EmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();

List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
for (int i = 0; i < segments.size(); i++) {
embeddingStore.add(segments.get(i), embeddings.get(i));
}

// 创建检索器
return EmbeddingStoreRetriever.from(embeddingStore, embeddingModel, 5);
}

public String askDocument(String question) {
// 检索相关文档片段
List<TextSegment> relevantSegments = retriever.findRelevant(question);

// 构建上下文
StringBuilder context = new StringBuilder();
context.append("基于以下文档内容回答问题:\n\n");
for (TextSegment segment : relevantSegments) {
context.append(segment.text()).append("\n\n");
}
context.append("问题:").append(question);

// 调用AI模型
AiMessage response = chatModel.generate(UserMessage.from(context.toString()));
return response.text();
}
}

部署和优化

环境配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# application.yml
spring:
application:
name: llm-qa-system
profiles:
active: dev

server:
port: 8080

openai:
api-key: ${OPENAI_API_KEY:your-api-key-here}
model: gpt-3.5-turbo
temperature: 0.3
max-tokens: 1000

logging:
level:
com.example.qa: DEBUG
1
2
3
4
5
6
7
8
9
10
11
12
13
# docker-compose.yml
version: '3.8'
services:
llm-qa-app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- OPENAI_API_KEY=${OPENAI_API_KEY}
volumes:
- ./logs:/app/logs
restart: unless-stopped
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Dockerfile
FROM openjdk:17-jdk-slim

WORKDIR /app

# 复制Maven构建产物
COPY target/*.jar app.jar

# 创建非root用户
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser

EXPOSE 8080

CMD ["java", "-jar", "app.jar"]

性能优化策略

  1. 缓存机制

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    import com.github.benmanes.caffeine.cache.Cache;
    import com.github.benmanes.caffeine.cache.Caffeine;
    import java.time.Duration;

    @Service
    public class AiCacheService {

    private final Cache<String, String> responseCache;

    public AiCacheService() {
    this.responseCache = Caffeine.newBuilder()
    .maximumSize(1000)
    .expireAfterWrite(Duration.ofHours(1))
    .build();
    }

    public String getCachedResponse(String question) {
    return responseCache.getIfPresent(question);
    }

    public void cacheResponse(String question, String response) {
    responseCache.put(question, response);
    }

    public String getOrCompute(String question, Function<String, String> supplier) {
    return responseCache.get(question, supplier);
    }
    }
  2. 异步处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Service;
    import java.util.concurrent.CompletableFuture;

    @Service
    public class AsyncAiService {

    private final OpenAiChatModel chatModel;

    public AsyncAiService(OpenAiChatModel chatModel) {
    this.chatModel = chatModel;
    }

    @Async
    public CompletableFuture<String> asyncQuery(String question) {
    try {
    AiMessage response = chatModel.generate(UserMessage.from(question));
    return CompletableFuture.completedFuture(response.text());
    } catch (Exception e) {
    return CompletableFuture.failedFuture(e);
    }
    }

    // 并发处理多个查询
    public CompletableFuture<List<String>> batchQueries(List<String> questions) {
    List<CompletableFuture<String>> futures = questions.stream()
    .map(this::asyncQuery)
    .collect(Collectors.toList());

    return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
    .thenApply(v -> futures.stream()
    .map(CompletableFuture::join)
    .collect(Collectors.toList()));
    }
    }
  3. 流式响应

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    import reactor.core.publisher.Flux;
    import dev.langchain4j.model.StreamingResponseHandler;
    import dev.langchain4j.model.response.AiMessage;

    @RestController
    public class StreamingController {

    private final OpenAiStreamingChatModel streamingChatModel;

    public StreamingController(OpenAiStreamingChatModel streamingChatModel) {
    this.streamingChatModel = streamingChatModel;
    }

    @GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> streamChat(@RequestParam String message) {
    return Flux.create(sink -> {
    streamingChatModel.generate(
    UserMessage.from(message),
    new StreamingResponseHandler<AiMessage>() {
    @Override
    public void onNext(String token) {
    sink.next(token);
    }

    @Override
    public void onComplete(Response<AiMessage> response) {
    sink.complete();
    }

    @Override
    public void onError(Throwable error) {
    sink.error(error);
    }
    }
    );
    });
    }
    }

最佳实践

1. 错误处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import io.github.resilience4j.retry.annotation.Retry;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.stereotype.Service;

@Service
public class ResilientAiService {

private final OpenAiChatModel chatModel;

public ResilientAiService(OpenAiChatModel chatModel) {
this.chatModel = chatModel;
}

@Retry(name = "ai-service", fallbackMethod = "fallbackQuery")
@CircuitBreaker(name = "ai-service", fallbackMethod = "fallbackQuery")
public String robustQuery(String question) {
AiMessage response = chatModel.generate(UserMessage.from(question));
return response.text();
}

public String fallbackQuery(String question, Throwable t) {
log.error("AI服务调用失败,使用降级方案", t);
return "抱歉,AI服务暂时不可用,请稍后再试。";
}
}

2. 日志记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class LoggingAiService {

private final OpenAiChatModel chatModel;

public LoggingAiService(OpenAiChatModel chatModel) {
this.chatModel = chatModel;
}

public String queryWithLogging(String question, String userId) {
long startTime = System.currentTimeMillis();

try {
log.info("用户[{}]发起AI查询: {}", userId, question);

AiMessage response = chatModel.generate(UserMessage.from(question));
String answer = response.text();

long duration = System.currentTimeMillis() - startTime;
log.info("用户[{}]查询完成,耗时: {}ms", userId, duration);

return answer;

} catch (Exception e) {
long duration = System.currentTimeMillis() - startTime;
log.error("用户[{}]查询失败,耗时: {}ms,错误: {}",
userId, duration, e.getMessage(), e);
throw e;
}
}
}

3. 安全考虑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

@Service
public class SecureAiService {

private final OpenAiChatModel chatModel;
private final RateLimiter rateLimiter;

public SecureAiService(OpenAiChatModel chatModel) {
this.chatModel = chatModel;
// 配置速率限制器
this.rateLimiter = RateLimiter.create(10.0); // 每秒10个请求
}

public String secureQuery(String question, Authentication authentication) {
// 速率限制
if (!rateLimiter.tryAcquire()) {
throw new RuntimeException("请求过于频繁,请稍后再试");
}

// 输入验证和过滤
String sanitizedQuestion = sanitizeInput(question);

// 内容安全检查
if (!isContentSafe(sanitizedQuestion)) {
throw new RuntimeException("输入内容包含不当内容");
}

try {
AiMessage response = chatModel.generate(UserMessage.from(sanitizedQuestion));
String answer = response.text();

// 输出内容安全检查
if (!isContentSafe(answer)) {
log.warn("AI输出包含不当内容,已过滤");
return "抱歉,无法提供相关回答。";
}

return answer;

} catch (Exception e) {
log.error("AI服务调用失败", e);
throw new RuntimeException("服务暂时不可用");
}
}

private String sanitizeInput(String input) {
// 实现输入过滤逻辑
return input.replaceAll("<script.*?>.*?</script>", "")
.replaceAll("javascript:", "")
.trim();
}

private boolean isContentSafe(String content) {
// 实现内容安全检查逻辑
// 这里可以集成第三方内容审核服务
return !content.toLowerCase().contains("不当内容");
}
}

4. 监控和指标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.springframework.stereotype.Service;

@Service
public class MonitoredAiService {

private final Counter queryCounter;
private final Counter errorCounter;
private final Timer queryTimer;

private final OpenAiChatModel chatModel;

public MonitoredAiService(MeterRegistry registry, OpenAiChatModel chatModel) {
this.chatModel = chatModel;

// 注册监控指标
this.queryCounter = Counter.builder("ai.queries.total")
.description("Total number of AI queries")
.register(registry);

this.errorCounter = Counter.builder("ai.errors.total")
.description("Total number of AI query errors")
.register(registry);

this.queryTimer = Timer.builder("ai.query.duration")
.description("AI query duration")
.register(registry);
}

public String monitoredQuery(String question) {
queryCounter.increment();

return queryTimer.recordCallable(() -> {
try {
AiMessage response = chatModel.generate(UserMessage.from(question));
return response.text();
} catch (Exception e) {
errorCounter.increment();
throw e;
}
});
}
}

安全考虑要点

  • API密钥管理:使用Spring Cloud Config或Vault进行密钥管理
  • 输入验证:使用Bean Validation和自定义验证器
  • 速率限制:集成Spring Cloud Gateway或Resilience4j
  • 内容审核:集成第三方内容审核服务
  • 审计日志:记录所有AI交互用于安全审计
  • HTTPS通信:确保所有API调用使用HTTPS

总结与注意事项

技术要点总结

  1. LangChain4j是Java生态中强大的LLM应用开发框架
  2. 核心组件包括Models、Prompts、Chains、Memory、Agents、Tools
  3. Spring Boot集成提供了企业级的应用开发体验
  4. 实际应用可以构建问答系统、文档分析、代码生成等多种场景
  5. 部署优化需要考虑性能、缓存、安全、监控等因素

使用注意事项

  • 合理控制API调用频率,避免超出OpenAI的速率限制
  • 注意数据隐私和安全问题,遵守相关法律法规
  • 根据实际需求选择合适的模型和参数配置
  • 定期更新LangChain4j和Spring Boot版本,保持安全性和性能
  • 在生产环境中使用连接池和熔断器提高系统稳定性
  • 监控AI服务的调用情况和性能指标

扩展阅读建议

  • 深入学习LangChain4j官方文档和示例代码
  • 探索Spring AI项目,了解更多AI集成方案
  • 学习Spring Boot最佳实践和微服务架构
  • 关注LLM技术的最新发展动态和行业趋势
  • 学习相关的AI安全、伦理和合规要求
  • 研究向量数据库和RAG(检索增强生成)技术

通过本文的介绍,你已经掌握了使用LangChain4j构建LLM应用的基本技能。开始动手实践,打造属于自己的AI应用吧!

参考资料

  1. LangChain4j官方文档
  2. LangChain4j GitHub仓库
  3. OpenAI API文档
  4. Spring Boot官方文档
  5. Spring WebFlux响应式编程
  6. Resilience4j故障恢复库
  7. Micrometer监控指标
  8. Docker最佳实践
  9. Spring Security安全框架
  10. 向量数据库对比分析