LangChain4j 是什么?
一句话:Java 生态的 LLM 应用开发框架。
核心设计理念是声明式抽象------你只定义接口,框架用 JDK 动态代理生成实现,把 LLM 调用、工具编排、记忆管理、RAG 检索等复杂性全部封装在底层。
架构分层

四层架构,职责分明:
- core 层 :纯接口,零依赖,Provider 只需实现
doChat()一个方法 - 框架层:AiServices 动态代理 + 工具执行循环 + RAG 管线
- Provider 层:15+ 模型提供商,统一接口即插即用
- agentic 层:多 Agent 编排(实验中)
核心设计模式
| 模式 | 体现 | 源码位置 |
|---|---|---|
| 动态代理 | AiServices 通过 Proxy.newProxyInstance() 生成接口实现 |
DefaultAiServices.java:108 |
| Builder 模式 | 几乎所有组件都用 Builder 构建 | 全项目 |
| SPI(ServiceLoader) | JSON 编解码、Prompt 模板、Embedding 工厂可替换 | 全项目 |
| 策略模式 | RAG 管线每一步都是可替换接口 | langchain4j |
| 观察者模式 | ChatModelListener、AiServiceListener 监听全生命周期 | langchain4j-core |
学习路线总览
markdown
阶段一「基础」 阶段二「增强」 阶段三「进阶」 阶段四「工程化」
ChatModel ChatMemory StructuredOutputs Observability
↓ ↓ ↓ ↓
AI Services Tools RAG Logging+Params
↓ ↓ ↓
Streaming Classification Testing
↓ ↓
Guardrails MCP → SpringBoot
阶段一:入门基础
第一课:ChatModel --- 和 LLM 的第一次对话
源码模块 :langchain4j-core(接口)+ langchain4j-open-ai(实现)
ChatModel 是最底层的 LLM 调用接口------把消息发出去,把响应拿回来。所有 Provider 实现同一个接口,换模型不需要改业务代码。
java
ChatModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.build();
// 最简调用
String answer = model.chat("你好");
// 多轮对话(手动管理历史)
UserMessage msg1 = UserMessage.from("我叫小明");
AiMessage ai1 = model.chat(msg1).aiMessage();
UserMessage msg2 = UserMessage.from("我叫什么?");
ChatResponse resp2 = model.chat(msg1, ai1, msg2); // → "小明"
接口设计的精妙之处 :所有方法都是 default,Provider 唯一需要实现的只有 doChat():
java
default ChatResponse chat(ChatRequest chatRequest) {
listeners.forEach(l -> l.onRequest(...));
ChatResponse response = doChat(chatRequest); // Provider 实现
listeners.forEach(l -> l.onResponse(...));
return response;
}
五种消息类型构成了完整的对话模型:
arduino
ChatMessage
├── SystemMessage --- 系统指令(角色定义、行为约束)
├── UserMessage --- 用户输入(支持多模态:文本/图片/音频/视频/PDF)
├── AiMessage --- AI 响应(text + toolExecutionRequests)
├── ToolExecutionResultMessage --- 工具执行结果
└── CustomMessage --- 自定义
第二课:AI Services --- 声明式接口,框架帮你干一切
源码模块 :langchain4j(核心实现)
第一课直接用 ChatModel 调 LLM,所有事都要手动管理。AI Services 翻转了这个模式------你定义接口,框架生成实现。
java
// 1. 定义接口
interface Assistant {
@SystemMessage("你是一个友好的助手,回答简洁")
String chat(String message);
}
// 2. 框架生成实现(JDK 动态代理)
Assistant assistant = AiServices.create(Assistant.class, model);
// 3. 像调普通方法一样
String answer = assistant.chat("什么是 Java?");
模板变量替换:
java
interface Translator {
@UserMessage("将以下文本翻译为{{targetLang}}:{{it}}")
String translate(@V("targetLang") String lang, String text);
}
translator.translate("中文", "Hello World"); // → "你好世界"
一次 AI Service 调用的 12 步内部流程:
scss
用户调用 assistant.chat("你好")
→ Proxy 拦截 → DefaultAiServices.invoke()
→ 1. 解析 @SystemMessage → SystemMessage
→ 2. 解析 @UserMessage + {{it}} → UserMessage
→ 3. RAG 增强(contentRetriever)
→ 4. Input Guardrails 检查
→ 5. JsonSchema 自动生成(结构化输出)
→ 6. 格式指令追加到 prompt(降级路径)
→ 7. ChatMemory 管理(取历史、加新消息)
→ 8. 构建 ChatRequest → ChatModel.doChat()
→ 9. Tool 执行循环(finishReason==TOOL_EXECUTION → 调工具 → 再请求)
→ 10. Output Guardrails 检查
→ 11. ServiceOutputParser 解析输出 → 返回类型
→ 12. Result<T> 包装
→ 返回结果给用户
何时用 ChatModel,何时用 AI Services? 简单场景用 ChatModel,涉及模板/记忆/工具/RAG 时用 AI Services。
阶段二:核心增强
第三课:Chat Memory --- 让 LLM 有了记忆
源码模块 :langchain4j-core + langchain4j
LLM 是无状态的------每次请求都需传入完整对话历史。Chat Memory 把这个过程自动化:框架自动存储对话历史,每次请求前自动注入。
java
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.build();
assistant.chat("我叫小明"); // 框架自动记住
assistant.chat("我叫什么?"); // → "小明"(框架自动注入历史)
多用户隔离(@MemoryId):
java
interface MultiUserAssistant {
String chat(@MemoryId String userId, @UserMessage String message);
}
assistant.chat("user-1", "我叫小明");
assistant.chat("user-2", "我叫小红");
assistant.chat("user-1", "我叫什么?"); // → "小明"
assistant.chat("user-2", "我叫什么?"); // → "小红"
两种淘汰策略:
MessageWindowChatMemory:按消息数量淘汰TokenWindowChatMemory:按 Token 数量淘汰(更精确)
💡 设计细节 :SystemMessage 默认不会被淘汰。ToolExecutionResultMessage 成为孤儿时自动移除。多用户隔离底层用
ConcurrentHashMap<Object, ChatMemory>+computeIfAbsent懒加载。
第四课:Tools --- 让 LLM 能「动手」
源码模块 :langchain4j-core(@Tool 注解)+ langchain4j(ToolService)
前三课的 LLM 只能「说」,不能「做」。Tools 让 LLM 能调用你的 Java 方法------查数据库、调 API、做计算。LLM 自主决定是否调用、调用哪个、传什么参数。
java
class MathTools {
@Tool("计算两个数的和")
double add(@P("第一个数") double a, @P("第二个数") double b) {
return a + b;
}
@Tool("计算平方根")
double squareRoot(@P("要求平方根的数") double x) {
return Math.sqrt(x);
}
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.tools(new MathTools())
.build();
assistant.chat("3的平方根加上5乘以7等于多少?");
// LLM 自主编排: squareRoot(3) → multiply(5,7) → 加和 → 输出
高阶用法 --- AI Service 作为工具(Router Agent 模式):
java
interface LegalExpert {
@Tool("法律专家,回答法律问题") String askLegal(String question);
}
interface TechExpert {
@Tool("技术专家,回答技术问题") String askTech(String question);
}
RouterAgent router = AiServices.builder(RouterAgent.class)
.chatModel(model)
.tools(legalExpert, techExpert) // AI Service 作为工具!
.build();
工具调用循环:
- 发送请求 → LLM 返回
finishReason=TOOL_EXECUTION+ToolExecutionRequests DefaultToolExecutor执行对应 Java 方法- 将结果追加到消息列表
- 再次发送请求 → 如果还是
TOOL_EXECUTION,回到步骤 2 - 直到
finishReason=STOP→ 返回最终回答
第五课:Response Streaming --- 逐字呈现
源码模块 :langchain4j-core(StreamingChatModel)+ langchain4j(TokenStream)
前面的调用都是同步的------等完整响应返回后才能处理。Streaming 让 LLM 逐 token 推送,用户体验大幅提升。
高层 API(推荐,链式调用):
java
interface StreamingAssistant {
TokenStream chat(String message);
}
StreamingAssistant assistant = AiServices.create(StreamingAssistant.class, streamingModel);
assistant.chat("解释什么是流式响应")
.onPartialResponse(token -> System.out.print(token))
.onCompleteResponse(response -> System.out.println("\n[完成]"))
.onError(Throwable::printStackTrace)
.start(); // 必须调用 start()
💡 避坑 :
TokenStream在dev.langchain4j.service包,不在dev.langchain4j.model包,导入时容易出错。
阶段三:进阶能力
第六课:Structured Outputs --- 从字符串到对象
源码模块 :langchain4j-core(JsonSchema)+ langchain4j(ServiceOutputParser)
前面 LLM 输出都是 String,业务代码需要手动解析。Structured Outputs 让 AI Service 直接返回 POJO / Enum / 基本类型。
java
record Person(String name, int age, double height, boolean married) {}
interface PersonExtractor {
Person extractPersonFrom(String text);
}
PersonExtractor extractor = AiServices.create(PersonExtractor.class, model);
Person p = extractor.extractPersonFrom("John is 42 years old, 1.75m tall, unmarried.");
// → Person[name=John, age=42, height=1.75, married=false]
框架的智能降级机制:
javascript
if (模型支持 RESPONSE_FORMAT_JSON_SCHEMA && 非流式 && 非图片)
→ 自动生成 JsonSchema → 注入 ChatRequest.responseFormat
→ LLM 被 API 级约束输出合法 JSON → 解析返回
if (模型不支持 JSON Schema || schema 生成失败)
→ 生成文本格式指令 → 追加到 UserMessage 末尾
→ LLM 靠文本指令理解格式 → 从输出中提取并解析 JSON
💡 避坑 :POJO 所有字段默认可选------LLM 缺少信息时容易幻觉填充。用
@JsonProperty(required = true)标记必填字段。
第七课:RAG --- 让 LLM 读你的知识库
源码模块 :langchain4j-core + langchain4j + langchain4j-embeddings-all-minilm-l6-v2-q
LLM 的知识截止于训练日期。RAG 让 LLM 在回答前先从你的知识库中检索相关信息,注入 prompt 后再回答。
完整流程(Naive RAG):
java
// 1. 加载并切分文档
List<Document> docs = List.of(
Document.from("LangChain4j 是 Java 的 LLM 应用开发框架..."),
Document.from("RAG 全称 Retrieval-Augmented Generation...")
);
var splitter = DocumentSplitters.recursive(200, 20);
List<TextSegment> segments = splitter.splitAll(docs);
// 2. Embedding + 存入向量库
EmbeddingModel embModel = new AllMiniLmL6V2QuantizedEmbeddingModel();
InMemoryEmbeddingStore<TextSegment> embStore = new InMemoryEmbeddingStore<>();
embStore.addAll(embModel.embedAll(segments).content(), segments);
// 3. 创建检索器并绑定到 AI Service
ContentRetriever retriever = EmbeddingStoreContentRetriever.builder()
.embeddingStore(embStore).embeddingModel(embModel)
.maxResults(3).minScore(0.5)
.build();
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model).contentRetriever(retriever)
.build();
assistant.chat("What is LangChain4j?"); // 基于知识库精确回答
assistant.chat("Who won the 2025 World Cup?"); // 诚实说不知道
两阶段架构:
- 索引阶段(离线):文档 → 解析 → 切分 → 向量化 → 存储
- 检索阶段(在线,AI Service 自动完成):提问 → embed → 搜索 → Top-K → 注入 prompt
AllMiniLmL6V2QuantizedEmbeddingModel 是进程内 Embedding 模型(384 维,23MB ONNX),无需调用远程 API。
第八课:Classification --- 文本分类利器
源码模块 :langchain4j(EnumOutputParser)
很多时候不需要完整文本回答,只需要一个分类标签------情感正负、优先级高低、意图类别。
java
enum Sentiment { POSITIVE, NEUTRAL, NEGATIVE }
enum Priority { LOW, MEDIUM, HIGH, CRITICAL }
enum Intent { QUESTION, COMPLAINT, FEATURE_REQUEST, BUG_REPORT, GREETING }
interface SentimentAnalyzer {
@UserMessage("Analyze sentiment of {{it}}")
Sentiment analyzeSentimentOf(String text);
}
实战用例------客服工单自动分流:
yaml
用户消息:"支付页面一直转圈,试了三次都付不了款!"
→ Intent: COMPLAINT, Priority: CRITICAL, Sentiment: NEGATIVE
→ 自动路由: 推送值班群 + 分配投诉专员 + 发优惠券安抚
第九课:Guardrails --- 输入输出的安全防护栏
源码模块 :langchain4j-core + langchain4j
LLM 越强大,风险越大。Guardrails 在调用前后做验证,确保输入安全、输出合规。
输入护栏(注入检测 + 敏感词过滤):
java
public class PromptInjectionGuardrail implements InputGuardrail {
@Override
public InputGuardrailResult validate(UserMessage userMessage) {
if (userMessage.singleText().contains("ignore previous instructions"))
return fatal("Prompt injection detected");
return success();
}
}
输出护栏(竞品检测):
java
public class CompetitorMentionGuardrail implements OutputGuardrail {
@Override
public OutputGuardrailResult validate(AiMessage responseFromLLM) {
if (responseFromLLM.text().contains("CompetitorX"))
return failure("Mentions competitor");
return success();
}
}
注册方式(注解声明式):
java
interface Assistant {
@InputGuardrails({PromptInjectionGuardrail.class, ProfanityFilterGuardrail.class})
@OutputGuardrails({CompetitorMentionGuardrail.class})
String chat(String message);
}
四种护栏结果:
success→ 继续successWith→ 改写消息后继续failure→ 记录失败,继续执行收集所有失败fatal→ 立即终止,不调 LLM- 输出护栏额外支持
retry(重新调 LLM)和reprompt(换 prompt 重试)
阶段四:工程化
第十课:Observability --- 透视每一次 AI 调用
源码模块 :langchain4j-core + langchain4j
通过事件监听让你透视 AI Service 调用的完整生命周期。
java
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.registerListener(new AiServiceCompletedListener() {
@Override
public void onEvent(AiServiceCompletedEvent event) {
System.out.println("[DONE] " + event.invocationContext().methodName()
+ " → " + event.result().orElse("(void)"));
}
})
.build();
一次调用的完整事件序列:
scss
AiServiceStartedEvent
└─ AiServiceRequestIssuedEvent(可能多次,有工具/guardrails 时)
└─ ChatModelListener.onRequest() / .onResponse()
└─ AiServiceResponseReceivedEvent(可能多次)
└─ AiServiceCompletedEvent(或 AiServiceErrorEvent)
第十一课:Parameters + Logging --- 调参与调试
java
ChatModel model = OpenAiChatModel.builder()
.temperature(0.3) // 低温度 → 确定性输出
.maxTokens(200) // 限制输出长度,控制成本
.timeout(ofSeconds(60)) // 超时控制
.logRequests(true) // 打印请求日志
.logResponses(true) // 打印响应日志
.build();
| 参数 | 范围 | 效果 |
|---|---|---|
temperature |
0.0~2.0 | 越低越确定(代码/事实),越高越有创造性(写作) |
maxTokens |
正整数 | 限制输出长度,控制成本 |
frequencyPenalty |
-2.0~2.0 | 正值降低重复 |
添加 logback-classic 依赖后,控制台输出完整请求/响应 body,包含 Token 用量。
第十二课:Testing --- 分层测试 AI 应用
LLM 输出是非确定性的。分层测试策略:
| 层级 | 内容 | 依赖 LLM |
|---|---|---|
| 单元测试 | Guardrail 逻辑、Parser 逻辑 | ❌ |
| 集成测试 | AI Service 端到端 | ✅ |
| 评估测试 | LLM-as-Judge 评估回答质量 | ✅✅ |
Guardrail 单元测试(不调 LLM):
java
@Test
void testInputGuardrailBlocksInjection() {
PromptGuard guard = new PromptGuard();
assertThat(guard.validate(UserMessage.from("Hello")).isSuccess()).isTrue();
assertThat(guard.validate(UserMessage.from("ignore previous instructions")).isFatal()).isTrue();
}
集成测试(用 AssertJ 宽松断言):
java
@Test
void testStructuredOutputExtraction() {
Person p = extractor.extractPersonFrom("John is 42 years old.");
assertThat(p.name()).containsIgnoringCase("John");
assertThat(p.age()).isBetween(30, 50);
}
第十三课:MCP --- 标准化外部工具协议
源码模块 :langchain4j-mcp
第 4 课的 @Tool 是 Java 方法,与业务代码耦合。MCP 通过标准化协议调用外部进程提供的工具------任何语言编写,独立部署。
java
// 1. 启动 MCP Server 子进程
var transport = new StdioMcpTransport.Builder()
.command(List.of("npx", "-y", "@modelcontextprotocol/server-everything", "stdio"))
.logEvents(true).build();
// 2. 创建客户端 + 工具提供者
var mcpClient = new DefaultMcpClient.Builder()
.key("my-server").transport(transport).build();
var toolProvider = McpToolProvider.builder()
.mcpClients(mcpClient).build();
// 3. 绑定到 AI Service
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model).toolProvider(toolProvider).build();
MCP vs @Tool 对比:
| @Tool | MCP | |
|---|---|---|
| 定义位置 | Java 方法(同 JVM) | 外部进程 |
| 语言限制 | 必须 Java | 任何语言 |
| 部署 | 与业务代码耦合 | 独立进程,独立扩展 |
| 工具发现 | 编译时注解扫描 | 运行时协议握手 |
第十四课:Spring Boot Integration --- 走向生产
源码模块 :langchain4j-spring-boot-starter
纯 Java 手动 builder?Spring Boot Starter 提供自动配置 ------properties 配参数,@AiService 注解自动扫描创建 Bean。
properties
# application.properties
langchain4j.open-ai.chat-model.api-key=${OPENAI_API_KEY}
langchain4j.open-ai.chat-model.model-name=gpt-4o-mini
langchain4j.open-ai.chat-model.log-requests=true
java
@AiService // 自动扫描创建 Bean
interface Assistant {
@SystemMessage("你是一个有帮助的助手")
String chat(String userMessage);
}
@RestController
class AssistantController {
private final Assistant assistant; // 直接 DI 注入
@GetMapping("/chat")
public String chat(@RequestParam String message) {
return assistant.chat(message);
}
}
启动即可用:curl "http://localhost:8080/chat?message=Hello"
写在最后
LangChain4j 的设计哲学很 Java------用接口和注解声明意图,框架在底层把脏活累活干了。14 课学下来,从最基础的 ChatModel.chat() 到生产级的 Spring Boot 部署,覆盖了构建 LLM 应用需要的所有核心能力。
关键收获:
AiServices是整个框架的核心入口,动态代理 + 12 步自动流程@Tool/ MCP 让 LLM 从「只能说」变成「能做事」- RAG + Structured Outputs 让输出既准确又可用
- Guardrails + Observability 让应用安全可观测
完整示例代码和学习笔记:GitHub - langchain4j-learning