LangChain4j 1.23 架构全景:Java 大模型应用的 6 层抽象
一句话结论 :LangChain4j 1.23 的价值不在"帮你调通模型",而在用 Model → Prompt → Memory → RAG → Tool → Agent 六层可替换抽象,把 LLM 应用从 Demo 带到可运维的生产系统。选型时先看团队约束,再看框架能力。
适用版本 :LangChain4j
1.23.0/ JDK21/ Spring Boot4.1.x阅读时间 :约 10 分钟
适合读者:正在做 Java LLM 应用选型、架构设计,或准备把 Demo 推向生产的工程师
0. 先说结论
先看一个典型的故障复盘。
一个客服助手 Demo,两周跑通;接入真实业务三周后,出现四个问题:
- A 用户的对话记忆串到了 B 用户;
- 模型调用了本不该开放的退款工具;
- 知识库答非所问,换了 embedding 模型也没用;
- 月底账单是预算的 4 倍。
代码每一行都能跑,但系统不可用。
根因不是模型,也不是 LangChain4j,而是把它当成了一个 HTTP 客户端:只关心 chat() 能不能返回字符串,没有按层设计隔离、权限、检索与可观测。
本文不复述 API,而是给出一张架构地图,回答四个问题:
- LangChain4j 在 Java AI 生态里到底处于什么位置?
- 六层抽象分别解决什么、不解决什么?
- 1.23 这次更新,对架构决策有什么影响?
- 和 Spring AI 比,什么时候该选谁?
1. LangChain4j 到底是什么?
一句话:它是 Java 生态的 LLM 应用装配框架,不是模型提供方,也不是 Spring 的替代品。
它有三个关键设计选择。
1.1 框架无关
langchain4j-core 不依赖 Spring。你可以在 Spring Boot、Quarkus、Micronaut、Helidon,甚至一个普通 main 方法里使用它。
Spring Boot Starter 只是集成层,不是框架本体。这一点很重要:它意味着你的领域代码可以不被 Web 框架绑架。
1.2 声明式:AiServices
LangChain4j 最有辨识度的设计是 AiServices:你定义一个 Java 接口,框架用动态代理生成实现,把 Prompt、记忆、RAG、工具装配进去。
java
interface Assistant {
String chat(String userMessage);
}
Assistant assistant = AiServices.create(Assistant.class, model);
这类似 Spring Data JPA 或 Retrofit:你声明"要什么",框架负责"怎么接"。
1.3 可替换:一切皆接口
模型、Embedding、向量库、记忆存储、检索器、工具执行器,全部是接口。换模型不应该影响记忆;换向量库不应该影响工具;换记忆存储不应该影响 Prompt。
这是六层抽象能够成立的前提。
1.4 模块地图
| 模块 | 作用 | 什么时候引入 |
|---|---|---|
langchain4j-core |
核心抽象:模型、消息、记忆、RAG、工具接口 | 必选 |
langchain4j |
主模块,聚合常用能力 | 必选 |
langchain4j-open-ai / langchain4j-ollama / langchain4j-anthropic |
具体模型接入 | 按厂商 |
langchain4j-spring-boot-starter |
Spring Boot 集成、@AiService |
Spring 项目 |
langchain4j-mcp |
MCP 客户端 | 接外部工具生态 |
langchain4j-agentic |
工作流、子代理、Supervisor | 多步/多 Agent(实验性) |
langchain4j-test |
Mock、Judge、请求断言 | 测试 |
langchain4j-community-* |
DashScope、国内模型等社区集成 | 按需 |
注意:Beta 模块的版本号独立于 core。
1.23.0的 core 不一定对应同版本的 starter 或 MCP 包,引入前要核对 Maven 坐标。
2. 六层抽象:一张图看懂 LangChain4j
"六层"不是官方术语,而是我在多个落地项目里总结的分层法。官方文档按组件组织,分层法更适合做架构决策:每一层都可以单独替换、单独测试、单独治理。
#mermaid-svg-WVqtvyx2qZl6lCrA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WVqtvyx2qZl6lCrA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WVqtvyx2qZl6lCrA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WVqtvyx2qZl6lCrA .error-icon{fill:#552222;}#mermaid-svg-WVqtvyx2qZl6lCrA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WVqtvyx2qZl6lCrA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WVqtvyx2qZl6lCrA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WVqtvyx2qZl6lCrA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WVqtvyx2qZl6lCrA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WVqtvyx2qZl6lCrA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WVqtvyx2qZl6lCrA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WVqtvyx2qZl6lCrA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WVqtvyx2qZl6lCrA .marker.cross{stroke:#333333;}#mermaid-svg-WVqtvyx2qZl6lCrA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WVqtvyx2qZl6lCrA p{margin:0;}#mermaid-svg-WVqtvyx2qZl6lCrA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-WVqtvyx2qZl6lCrA .cluster-label text{fill:#333;}#mermaid-svg-WVqtvyx2qZl6lCrA .cluster-label span{color:#333;}#mermaid-svg-WVqtvyx2qZl6lCrA .cluster-label span p{background-color:transparent;}#mermaid-svg-WVqtvyx2qZl6lCrA .label text,#mermaid-svg-WVqtvyx2qZl6lCrA span{fill:#333;color:#333;}#mermaid-svg-WVqtvyx2qZl6lCrA .node rect,#mermaid-svg-WVqtvyx2qZl6lCrA .node circle,#mermaid-svg-WVqtvyx2qZl6lCrA .node ellipse,#mermaid-svg-WVqtvyx2qZl6lCrA .node polygon,#mermaid-svg-WVqtvyx2qZl6lCrA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WVqtvyx2qZl6lCrA .rough-node .label text,#mermaid-svg-WVqtvyx2qZl6lCrA .node .label text,#mermaid-svg-WVqtvyx2qZl6lCrA .image-shape .label,#mermaid-svg-WVqtvyx2qZl6lCrA .icon-shape .label{text-anchor:middle;}#mermaid-svg-WVqtvyx2qZl6lCrA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-WVqtvyx2qZl6lCrA .rough-node .label,#mermaid-svg-WVqtvyx2qZl6lCrA .node .label,#mermaid-svg-WVqtvyx2qZl6lCrA .image-shape .label,#mermaid-svg-WVqtvyx2qZl6lCrA .icon-shape .label{text-align:center;}#mermaid-svg-WVqtvyx2qZl6lCrA .node.clickable{cursor:pointer;}#mermaid-svg-WVqtvyx2qZl6lCrA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-WVqtvyx2qZl6lCrA .arrowheadPath{fill:#333333;}#mermaid-svg-WVqtvyx2qZl6lCrA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-WVqtvyx2qZl6lCrA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-WVqtvyx2qZl6lCrA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WVqtvyx2qZl6lCrA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-WVqtvyx2qZl6lCrA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WVqtvyx2qZl6lCrA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-WVqtvyx2qZl6lCrA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-WVqtvyx2qZl6lCrA .cluster text{fill:#333;}#mermaid-svg-WVqtvyx2qZl6lCrA .cluster span{color:#333;}#mermaid-svg-WVqtvyx2qZl6lCrA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-WVqtvyx2qZl6lCrA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-WVqtvyx2qZl6lCrA rect.text{fill:none;stroke-width:0;}#mermaid-svg-WVqtvyx2qZl6lCrA .icon-shape,#mermaid-svg-WVqtvyx2qZl6lCrA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WVqtvyx2qZl6lCrA .icon-shape p,#mermaid-svg-WVqtvyx2qZl6lCrA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-WVqtvyx2qZl6lCrA .icon-shape rect,#mermaid-svg-WVqtvyx2qZl6lCrA .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WVqtvyx2qZl6lCrA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-WVqtvyx2qZl6lCrA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-WVqtvyx2qZl6lCrA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-WVqtvyx2qZl6lCrA .agent>*{fill:#2A1F3D!important;stroke:#A78BFA!important;color:#E8EEF9!important;stroke-width:2px!important;}#mermaid-svg-WVqtvyx2qZl6lCrA .agent span{fill:#2A1F3D!important;stroke:#A78BFA!important;color:#E8EEF9!important;stroke-width:2px!important;}#mermaid-svg-WVqtvyx2qZl6lCrA .agent tspan{fill:#E8EEF9!important;}#mermaid-svg-WVqtvyx2qZl6lCrA .service>*{fill:#111A2E!important;stroke:#4F8CFF!important;color:#E8EEF9!important;stroke-width:2px!important;}#mermaid-svg-WVqtvyx2qZl6lCrA .service span{fill:#111A2E!important;stroke:#4F8CFF!important;color:#E8EEF9!important;stroke-width:2px!important;}#mermaid-svg-WVqtvyx2qZl6lCrA .service tspan{fill:#E8EEF9!important;}#mermaid-svg-WVqtvyx2qZl6lCrA .store>*{fill:#0F2A1D!important;stroke:#22C55E!important;color:#E8EEF9!important;stroke-width:2px!important;}#mermaid-svg-WVqtvyx2qZl6lCrA .store span{fill:#0F2A1D!important;stroke:#22C55E!important;color:#E8EEF9!important;stroke-width:2px!important;}#mermaid-svg-WVqtvyx2qZl6lCrA .store tspan{fill:#E8EEF9!important;}#mermaid-svg-WVqtvyx2qZl6lCrA .model>*{fill:#2A1F0B!important;stroke:#F5A524!important;color:#E8EEF9!important;stroke-width:2px!important;}#mermaid-svg-WVqtvyx2qZl6lCrA .model span{fill:#2A1F0B!important;stroke:#F5A524!important;color:#E8EEF9!important;stroke-width:2px!important;}#mermaid-svg-WVqtvyx2qZl6lCrA .model tspan{fill:#E8EEF9!important;} AiServices / AgenticServices
L6 编排层
Prompt 模板
- 输出解析
L2
ChatMemory
- ChatMemoryStore
L3
ContentRetriever
- EmbeddingStore
L4
@Tool
- ToolExecutor
L5
ChatModel + EmbeddingModel
- DecisionModel
L1
图 1:LangChain4j 的六层抽象。箭头表示装配/调用关系,不表示唯一的数据流向。
| 层 | 核心接口/类 | 解决什么 | 不解决什么 |
|---|---|---|---|
| L1 模型 | ChatModel、StreamingChatModel、EmbeddingModel、ImageModel、DecisionModel |
统一不同厂商的模型调用 | 模型能力差异、成本、稳定性 |
| L2 提示与 IO | @SystemMessage、@UserMessage、@V、PromptTemplate、结构化输出 |
输入格式化、输出解析 | Prompt 质量、注入防护 |
| L3 记忆 | ChatMemory、ChatMemoryStore、ChatMemoryProvider |
多轮上下文 | 长期知识、租户隔离 |
| L4 检索 | DocumentLoader、DocumentSplitter、EmbeddingStore、ContentRetriever、RetrievalAugmentor |
外部知识注入 | 数据质量、检索评估 |
| L5 工具 | @Tool、@P、ToolSpecification、ToolExecutor |
让模型调用外部能力 | 权限、幂等、审计 |
| L6 编排 | AiServices、AgenticServices、工作流、Supervisor |
组件装配、多步编排 | 业务正确性、失败恢复 |
2.1 L1 模型层:模型是依赖,不是架构
ChatModel 负责同步对话,StreamingChatModel 负责流式;EmbeddingModel 负责向量化;ImageModel 负责图像;1.21 之后新增的 DecisionModel 负责"带概率的类型化判断"。
这一层最容易让人产生"框架就是 SDK"的错觉。实际上,模型只是六层中的一层。换模型不应该影响记忆、检索和工具。
生产关注:模型路由、超时、重试、降级、Token 成本计量。
2.2 L2 提示与 IO 层:Prompt 是代码,不是配置
@SystemMessage、@UserMessage、@V 是声明式入口;PromptTemplate 是底层模板;结构化输出由框架的输出解析器负责。
很多团队把 Prompt 写在数据库或配置中心,改起来很方便,但没有版本、没有测试、没有灰度。上线后一次"优化 Prompt"就可能让结构化输出解析失败。
生产关注:Prompt 版本控制、注入防护、输出校验、回归测试。
2.3 L3 记忆层:记忆是有状态数据
ChatMemory 管消息窗口,ChatMemoryStore 管持久化,ChatMemoryProvider 按 @MemoryId 隔离会话。
官方文档特别提醒:同一个 @MemoryId 的 AI Service 不应并发调用 ,否则可能损坏 ChatMemory;框架目前不负责加锁。这是生产必须自己补的约束。
生产关注:租户隔离、Redis 持久化、Token 预算、并发控制、过期清理。
2.4 L4 检索层:RAG 的上限由数据管线决定
RAG 的主链路是:
text
DocumentLoader → DocumentSplitter → EmbeddingModel → EmbeddingStore
→ ContentRetriever → RetrievalAugmentor → Prompt
EmbeddingStoreIngestor 把摄取管线串起来;EmbeddingStoreContentRetriever 提供 naive RAG;DefaultRetrievalAugmentor 支持 Query 改写、路由、聚合、注入,适合高级 RAG。
一个常见误区是"召回不准就换向量库"。在真实项目里,分块、元数据、混合检索、重排带来的收益,通常远大于换一个向量库。
生产关注:分块策略、元数据 Schema、混合检索、Rerank、评估集。
2.5 L5 工具层:工具是对外 API,不是普通方法
@Tool 描述工具,@P 描述参数,ToolExecutor 负责执行;底层还有 ToolSpecification。
工具一旦交给模型,就意味着模型可以触发真实副作用:查订单、发退款、重启服务。它必须有权限边界、幂等设计、超时、审计,必要时还要人工审批。
生产关注:白名单、参数校验、并行调用、失败回传、审批与审计。
2.6 L6 编排层:复杂度转移,不是复杂度消失
AiServices 是单服务装配器;AgenticServices 提供顺序、并行、循环、监督等工作流原语;@Agent 定义子代理。
这一层让代码看起来更少,但状态、失败恢复、成本控制变得更难。能用确定性代码解决的,不要交给 Agent。
生产关注:状态机、超时、重试、人工介入、成本上限。
2.7 故障与层的映射
| 现象 | 根因层 | 典型修复 | 对应篇目 |
|---|---|---|---|
| 记忆串号 | L3 | 按 tenant/user 设计 memoryId 与 store key |
A04 |
| 答非所问 | L4 | 分块、混合检索、重排、评估 | A07--A11 |
| 工具越权 | L5 | 权限矩阵、审批、幂等、审计 | A06、A14 |
| 账单失控 | L1/L6 | 路由、缓存、预算、级联 | A13、A18 |
| 无法定位 | 全链路 | Listener、Trace、指标 | A13 |
2.8 六层的替换成本
| 层 | 变更频率 | 替换成本 | 主要风险 | 建议 |
|---|---|---|---|---|
| L1 模型 | 高 | 低 | 能力与价格波动 | 用统一 Provider 隔离 |
| L2 Prompt | 高 | 低 | 输出回归 | 版本化 + 回归集 |
| L3 记忆 | 中 | 中 | 串号、Token 成本 | 统一 memoryId 规范 |
| L4 检索 | 中 | 高 | 索引重建、召回下降 | 先评估再换 |
| L5 工具 | 低 | 高 | 权限、真实副作用 | 权限矩阵 + 审批 |
| L6 编排 | 低 | 高 | 状态、失败恢复 | 先确定性,后 Agent |
表中为经验判断,不是绝对结论;团队规模、合规要求与存量系统会显著改变替换成本。
3. 1.23 到底变了什么?
先看版本线。LangChain4j 的迭代节奏很快,单看 1.23 容易误判,放到时间线上才清楚。
| 版本 | 关键变化 | 对架构的影响 |
|---|---|---|
| 1.19 | MCP 传输收敛到 Streamable HTTP;混合检索实现增多 | MCP 接入方式变化,RAG 融合能力增强 |
| 1.20 | 返回类型驱动执行:CompletableFuture、Flow.Publisher、TokenStream |
流式与异步不再依赖额外适配器 |
| 1.21 | DecisionModel / DecisionServices |
分类、路由、门控可以用低成本模型 |
| 1.22 | OpenAiDecisionModel、Anthropic Prompt Caching TTL 等 |
决策模型跨厂商,缓存成为降本手段 |
| 1.23 | OllamaDecisionModel、Confidence Cascade、AiServiceInvocationSummaryEvent、测试增强、MCP 服务端文档 |
本地决策、级联降本、可观测与测试补强 |
3.1 决策模型:从"生成文本"到"回答类型化问题"
DecisionModel 不生成文本,而是回答带名称的类型化问题,并返回概率。典型问题包括:
- 这条工单该给哪个团队?(Choice)
- 这条消息需要今天处理吗?(Yes/No)
- 用户情绪有多强烈?(Scale)
1.23 新增 OllamaDecisionModel,意味着决策模型可以跑在本地;同时引入 Confidence Cascade 模式:先让便宜的小模型判断,置信度不足再升级到大模型。
这类能力的价值在"热路径":每个请求都要做的分类、路由、门控。用大模型做这些事,既慢又贵。
DecisionModel与整个langchain4j-agentic模块目前都标注为实验性,生产使用要锁定版本,并准备回退方案。
3.2 可观测性:把一次调用的所有事件串起来
1.23 引入 AiServiceInvocationSummaryEvent,把一次 AI Service 调用产生的所有事件按 invocationId 聚合。配合 Micrometer 的 chat 调用耗时指标,可以回答一个关键问题:
这次回答慢,是检索慢、模型慢、工具慢,还是网络慢?
没有这层聚合,线上只能看到"总耗时",无法归因。
3.3 测试:不打真实模型,也能测行为
langchain4j-test 在 1.23 增强了 LLM Judge,并支持对出站 ChatRequest 做断言:Prompt 是否包含关键指令、工具是否注册、记忆是否被带上。
这意味着大部分回归测试不需要调用真实模型,成本和稳定性都可控。
3.4 MCP:Streamable HTTP 成为主线
MCP 传输在 1.19 收敛到 Streamable HTTP;1.23 补上了服务端文档。对 Java 团队的影响是:
- 新接入不要再基于 SSE 设计;
- 工具服务端要考虑无状态、鉴权、超时与审计;
- MCP 适合把"工具"从应用内解耦成可复用服务,但不等于可以跳过权限设计。
4. LangChain4j vs Spring AI:怎么选?
先说结论:不是二选一。
Spring AI 2.x 绑定 Spring Boot 4.x,优势是 Spring 生态整合;LangChain4j 框架无关,优势是抽象细粒度与 Agent 编排。两者可以共存:Spring Boot 做工程骨架,LangChain4j 做复杂 RAG/Agent 管线。
4.1 能力对比
| 维度 | Spring AI 2.x | LangChain4j 1.23 | 判断 |
|---|---|---|---|
| 框架绑定 | Spring 强绑定 | 框架无关 | 已有 Spring 团队,Spring AI 集成成本更低 |
| 声明式 API | ChatClient + Advisors |
AiServices 动态代理 |
LangChain4j 更接近"服务层" |
| 对话记忆 | 内置 ChatMemory | ChatMemory + ChatMemoryStore |
两者都能做,LangChain4j 存储扩展更灵活 |
| RAG 粒度 | VectorStore + Advisors | Retriever / RetrievalAugmentor |
复杂 RAG 选 LangChain4j |
| 工具调用 | @Tool |
@Tool + ToolExecutor |
平手 |
| Agent 编排 | Advisors 组合 | Agentic 工作流(实验性) | 多 Agent 选 LangChain4j,但注意实验性 |
| 可观测性 | Micrometer / OTel | Listener / Micrometer | 两者都能做,取决于接入深度 |
| 测试 | 通用测试 + 少量 AI 工具 | langchain4j-test |
LangChain4j 更完整 |
| 国内模型 | Spring AI Alibaba 等 | 社区模块丰富 | 看具体模型与维护状态 |
以上为基于公开文档的定性对比,不是性能 Benchmark;能力判断会随版本变化,选型前以目标版本为准。
4.2 选型决策树
#mermaid-svg-RdHkWNm27CYdkKLB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RdHkWNm27CYdkKLB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RdHkWNm27CYdkKLB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RdHkWNm27CYdkKLB .error-icon{fill:#552222;}#mermaid-svg-RdHkWNm27CYdkKLB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RdHkWNm27CYdkKLB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RdHkWNm27CYdkKLB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RdHkWNm27CYdkKLB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RdHkWNm27CYdkKLB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RdHkWNm27CYdkKLB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RdHkWNm27CYdkKLB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RdHkWNm27CYdkKLB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RdHkWNm27CYdkKLB .marker.cross{stroke:#333333;}#mermaid-svg-RdHkWNm27CYdkKLB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RdHkWNm27CYdkKLB p{margin:0;}#mermaid-svg-RdHkWNm27CYdkKLB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RdHkWNm27CYdkKLB .cluster-label text{fill:#333;}#mermaid-svg-RdHkWNm27CYdkKLB .cluster-label span{color:#333;}#mermaid-svg-RdHkWNm27CYdkKLB .cluster-label span p{background-color:transparent;}#mermaid-svg-RdHkWNm27CYdkKLB .label text,#mermaid-svg-RdHkWNm27CYdkKLB span{fill:#333;color:#333;}#mermaid-svg-RdHkWNm27CYdkKLB .node rect,#mermaid-svg-RdHkWNm27CYdkKLB .node circle,#mermaid-svg-RdHkWNm27CYdkKLB .node ellipse,#mermaid-svg-RdHkWNm27CYdkKLB .node polygon,#mermaid-svg-RdHkWNm27CYdkKLB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RdHkWNm27CYdkKLB .rough-node .label text,#mermaid-svg-RdHkWNm27CYdkKLB .node .label text,#mermaid-svg-RdHkWNm27CYdkKLB .image-shape .label,#mermaid-svg-RdHkWNm27CYdkKLB .icon-shape .label{text-anchor:middle;}#mermaid-svg-RdHkWNm27CYdkKLB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RdHkWNm27CYdkKLB .rough-node .label,#mermaid-svg-RdHkWNm27CYdkKLB .node .label,#mermaid-svg-RdHkWNm27CYdkKLB .image-shape .label,#mermaid-svg-RdHkWNm27CYdkKLB .icon-shape .label{text-align:center;}#mermaid-svg-RdHkWNm27CYdkKLB .node.clickable{cursor:pointer;}#mermaid-svg-RdHkWNm27CYdkKLB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RdHkWNm27CYdkKLB .arrowheadPath{fill:#333333;}#mermaid-svg-RdHkWNm27CYdkKLB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RdHkWNm27CYdkKLB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RdHkWNm27CYdkKLB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RdHkWNm27CYdkKLB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RdHkWNm27CYdkKLB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RdHkWNm27CYdkKLB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RdHkWNm27CYdkKLB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RdHkWNm27CYdkKLB .cluster text{fill:#333;}#mermaid-svg-RdHkWNm27CYdkKLB .cluster span{color:#333;}#mermaid-svg-RdHkWNm27CYdkKLB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RdHkWNm27CYdkKLB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RdHkWNm27CYdkKLB rect.text{fill:none;stroke-width:0;}#mermaid-svg-RdHkWNm27CYdkKLB .icon-shape,#mermaid-svg-RdHkWNm27CYdkKLB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RdHkWNm27CYdkKLB .icon-shape p,#mermaid-svg-RdHkWNm27CYdkKLB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RdHkWNm27CYdkKLB .icon-shape rect,#mermaid-svg-RdHkWNm27CYdkKLB .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RdHkWNm27CYdkKLB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RdHkWNm27CYdkKLB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RdHkWNm27CYdkKLB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-RdHkWNm27CYdkKLB .q>*{fill:#111A2E!important;stroke:#4F8CFF!important;color:#E8EEF9!important;stroke-width:2px!important;}#mermaid-svg-RdHkWNm27CYdkKLB .q span{fill:#111A2E!important;stroke:#4F8CFF!important;color:#E8EEF9!important;stroke-width:2px!important;}#mermaid-svg-RdHkWNm27CYdkKLB .q tspan{fill:#E8EEF9!important;}#mermaid-svg-RdHkWNm27CYdkKLB .lc>*{fill:#2A1F3D!important;stroke:#A78BFA!important;color:#E8EEF9!important;stroke-width:2px!important;}#mermaid-svg-RdHkWNm27CYdkKLB .lc span{fill:#2A1F3D!important;stroke:#A78BFA!important;color:#E8EEF9!important;stroke-width:2px!important;}#mermaid-svg-RdHkWNm27CYdkKLB .lc tspan{fill:#E8EEF9!important;}#mermaid-svg-RdHkWNm27CYdkKLB .sa>*{fill:#0F2A1D!important;stroke:#22C55E!important;color:#E8EEF9!important;stroke-width:2px!important;}#mermaid-svg-RdHkWNm27CYdkKLB .sa span{fill:#0F2A1D!important;stroke:#22C55E!important;color:#E8EEF9!important;stroke-width:2px!important;}#mermaid-svg-RdHkWNm27CYdkKLB .sa tspan{fill:#E8EEF9!important;} 是
否
是
否
是
否
新项目 / 重构?
已深度使用
Spring 生态?
需要复杂 RAG
或 Agent 编排?
需要跨框架复用?
LangChain4j 编排
Spring AI 接入
Spring AI 2.x
LangChain4j
两者都可
按团队熟悉度选
图 2:选型决策树。先看团队约束,再看框架能力。
4.3 三个典型场景
场景 A:Spring Boot 为主,业务只需要模型调用 + 简单 RAG。
选 Spring AI。少一层抽象,团队学习成本低,和 Spring Security、Actuator、Micrometer 的整合更顺。
场景 B:需要复杂 RAG 管线、工具编排、多 Agent 工作流。
选 LangChain4j。RetrievalAugmentor、AiServices、AgenticServices 的可组合性更强。
场景 C:多框架、多模型、需要源码级定制。
选 LangChain4j。core 不绑定框架,替换模型与存储的代价更低。
5. 一条最小但完整的调用链
下面这段代码把 L1--L6 串起来:模型、Embedding、记忆、RAG、工具、AiServices。
示例使用
InMemoryEmbeddingStore,目的是复制即可运行;生产环境替换为 PgVector/Elasticsearch,记忆替换为 Redis 持久化。完整工程骨架见 A02。
java
// LangChain4j 1.23.0 | JDK 21
import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiEmbeddingModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
public class ProductionCallChain {
public interface SupportAssistant {
@SystemMessage("""
你是企业客服助手。回答必须基于检索到的资料和工具结果。
如果资料不足,直接说明"需要人工确认",不要编造。
""")
String chat(@MemoryId String conversationId,
@UserMessage String question);
}
public static class OrderTools {
@Tool("查询订单状态。只读操作,不会修改订单。")
public String queryOrderStatus(@P("订单号,例如 SO-2026-0001") String orderId) {
return switch (orderId) {
case "SO-2026-0001" -> "已发货,预计 2026-11-07 送达";
default -> "未找到订单:" + orderId;
};
}
}
public static void main(String[] args) {
// L1:模型与 Embedding
ChatModel chatModel = OpenAiChatModel.builder()
.baseUrl("https://api.deepseek.com/v1")
.apiKey(System.getenv("DEEPSEEK_API_KEY"))
.modelName("deepseek-chat")
.temperature(0.2)
.build();
// 也可替换为本地 Embedding 模型;这里用 DashScope 兼容接口演示
EmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder()
.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.modelName("text-embedding-v3")
.build();
// 注意:Embedding 维度必须与向量库表结构一致,换模型要重建索引
// L4:摄取与检索(示例用内存存储)
EmbeddingStore<TextSegment> store = new InMemoryEmbeddingStore<>();
EmbeddingStoreIngestor.builder()
.documentSplitter(DocumentSplitters.recursive(500, 80))
.embeddingModel(embeddingModel)
.embeddingStore(store)
.build()
.ingest(Document.from("""
退货政策:签收后 7 天内可无理由退货,商品需保持完好。
运费规则:质量问题由商家承担运费,非质量问题由买家承担。
"""));
ContentRetriever retriever = EmbeddingStoreContentRetriever.builder()
.embeddingStore(store)
.embeddingModel(embeddingModel)
.maxResults(4)
.minScore(0.65)
.build();
// L6:装配 L2/L3/L4/L5
SupportAssistant assistant = AiServices.builder(SupportAssistant.class)
.chatModel(chatModel)
.chatMemoryProvider(id -> MessageWindowChatMemory.withMaxMessages(20))
.contentRetriever(retriever)
.tools(new OrderTools())
.build();
String answer = assistant.chat(
"user-42",
"订单 SO-2026-0001 到哪了?顺便说下退货政策。");
System.out.println(answer);
}
}
90 行代码里,真正体现六层装配的只有
AiServices.builder(...)这 6 行;其余是模型、Embedding、示例数据与工具。框架的价值是让这 6 行可替换、可治理,而不是让代码变短。
5.1 一次请求发生了什么
ChatModel OrderTools ContentRetriever ChatMemory SupportAssistant 用户 ChatModel OrderTools ContentRetriever ChatMemory SupportAssistant 用户 #mermaid-svg-fDsl4FkvIrSjmrQK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fDsl4FkvIrSjmrQK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fDsl4FkvIrSjmrQK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fDsl4FkvIrSjmrQK .error-icon{fill:#552222;}#mermaid-svg-fDsl4FkvIrSjmrQK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fDsl4FkvIrSjmrQK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fDsl4FkvIrSjmrQK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fDsl4FkvIrSjmrQK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fDsl4FkvIrSjmrQK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fDsl4FkvIrSjmrQK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fDsl4FkvIrSjmrQK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fDsl4FkvIrSjmrQK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fDsl4FkvIrSjmrQK .marker.cross{stroke:#333333;}#mermaid-svg-fDsl4FkvIrSjmrQK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fDsl4FkvIrSjmrQK p{margin:0;}#mermaid-svg-fDsl4FkvIrSjmrQK .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-fDsl4FkvIrSjmrQK text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-fDsl4FkvIrSjmrQK .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-fDsl4FkvIrSjmrQK .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-fDsl4FkvIrSjmrQK .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-fDsl4FkvIrSjmrQK .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-fDsl4FkvIrSjmrQK #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-fDsl4FkvIrSjmrQK .sequenceNumber{fill:white;}#mermaid-svg-fDsl4FkvIrSjmrQK #sequencenumber{fill:#333;}#mermaid-svg-fDsl4FkvIrSjmrQK #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-fDsl4FkvIrSjmrQK .messageText{fill:#333;stroke:none;}#mermaid-svg-fDsl4FkvIrSjmrQK .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-fDsl4FkvIrSjmrQK .labelText,#mermaid-svg-fDsl4FkvIrSjmrQK .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-fDsl4FkvIrSjmrQK .loopText,#mermaid-svg-fDsl4FkvIrSjmrQK .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-fDsl4FkvIrSjmrQK .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-fDsl4FkvIrSjmrQK .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-fDsl4FkvIrSjmrQK .noteText,#mermaid-svg-fDsl4FkvIrSjmrQK .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-fDsl4FkvIrSjmrQK .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-fDsl4FkvIrSjmrQK .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-fDsl4FkvIrSjmrQK .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-fDsl4FkvIrSjmrQK .actorPopupMenu{position:absolute;}#mermaid-svg-fDsl4FkvIrSjmrQK .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-fDsl4FkvIrSjmrQK .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-fDsl4FkvIrSjmrQK .actor-man circle,#mermaid-svg-fDsl4FkvIrSjmrQK line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-fDsl4FkvIrSjmrQK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} chat("user-42", "订单到哪了?") 1 读取 user-42 历史 2 检索退货政策 3 Top-4 文档 4 System + 历史 + 文档 + 问题 5 请求调用 queryOrderStatus 6 执行只读工具 7 订单状态 8 回填工具结果 9 最终回答 10 写入本轮消息 11 回答 12
图 3:一次带 RAG 与工具调用的请求生命周期。
5.2 这段代码对应六层
| 代码位置 | 对应层 | 说明 |
|---|---|---|
OpenAiChatModel / OpenAiEmbeddingModel |
L1 | 模型与 Embedding 接入 |
@SystemMessage / @UserMessage |
L2 | 提示模板 |
MessageWindowChatMemory / @MemoryId |
L3 | 按会话隔离的记忆 |
EmbeddingStoreIngestor / ContentRetriever |
L4 | 摄取与检索 |
@Tool / OrderTools |
L5 | 只读工具 |
AiServices.builder(...) |
L6 | 装配与编排 |
5.3 这段代码故意省略了什么
这不是一份可以直接上生产的完整实现,以下约束需要后续篇目补齐:
- 记忆持久化与多租户隔离:A04;
- 流式输出、取消、背压:A05;
- 工具权限、幂等、审批:A06、A14;
- 混合检索、Rerank、评估:A09--A11;
- 可观测性与成本治理:A13;
- 测试与护栏:A14、A15。
6. 五个常见误区
误区 1:把 AiServices 当黑盒
AiServices 不是魔法,它是"动态代理 + 上下文装配 + 输出解析"。不理解装配顺序,遇到"工具不触发""结构化输出失败"只能猜。
纠正:先理解 L2--L5 如何被织入,再使用声明式接口。A03 会拆源码。
误区 2:所有问题都上 RAG
RAG 适合"答案在文档里"的问题;结构化查询、实时状态、计算类问题,应该用工具或 SQL。
纠正:先做问题分类,再决定检索、工具还是直接生成。
误区 3:记忆就是保存全部历史
全量历史会带来三个问题:Token 成本、噪声干扰、串号风险。
纠正:窗口、摘要、检索式记忆按场景组合;记忆是有状态数据,要隔离、要过期、要审计。
误区 4:工具就是给方法加 @Tool
工具是模型可以触发的真实副作用,必须有权限、幂等、超时、审计,高风险操作还要人工审批。
纠正:把工具当 API 设计,而不是当方法暴露。
误区 5:选型只看 GitHub Star
Star 数不代表生产可用性。要看版本节奏、Beta 模块稳定性、团队生态、可观测与测试能力。
纠正:用约束清单做选型:团队技术栈、上线时间、定制深度、运维能力、合规要求。
7. 适用边界
适合 LangChain4j 的场景:
- Java 团队需要把 LLM 能力嵌入现有服务;
- 需要 RAG、工具调用、多步编排;
- 需要跨框架、多模型、可替换抽象;
- 需要源码级定制与可观测性。
不适合的场景:
- 只调用一次模型、无状态、无 RAG/工具需求;
- 团队完全绑定 Spring,且只需要简单
ChatClient; - 需要开箱即用的托管平台,而不是自建;
- 对实验性 API 零容忍(应避开 Agentic/DecisionModel,或锁定版本)。
8. FAQ
LangChain4j 和 Python LangChain 是同一个东西吗?
不是。两者同名,但 LangChain4j 是独立的 Java 实现,不是逐 API 移植。不要把 Python LangChain 的教程直接套到 Java 代码上。
1.23 可以上生产吗?Beta 模块能用吗?
core 1.23.0 是 GA;langchain4j-mcp、langchain4j-agentic 等 Beta 模块按独立节奏发布,部分 API 标注实验性。生产使用要锁定版本、做兼容性测试、准备回退方案。
必须用 Spring Boot 吗?
不必须。core 不依赖 Spring;Spring Boot Starter 只是集成层。Quarkus、Micronaut、Helidon 或普通 Java 项目都可以用。
国内模型怎么接?
两种方式:一是使用厂商的 OpenAI 兼容接口(如 DeepSeek、DashScope);二是使用社区模块。注意不同模型的 Embedding 维度、上下文长度、工具调用能力不同,要做适配与回归测试。
AiServices 有性能损耗吗?
有,主要是动态代理、Prompt 组装、输出解析的开销;从调用链看,这部分通常远小于模型调用本身。真正的性能瓶颈通常在模型、网络、检索和线程模型,而不是 AiServices。
"六层抽象"是官方定义吗?
不是。这是我为架构决策总结的分层法,官方文档按组件组织。它的价值是帮助团队定位问题与划分职责,不是替代官方文档。
学习顺序建议?
先 L1--L3(模型、Prompt、记忆),再 L4(RAG),然后 L5(工具),最后 L6(编排)。每一层都要配一个可运行示例,不要只看不写。
9. 速查表
| 层 | 关键接口 | 一句话 | 对应篇目 |
|---|---|---|---|
| L1 模型 | ChatModel / EmbeddingModel / DecisionModel |
统一模型调用,不统一模型能力 | A01、A18 |
| L2 提示与 IO | @SystemMessage / @UserMessage / PromptTemplate |
Prompt 是代码,要版本化 | A03 |
| L3 记忆 | ChatMemory / ChatMemoryStore |
记忆是有状态数据,要隔离 | A04 |
| L4 检索 | ContentRetriever / RetrievalAugmentor |
RAG 上限由数据管线决定 | A07--A11 |
| L5 工具 | @Tool / ToolExecutor |
工具是 API,不是普通方法 | A06、A14 |
| L6 编排 | AiServices / AgenticServices |
复杂度转移,不是消失 | A03、A17 |
10. 系列导航
- 下一篇:A02《LangChain4j 生产级工程骨架:12 个模块 + Docker 一键启动》------把本文的六层落成一个可运行仓库。
- 全系列:《LangChain4j 生产级实战》20 篇路线图(专栏内查看)。
参考资料
- LangChain4j 1.23.0 Release Notes
- LangChain4j AI Services 文档
- LangChain4j RAG 文档
- LangChain4j Agents 文档
- LangChain4j Decision Models 文档
- Spring AI Reference