LangChain4j 完全指南
从零开始构建 AI 驱动的 Java 应用
这是一份面向零基础开发者的交互式教程。你将学会如何使用 LangChain4j 这个强大的 Java 库,把大语言模型(LLM)集成到你的应用中——从简单的对话聊天,到工具调用、RAG 检索增强生成、多模态处理,再到完整的 AI Agent 构建。
一、开篇概览
LangChain4j 是一个开源 Java 库,它的使命很简单:让 Java 开发者能够轻松地将大语言模型(LLM)集成到自己的应用中。
想象一下,你想在自己的 Java 项目里加入"AI 对话"功能、让 AI 帮你查询数据库、或者基于公司内部文档自动回答用户问题——这些以前要写大量胶水代码的事情,LangChain4j 帮你用几行代码就搞定了。
与同类库对比
LangChain4j ✦
- 原生 Java,与 Spring Boot 深度集成
- 支持 20+ LLM 提供商,30+ 向量数据库
- 统一 API,一行代码切换模型
- 完善的类型安全与注解驱动
- 活跃社区,持续更新
Spring AI
- Spring 官方出品,配置驱动
- 深度融合 Spring 生态
- 相对较新,API 仍在演进
- 功能覆盖面略窄于 LangChain4j
LangChain (Python)
- Python 生态最成熟的 LLM 框架
- 社区最大,教程最多
- Java 开发者无法直接使用
- LangChain4j 从中汲取了大量灵感
Semantic Kernel (Java)
- 微软出品,侧重 Azure 生态
- Java 版功能不如 C# 版完善
- 适合重度 Azure 用户
学完本教程,你将掌握
✅ 本节小结
- LangChain4j 是 Java 世界最流行的 LLM 集成框架
- 它提供统一 API,屏蔽底层 LLM 差异
- 适合所有 Java 开发者,零 AI 基础也能上手
二、环境搭建
开始之前,请确保你的环境满足以下条件:
Maven 3.8+ 或 Gradle 7+
一个 LLM API Key(推荐先用 OpenAI,也可以用免费的 Ollama 本地模型)
添加依赖
LangChain4j 采用模块化设计——你只需引入用到的模块。最常见的组合是「核心库 + OpenAI 集成」:
Maven(推荐)
<!-- 使用 BOM 统一管理版本 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 核心库(AI Services 等高级 API 需要) -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
</dependency>
<!-- OpenAI 集成 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
</dependency>
</dependencies>
Gradle
dependencies {
implementation platform('dev.langchain4j:langchain4j-bom:1.0.0')
implementation 'dev.langchain4j:langchain4j'
implementation 'dev.langchain4j:langchain4j-open-ai'
}
想用 Ollama(本地免费模型)?
如果你不想花钱买 API Key,可以用 Ollama 在本地运行开源模型。先安装 Ollama(ollama.com),然后拉取一个模型:
# 安装后,拉取模型
ollama pull llama3.2
Maven 依赖改用:
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-ollama</artifactId>
</dependency>
最小可运行 Demo
下面这段代码不到 10 行,让你 30 秒看到 AI 回复:
import dev.langchain4j.model.openai.OpenAiChatModel;
public class HelloLangChain4j {
public static void main(String[] args) {
// 1. 创建模型实例(替换成你的 API Key)
var model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY")) // 从环境变量读取
.modelName("gpt-4o-mini")
.build();
// 2. 发送消息并获取回复
String answer = model.chat("用一句话解释什么是 Java?");
// 3. 输出结果
System.out.println(answer);
}
}
export OPENAI_API_KEY="sk-...",然后运行。如果看到 AI 的回复,恭喜你——环境搭建成功!
如果你用 Ollama,代码改为:
import dev.langchain4j.model.ollama.OllamaChatModel;
var model = OllamaChatModel.builder()
.baseUrl("http://localhost:11434")
.modelName("llama3.2")
.build();
String answer = model.chat("用一句话解释什么是 Java?");
System.out.println(answer);
✅ 本节小结
- 使用 BOM 统一管理 LangChain4j 版本
- 核心依赖 = langchain4j(高级 API)+ 具体模型集成包
- 10 行代码就能完成第一次 AI 对话
三、核心概念
在正式动手之前,让我们先建立 LangChain4j 的心智模型。别担心,我会用生活中的类比来解释。
ChatLanguageModel — 翻译员
ChatLanguageModel 是你与 LLM 交互的核心接口。不管底层是 OpenAI、Anthropic 还是 Ollama,它们都实现这个接口。这就是"统一 API"的含义——换模型不需要改业务代码。
// OpenAI 翻译员
ChatLanguageModel openai = OpenAiChatModel.builder()
.apiKey("sk-...")
.modelName("gpt-4o-mini")
.build();
// Ollama 翻译员(本地免费)
ChatLanguageModel ollama = OllamaChatModel.builder()
.baseUrl("http://localhost:11434")
.modelName("llama3.2")
.build();
// 完全相同的调用方式!
String reply = openai.chat("你好");
String reply2 = ollama.chat("你好");
Message 体系 — 信件
LLM 交互的基本单位是消息(Message)。LangChain4j 定义了四种消息类型:
| 消息类型 | 类比 | 作用 |
|---|---|---|
SystemMessage | 给翻译员的工作说明书 | 设定 AI 的角色和行为规则 |
UserMessage | 你写的信 | 用户发给 AI 的消息 |
AiMessage | 翻译员的回信 | AI 的回复 |
ToolExecutionResultMessage | 翻译员查资料后的结果 | 工具调用的返回结果 |
import dev.langchain4j.data.message.*;
// 构建一组消息
SystemMessage system = SystemMessage.from("你是一个友好的 Java 助手");
UserMessage user = UserMessage.from("什么是 Spring Boot?");
// 发送多条消息给模型
ChatResponse response = model.chat(system, user);
AiMessage aiReply = response.aiMessage();
System.out.println(aiReply.text());
AI Services — 全自动服务台
这是 LangChain4j 最强大的高级抽象。你只需定义一个 Java 接口,LangChain4j 帮你生成完整实现——包括消息构建、记忆管理、工具调用等。就像你去银行柜台,只说需求,柜员帮你搞定一切。
// 1. 定义接口(声明式,像写 MyBatis Mapper 一样自然)
interface Assistant {
String chat(String userMessage);
}
// 2. 让 LangChain4j 生成实现
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(model)
.build();
// 3. 像调用普通 Java 方法一样使用 AI
String answer = assistant.chat("Java 和 Kotlin 有什么区别?");
Chat Memory — 笔记本
LLM 本身是无状态的——每次请求它都"失忆"了。Chat Memory 就是给 AI 配的笔记本,帮它记住之前的对话,这样才能实现连贯的多轮对话。
// MessageWindowChatMemory:只记住最近 N 条消息(滑动窗口)
ChatMemory memory = MessageWindowChatMemory.withMaxMessages(20);
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(model)
.chatMemory(memory) // 装上笔记本
.build();
assistant.chat("我叫小明"); // AI 记住了
assistant.chat("我叫什么名字?"); // AI: "你叫小明!"
✅ 本节小结
- ChatLanguageModel:统一的 LLM 调用接口,切换模型零成本
- Message:四种消息类型构成对话的基本单位
- AI Services:声明式接口,自动生成 AI 调用逻辑
- Chat Memory:让 AI 拥有对话记忆能力
四、Prompt 模板
场景:你需要让 AI 根据不同的输入执行相似的任务——比如"翻译成不同语言"、"总结不同的文章"。每次手动拼字符串太累了,而且容易出错。
Prompt 模板就像 Java 的 String.format() 的 AI 版本——你定义一个带占位符的模板,运行时填入变量。
基础用法:PromptTemplate
import dev.langchain4j.model.input.PromptTemplate;
// 定义模板,{{variable}} 是占位符
PromptTemplate template = PromptTemplate.from(
"请将以下文本翻译成{{language}}:\n\n{{text}}"
);
// 填入变量
Prompt prompt = template.apply(Map.of(
"language", "日语",
"text", "今天天气真好"
));
// 发送给模型
String result = model.chat(prompt.text());
System.out.println(result); // 今日はとても良い天気ですね
在 AI Services 中使用模板注解
更优雅的方式是用 @UserMessage 注解直接在接口上定义模板:
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;
interface Translator {
@UserMessage("请将以下文本翻译成{{language}}:\n\n{{text}}")
String translate(@V("language") String language,
@V("text") String text);
}
Translator translator = AiServices.create(Translator.class, model);
String japanese = translator.translate("日语", "今天天气真好");
String french = translator.translate("法语", "今天天气真好");
系统消息模板 @SystemMessage
你也可以通过 @SystemMessage 注解给 AI 设定角色:
interface Chef {
@SystemMessage("你是一位米其林三星厨师。用专业但友好的口吻回答关于烹饪的问题。"
+ "如果问题与烹饪无关,礼貌地拒绝。")
@UserMessage("{{question}}")
String ask(@V("question") String question);
}
Chef chef = AiServices.create(Chef.class, model);
System.out.println(chef.ask("如何做一道完美的意大利面?"));
System.out.println(chef.ask("请帮我写一段 Python 代码")); // 会被礼貌拒绝
{{language}} 必须和 @V("language") 完全一致,大小写敏感。忘记转义:如果你的模板里需要出现双大括号
{{,需要用 \{{ 转义。
✅ 本节小结
PromptTemplate.from()创建带变量的 Prompt 模板@UserMessage+@V注解让模板写在接口上,更加优雅@SystemMessage用于设定 AI 的角色和行为边界
五、流式响应
场景:AI 的回复通常需要好几秒。如果等全部生成完再显示,用户体验很差。流式响应(Streaming)让你能一个 token 一个 token 地接收——就像 ChatGPT 那种"打字机"效果。
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
// 1. 创建流式模型
StreamingChatLanguageModel streamingModel = OpenAiStreamingChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.build();
// 2. 使用回调处理流式响应
streamingModel.chat("写一首关于 Java 的短诗", new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String partialResponse) {
// 每收到一小段文字就触发
System.out.print(partialResponse);
}
@Override
public void onCompleteResponse(ChatResponse completeResponse) {
// 完整响应结束
System.out.println("\n\n--- 生成完毕 ---");
}
@Override
public void onError(Throwable error) {
System.err.println("出错了: " + error.getMessage());
}
});
在 AI Services 中使用流式响应
AI Services 也支持流式。返回类型改为 TokenStream:
interface StreamingAssistant {
TokenStream chat(String message);
}
StreamingAssistant assistant = AiServices.builder(StreamingAssistant.class)
.streamingChatLanguageModel(streamingModel)
.build();
TokenStream stream = assistant.chat("讲一个关于程序员的笑话");
stream.onPartialResponse(token -> System.out.print(token))
.onCompleteResponse(resp -> System.out.println("\n完毕"))
.onError(err -> err.printStackTrace())
.start(); // 别忘了调用 start()!
.start():TokenStream 是懒加载的,必须调用 start() 才会开始请求。线程问题:流式回调在单独的线程中执行。如果在 main 方法中使用,需要阻塞主线程等待完成(比如用
CountDownLatch)。
✅ 本节小结
- 流式模型使用
StreamingChatLanguageModel接口 - 通过回调函数逐步接收 AI 的回复
- AI Services 通过
TokenStream返回流式数据
六、Chat Memory 实战
场景:你要构建一个多轮对话聊天机器人,用户可以追问,AI 能记住上下文。
LangChain4j 提供两种内置的 Chat Memory 实现:
| 实现 | 策略 | 适用场景 |
|---|---|---|
MessageWindowChatMemory |
滑动窗口,保留最近 N 条消息 | 大多数场景,简单高效 |
TokenWindowChatMemory |
按 Token 数量保留,精确控制成本 | 需要精确控制 Token 消耗时 |
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.memory.chat.TokenWindowChatMemory;
import dev.langchain4j.model.openai.OpenAiTokenCountEstimator;
// 方式 1:消息窗口(推荐入门使用)
ChatMemory messageMemory = MessageWindowChatMemory.builder()
.maxMessages(20) // 保留最近 20 条
.build();
// 方式 2:Token 窗口(精确控制)
ChatMemory tokenMemory = TokenWindowChatMemory.builder()
.maxTokens(2000)
.tokenCountEstimator(new OpenAiTokenCountEstimator("gpt-4o-mini"))
.build();
多用户隔离:ChatMemoryProvider
实际应用中,每个用户需要独立的记忆空间。使用 chatMemoryProvider 为每个用户创建独立记忆:
interface Assistant {
String chat(@MemoryId String oderId, @UserMessage String message);
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(model)
.chatMemoryProvider(userId ->
MessageWindowChatMemory.builder()
.id(userId)
.maxMessages(20)
.build()
)
.build();
// 不同用户的对话互不干扰
assistant.chat("user-001", "我叫张三");
assistant.chat("user-002", "我叫李四");
assistant.chat("user-001", "我叫什么?"); // AI: "你叫张三"
assistant.chat("user-002", "我叫什么?"); // AI: "你叫李四"
持久化 Chat Memory
默认的记忆存在内存中,重启就丢失了。你可以实现 ChatMemoryStore 接口把记忆持久化到数据库:
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
public class JdbcChatMemoryStore implements ChatMemoryStore {
@Override
public List<ChatMessage> getMessages(Object memoryId) {
// 从数据库加载消息
return db.loadMessages(memoryId.toString());
}
@Override
public void updateMessages(Object memoryId, List<ChatMessage> messages) {
// 保存到数据库
db.saveMessages(memoryId.toString(), messages);
}
@Override
public void deleteMessages(Object memoryId) {
db.deleteMessages(memoryId.toString());
}
}
// 使用自定义存储
ChatMemory memory = MessageWindowChatMemory.builder()
.maxMessages(20)
.chatMemoryStore(new JdbcChatMemoryStore())
.build();
✅ 本节小结
- 两种内存策略:消息窗口 vs Token 窗口
- 用
@MemoryId+chatMemoryProvider实现多用户记忆隔离 - 实现
ChatMemoryStore接口可持久化对话记忆
七、AI Services 深入
场景:你希望 AI 不只返回纯文本,而是返回结构化数据(比如一个 Java 对象),或者根据情况返回不同类型的结果。
结构化输出:让 AI 返回 Java 对象
这是 LangChain4j 最"魔法"的功能之一。你只需把返回类型定义成 POJO,AI 会自动返回符合格式的 JSON 并解析成对象:
// 定义返回结构
record Recipe(
String name, // 菜名
List<String> ingredients, // 食材列表
String steps, // 步骤
int cookingTimeMinutes // 烹饪时间
) {}
// 定义 AI Service
interface ChefAI {
@UserMessage("给我一个{{dish}}的食谱")
Recipe getRecipe(@V("dish") String dish);
}
ChefAI chef = AiServices.create(ChefAI.class, model);
Recipe recipe = chef.getRecipe("宫保鸡丁");
System.out.println(recipe.name()); // 宫保鸡丁
System.out.println(recipe.ingredients()); // [鸡胸肉, 花生, ...]
System.out.println(recipe.cookingTimeMinutes()); // 25
情感分析:返回枚举
enum Sentiment { POSITIVE, NEGATIVE, NEUTRAL }
interface SentimentAnalyzer {
@UserMessage("分析以下文本的情感倾向:{{text}}")
Sentiment analyze(@V("text") String text);
}
SentimentAnalyzer analyzer = AiServices.create(SentimentAnalyzer.class, model);
Sentiment result = analyzer.analyze("这个产品太棒了,我非常喜欢!");
// result == Sentiment.POSITIVE
返回 boolean / int 等基本类型
interface ContentModerator {
@UserMessage("以下内容是否包含不当言论?回答 true 或 false:{{content}}")
boolean isInappropriate(@V("content") String content);
}
interface Scorer {
@UserMessage("请给以下文章的写作质量打分(1-10):{{article}}")
int score(@V("article") String article);
}
String、boolean、int、double、枚举、POJO/Record、List<T>、TokenStream(流式)等。几乎你能想到的类型都可以。
✅ 本节小结
- AI Services 自动将 AI 回复解析成 Java 对象(JSON → POJO)
- 支持返回枚举、Record、List 等丰富类型
- 声明式接口让 AI 调用像调用普通 Java 方法一样简单
八、Tool / 函数调用
场景:AI 只会"说",不会"做"。当用户问"北京现在几点?"或"帮我查一下订单状态"时,AI 本身无法获取这些实时信息。Tool 调用(Function Calling)让 AI 能够在对话中调用你定义的 Java 方法,获取真实数据。
import dev.langchain4j.agent.tool.Tool;
// 1. 定义工具类
public class WeatherTool {
@Tool("查询指定城市的当前天气")
public String getWeather(String city) {
// 实际项目中调用天气 API
return city + ":晴天,25°C,湿度 60%";
}
}
// 2. 定义 AI Service
interface WeatherAssistant {
String chat(String message);
}
// 3. 注册工具
WeatherAssistant assistant = AiServices.builder(WeatherAssistant.class)
.chatLanguageModel(model)
.tools(new WeatherTool()) // 把工具交给 AI
.build();
// 4. AI 会自动决定是否调用工具
System.out.println(assistant.chat("北京今天天气怎么样?"));
// AI 内部:识别需要天气信息 → 调用 getWeather("北京") → 组织回复
// 输出:根据查询,北京今天是晴天,温度 25°C,湿度 60%。
多参数工具 + @P 参数描述
import dev.langchain4j.agent.tool.P;
public class OrderTool {
@Tool("根据订单号查询订单详情")
public String queryOrder(
@P("订单编号,格式如 ORD-12345") String orderId) {
// 查数据库
return "订单 " + orderId + ":已发货,预计明天送达";
}
@Tool("计算两个数的和")
public double add(
@P("第一个数") double a,
@P("第二个数") double b) {
return a + b;
}
}
@P 注解为参数提供描述,帮助 AI 理解每个参数的含义和格式——这对准确调用工具至关重要。
动态工具:ToolProvider
有时候你需要根据上下文动态提供工具(比如不同用户有不同权限)。使用 ToolProvider:
ToolProvider toolProvider = (toolProviderRequest) -> {
// 根据用户角色动态决定提供哪些工具
String userId = toolProviderRequest.userMessage().text();
if (isAdmin(userId)) {
return ToolProviderResult.builder()
.add(new AdminTool())
.add(new UserTool())
.build();
}
return ToolProviderResult.builder()
.add(new UserTool())
.build();
};
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(model)
.toolProvider(toolProvider)
.build();
@Tool 的描述是 AI 判断"是否调用"的依据。描述越精准,AI 调用越准确。返回值太复杂:Tool 的返回值建议是简单的 String。AI 需要"读懂"返回值来组织回复。
不是所有模型都支持:Tool 调用需要模型支持 Function Calling(GPT-4o、Claude 3.5 等),一些小模型可能不支持。
✅ 本节小结
@Tool注解将 Java 方法暴露给 AI 调用@P注解为参数提供描述- AI 自动判断何时调用哪个工具
- ToolProvider 支持动态工具注册
九、RAG 检索增强生成
场景:你希望 AI 能回答关于你自己的数据的问题——比如公司内部文档、产品说明书、FAQ 等。但这些内容 LLM 训练时没有见过。
RAG 的完整流程分两步:
摄入阶段(Ingestion):把文档切分成小块 → 将每块转换成向量(Embedding) → 存入向量数据库
检索阶段(Retrieval):用户提问 → 将问题也转成向量 → 在向量数据库中找最相关的文档块 → 连同问题一起发给 AI
简单 RAG(内存向量存储)
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.openai.OpenAiEmbeddingModel;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
// 1. 准备 Embedding 模型(把文本变成向量)
EmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("text-embedding-3-small")
.build();
// 2. 创建内存向量存储
InMemoryEmbeddingStore<TextSegment> embeddingStore =
new InMemoryEmbeddingStore<>();
// 3. 摄入文档
EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
.embeddingModel(embeddingModel)
.embeddingStore(embeddingStore)
.build();
// 假设我们有一些公司文档
Document doc = Document.from("我们公司成立于 2020 年,总部位于北京。"
+ "主要产品包括智能客服系统和数据分析平台。"
+ "目前员工人数超过 500 人。");
ingestor.ingest(doc);
// 4. 创建检索器
EmbeddingStoreContentRetriever retriever = EmbeddingStoreContentRetriever.builder()
.embeddingStore(embeddingStore)
.embeddingModel(embeddingModel)
.maxResults(3) // 最多返回 3 个相关片段
.minScore(0.7) // 最低相关度
.build();
// 5. 创建带 RAG 能力的 Assistant
interface Assistant {
String chat(String message);
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(model)
.contentRetriever(retriever) // 接入 RAG
.build();
// 6. 提问!AI 会先检索再回答
System.out.println(assistant.chat("公司有多少员工?"));
// AI: "根据公司资料,目前员工人数超过 500 人。"
使用 EasyRAG 极简版
如果你想用最少的代码体验 RAG,LangChain4j 还提供了 langchain4j-easy-rag 模块:
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-easy-rag</artifactId>
</dependency>
// 一行代码加载文件夹里的所有文档
List<Document> documents = FileSystemDocumentLoader.loadDocuments("/path/to/docs");
// 使用 EasyRAG 内置的 Embedding 模型(无需 API Key)
EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
.embeddingStore(embeddingStore)
.build(); // EasyRAG 自动使用内置 embedding 模型
ingestor.ingest(documents);
✅ 本节小结
- RAG = 检索 + 生成,让 AI 能回答关于你自己数据的问题
- 核心组件:EmbeddingModel、EmbeddingStore、ContentRetriever
- 通过
contentRetriever把 RAG 接入 AI Services
十、文档加载与解析
场景:你的知识库不只是纯文本,可能包含 PDF、Word、HTML 等多种格式。LangChain4j 内置了多种文档加载器。
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.document.parser.apache.tika.ApacheTikaDocumentParser;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
// 1. 加载单个文件(自动识别格式)
Document doc = FileSystemDocumentLoader.loadDocument(
"/path/to/report.pdf",
new ApacheTikaDocumentParser() // 支持 PDF、Word、PPT 等
);
// 2. 加载整个目录
List<Document> docs = FileSystemDocumentLoader.loadDocuments(
"/path/to/docs/",
new ApacheTikaDocumentParser()
);
// 3. 切分文档(RAG 必须步骤)
var splitter = DocumentSplitters.recursive(500, 50);
// 每块最多 500 字符,相邻块重叠 50 字符
List<TextSegment> segments = splitter.splitAll(docs);
ApacheTikaDocumentParser 需要额外引入:
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-document-parser-apache-tika</artifactId>
</dependency>
从 URL 加载网页内容
import dev.langchain4j.data.document.loader.UrlDocumentLoader;
Document webDoc = UrlDocumentLoader.load(
"https://example.com/article",
new ApacheTikaDocumentParser()
);
文档元数据与过滤
// 每个文档段可以携带元数据
TextSegment segment = TextSegment.from("内容...",
Metadata.from("source", "product-manual.pdf")
.put("page", "5")
.put("department", "engineering"));
// 检索时可以过滤
EmbeddingStoreContentRetriever retriever = EmbeddingStoreContentRetriever.builder()
.embeddingStore(embeddingStore)
.embeddingModel(embeddingModel)
.filter(metadataKey("department").isEqualTo("engineering"))
.build();
✅ 本节小结
FileSystemDocumentLoader支持从文件 / 目录加载文档ApacheTikaDocumentParser解析 PDF、Word 等多种格式DocumentSplitters.recursive()智能切分文档为小块- 元数据可用于检索时的精确过滤
十一、图像与多模态
场景:你想让 AI 看图说话——分析图片内容、提取信息、或生成图片。
图像理解(Vision)
import dev.langchain4j.data.message.ImageContent;
import dev.langchain4j.data.message.TextContent;
// GPT-4o 等支持视觉的模型
var visionModel = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o")
.build();
// 构建包含图片的消息
UserMessage message = UserMessage.from(
ImageContent.from("https://example.com/photo.jpg"), // 图片 URL
TextContent.from("请详细描述这张图片中的内容")
);
ChatResponse response = visionModel.chat(message);
System.out.println(response.aiMessage().text());
图像生成
import dev.langchain4j.model.image.ImageModel;
import dev.langchain4j.model.openai.OpenAiImageModel;
import dev.langchain4j.data.image.Image;
ImageModel imageModel = OpenAiImageModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("dall-e-3")
.quality("hd")
.build();
Response<Image> response = imageModel.generate("一只戴着程序员帽子的猫咪,像素风格");
String imageUrl = response.content().url().toString();
System.out.println("生成的图片: " + imageUrl);
✅ 本节小结
- 使用
ImageContent向支持视觉的模型发送图片 ImageModel接口用于生成图片- 多模态能力取决于底层模型的支持程度
十二、MCP 协议集成
MCP(Model Context Protocol,模型上下文协议)是由 Anthropic 发起的开放协议,它标准化了 AI 应用与外部工具/数据源的连接方式。可以把它理解为"AI 的 USB 接口"——任何符合 MCP 标准的工具,都可以即插即用。
import dev.langchain4j.mcp.McpToolProvider;
import dev.langchain4j.mcp.client.McpClient;
import dev.langchain4j.mcp.client.transport.stdio.StdioMcpTransport;
// 1. 创建 MCP 传输层(以 stdio 方式启动本地 MCP 服务)
McpTransport transport = new StdioMcpTransport.Builder()
.command(List.of("npx", "-y", "@modelcontextprotocol/server-filesystem",
"/path/to/allowed/directory"))
.build();
// 2. 创建 MCP 客户端
McpClient mcpClient = new McpClient.Builder()
.transport(transport)
.build();
// 3. 使用 McpToolProvider 把 MCP 工具注入 AI Service
ToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(List.of(mcpClient))
.build();
interface FileAssistant {
String chat(String message);
}
FileAssistant assistant = AiServices.builder(FileAssistant.class)
.chatLanguageModel(model)
.toolProvider(toolProvider)
.build();
// AI 现在可以读写文件了!
System.out.println(assistant.chat("列出目录中的所有文件"));
@Tool 方法——社区已有大量现成的 MCP Server(文件系统、GitHub、数据库、Slack 等)。只要连上就能用。
✅ 本节小结
- MCP 是标准化的 AI 工具协议
- LangChain4j 通过
McpToolProvider无缝集成 MCP 服务 - 支持 stdio 和 HTTP SSE 两种传输方式
十三、综合实战项目
现在让我们把前面学到的所有知识串联起来,构建一个"智能客服助手"——它能回答公司产品问题(RAG)、查询订单状态(Tool 调用)、记住对话上下文(Chat Memory)、并返回结构化数据。
定义工具类——模拟订单查询和产品价格查询
public class CustomerServiceTools {
@Tool("根据订单号查询订单状态")
public String queryOrder(@P("订单号") String orderId) {
// 模拟数据库查询
Map<String, String> orders = Map.of(
"ORD-001", "已发货,预计明天到达",
"ORD-002", "待支付",
"ORD-003", "已签收"
);
return orders.getOrDefault(orderId, "未找到订单 " + orderId);
}
@Tool("查询产品价格")
public String queryPrice(@P("产品名称") String productName) {
Map<String, String> prices = Map.of(
"智能客服系统", "标准版 ¥9,800/年,企业版 ¥29,800/年",
"数据分析平台", "基础版 ¥5,800/年,高级版 ¥19,800/年"
);
return prices.getOrDefault(productName, "暂无该产品信息");
}
}
准备知识库——加载公司文档并建立向量索引
// 模拟公司文档
Document faqDoc = Document.from(
"Q: 智能客服系统支持哪些渠道?\n"
+ "A: 支持微信、网页、APP、电话四大渠道,统一后台管理。\n\n"
+ "Q: 数据分析平台的技术栈?\n"
+ "A: 基于 Apache Flink 实时计算引擎,支持 SQL 和 Java API。\n\n"
+ "Q: 如何申请试用?\n"
+ "A: 访问官网填写试用申请表,24 小时内审核通过。"
);
InMemoryEmbeddingStore<TextSegment> store = new InMemoryEmbeddingStore<>();
EmbeddingStoreIngestor.builder()
.embeddingModel(embeddingModel)
.embeddingStore(store)
.documentSplitter(DocumentSplitters.recursive(300, 30))
.build()
.ingest(faqDoc);
组装 AI Service——集成所有能力
interface CustomerServiceBot {
@SystemMessage("""
你是「极客科技」的智能客服助手。请遵守以下规则:
1. 友好、专业地回答用户问题
2. 优先使用知识库中的信息回答产品问题
3. 需要查询订单时,使用查询工具
4. 如果不确定,诚实说不知道,不要编造
""")
String chat(@MemoryId String userId, @UserMessage String message);
}
CustomerServiceBot bot = AiServices.builder(CustomerServiceBot.class)
.chatLanguageModel(model)
.chatMemoryProvider(userId ->
MessageWindowChatMemory.builder()
.id(userId)
.maxMessages(30)
.build())
.contentRetriever(EmbeddingStoreContentRetriever.builder()
.embeddingStore(store)
.embeddingModel(embeddingModel)
.maxResults(3)
.build())
.tools(new CustomerServiceTools())
.build();
交互测试
String userId = "user-001";
// 测试产品问答(RAG)
System.out.println(bot.chat(userId, "你们的客服系统支持哪些渠道?"));
// → 支持微信、网页、APP、电话四大渠道...
// 测试订单查询(Tool 调用)
System.out.println(bot.chat(userId, "帮我查一下订单 ORD-001 的状态"));
// → 您的订单 ORD-001 已发货,预计明天到达。
// 测试价格咨询(Tool 调用)
System.out.println(bot.chat(userId, "数据分析平台多少钱?"));
// → 基础版 ¥5,800/年,高级版 ¥19,800/年。
// 测试上下文记忆(Chat Memory)
System.out.println(bot.chat(userId, "刚才那个产品怎么申请试用?"));
// → 访问官网填写试用申请表,24 小时内审核通过。
// 测试多用户隔离
System.out.println(bot.chat("user-002", "刚才我问了什么?"));
// → 这是我们的第一次对话...
✅ 本节小结
- 综合运用了 RAG、Tool、Chat Memory、AI Services
- 一个接口 + 一个 builder 就能组装出强大的 AI 应用
- 声明式开发让复杂的 AI 编排变得简单直观
十四、速查表 / Cheat Sheet
模型相关
| API | 用途 | 示例 |
|---|---|---|
OpenAiChatModel.builder() | 创建 OpenAI 聊天模型 | .apiKey("...").modelName("gpt-4o-mini").build() |
OllamaChatModel.builder() | 创建 Ollama 本地模型 | .baseUrl("http://localhost:11434").modelName("llama3.2").build() |
model.chat(String) | 简单文本问答 | model.chat("你好") |
model.chat(Message...) | 发送多条消息 | model.chat(systemMsg, userMsg) |
OpenAiStreamingChatModel | 流式响应模型 | 同 ChatModel 配置,返回逐步响应 |
OpenAiEmbeddingModel | 文本向量化模型 | .modelName("text-embedding-3-small").build() |
OpenAiImageModel | 图像生成 | .modelName("dall-e-3").build() |
AI Services 注解
| 注解 | 用途 | 示例 |
|---|---|---|
@SystemMessage | 设定 AI 角色/系统提示 | @SystemMessage("你是一个翻译助手") |
@UserMessage | 定义用户消息模板 | @UserMessage("翻译:{{text}}") |
@V("name") | 绑定模板变量 | @V("text") String text |
@MemoryId | 标记用户 ID 参数 | @MemoryId String oderId |
@Tool | 暴露方法为 AI 工具 | @Tool("查询天气") String getWeather(...) |
@P | 工具参数描述 | @P("城市名称") String city |
RAG & 文档
| API | 用途 | 示例 |
|---|---|---|
InMemoryEmbeddingStore | 内存向量存储 | new InMemoryEmbeddingStore<>() |
EmbeddingStoreIngestor | 文档摄入管道 | .embeddingModel(em).embeddingStore(es).build() |
EmbeddingStoreContentRetriever | 向量检索器 | .maxResults(3).minScore(0.7).build() |
FileSystemDocumentLoader | 文件系统加载文档 | .loadDocuments("/path/") |
DocumentSplitters.recursive() | 递归切分文档 | .recursive(500, 50) |
ApacheTikaDocumentParser | 解析 PDF/Word 等 | 需单独引入 Tika 依赖 |
Chat Memory
| API | 用途 | 示例 |
|---|---|---|
MessageWindowChatMemory | 按消息数滑动窗口 | .maxMessages(20).build() |
TokenWindowChatMemory | 按 Token 数滑动窗口 | .maxTokens(2000).build() |
ChatMemoryStore | 持久化接口 | 实现 get/update/deleteMessages |
十五、进阶路线图
恭喜你完成了整个教程!接下来是一些进阶方向和资源:
高级用法探索
Agent 模式:让 AI 自主规划和执行多步骤任务,结合多个工具完成复杂目标
查询路由:根据用户问题类型自动路由到不同的处理管道
高级 RAG:查询压缩、重排序(Re-ranking)、混合检索(关键词 + 向量)
可观测性:使用 ChatModelListener 监控 Token 消耗、延迟、成功率
Guard Rails:输入/输出守卫,确保 AI 回复的安全性和合规性
Spring Boot 集成:使用 langchain4j-spring-boot-starter 实现自动配置
Quarkus 集成:quarkus-langchain4j 提供原生 Quarkus 支持
推荐资源
| 资源 | 链接 | 说明 |
|---|---|---|
| 官方文档 | docs.langchain4j.dev | 最权威的参考,持续更新 |
| GitHub 仓库 | github.com/langchain4j | 源码、Issue、讨论 |
| 示例代码库 | langchain4j-examples | 大量可运行的示例 |
| Discord 社区 | LangChain4j Discord | 活跃的社区交流 |
🎓 教程完成!
你已经掌握了 LangChain4j 的核心用法。现在去构建属于你的 AI 应用吧!