LangChain4j 实战——Java 版 LangChain 的完整使用指南
LangChain4j 实战——Java 版 LangChain 的完整使用指南
适读人群:Java 工程师、AI 应用开发者 | 阅读时长:约18分钟 | 核心价值:LangChain4j 核心 API 完整掌握,选型时能做出清醒判断
有读者问我:LangChain4j 和 Spring AI 到底用哪个?
这个问题没有标准答案,但我可以告诉你我自己的判断:如果你是 Spring Boot 项目、已经在用 Spring 全家桶,用 Spring AI 更顺滑;如果你的项目不是 Spring 架构,或者你想要更灵活的 Agent 编排能力,LangChain4j 值得考虑。
这篇文章先把 LangChain4j 讲透,下一篇再做横向对比。
LangChain4j 简介
LangChain4j 是 LangChain 的 Java 移植版,但不是简单照搬,做了很多 Java 风格的适配。当前版本 0.36.x,核心 API 已经稳定。
特点:
- 不依赖 Spring,可以用在任何 Java 项目里
- 内置 AI Services(用接口定义 AI 行为,类似 OpenFeign 风格)
- Agent 编排能力更成熟(支持 Plan-and-Execute、ReAct 等模式)
- 支持 60+ 大模型提供商
快速入门
Maven 依赖:
<!-- LangChain4j 核心 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>0.36.2</version>
</dependency>
<!-- OpenAI 支持 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>0.36.2</version>
</dependency>
<!-- Spring Boot 集成(可选)-->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring-boot-starter</artifactId>
<version>0.36.2</version>
</dependency>最简单的对话:
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.temperature(0.7)
.maxTokens(2048)
.timeout(Duration.ofSeconds(30))
.build();
String response = model.generate("用一句话解释什么是 JVM");
System.out.println(response);
// 输出(实测延迟 0.9s):
// JVM(Java虚拟机)是运行Java字节码的运行时环境,实现了Java跨平台执行的能力。AI Services——LangChain4j 最核心的特性
这是 LangChain4j 区别于其他框架最显著的设计:用接口定义 AI 行为,让 AI 实现你的接口。
// 1. 定义接口
interface TechAssistant {
@SystemMessage("你是一个专业的 Java 技术顾问,回答要精准简洁。")
String answer(String question);
@SystemMessage("你是一个代码审查专家,请找出代码中的问题。")
@UserMessage("请审查以下 {{language}} 代码:\n```\n{{code}}\n```")
String reviewCode(@V("language") String language, @V("code") String code);
@SystemMessage("根据需求描述生成 Java 方法,只返回代码,不要解释。")
String generateMethod(@UserMessage String requirement);
}
// 2. 创建实现(框架自动生成)
TechAssistant assistant = AiServices.builder(TechAssistant.class)
.chatLanguageModel(model)
.build();
// 3. 像调普通方法一样使用
String answer = assistant.answer("什么是 CAS 操作?");
String review = assistant.reviewCode("Java", "for(int i=0;i<list.size();i++){}");这种风格对 Java 工程师非常友好,就像写 Service 接口一样自然。
多轮对话与记忆
interface CustomerService {
@SystemMessage("""
你是智能客服小美,负责处理订单和售后问题。
保持友好耐心的语气,用中文回答。
""")
String chat(@MemoryId String userId, @UserMessage String message);
}
// 创建带记忆的 AI Service
CustomerService service = AiServices.builder(CustomerService.class)
.chatLanguageModel(model)
.chatMemoryProvider(memoryId ->
MessageWindowChatMemory.withMaxMessages(20) // 最多保留 20 条消息
)
.build();
// 不同 userId 的对话完全隔离
String r1 = service.chat("user001", "我的订单什么时候发货?");
String r2 = service.chat("user002", "我想退货");
String r3 = service.chat("user001", "订单号是 ORD-123"); // 记得上下文工具(Tools)
// 定义工具类
class OrderTools {
private final OrderRepository orderRepository;
@Tool("查询订单状态,输入订单号,返回订单详情")
OrderDetail queryOrder(@P("订单号,格式ORD-XXXXXXXX") String orderId) {
return orderRepository.findById(orderId)
.orElseThrow(() -> new RuntimeException("订单不存在: " + orderId));
}
@Tool("查询商品库存,输入商品ID")
int queryStock(@P("商品ID") String productId) {
return inventoryService.getStock(productId);
}
@Tool("创建售后工单,当用户需要退换货或投诉时调用")
String createAftersaleTicket(
@P("用户ID") String userId,
@P("问题类型:RETURN/EXCHANGE/COMPLAINT") String type,
@P("问题描述") String description) {
String ticketId = afterSaleService.create(userId, type, description);
return "已为您创建工单,编号:" + ticketId + ",预计24小时内处理";
}
}
// 集成工具的 AI Service
CustomerService service = AiServices.builder(CustomerService.class)
.chatLanguageModel(model)
.chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(20))
.tools(new OrderTools(orderRepository))
.build();RAG 完整实现
// 1. 加载和索引文档
EmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("text-embedding-3-small")
.build();
EmbeddingStore<TextSegment> embeddingStore = PgVectorEmbeddingStore.builder()
.host("localhost")
.port(5432)
.database("ragdb")
.user("postgres")
.password(System.getenv("DB_PASSWORD"))
.table("document_embeddings")
.dimension(1536)
.build();
// 索引文档
EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
.documentSplitter(DocumentSplitters.recursive(500, 50)) // 500 chars,50 overlap
.embeddingModel(embeddingModel)
.embeddingStore(embeddingStore)
.build();
// 支持多种格式
Document pdfDoc = FileSystemDocumentLoader.loadDocument("manual.pdf", new ApachePdfBoxDocumentParser());
Document wordDoc = FileSystemDocumentLoader.loadDocument("guide.docx", new ApacheTikaDocumentParser());
ingestor.ingest(pdfDoc, wordDoc);
// 2. 构建 RAG 查询管道
ContentRetriever retriever = EmbeddingStoreContentRetriever.builder()
.embeddingStore(embeddingStore)
.embeddingModel(embeddingModel)
.maxResults(5)
.minScore(0.7)
.build();
// 3. 创建 RAG AI Service
interface DocumentAssistant {
@SystemMessage("你是专业技术文档助手,只基于提供的参考资料回答,无相关信息时如实告知。")
String answer(String question);
}
DocumentAssistant assistant = AiServices.builder(DocumentAssistant.class)
.chatLanguageModel(model)
.contentRetriever(retriever)
.build();
String answer = assistant.answer("电机过热的处理步骤是什么?");结构化输出
// 定义输出结构
record BugReport(
String severity, // HIGH/MEDIUM/LOW
String description, // 问题描述
List<String> affectedFiles,
String suggestedFix
) {}
interface CodeAnalyzer {
@UserMessage("分析以下代码中的 Bug,给出结构化报告:\n{{code}}")
BugReport analyzeBug(@V("code") String code);
@UserMessage("给以下代码打分(0-100)并给出改进建议")
CodeScore scoreCode(@V("code") String code);
}
record CodeScore(int score, String grade, List<String> improvements) {}
// LangChain4j 自动处理 JSON 解析
CodeAnalyzer analyzer = AiServices.create(CodeAnalyzer.class, model);
BugReport report = analyzer.analyzeBug(suspiciousCode);
System.out.printf("严重级别:%s,问题:%s%n", report.severity(), report.description());踩坑实录
坑一:@MemoryId 和 @UserMessage 参数顺序问题
现象:多轮对话记忆没有生效,每次对话都是独立的。
原因:参数顺序写错了。@MemoryId 和 @UserMessage 的参数顺序不对时,框架可能无法正确识别。
// 错误写法
String chat(@UserMessage String message, @MemoryId String userId);
// 正确写法:@MemoryId 在前
String chat(@MemoryId String userId, @UserMessage String message);坑二:Tool 方法抛异常的处理
现象:工具执行失败后,模型收到异常信息,开始在回复里解释"发生了技术错误",但用不友好的英文错误信息直接暴露给用户。
原因:LangChain4j 默认把工具异常的 getMessage() 返回给模型,模型会把这个信息用在回复里。
解法:在工具方法里捕获异常,返回友好的错误描述:
@Tool("查询订单状态")
String queryOrder(String orderId) {
try {
OrderDetail order = orderService.findById(orderId);
return order.toString();
} catch (OrderNotFoundException e) {
return "未找到订单:" + orderId + ",请确认订单号是否正确";
} catch (Exception e) {
log.error("查询订单失败", e);
return "系统繁忙,暂时无法查询订单,请稍后重试";
}
}坑三:Spring Boot 集成时 Bean 冲突
现象:项目里同时有 Spring AI 和 LangChain4j 的 starter,启动报 No qualifying bean of type 'ChatLanguageModel'。
原因:两个框架都尝试自动注入 AI 相关的 Bean,产生冲突。
解法:禁用其中一个的自动配置,或者显式指定 qualifier:
// 方案一:排除 LangChain4j 的 Spring Boot 自动配置
@SpringBootApplication(exclude = {
dev.langchain4j.springboot.autoconfigure.LangChain4jAutoConfiguration.class
})
// 方案二:显式创建 Bean,不依赖自动配置
@Bean
public ChatLanguageModel chatLanguageModel() {
return OpenAiChatModel.withApiKey(System.getenv("OPENAI_API_KEY"));
}LangChain4j vs Spring AI 选型建议
| 场景 | 推荐 |
|---|---|
| Spring Boot 新项目 | Spring AI |
| 非 Spring 项目(Quarkus、纯 Java) | LangChain4j |
| 需要复杂 Agent 编排 | LangChain4j(Agent 功能更成熟) |
| 已有 Spring 全家桶,追求快速集成 | Spring AI |
| 需要大量国产模型支持 | LangChain4j(集成模型更多) |
两者都在活跃迭代,差距不大,选你们团队更熟悉的生态就行。
AI Services 的高级特性
LangChain4j 的 AI Services 还有几个值得深入了解的特性。
流式输出(Streaming)
interface StreamingAssistant {
@SystemMessage("你是一个技术文章创作助手")
TokenStream writeArticle(@UserMessage String topic);
}
StreamingAssistant assistant = AiServices.builder(StreamingAssistant.class)
.streamingChatLanguageModel(streamingModel)
.build();
// 流式输出,每个 token 都会触发回调
assistant.writeArticle("Spring Boot 性能优化")
.onNext(token -> System.out.print(token))
.onComplete(response -> System.out.println("\n完成"))
.onError(error -> System.err.println("出错: " + error.getMessage()))
.start();动态 System Message
很多场景下 System Message 需要根据用户信息动态变化:
interface PersonalizedAssistant {
@SystemMessage("你是{{userName}}的专属助手,他/她是{{role}},请用适合其专业背景的方式回答")
String chat(@MemoryId String userId,
@V("userName") String userName,
@V("role") String role,
@UserMessage String message);
}
// 使用
String response = assistant.chat("user001", "张三", "Java工程师", "如何优化JVM性能?");Guard 机制(输入/输出验证)
// 输入验证
@InputGuardrails(NoHarmfulContentGuardrail.class)
// 输出验证
@OutputGuardrails(NoPIIOutputGuardrail.class)
interface SafeAssistant {
String answer(String question);
}
// 实现输出验证
public class NoPIIOutputGuardrail implements OutputGuardrail {
@Override
public OutputGuardrailResult validate(AiMessage responseFromLLM) {
String content = responseFromLLM.text();
if (containsPhoneNumber(content) || containsIdCard(content)) {
return failure("输出包含个人隐私信息,已拦截");
}
return success();
}
}Guard 机制是 LangChain4j 比 Spring AI 更成熟的地方,内置了安全防护的扩展点,对安全要求高的场景很有用。
LangChain4j 的 Agent 实现
LangChain4j 提供了两种 Agent 模式:
AiServices 工具调用 Agent(简单,推荐)
在 AI Services 里加工具,LangChain4j 自动处理工具调用循环:
// 工具类
class DatabaseTools {
@Tool("执行 SQL 查询,用于查询业务数据")
String executeSql(@P("SQL语句,只允许SELECT查询") String sql) {
// 安全校验:只允许 SELECT
if (!sql.trim().toUpperCase().startsWith("SELECT")) {
return "错误:只允许查询操作";
}
return jdbcTemplate.queryForList(sql).toString();
}
@Tool("列出所有可用的数据库表")
String listTables() {
return jdbcTemplate.queryForList("SHOW TABLES").toString();
}
}
interface DataAnalystAgent {
@SystemMessage("""
你是一个数据分析师助手。
你可以通过工具查询数据库获取数据。
回答数据相关问题时,先了解表结构,再执行查询,最后分析结果。
""")
String analyze(@UserMessage String question);
}
DataAnalystAgent agent = AiServices.builder(DataAnalystAgent.class)
.chatLanguageModel(model)
.tools(new DatabaseTools(jdbcTemplate))
.build();
// 使用:自然语言问数据问题
String result = agent.analyze("最近7天每天的新注册用户数是多少?");
// 模型会自动:1.查表结构 2.写SQL 3.执行 4.分析结果 5.给出回答这种模式实现简单,而且 LangChain4j 会自动处理工具调用的循环,你不需要写 while 循环。
内置的 AgentExecutor(复杂推理)
// 对于需要复杂推理的场景,用 AiAgentExecutor
ReActAiAgentExecutor executor = ReActAiAgentExecutor.builder()
.chatLanguageModel(model)
.tools(List.of(new WebSearchTool(), new CalculatorTool(), new FileWriterTool()))
.maxIterations(20)
.build();
AgentExecutionResult result = executor.execute("搜索今天的股市数据,计算上证指数的7日均线,保存到 result.txt");与 Spring Boot 集成的最佳实践
如果你的项目是 Spring Boot,LangChain4j 和 Spring 的集成有几个点要注意:
Bean 的生命周期管理
@Configuration
public class LangChain4jConfig {
@Bean
@Scope("singleton") // 模型对象是重量级的,一定要单例
public ChatLanguageModel chatModel(
@Value("${openai.api-key}") String apiKey) {
return OpenAiChatModel.builder()
.apiKey(apiKey)
.modelName("gpt-4o-mini")
.timeout(Duration.ofSeconds(30))
.build();
}
@Bean
public EmbeddingModel embeddingModel(
@Value("${openai.api-key}") String apiKey) {
return OpenAiEmbeddingModel.builder()
.apiKey(apiKey)
.modelName("text-embedding-3-small")
.build();
}
// AI Service 也可以注册为 Spring Bean,方便注入
@Bean
public CustomerService customerService(
ChatLanguageModel model,
ChatMemoryProvider memoryProvider) {
return AiServices.builder(CustomerService.class)
.chatLanguageModel(model)
.chatMemoryProvider(memoryProvider)
.tools(new OrderTools(), new ProductTools())
.build();
}
}这样 CustomerService 就可以像普通 Service 一样 @Autowired 注入到任何地方了,完全符合 Spring 的编程习惯。
