LangChain4j是Java生态中强大的框架,帮助开发者快速构建基于大语言模型的应用。本文将带你从零开始,掌握LangChain4j的核心概念和Spring Boot集成技巧,打造属于自己的AI应用。
LangChain4j简介
LangChain4j是一个开源框架,专门为Java开发者设计,用于开发基于大语言模型的应用程序。它提供了一套完整的工具链,包括模型集成、提示工程、链式调用、记忆管理、代理系统等核心组件,并与Spring Boot等Java生态完美集成。
为什么选择LangChain4j?
- Java原生支持:专为Java生态设计,与Spring Boot完美集成
- 类型安全:利用Java的类型系统,提供更好的开发体验
- 简化开发流程:提供标准化组件,减少重复代码
- 模型无关性:支持多种LLM,如OpenAI、Anthropic、Hugging Face等
- 可扩展性:模块化设计,易于定制和扩展
- 生产就绪:内置错误处理、日志记录等企业级功能
- 社区活跃:拥有完善的文档和活跃的开源社区
核心组件详解
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);
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);
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" + "请提供准确、详细的回答:" );
AiMessage aiResponse = chatModel.generate(prompt.toUserMessage());
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) { return "搜索结果:根据查询 '" + query + "' 找到的相关信息..."; }
@Tool("计算数学表达式") public double calculate(String expression) { try { return Double.parseDouble(expression); } catch (Exception e) { return 0.0; } }
@Tool("获取当前时间") public String getCurrentTime() { return LocalDateTime.now().toString(); } }
interface IntelligentAssistant { @dev.langchain4j.service.UserMessage("请帮我处理以下请求:{{request}}") Result<String> processRequest(String request); }
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("请根据用户查询选择合适的工具来回答问题。");
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
| <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<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
| @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
| @Data @NoArgsConstructor @AllArgsConstructor public class QaRequest { @NotBlank(message = "问题不能为空") private String question;
private String sessionId;
@Builder.Default private String role = "技术专家"; }
@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
| @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);
AiMessage aiResponse = chatModel.generate(prompt.toUserMessage());
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
| @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
| @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
| @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);
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
| 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
| 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
| FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/*.jar app.jar
RUN groupadd -r appuser && useradd -r -g appuser appuser USER appuser
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]
|
性能优化策略
缓存机制:
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); } }
|
异步处理:
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())); } }
|
流式响应:
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); }
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
总结与注意事项
技术要点总结
- LangChain4j是Java生态中强大的LLM应用开发框架
- 核心组件包括Models、Prompts、Chains、Memory、Agents、Tools
- Spring Boot集成提供了企业级的应用开发体验
- 实际应用可以构建问答系统、文档分析、代码生成等多种场景
- 部署优化需要考虑性能、缓存、安全、监控等因素
使用注意事项
- 合理控制API调用频率,避免超出OpenAI的速率限制
- 注意数据隐私和安全问题,遵守相关法律法规
- 根据实际需求选择合适的模型和参数配置
- 定期更新LangChain4j和Spring Boot版本,保持安全性和性能
- 在生产环境中使用连接池和熔断器提高系统稳定性
- 监控AI服务的调用情况和性能指标
扩展阅读建议
- 深入学习LangChain4j官方文档和示例代码
- 探索Spring AI项目,了解更多AI集成方案
- 学习Spring Boot最佳实践和微服务架构
- 关注LLM技术的最新发展动态和行业趋势
- 学习相关的AI安全、伦理和合规要求
- 研究向量数据库和RAG(检索增强生成)技术
通过本文的介绍,你已经掌握了使用LangChain4j构建LLM应用的基本技能。开始动手实践,打造属于自己的AI应用吧!
参考资料
- LangChain4j官方文档
- LangChain4j GitHub仓库
- OpenAI API文档
- Spring Boot官方文档
- Spring WebFlux响应式编程
- Resilience4j故障恢复库
- Micrometer监控指标
- Docker最佳实践
- Spring Security安全框架
- 向量数据库对比分析