跳到主要内容

数据截至 (上游 commit daa7624a2755)

LangChain4j — 架构与原理

30 秒导读: LangChain4j 是一个 Java 库,让你不写调用代码就能用大模型——你只声明一个带注解的 Java 接口,它用 JDK 动态代理生成实现,背后自动串起记忆、RAG、护栏、工具调用和类型化解析。给的是 Java 工程师熟悉的东西:接口、注解、POJO、依赖注入,而不是 Python 那套链式对象。


1. 这是什么(零基础也能懂)

一句话定义。 LangChain4j 是运行在 JVM 上的 LLM 应用开发库,把"调模型"这件事包装成声明一个 Java 接口

解决什么问题。 假设你要在一个 Spring Boot 服务里加个客服助手。原始做法你得自己干这些活:

  • 拼 HTTP 请求体(每家厂商格式不一样);
  • 手动维护对话历史,还要防止超出上下文窗口;
  • 把知识库检索到的片段拼进 prompt;
  • 模型说"我要调 getOrderStatus(12345)",你得解析这段 JSON、反射调用你的方法、把结果再发回去,循环直到模型不再要工具;
  • 模型返回一坨自然语言,你还得把它解析成 Order 对象。

这五件事,LangChain4j 全部替你做了,代价是你写一个接口

给谁用。 JVM 上的应用开发者——尤其是 Spring Boot / Quarkus / Helidon / Micronaut 用户,这些框架都有对应的 LangChain4j 集成(依据:README.md:64-69)。

它能做什么(功能清单):

能力一句话
统一模型 API换 OpenAI ↔ Anthropic ↔ Ollama 只改依赖和构造器,业务代码不动
AI Services带注解的 Java 接口 → 自动生成实现(本库最核心的东西)
聊天记忆按窗口/按 token 数自动裁剪历史,支持多用户隔离
工具调用@Tool 标在普通方法上,自动生成 schema、自动执行、自动回灌
结构化输出方法返回 SentimentList<String>、任意 POJO,自动生成 JSON Schema 并解析
护栏 Guardrails输入拦截 / 输出校验,输出不合格可自动 reprompt 重试
RAG摄取流水线(切分 → 向量化 → 入库)+ 检索流水线(改写 → 路由 → 扇出检索 → 融合 → 注入)
agentic顺序 / 并行 / 并行 mapper / 循环 / 条件 / supervisor 六种多智能体编排,外加自定义 Planner 出口

用起来什么样。 这是官方 javadoc 里给的最小例子(langchain4j/src/main/java/dev/langchain4j/service/AiServices.java:99-109):

// 示意,非源码(改写自 AiServices 的 javadoc 示例)
interface Assistant {
String chat(String userMessage); // 一个方法,没有实现
}

Assistant assistant = AiServices.create(Assistant.class, model); // 动态代理生成实现
String answer = assistant.chat("hello"); // 这一行背后是完整的一次 LLM 调用

再进一步,加上注解就能做分类——注意返回类型是枚举,不是 String:

// 示意,非源码(改写自 AiServices 的 javadoc 示例)
enum Sentiment { POSITIVE, NEUTRAL, NEGATIVE }

interface SentimentAnalyzer {
@UserMessage("Analyze sentiment of {{it}}") // {{it}} = 唯一那个参数
Sentiment analyzeSentimentOf(String text); // 返回枚举 → 框架负责让模型只能吐这三个值
}

重点看:返回类型 Sentiment 不是装饰,它是输入——框架会据此生成 JSON Schema 或往 prompt 里追加格式说明,再把模型的回答解析回枚举。

一句话直觉。 把它当成 LLM 世界的 MyBatis / Feign:你写接口 + 注解,它生成代理实现,把"远程调用"的脏活藏起来。区别是 Feign 代理的是 HTTP,它代理的是"一整条 LLM 编排管线"。

顺便澄清一个常见误会。 尽管叫 LangChain4j,它不是 Python LangChain 的 Java 移植;README 明确写了它的 API、内部实现和发布周期都独立(README.md:42-45)。


2. 顶层全景(它大概怎么转)

2.1 一次 AI Service 调用的完整管线

怎么读这张图: 从上到下是一次方法调用的时间顺序,每一层都是可选的——没配 RAG 就跳过 ③,没配护栏就跳过 ④⑥。全部逻辑在同一个 InvocationHandler.invoke 里(langchain4j/src/main/java/dev/langchain4j/service/DefaultAiServices.java:188-450)。

assistant.chat("我的订单到哪了?")


┌───────────────────────────────────┐
│ ① 动态代理拦截 │
│ 读方法注解 + 参数 → 拼消息 │
└───────────────┬───────────────────┘

② 记忆:取出历史消息

③ RAG:检索片段,塞进 User 消息

④ 输入护栏:拦截或改写 User 消息

┌───────────────────────────────────┐
│ ⑤ 工具 round-trip 循环 │
│ 问模型 → 要工具? → 执行 → 回灌 │
└───────────────┬───────────────────┘

⑥ 输出护栏:不合格就 reprompt

⑦ 类型化解析 → 返回 Java 对象

2.2 部件一句话职责

部件干什么在哪个文件(相对克隆根)
AiServices建造器,把模型/记忆/工具/RAG/护栏挂上去langchain4j/src/main/java/dev/langchain4j/service/AiServices.java
DefaultAiServices真正生成 JDK 动态代理,并在 invoke 里串起整条管线langchain4j/src/main/java/dev/langchain4j/service/DefaultAiServices.java
AiServiceContext所有配置的容器(模型、记忆、工具服务、护栏、RAG)langchain4j/src/main/java/dev/langchain4j/service/AiServiceContext.java
ChatModel统一模型接口,chat(ChatRequest) → ChatResponselangchain4j-core/src/main/java/dev/langchain4j/model/chat/ChatModel.java
ToolService工具注册 + round-trip 循环langchain4j/src/main/java/dev/langchain4j/service/tool/ToolService.java
RetrievalAugmentorRAG 检索侧的五插槽流水线:改写 → 路由 → 扇出检索 → 融合 → 注入langchain4j-core/src/main/java/dev/langchain4j/rag/DefaultRetrievalAugmentor.java
ServiceOutputParser按返回类型生成 JSON Schema、解析模型输出langchain4j/src/main/java/dev/langchain4j/service/output/ServiceOutputParser.java
Planneragentic 编排的唯一抽象:每步返回一个 Actionlangchain4j-agentic/src/main/java/dev/langchain4j/agentic/planner/Planner.java

2.3 主线走一遍(高层,不进代码)

  1. 拦截。 你调 assistant.chat(...),进的是 Proxy.newProxyInstance 生成的代理对象(DefaultAiServices.java:113-116)。框架读方法上的 @SystemMessage/@UserMessage 注解和参数,渲染成 prompt 模板。
  2. 补历史。 如果配了 ChatMemory,取出该 @MemoryId 对应的历史消息(DefaultAiServices.java:191-193)。
  3. 补知识。 如果配了 RetrievalAugmentor,检索并把片段注入 User 消息(DefaultAiServices.java:218-230)。
  4. 过输入护栏。(DefaultAiServices.java:243-244)
  5. 定输出契约。 看方法返回类型:模型若支持 JSON Schema 就下发 schema,否则往 User 消息末尾追加格式说明文字(DefaultAiServices.java:255-260)。
  6. 进循环。 发第一次请求拿到响应后,交给 ToolService.executeInferenceAndToolsLoop —— 只要模型还在要工具,就执行、回灌、再问(DefaultAiServices.java:345-354)。
  7. 过输出护栏 + 解析。 护栏不通过可以带着 reprompt 重来;通过后按返回类型解析成 Java 对象(DefaultAiServices.java:417-449)。

2.4 两个循环,是理解这个库的钥匙

整个库看起来东西很多(105 个 Maven 模块,依据:pom.xml<module> 计数 = 105),但真正的"运行时"只有两个 while 循环

单个 AI Service 内部 多智能体编排
┌──────────────────────────┐ ┌──────────────────────────┐
│ ToolService round-trip │ │ Planner 循环 │
│ │ │ │
│ 问模型 │ │ planner.nextAction() │
│ ↓ │ │ ↓ │
│ 有 toolExecutionRequest?│ │ Action 是 done? │
│ ↓ 有 │ │ ↓ 否 │
│ 执行工具 → 结果入消息 │ │ 执行这批 agent │
│ ↓ │ │ ↓ │
│ └──── 回到"问模型" ────┘ │ │ └── 回到 nextAction ──┘ │
└──────────────────────────┘ └──────────────────────────┘
ToolService.java:550-660 PlannerBasedInvocationHandler
.java:363-388

两者的共同形状:一个状态机反复被问"下一步干什么",直到它说"完了"。区别只在于谁来决定下一步——round-trip 循环里是模型决定(它还要不要调工具),Planner 循环里是代码或模型决定(SequentialPlanner 按顺序数数,SupervisorPlanner 让模型挑下一个 agent)。


3. 阅读地图

六章由浅入深。建议顺序:02 → 03 → 04 → 01 → 05 → 06。 先看主线(AiServices),再看两个循环,最后回头补底座和外围。

顺序章节讲什么什么时候读
起点AiServices:一个 Java 接口如何变成一次 LLM 调用动态代理、注解到 prompt、InvocationHandler 里那 260 行主线必读,理解全库的入口
2工具调用:从 @Tool 反射到 round-trip 循环@ToolToolSpecification 反射、ToolExecutor 参数强转、循环的终止与回滚想搞明白 function calling 到底怎么闭环
3让输出可用:类型化解析与输入输出护栏返回类型 → JSON Schema、21 个 OutputParser、护栏的 reprompt 重试想让 LLM 输出能直接喂给业务代码
4核心抽象层:模型接口、消息模型与 100+ 集成如何共存ChatModel/ChatRequest/ChatResponse、五种消息类型、Capability 能力协商、SPI 装配想加新模型厂商,或想知道"换模型不改代码"怎么做到
5RAG:摄取与检索两条流水线EmbeddingStoreIngestor 摄取五步、RetrievalAugmentor 五插槽检索要接知识库
6agentic:多智能体编排收敛成同一个 Planner 循环@AgentAgenticScope 黑板、六种编排 + 自定义 planner 全部走 build(Supplier<Planner>)要做多智能体,或想学一个把编排收敛成单一抽象的设计范例

只想拿一个结论就走的话: 读 §2.4 那两个循环 + 02 章。剩下四章都是这两个循环的周边设施。


4. 巧妙之处(可借鉴的技术)

4.1 返回类型是输入,不是输出

妙在哪: 大多数框架让你手写"请以 JSON 格式返回"。这里 Java 的方法签名本身就是契约——Sentiment analyze(String) 的返回类型被反过来读成对模型的约束。

关键分叉只有五行:模型支持 JSON Schema 就走 schema,不支持就退化成"往 prompt 末尾追加格式说明"(DefaultAiServices.java:255-260),追加逻辑还特意找最后一个 TextContent 拼接,以免打乱多模态消息的图文顺序(DefaultAiServices.java:523-546,appendOutputFormatInstructions)。

4.2 有记忆和无记忆,走两条不同的消息累积路径

妙在哪: round-trip 循环里最容易写错的就是"消息到底存哪"。ToolService 的处理是:有 ChatMemory 就往记忆里 add,没有就在本地 list 的副本上追加(ToolService.java:574-578)——messages = new ArrayList<>(messages) 这一步防止污染调用方传进来的列表。

4.3 工具结果可以直接当返回值,省掉最后一次模型调用

妙在哪: 常规工具循环是"执行完工具必须再问一次模型,让它总结"。ReturnBehavior.IMMEDIATE 让工具结果直接返回,少一次 token 消耗和一次延迟。

判定逻辑在 ToolService.shouldReturnImmediately(ToolService.java:753-764),命中后循环提前退出并置 immediateToolReturn(true)(ToolService.java:645-652);上层再据此决定怎么把工具结果映射到方法返回类型——只有一个非 null 结果且类型匹配才直接返回,否则报配置错误让你改用 Result<T>(DefaultAiServices.java:373-403)。

4.4 工具失败时的补偿事务:不只回滚,还要改写模型看到的历史

妙在哪: 这是整个库里最"分布式事务"味道的设计。开启 compensateOnToolErrors 后,一批并发工具里只要有一个失败:

  1. 已成功的工具逆序执行各自的补偿动作(ToolService.java:740-752,compensateToolsActions);
  2. 更狠的一步——把聊天记忆里那些已成功的工具结果消息重写掉,改成 "Tool 'X' was executed successfully but was rolled back due to failure of tool 'Y'" 并标记 isError(true)(ToolService.java:706-736,rolledBackResultMessage)。

第 2 步是关键:光回滚真实世界还不够,模型的记忆里也不能留下"我已经成功下单了"这种错误认知,否则下一轮它会基于幻觉继续推理。

4.5 护栏的 reprompt 不进记忆

妙在哪: 输出护栏失败时要带着"你上次答得不对,重来"再问一次模型。天真做法是把这条 reprompt 写进 ChatMemory,结果就是记忆被一堆纠错对话污染。

这里的做法:从 memory 取出消息副本,把 reprompt 追加到副本上重新执行,副本和新响应都不写回记忆(OutputGuardrailExecutor.java:84-97)。重试上限来自 config().maxRetries(),且 0 会被规范成 1、负数规范成默认值(OutputGuardrailExecutor.java:59-65)——这种输入规范化避免了"配 0 导致一次都不执行"的陷阱。

4.6 六种编排,一个 Planner 接口

妙在哪: 顺序、并行、并行 mapper、循环、条件、supervisor——六种内置形态没有六份执行引擎,全部只是不同的 Planner 实现,共用同一个循环;再加一个 plannerBuilder() 出口,让你塞自己的 Planner

证据是所有 builder 的 build() 都收敛到同一个方法 AbstractServiceBuilder.build(Supplier<Planner>)(langchain4j-agentic/src/main/java/dev/langchain4j/agentic/internal/AbstractServiceBuilder.java:170):

编排形态build() 传入的 Planner位置
顺序SequentialPlanner::newworkflow/impl/SequentialAgentServiceImpl.java:21
并行ParallelPlanner::newworkflow/impl/ParallelAgentServiceImpl.java:23
并行 mappernew ParallelMapperPlanner(...)workflow/impl/ParallelMapperServiceImpl.java:42
循环new LoopPlanner(...)workflow/impl/LoopAgentServiceImpl.java:39
条件new ConditionalPlanner(...)workflow/impl/ConditionalAgentServiceImpl.java:33
supervisornew SupervisorPlanner(...)supervisor/SupervisorAgentServiceImpl.java:53
(非内置)自定义你自己的 plannerSupplierplanner/PlannerBasedServiceImpl.java:41

Planner 接口的必须实现的方法只有一个:Action nextAction(PlanningContext)(planner/Planner.java:61)。Action 只有三种形态:调这批 agent、什么都不做、完事了(planner/Action.java:11-150)。SequentialPlanner 的实现就是一行:terminated() ? done() : call(agents.get(agentCursor++))(workflow/impl/SequentialPlanner.java:24)。

4.7 Planner 状态可持久化,支持崩溃后从中断处恢复

妙在哪: 每次子 agent 执行完,循环会把 planner 的 executionState() 写进 AgenticScope 并 checkpoint(internal/PlannerBasedInvocationHandler.java:523-540);下次 loop() 启动时先 restoreExecutionState 再取 firstAction(同文件:364-369)。

SequentialPlanner 来说这个状态就是"走到第几个 agent 了"这个游标——几个字节就换来了长流程的断点续跑。无状态的 planner(并行、条件)用接口默认的空实现即可(planner/Planner.java:22-33)。


5. 代码地图(导航索引)

按符号名 grep 比按行号更抗上游漂移。路径相对克隆根。

5.1 主线:AI Services

主题文件路径符号名
建造器入口langchain4j/src/main/java/dev/langchain4j/service/AiServices.javaAiServices.createAiServices.builder
动态代理与整条管线langchain4j/src/main/java/dev/langchain4j/service/DefaultAiServices.javaDefaultAiServices.build、内部 invoke(Method, Object[], InvocationContext)
配置容器langchain4j/src/main/java/dev/langchain4j/service/AiServiceContext.javaAiServiceContextstoreRetrievedContentInChatMemory
prompt 变量解析langchain4j/src/main/java/dev/langchain4j/service/InternalReflectionVariableResolver.javafindTemplateVariables
注解langchain4j/src/main/java/dev/langchain4j/service/@SystemMessage@UserMessage@V@MemoryId@UserName@Moderate
记忆装配langchain4j/src/main/java/dev/langchain4j/service/memory/ChatMemoryService.javaChatMemoryService.getOrCreateChatMemory
记忆策略langchain4j/src/main/java/dev/langchain4j/memory/chat/MessageWindowChatMemoryTokenWindowChatMemory

5.2 核心抽象层

主题文件路径符号名
统一模型接口langchain4j-core/src/main/java/dev/langchain4j/model/chat/ChatModel.javaChatModel.chatChatModel.doChatsupportedCapabilities
流式模型langchain4j-core/src/main/java/dev/langchain4j/model/chat/StreamingChatModel.javaStreamingChatModel
能力协商langchain4j-core/src/main/java/dev/langchain4j/model/chat/Capability.javaCapability.RESPONSE_FORMAT_JSON_SCHEMA
请求/响应langchain4j-core/src/main/java/dev/langchain4j/model/chat/request/ChatRequestChatRequestParameters
消息模型(五种)langchain4j-core/src/main/java/dev/langchain4j/data/message/ChatMessageTypeSystemMessageUserMessageAiMessageToolExecutionResultMessageCustomMessage
多模态内容langchain4j-core/src/main/java/dev/langchain4j/data/message/TextContentImageContentAudioContentVideoContentPdfFileContent

5.3 工具调用

主题文件路径符号名
round-trip 循环langchain4j/src/main/java/dev/langchain4j/service/tool/ToolService.javaexecuteInferenceAndToolsLoop
循环上限同上maxToolCallingRoundTrips(默认 100)
提前返回判定同上shouldReturnImmediately
补偿回滚同上compensateToolsActionsrewriteChatMemoryForCompensatedToolsrolledBackResultMessage
工具上下文构建同上createContextrefreshDynamicProviders
@Tool → schema 反射langchain4j-core/src/main/java/dev/langchain4j/agent/tool/ToolSpecifications.javatoolSpecificationFromtoolSpecificationsFrom
工具执行器langchain4j/src/main/java/dev/langchain4j/service/tool/DefaultToolExecutor.javaDefaultToolExecutor
动态工具供给langchain4j/src/main/java/dev/langchain4j/service/tool/ToolProvider.javaToolProvider.provideTools
工具检索(工具太多时)langchain4j/src/main/java/dev/langchain4j/service/tool/search/ToolSearchStrategyVectorToolSearchStrategy

5.4 结构化输出与护栏

主题文件路径符号名
解析总入口langchain4j/src/main/java/dev/langchain4j/service/output/ServiceOutputParser.javaparsejsonSchemaoutputFormatInstructions
解析器注册表langchain4j/src/main/java/dev/langchain4j/service/output/DefaultOutputParserFactory.javaDefaultOutputParserFactory
各类型解析器(21 个具体实现 + 3 个抽象集合基类)langchain4j/src/main/java/dev/langchain4j/service/output/EnumOutputParserPojoOutputParserPojoListOutputParserPojoCollectionOutputParser
护栏接口langchain4j-core/src/main/java/dev/langchain4j/guardrail/InputGuardrailOutputGuardrail
reprompt 重试langchain4j-core/src/main/java/dev/langchain4j/guardrail/OutputGuardrailExecutor.javaOutputGuardrailExecutor.execute
护栏装配langchain4j/src/main/java/dev/langchain4j/service/guardrail/DefaultGuardrailService.javaDefaultGuardrailService

5.5 RAG

主题文件路径符号名
摄取流水线langchain4j-core/src/main/java/dev/langchain4j/store/embedding/EmbeddingStoreIngestor.javaEmbeddingStoreIngestor.ingest
检索流水线langchain4j-core/src/main/java/dev/langchain4j/rag/DefaultRetrievalAugmentor.javaDefaultRetrievalAugmentor.augment
可替换的四个环节(第五个插槽是检索器,见下一行)langchain4j-core/src/main/java/dev/langchain4j/rag/QueryTransformerQueryRouterContentAggregatorContentInjector
检索器接口langchain4j-core/src/main/java/dev/langchain4j/rag/content/retriever/ContentRetriever.javaContentRetriever.retrieve
切分器langchain4j/src/main/java/dev/langchain4j/data/document/splitter/DocumentSplittersDocumentBySentenceSplitter
内存向量库langchain4j/src/main/java/dev/langchain4j/store/embedding/inmemory/InMemoryEmbeddingStore.javaInMemoryEmbeddingStore
开箱即用 RAGlangchain4j-easy-rag/

5.6 agentic

主题文件路径符号名
API 门面langchain4j-agentic/src/main/java/dev/langchain4j/agentic/AgenticServices.javasequenceBuilderparallelBuilderparallelMapperBuilderloopBuilderconditionalBuildersupervisorBuilderplannerBuilder
agent 注解langchain4j-agentic/src/main/java/dev/langchain4j/agentic/Agent.java@Agent(outputKeyasyncoptional)
编排抽象langchain4j-agentic/src/main/java/dev/langchain4j/agentic/planner/Planner.javaPlanner.nextActionexecutionStaterestoreExecutionState
动作类型langchain4j-agentic/src/main/java/dev/langchain4j/agentic/planner/Action.javaAgentCallActionDoneActionDoneWithResultAction
Planner 循环langchain4j-agentic/src/main/java/dev/langchain4j/agentic/internal/PlannerBasedInvocationHandler.java内部类 PlannerLoop.looponSubagentInvokedcomposeActions
收敛点langchain4j-agentic/src/main/java/dev/langchain4j/agentic/internal/AbstractServiceBuilder.javabuild(Supplier<Planner>)
子 agent 执行langchain4j-agentic/src/main/java/dev/langchain4j/agentic/internal/AgentExecutor.javaAgentExecutor.executecompleteAgentInvocation
共享黑板langchain4j-agentic/src/main/java/dev/langchain4j/agentic/scope/AgenticScope.javareadStatewriteStatecontextAsConversationagentInvocations
各具体 plannerlangchain4j-agentic/src/main/java/dev/langchain4j/agentic/workflow/impl/SequentialPlannerParallelPlannerLoopPlannerConditionalPlannerParallelMapperPlanner
LLM 驱动的编排langchain4j-agentic/src/main/java/dev/langchain4j/agentic/supervisor/SupervisorPlanner.javaSupervisorPlanner.nextAction
MCP / A2A 桥接langchain4j-agentic-mcp/langchain4j-agentic-a2a/