公司动态
LangChain4j实战:Java统一LLM应用开发框架与RAG实现
在实际 Java 项目中集成大语言模型LLM时开发者面临的最大挑战不是模型本身的能力而是如何将不同厂商的 API、向量数据库、工具调用和业务流程统一起来。每个 LLM 提供商都有自己的接口规范每个向量存储也有独特的数据操作方式直接对接意味着代码会充满厂商锁定的硬编码测试和切换成本极高。LangChain4j 正是为了解决这一问题而设计的 Java 库它提供了一套统一的 API让开发者可以用同一套代码对接 OpenAI、Google Gemini、Anthropic 等 20 多家 LLM 服务以及 Pinecone、Milvus、Chroma 等 30 多种向量数据库。更重要的是LangChain4j 不是简单地将 Python 版 LangChain 移植到 Java而是完全基于 Java 生态的习惯重新设计。它强调类型安全、POJO、接口隔离、依赖注入和流畅式 API并且与 Spring Boot、Quarkus、Helidon、Micronaut 等企业级框架无缝集成。这意味着你可以在熟悉的 Java 开发模式中快速构建 RAG检索增强生成、Agent智能体、工具调用等高级 LLM 应用而不必担心底层实现差异。本文将带你从零开始理解 LangChain4j 的核心设计思路完成环境准备和依赖配置实现一个可运行的 RAG 示例并深入讲解关键参数和常见问题排查方法。学完后你将掌握如何在生产环境中安全、高效地使用 LangChain4j 构建 LLM 应用。1. 理解 LangChain4j 的架构设计和工作原理LangChain4j 的核心价值在于抽象和统一。它把 LLM 应用开发中常见的环节——如对话管理、文档加载、向量化、检索、工具调用——都定义成接口并为每个接口提供多种实现。这种设计让代码不再依赖具体厂商而是依赖抽象层从而大幅提升可测试性和可维护性。1.1 核心模块划分LangChain4j 采用模块化设计主要分为以下几个核心模块langchain4j-core提供基础抽象如ChatLanguageModel聊天模型、EmbeddingModel嵌入模型、ChatMemory对话记忆、Tool工具调用等接口。langchain4j-{provider}针对特定 LLM 提供商的实现例如langchain4j-openai、langchain4j-google-ai-gemini。langchain4j-{embedding-store}针对特定向量数据库的实现例如langchain4j-pinecone、langchain4j-milvus。langchain4j-{framework}与 Java 框架的集成模块例如quarkus-langchain4j、与 Spring Boot 集成的自动配置。langchain4j-agentic提供 Agent智能体相关功能支持 ReAct、AutoGPT 等模式。这种模块化意味着你可以按需引入依赖避免引入不必要的代码和配置。1.2 统一 API 的工作机制以调用 LLM 为例不同厂商的 API 参数命名、格式、认证方式各不相同。LangChain4j 通过ChatLanguageModel接口统一了调用方式// 这是 LangChain4j 的统一接口 public interface ChatLanguageModel { ResponseString generate(String userMessage); ResponseAiMessage generate(UserMessage userMessage); // 其他重载方法... }无论底层是 OpenAI 还是 Gemini你都可以通过同一组方法调用。LangChain4j 在内部处理了请求转换、认证、重试、异常处理等细节。这种设计不仅简化了代码还让单元测试更容易——你可以轻松注入一个模拟的ChatLanguageModel进行测试。1.3 与 Spring Boot 的集成原理LangChain4j 为 Spring Boot 提供了自动配置支持。当你在项目中引入langchain4j-spring-boot-starter时Spring Boot 的自动配置机制会根据application.properties或application.yml中的配置如langchain4j.openai.api-key创建对应的 Bean。自动配置ChatLanguageModel、EmbeddingModel等实例。提供健康检查端点如果配置了 Spring Boot Actuator。这种集成方式让 LangChain4j 的使用变得像使用其他 Spring Boot 组件一样自然无需手动编写大量样板代码。2. 环境准备与项目配置开始编码前需要确保本地环境符合要求并正确配置项目依赖。LangChain4j 支持 Java 8 及以上版本但推荐使用 Java 17 或更高版本以获得更好的性能和功能支持。2.1 开发环境要求组件最低要求推荐版本备注JDK817 或 21Java 17 以上支持新特性Maven3.53.9或 Gradle 7.xIDE任意IntelliJ IDEA需要 Lombok 插件网络可访问 LLM API稳定连接测试需要实际调用 API确保 IDE 已安装 Lombok 插件因为 LangChain4j 大量使用 Lombok 注解简化代码。2.2 Maven 依赖配置对于 Spring Boot 项目最简单的入门方式是使用langchain4j-spring-boot-starter。在pom.xml中添加以下依赖properties langchain4j.version0.31.0/langchain4j.version spring-boot.version3.2.0/spring-boot.version /properties dependencies !-- Spring Boot Starter -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter/artifactId version${spring-boot.version}/version /dependency !-- LangChain4j Spring Boot Starter -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-spring-boot-starter/artifactId version${langchain4j.version}/version /dependency !-- OpenAI 集成如使用其他提供商可替换 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-open-ai/artifactId version${langchain4j.version}/version /dependency /dependencies如果你不使用 Spring Boot可以只引入核心模块和具体的提供商模块dependencies !-- LangChain4j 核心 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-core/artifactId version${langchain4j.version}/version /dependency !-- OpenAI 集成 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-open-ai/artifactId version${langchain4j.version}/version /dependency /dependencies2.3 配置文件设置在application.yml或application.properties中配置 LLM 提供商的相关参数# application.yml langchain4j: openai: api-key: ${OPENAI_API_KEY} # 从环境变量读取 model-name: gpt-3.5-turbo # 使用的模型 temperature: 0.7 # 创造性程度 max-tokens: 500 # 最大输出token数 timeout: 60s # 请求超时时间 # 日志配置便于调试 logging: level: dev.langchain4j: DEBUG或者使用application.properties# application.properties langchain4j.openai.api-key${OPENAI_API_KEY} langchain4j.openai.model-namegpt-3.5-turbo langchain4j.openai.temperature0.7 langchain4j.openai.max-tokens500 langchain4j.openai.timeout60s logging.level.dev.langchain4jDEBUG注意API Key 等敏感信息不要直接写在配置文件中应该使用环境变量或专门的密钥管理服务。上面的${OPENAI_API_KEY}表示从环境变量中读取值。2.4 非 Spring 项目的配置方式如果不使用 Spring Boot需要手动创建 LLM 实例import dev.langchain4j.model.openai.OpenAiChatModel; public class ManualConfig { public static void main(String[] args) { OpenAiChatModel model OpenAiChatModel.builder() .apiKey(System.getenv(OPENAI_API_KEY)) .modelName(gpt-3.5-turbo) .temperature(0.7) .maxTokens(500) .timeout(Duration.ofSeconds(60)) .build(); // 使用 model 进行对话... } }这种方式虽然灵活但需要手动管理对象的生命周期和依赖关系适合简单的命令行工具或非 Spring 项目。3. 实现一个完整的 RAG 应用示例RAGRetrieval-Augmented Generation是当前最实用的 LLM 应用模式之一它通过检索相关知识来增强生成的准确性和相关性。下面我们实现一个简单的文档问答系统演示 LangChain4j 的核心功能。3.1 项目结构设计src/main/java/ ├── com/example/rag/ │ ├── config/ │ │ └── EmbeddingConfig.java # 向量化配置 │ ├── service/ │ │ ├── DocumentService.java # 文档处理服务 │ │ └── QAService.java # 问答服务 │ ├── controller/ │ │ └── QAController.java # Web接口 │ └── Application.java # 启动类 resources/ ├── documents/ # 存放待处理的文档 │ └── example.txt └── application.yml3.2 核心代码实现首先实现文档处理服务负责加载文档、分割文本、生成向量并存储Service public class DocumentService { private final EmbeddingModel embeddingModel; private final EmbeddingStoreTextSegment embeddingStore; public DocumentService(EmbeddingModel embeddingModel, EmbeddingStoreTextSegment embeddingStore) { this.embeddingModel embeddingModel; this.embeddingStore embeddingStore; } public void processDocument(Path documentPath) { try { // 1. 加载文档 Document document DocumentLoader.load(documentPath); // 2. 分割文本按段落或固定大小 DocumentSplitter splitter new DocumentByParagraphSplitter(500, 50); ListTextSegment segments splitter.split(document); // 3. 生成向量 ListEmbedding embeddings embeddingModel.embedAll(segments).content(); // 4. 存储到向量数据库 embeddingStore.addAll(embeddings, segments); } catch (IOException e) { throw new RuntimeException(文档处理失败: documentPath, e); } } }接下来实现问答服务负责检索相关文档并生成答案Service public class QAService { private final ChatLanguageModel chatModel; private final EmbeddingModel embeddingModel; private final EmbeddingStoreTextSegment embeddingStore; public QAService(ChatLanguageModel chatModel, EmbeddingModel embeddingModel, EmbeddingStoreTextSegment embeddingStore) { this.chatModel chatModel; this.embeddingModel embeddingModel; this.embeddingStore embeddingStore; } public String answerQuestion(String question) { // 1. 将问题转换为向量 Embedding questionEmbedding embeddingModel.embed(question).content(); // 2. 检索最相关的文档片段 int maxResults 3; double minScore 0.7; ListEmbeddingMatchTextSegment relevantMatches embeddingStore.findRelevant(questionEmbedding, maxResults, minScore); // 3. 构建上下文 StringBuilder context new StringBuilder(); for (EmbeddingMatchTextSegment match : relevantMatches) { context.append(match.embedded().text()).append(\n\n); } // 4. 构建提示词 String prompt String.format( 请基于以下上下文回答问题。如果上下文不足以回答问题请说明。\n\n 上下文\n%s\n\n问题%s, context.toString(), question ); // 5. 调用 LLM 生成答案 return chatModel.generate(prompt); } }Web 控制器提供 REST APIRestController RequestMapping(/api/qa) public class QAController { private final QAService qaService; private final DocumentService documentService; public QAController(QAService qaService, DocumentService documentService) { this.qaService qaService; this.documentService documentService; } PostMapping(/ask) public ResponseEntityString askQuestion(RequestBody QuestionRequest request) { try { String answer qaService.answerQuestion(request.getQuestion()); return ResponseEntity.ok(answer); } catch (Exception e) { return ResponseEntity.status(500) .body(回答问题失败: e.getMessage()); } } PostMapping(/upload) public ResponseEntityString uploadDocument(RequestParam(file) MultipartFile file) { try { Path tempFile Files.createTempFile(upload-, .txt); file.transferTo(tempFile); documentService.processDocument(tempFile); Files.delete(tempFile); // 清理临时文件 return ResponseEntity.ok(文档处理完成); } catch (Exception e) { return ResponseEntity.status(500) .body(文档上传失败: e.getMessage()); } } public static class QuestionRequest { private String question; // getter 和 setter } }3.3 向量存储配置对于简单的演示项目可以使用内存中的向量存储。在生产环境中需要配置持久化向量数据库Configuration public class EmbeddingConfig { Bean ConditionalOnMissingBean public EmbeddingStoreTextSegment embeddingStore() { // 内存存储适合演示重启后数据丢失 return new InMemoryEmbeddingStore(); } // 生产环境使用 PostgreSQL pgvector 的配置示例 // Bean // public EmbeddingStoreTextSegment pgVectorEmbeddingStore(DataSource dataSource) { // return new PgVectorEmbeddingStore.Builder() // .dataSource(dataSource) // .tableName(document_embeddings) // .dimension(1536) // OpenAI 嵌入向量的维度 // .build(); // } }3.4 应用启动类SpringBootApplication public class Application { Bean CommandLineRunner init(DocumentService documentService) { return args - { // 启动时加载示例文档 Path exampleDoc Paths.get(ClassLoader.getSystemResource(documents/example.txt).toURI()); documentService.processDocument(exampleDoc); System.out.println(示例文档加载完成); }; } public static void main(String[] args) { SpringApplication.run(Application.class, args); } }4. 关键参数详解与性能调优LangChain4j 提供了丰富的配置参数理解这些参数的含义和影响对于构建稳定的生产应用至关重要。4.1 LLM 调用参数参数类型默认值说明调优建议temperaturedouble0.7控制输出的随机性问答类应用用 0.1-0.3创意类用 0.7-0.9maxTokensint500最大输出token数根据需求调整太长会增加成本和延迟topPdouble1.0核采样参数与temperature二选一通常用temperaturetimeoutDuration60s请求超时时间根据网络状况和模型复杂度调整maxRetriesint3最大重试次数对稳定性要求高的场景可增加到5# 生产环境推荐的配置 langchain4j: openai: temperature: 0.1 # 问答应用需要确定性 max-tokens: 1000 # 预留足够空间 timeout: 120s # 复杂问题需要更长时间 max-retries: 5 # 提高容错性 log-requests: true # 开启请求日志 log-responses: true # 开启响应日志4.2 向量检索参数向量检索的质量直接影响 RAG 的效果关键参数包括// 检索相关文档时的参数配置 EmbeddingStoreTextSegment store // ... 获取存储实例 // 查找最相关的文档片段 ListEmbeddingMatchTextSegment matches store.findRelevant( questionEmbedding, 5, // maxResults: 返回最相似的5个结果 0.6 // minScore: 相似度阈值0-1之间越高要求越严格 );参数调优建议maxResults根据文档密度调整。文档内容密集时用较小的值3-5内容稀疏时用较大的值5-10。minScore根据嵌入模型的质量调整。OpenAI text-embedding-ada-002 通常 0.7-0.8本地模型可能需要 0.5-0.6。4.3 对话记忆管理对于多轮对话场景需要管理对话历史// 创建对话记忆基于内存 ChatMemory chatMemory MessageWindowChatMemory.withMaxMessages(10); // 在对话中使用 UserMessage userMessage UserMessage.from(你好); AiMessage aiMessage chatModel.generate(userMessage).content(); chatMemory.add(userMessage); chatMemory.add(aiMessage); // 下一轮对话会自动包含历史 UserMessage nextMessage UserMessage.from(我刚才问了什么); // 这次生成会基于完整的对话历史 AiMessage response chatModel.generate(chatMemory.messages()).content();记忆管理策略消息窗口保留最近 N 条消息适合短对话。令牌窗口保留最近 N 个令牌适合控制成本。总结式记忆将长历史总结为简短描述适合长对话。5. 常见问题排查与解决方案在实际使用 LangChain4j 过程中会遇到各种问题。下面列出典型问题及其解决方法。5.1 依赖冲突问题问题现象java.lang.NoSuchMethodError: okhttp3.RequestBody.create([BLokhttp3/MediaType;)原因分析多个 HTTP 客户端库版本冲突特别是 OkHttp 的不同版本。解决方案使用 Maven 的依赖树分析工具mvn dependency:tree -Dincludescom.squareup.okhttp3:okhttp在pom.xml中统一版本properties okhttp.version4.12.0/okhttp.version /properties dependencyManagement dependencies dependency groupIdcom.squareup.okhttp3/groupId artifactIdokhttp-bom/artifactId version${okhttp.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement5.2 API 调用失败问题问题现象dev.langchain4j.model.openai.OpenAiHttpException: HTTP 429 Too Many Requests原因分析API 调用频率超过限制或者配额用尽。排查步骤检查 API Key 是否正确且有效查看当前使用量和配额限制检查是否有其他进程在使用同一密钥解决方案langchain4j: openai: max-retries: 5 # 增加重试次数 retryer: # 自定义重试策略 max-attempts: 5 wait-duration: 2s multiplier: 2.05.3 向量检索效果差问题现象RAG 系统返回的答案与问题不相关或者遗漏关键信息。原因分析文档分割策略不合适相似度阈值设置不合理嵌入模型不适合当前领域优化方案改进文档分割// 尝试不同的分割策略 DocumentSplitter splitter new DocumentBySentenceSplitter(50, 10); // 按句子分割 // 或 DocumentSplitter splitter new RecursiveDocumentSplitter(500, 50, 10); // 递归分割调整检索参数// 动态调整检索参数 int maxResults question.length() 50 ? 5 : 3; // 复杂问题返回更多结果 double minScore isTechnicalQuestion(question) ? 0.75 : 0.65; // 技术问题提高阈值5.4 内存泄漏问题问题现象应用运行时间越长内存占用越高最终出现 OutOfMemoryError。原因分析对话记忆未清理大文档处理时内存未释放向量存储缓存过大解决方案定期清理对话记忆Scheduled(fixedRate 3600000) // 每小时清理一次 public void cleanupOldConversations() { conversationService.cleanupOlderThan(Duration.ofHours(24)); }优化文档处理内存使用public void processLargeDocument(Path documentPath) { try (StreamString lines Files.lines(documentPath)) { // 流式处理大文件避免全部加载到内存 lines.forEach(line - { // 分批处理每一行 processTextSegment(line); }); } catch (IOException e) { // 异常处理 } }6. 生产环境最佳实践将 LangChain4j 应用到生产环境时需要考虑性能、安全、监控等多个方面。6.1 安全配置建议API 密钥管理# 不要硬编码密钥使用环境变量或密钥管理服务 langchain4j: openai: api-key: ${OPENAI_API_KEY:} # 从环境变量读取冒号后为默认值空输入输出过滤Service public class SafeQAService { private final QAService qaService; public String safeAnswerQuestion(String question) { // 1. 输入验证 if (!isValidQuestion(question)) { return 问题格式不正确; } // 2. 内容过滤 if (containsSensitiveContent(question)) { return 问题包含不合适内容; } // 3. 调用原始服务 String answer qaService.answerQuestion(question); // 4. 输出过滤 return filterSensitiveContent(answer); } private boolean isValidQuestion(String question) { return question ! null question.length() 0 question.length() 1000; } }6.2 性能优化策略缓存常用查询Service public class CachedQAService { private final QAService delegate; private final CacheString, String cache; public CachedQAService(QAService delegate) { this.delegate delegate; this.cache Caffeine.newBuilder() .maximumSize(1000) .expireAfterWrite(1, TimeUnit.HOURS) .build(); } public String answerQuestion(String question) { return cache.get(question, delegate::answerQuestion); } }异步处理耗时操作Async public CompletableFutureString answerQuestionAsync(String question) { return CompletableFuture.completedFuture(answerQuestion(question)); }6.3 监控与日志添加监控指标Component public class QAServiceMetrics { private final MeterRegistry meterRegistry; private final Counter requestCounter; private final Timer responseTimer; public QAServiceMetrics(MeterRegistry meterRegistry) { this.meterRegistry meterRegistry; this.requestCounter Counter.builder(qa.requests) .description(问答请求数量) .register(meterRegistry); this.responseTimer Timer.builder(qa.response.time) .description(问答响应时间) .register(meterRegistry); } public String trackRequest(String question) { requestCounter.increment(); return responseTimer.record(() - { // 实际处理逻辑 return processQuestion(question); }); } }结构化日志配置// logback-spring.json { appenders: { console: { type: console, encoder: { pattern: %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n } } }, loggers: { dev.langchain4j: DEBUG, com.example.rag: INFO } }6.4 部署检查清单在部署到生产环境前使用以下清单进行检查[ ] API 密钥已通过安全方式配置未硬编码在代码中[ ] 向量数据库连接参数正确且有备份策略[ ] 所有外部服务LLM API、向量DB都有重试机制[ ] 设置了合理的超时时间避免线程阻塞[ ] 内存使用有监控大文档处理有流式方案[ ] 输入输出有内容过滤防止注入攻击[ ] 关键操作有日志记录便于问题排查[ ] 性能指标有收集能够发现性能瓶颈[ ] 有降级方案当 LLM 服务不可用时能优雅处理LangChain4j 为 Java 开发者提供了构建 LLM 应用的完整工具箱但真正发挥其价值需要在理解核心概念的基础上结合具体业务场景进行恰当的配置和优化。从简单的问答系统到复杂的智能体应用LangChain4j 都能提供良好的支持关键是掌握其设计哲学并遵循企业级应用的最佳实践。