Skip to content

LangChain4j 介绍

这一篇把"在 JVM 生态里怎么用一套类 LangChain 的方式做大模型应用"讲清楚:LangChain4j 是什么、解决了哪些问题、它的核心抽象、什么时候应该用它而不是 Spring AI。

1. 什么是 LangChain4j

LangChain4j 是 LangChain 在 JVM 生态(Java / Kotlin / Scala)上的官方实现,由 LangChain 团队与社区共同维护。

它的目标可以一句话概括:

把 Python 生态中 LangChain 已经验证过的"Chain / Agent / RAG / Tool"这套抽象,在不丢失语义的前提下,迁移到 JVM 上来。

它不是 Spring AI 的替代品,但也绝对不是"另一套轮子"——大多数核心概念(ChatLanguageModelChatMemoryToolSpecificationEmbeddingStoreRetrieval-Augmented Generation)在两个项目里几乎一一对应,只在 API 风格和生态集成上有些差异。

2. 它想解决的核心问题

LangChain4j 的设计哲学可以概括成三层:

  1. 统一抽象:屏蔽"模型厂商"差异,ChatLanguageModel 是顶层接口,任何厂商(OpenAI / Anthropic / Azure / Ollama / Qwen / DeepSeek / vLLM / 内部推理网关)都是它的一种实现。
  2. 可组合:高层 API 用 ChainAgentAiServices 等 builder 风格类,把"Prompt + Memory + Tool + RAG"像乐高一样拼起来。
  3. 可嵌入:不强绑定 Web 框架,不强迫 Spring。可以是 Spring Boot、Quarkus、Micronaut、Ktor、Vert.x,甚至是普通 main 方法。

这三点合在一起,它的目标用户是:"既不想被某个 LLM 厂商绑死,又不想自己手撕函数调用/RAG/记忆机制的 Java 团队"

3. 核心模块一览

3.1 三大核心抽象

  • ChatLanguageModel:对话模型顶层接口。所有厂商都实现它。
  • ChatMessage / UserMessage / AiMessage / SystemMessage / ToolExecutionResultMessage:与 OpenAI 的多角色协议对齐。
  • ChatMemory:对话记忆抽象,常见实现有 MessageWindowChatMemoryTokenWindowChatMemory

3.2 高层 API:AiServices 与 Agent

  • AiServices.create(YourAssistant.class, model)动态生成一个接口的实现:
    • 方法参数作为 prompt 输入;
    • 方法返回值可以是 String、普通 POJO(自动结构化映射)甚至 Result<Pojo>(拿到 token 用量等元信息)。
  • 同一个接口上能用 @Tool / @ToolMemoryId / @SystemMessage 等注解声明"需要被模型调用""需要按 user 隔离记忆""需要把方法描述变成 system 提示词"。

一个最简示例:

java
interface Assistant {
    @SystemMessage("你是一个严谨的中文技术助手,回答不超过 200 字。")
    String chat(@UserMessage String userMessage);
}

ChatLanguageModel model = OpenAiChatModel.builder()
    .apiKey(System.getenv("OPENAI_API_KEY"))
    .modelName("gpt-4o-mini")
    .build();

Assistant assistant = AiServices.create(Assistant.class, model);
String answer = assistant.chat("用一句话解释什么是 RAG。");

3.3 工具调用(Agent 基础)

  • @Tool 把普通方法暴露给模型。
  • ToolSpecification + ToolExecutor 是底层的协议侧接口,大多数业务方用注解就够了。
  • 函数调用是 LangChain4j 中"Agent 自己决定什么时候调、怎么调、调哪个"的基础。
java
class WeatherTools {
    @Tool("根据城市名查询当前天气")
    String currentWeather(
        @P("城市名,例如 '上海'") String city
    ) {
        return city + " 当前晴,25°C";
    }
}

Assistant agent = AiServices.builder(Assistant.class)
    .chatLanguageModel(model)
    .tools(new WeatherTools())
    .build();

3.4 RAG(检索增强生成)

LangChain4j 的 RAG 链路抽象相对完整:

DocumentSource -> DocumentLoader -> DocumentParser
            -> DocumentSplitter
            -> EmbeddingModel -> EmbeddingStore
检索:Query -> EmbeddingModel -> EmbeddingStore.search() -> 文档
            -> Prompt 拼装 -> ChatLanguageModel
  • 内置解析:PdfDocumentParserTextDocumentParserApachePoiDocumentParser(Word/PPT/Excel)、JsoupDocumentParser(HTML)。
  • 切分器:DocumentSplitter 接口,默认实现有按字符、按 token(TokenTextSplitter)、按段落、按正则等。
  • 向量库:EmbeddingStore 接口,支持内存版、Pinecone、Milvus、Qdrant、Chroma、Weaviate、PGvector、Redis、ES、Neo4j、Vespa 等。

3.5 MCP 集成

  • LangChain4j 同时支持 MCP 客户端 / 服务端,可以用 @McpTool 等注解把方法暴露为 MCP 工具。
  • 对需要"内部能力协议化、被多个 Agent(无论 Java 还是 Python)消费"的场景非常合适。

3.6 可观测性

  • 内置 Micrometer 集成,自动暴露模型调用次数、token 用量、错误率、延迟等。
  • 同时支持 OpenTelemetry:LangChain4j 自带 OpenTelemetry*Integration
  • 对接 Prometheus / Grafana / Tempo 与 Spring Boot Actuator / Quarkus 配合很顺。

4. Spring AI vs LangChain4j:怎么选

这两个项目在抽象层上几乎可以对齐,差异主要在"风格 / 集成 / 团队习惯"。

维度Spring AILangChain4j
出身Spring 团队(VWare/Broadcom)LangChain 团队 + Java 社区
编程风格Spring 风格:ChatClient.Builder、注解、RestTemplate-likeBuilder/AiServices 风格:动态代理接口 + 注解
框架绑定与 Spring Boot 强绑定(虽然不是不行但不是主场)完全框架无关(Spring/Quarkus/Ktor/裸 main 都行)
高层 APIChatClientAdvisor 环绕AiServices 接口代理
Agent / 工作流偏"组合 Advisor"自己做同样能力 + AgenticScope、LangGraph4j 组合
厂商支持OpenAI / Anthropic / Vertex / Bedrock / Ollama / 大量社区 starter同样主流厂商齐备,列表略小一点但主流都在
与 Java 生态集成Spring 全家桶(Actuator/Security/Integration)不绑定 Spring,但有 Spring Boot starter 和 Quarkus 扩展
适合场景已有 Spring 重资产的工程团队多框架/多厂商/想跟 LangChain Python 对齐的团队

经验法则:

  • Spring 包袱大、团队以 Spring 为主 -> Spring AI
  • 想用 Java 写 LangChain 风格的代码,或同时维护 Python/Java 两套 -> LangChain4j
  • 两者完全可以混用,但同一个项目里只选一套更省心

5. 一个最小可运行的 Agent

java
// 1. 模型
ChatLanguageModel model = AnthropicChatModel.builder()
    .apiKey(System.getenv("ANTHROPIC_API_KEY"))
    .modelName("claude-3-5-sonnet-latest")
    .build();

// 2. 工具
class OrderTools {
    @Tool("根据订单号查询当前订单状态")
    String orderStatus(@P("订单号") String orderId) {
        // 调内部订单服务
        return orderId + " 已发货,预计明天到";
    }
}

// 3. 助手接口
interface OrderAssistant {
    @SystemMessage("你是订单助手,先用工具查真实订单再回答。")
    String ask(String userMessage);
}

// 4. 装配
OrderAssistant agent = AiServices.builder(OrderAssistant.class)
    .chatLanguageModel(model)
    .tools(new OrderTools())
    .chatMemory(MessageWindowChatMemory.withMaxMessages(10))
    .build();

// 5. 使用
String reply = agent.ask("帮我看看订单 D20260714-001 的状态");

只要把 AnthropicChatModel 换成 OpenAiChatModel / OllamaChatModel / QwenChatModel,其他一行都不用改。

6. 常见坑与经验

  • Prompt 不要过长:@SystemMessage 里塞大量规则,不仅挤占上下文窗口,还会让模型注意力漂移;真正稳定的规则应该拆成"顾问 / 工具 / Few-shot 示例"。
  • 工具描述要"人话":@Tool("...") 的 description 是模型判断"什么时候调"的关键信息,务必用业务可理解的语言,而不是 Java 方法名复制。
  • 结构化输出用 POJO 而非 Map:@Description 注解 + 嵌套 record,比 Map<String,Object> 让模型更稳定。
  • 多轮记忆按用户隔离:ChatMemory 默认是单例,生产环境务必按 userId / sessionId 隔离(MessageWindowChatMemoryChatMemoryProvider)。
  • RAG 不要追求 100% 召回:真正决定效果的是 chunking 粒度 + 检索 query 改写 + 模型回答时的"允许拒答"约束,而不是换更强的向量库。

7. 进一步阅读

  • 官方站: https://docs.langchain4j.dev/
  • GitHub: https://github.com/langchain4j/langchain4j
  • LangGraph4j:在 LangChain4j 上做有状态/可分支 Agent 工作流
  • 对照阅读:本目录下 1-springAi/springAi-介绍 了解 Spring 生态的另一条路

(后续本子分类会按需补充:Prompt 与 AiServices 进阶、工具调用与权限隔离、RAG 实战、可观测与评估、多 Agent 协作等专题。)