type
Post
status
Published
date
May 28, 2025
slug
summary
tags
开发
思考
工具
category
技术分享
icon
password
1、写在开头2、运行环境支持3、项目集成示例3.1 SpringBoot引入使用(硬编码)3.2 包装接口3.3 常用配置3.4 视觉理解3.5 流式输出3.6 记忆缓存3.7 Prompt提示词3.8 FunctionCall工具回调3.9 RAG向量存储查询4、引用
1、写在开头
LangChain4j是什么
LangChain4j是Java生态中的LangChain实现,弥补了Python和JavaScript版本在 Java领域的空白。它通过模块化设计和统一API,帮助开发者快速构建基于LLM的智能应用(如聊天机器人、知识库系统等),尤其适合需要与企业现有系统(数据库、API等)深度结合的场景。
核心功能
- 模型集成:支持多种主流 LLM,包括 OpenAI GPT、Anthropic Claude、百度文心一言等,提供标准化调用接口
- 链式编排:通过组合预定义模块(如模型调用、数据处理、工具调用)实现复杂任务流程,例如多步骤推理或文档检索
- 检索增强生成(RAG):结合向量数据库(如 Qdrant)实现语义搜索,提升回答的准确性和实时性
- 内存管理:支持短期会话记忆和长期知识存储,优化多轮对话的连贯性
- 函数调用:扩展 LLM 能力,使其能调用外部 API 或业务逻辑(如查询实时天气、执行数据库操作)
- 其他…
官方文档
Github地址
langchain4j
langchain4j • Updated Aug 4, 2025
LangChain4j与Spring AI对比
核心定位差异
维度 | LangChain4j | Spring AI |
开发理念 | 模块化、链式调用,强调灵活性和扩展性 | Spring 生态原生集成,强调标准化和易用性 |
目标场景 | 复杂 AI 应用(如多步骤 RAG、自动化流程) | 快速构建基础 AI 功能(如聊天机器人) |
生态定位 | 跨框架支持(Spring、Quarkus、原生 Java) | 深度绑定 Spring Boot 和 Cloud |
适用场景
1. 优先选择 LangChain4j 的情况
- 需要处理 非结构化文档(如合同解析、知识库构建)
- 涉及 多模型协作(如先用 Claude 生成文本,再用 Stable Diffusion 生成配图)
- 企业已有 非 Spring 技术栈(如 Quarkus 或原生 Java 应用)
2. 优先选择 Spring AI 的情况
- 项目基于 Spring 生态,需快速集成 AI 能力
- 场景简单且追求 低代码实现(如客服机器人原型)
- 需要 高并发稳定性(内置限流和重试机制)
2、运行环境支持
LangChain4j提供与许多LLM提供商、嵌入/矢量存储等的集成。每个集成都有自己的maven依赖关系。
原生支持SpringBoot、Quarkus、Helidon框架
最低环境要求
SpringBoot集成:JDK17
引入依赖
<dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-spring-boot-starter</artifactId> <version>${langchain4j.version}</version> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-open-ai-spring-boot-starter</artifactId> <version>${langchain4j.version}</version> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-reactor</artifactId> <version>${langchain4j.version}</version> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-mcp</artifactId> <version>${langchain4j.version}</version> </dependency>
3、项目集成示例
3.1 SpringBoot引入使用(硬编码)
引入模型配置
# LangChain4j OpenAI langchain4j.open-ai.chat-model.base-url=https://api.deepseek.com/v1 langchain4j.open-ai.chat-model.model-name=deepseek-chat langchain4j.open-ai.chat-model.api-key=xxx langchain4j.open-ai.chat-model.log-requests=true langchain4j.open-ai.chat-model.log-responses=true langchain4j.open-ai.streaming-chat-model.base-url=https://api.deepseek.com/v1 langchain4j.open-ai.streaming-chat-model.model-name=deepseek-chat langchain4j.open-ai.streaming-chat-model.api-key=xxx langchain4j.open-ai.streaming-chat-model.log-requests=true langchain4j.open-ai.streaming-chat-model.log-responses=true
注入调用
@Resource private ChatModel chatModel; @GetMapping(value = "/api/chat/hello1") public String hello1(@RequestBody AiMessageRequest request) { return chatModel.chat(request.message()); }

3.2 包装接口
LangChian4j提供大模型接口、提示词模板、结构化输出、对话记忆、文档加载、文档分割、向量模型、向量存储等组件

LangChain4j在两个抽象层提供不同的API
- low level
ChatLanguageModel、UserMessage、AiMessage、EmbeddingStore、Embedding等,优点是你可以自由的组合使用各个组件,但是可能编码量比较高
- high level
AiServices、Tools等,优点是api封装度比较高,减少了代码的复杂性,但仍可以进行灵活的微调
Chains (legacy)
Chains的概念起源于Python的LangChain(在引入LCEL之前)。我们的想法是为每个常见用例都有一个链,比如聊天机器人、RAG等。链将多个低级组件组合在一起,并协调它们之间的交互。它们的主要问题是,如果你需要定制一些东西,它们太僵化了。LangChain4j只实现了两个链(Conversational Chain和Conversational RetrievalChain),我们目前不打算添加更多。
- AI Service
- 格式化LLM的输入
- 解析LLM的输出
- 它们还支持更高级的功能:
- Chat memory
- Tools
- RAG
您用所需的API声明性地定义一个接口,LangChain4j提供一个实现该接口的对象(代理)。您可以将AI服务视为应用程序中服务层的一个组件。
AI服务处理最常见的操作:
AI服务可用于构建有状态的聊天机器人,以促进来回交互,并自动化对LLM的每次调用都是隔离的流程。
利用High Level自定义AI Service
public interface CustomChatAiService { String chat(String message); }
// 配置模型 @Bean("openAiChatModel") public ChatModel openAiChatModel() { return OpenAiChatModel.builder() .apiKey(System.getenv("DEEPSEEK_API_KEY")) .modelName("deepseek-chat") .baseUrl("https://api.deepseek.com/v1") .build(); } // 生成AI Service: openAiChatModel @Bean public CustomChatAiService openApiChatAiService(ChatModel openAiChatModel) { return AiServices.create(CustomChatAiService.class, openAiChatModel); }
@Resource private CustomChatAiService customChatAiService; @GetMapping(value = "/api/chat/hello2") public String hello2(@RequestBody AiMessageRequest request) { return customChatAiService.chat(request.message()); }
区别:一个通过ChatModel,一个通过AiService
3.3 常用配置
打印模型请求日志
langchain4j.open-ai.chat-model.log-requests=true langchain4j.open-ai.chat-model.log-responses=true
配置监听器,请求步骤日志
@Slf4j public class MyChatModelListener implements ChatModelListener { @Override public void onRequest(ChatModelRequestContext requestContext) { log.info("onRequest(): {}", requestContext.chatRequest()); } @Override public void onResponse(ChatModelResponseContext responseContext) { log.info("onResponse(): {}", responseContext.chatResponse()); } @Override public void onError(ChatModelErrorContext errorContext) { log.info("onError(): {}", errorContext.error().getMessage()); } // @Bean // ChatModelListener chatModelListener() { // return new ChatModelListener() { // @Override // public void onRequest(ChatModelRequestContext requestContext) { // log.info("onRequest(): {}", requestContext.chatRequest()); // } // // @Override // public void onResponse(ChatModelResponseContext responseContext) { // log.info("onResponse(): {}", responseContext.chatResponse()); // } // // @Override // public void onError(ChatModelErrorContext errorContext) { // log.info("onError(): {}", errorContext.error().getMessage()); // } // }; // } }
ChatModel配置
public ChatModel openAiChatModel() { return OpenAiChatModel.builder() .apiKey(System.getenv("DEEPSEEK_API_KEY")) .modelName("deepseek-chat") .baseUrl("https://api.deepseek.com/v1") // 请求响应日志 .logRequests(true) .logResponses(true) // 监听器 .listeners(List.of(new MyChatModelListener())) // 重试 .maxRetries(2) // 超时 .timeout(Duration.ofSeconds(10)) .build(); }

3.4 视觉理解
@Bean public ChatModel openAiChatModel() { return OpenAiChatModel.builder() .apiKey(System.getenv("ALIYUN_API_KEY")) .modelName("qwen-vl-max") .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1") .logRequests(true) .logResponses(true) .build(); }
@Value("classpath:1.jpg") private org.springframework.core.io.Resource resource; @GetMapping(value = "/api/chat/image1") public String image1(@RequestBody AiMessageRequest request) throws IOException { byte[] byteArray = resource.getContentAsByteArray(); String base64Data = Base64.getEncoder().encodeToString(byteArray); UserMessage userMessage = UserMessage.from(TextContent.from(request.message()), ImageContent.from(base64Data, "image/jpg")); ChatResponse chatResponse = chatModel.chat(userMessage); return chatResponse.aiMessage().text(); }

3.5 流式输出
@Bean public StreamingChatModel openAiStreamingChatModel() { return OpenAiStreamingChatModel.builder() .apiKey(System.getenv("ALIYUN_API_KEY")) .modelName("qwen-turbo") .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1") .logRequests(true) .logResponses(true) .timeout(Duration.ofSeconds(10)) .build(); } @Bean public OpenApiChatAiService openApiChatAiService(ChatModel openAiChatModel, StreamingChatModel openAiStreamingChatModel) { return AiServices.builder(OpenApiChatAiService.class) .chatModel(openAiChatModel) .streamingChatModel(openAiStreamingChatModel) .build(); }
public interface OpenApiChatAiService { String chat(String message); Flux<String> streamingChat(String message); }
@Resource private OpenApiChatAiService openApiChatAiService; @GetMapping(value = "/api/streamingChat", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> streamingChat(@RequestBody AiMessageRequest request) { return openApiChatAiService.streamingChat(request.message()); }

3.6 记忆缓存
@Bean public OpenApiChatAiService openApiChatAiService(ChatModel openAiChatModel, StreamingChatModel openAiStreamingChatModel) { return AiServices.builder(OpenApiChatAiService.class) .chatModel(openAiChatModel) .streamingChatModel(openAiStreamingChatModel) // 自定义 chatMemoryProvider .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(100)) .build(); } public interface OpenApiChatAiService { // 带记忆 String chat(@MemoryId Long userId, @UserMessage String message); // 不带记忆 String chat(String message); }
3.7 Prompt提示词
接口上的注解三件套
- @SystemMessage 具有高优先级,能有效的指导模型的整体行为
- @UserMessage 作为关键词的占位符表明
- @V 获得@UserMessage 中的占位符
面向对象的注解
@SystemMessage(""" 你是一个聊天小助手 """) String chat(@UserMessage String message); @Data @StructuredPrompt("按照标题{{title}},根据用户的要求,写一篇{{contentLength}}长度的文章") public class ArticleInfo { private String title; private Long contentLength; }
PromptTemplate方式硬编码
3.8 FunctionCall工具回调
功能调用(Function Calling)”允许大型语言模型(LLM)在必要时调用一个或多个可用的工具,这些工具通常由开发者定义。工具可以是任何东西:网页搜索、对外部 API 的调用,或特定代码的执行等。LLM 本身不能实际调用工具;相反,它们会在响应中表达调用特定工具的意图(而不是以纯文本回应)。然后,我们应用程序应该执行这个工具,并报告工具执行的结果给模型。
@Slf4j @Component public class AiServiceRegisteredEventListener implements ApplicationListener<AiServiceRegisteredEvent> { @Override public void onApplicationEvent(AiServiceRegisteredEvent event) { Class<?> aiServiceClass = event.aiServiceClass(); List<ToolSpecification> toolSpecifications = event.toolSpecifications(); // 注册通用McpToolProvider for (int i = 0; i < toolSpecifications.size(); i++) { log.info("\u001B[33mAiServiceRegistered [{}]: [Tool-{}]: {}\u001B[0m", aiServiceClass.getSimpleName(), i + 1, toolSpecifications.get(i)); } } }

通过注解实现
@Slf4j @Component public class CalculatorToolService { @Tool(name = "sum", value = "加法:计算两个数字的和") double sum(@P("数字1") double a, @P("数字2") double b) { double r = a + b; log.info("\u001B[33mexecuting sum tool: {}+{}={}\u001B[0m", a, b, r); return r; } @Tool(name = "multiply", value = "乘法:计算两个数字的乘积") double multiply(@P("数字1") double a, @P("数字2") double b) { double r = a * b; log.info("\u001B[33mexecuting multiply tool: {}*{}={}\u001B[0m", a, b, r); return r; } @Tool(name = "subtract", value = "减法:计算两个数字的差") double subtract(@P("数字1") double a, @P("数字2") double b) { double r = a - b; log.info("\u001B[33mexecuting subtract tool: {}-{}={}\u001B[0m", a, b, r); return r; } @Tool(name = "divide", value = "除法:计算两个数字的商") double divide(@P("数字1") double a, @P("数字3") double b) { double r = a / b; log.info("\u001B[33mexecuting subtract tool: {}/{}={}\u001B[0m", a, b, r); return r; } }

硬编码构造工具
调用MCP的工具(Studio/SSE)
public class AMapHttpMcpToolProviderUtil { public static DefaultMcpClient buildAMapHttpMcpToolProvider() { HttpMcpTransport transport = new HttpMcpTransport.Builder() .sseUrl("https://mcp.amap.com/sse?key=c65039a4b6a960750238917e99a7dc0d") .timeout(Duration.ofSeconds(300)) .logRequests(true) .logResponses(true) .build(); return new DefaultMcpClient.Builder() .transport(transport) .build(); } } public class EdgeOnePageHttpMcpToolProviderUtil { public static DefaultMcpClient buildEdgeOnePageHttpMcpToolProvider() { HttpMcpTransport transport = new HttpMcpTransport.Builder() .sseUrl("https://mcp-on-edge.edgeone.site/mcp-server") .timeout(Duration.ofSeconds(300)) .logRequests(true) .logResponses(true) .build(); return new DefaultMcpClient.Builder() .transport(transport) .build(); } }
@Bean public OpenApiChatAiService openApiChatAiService(ChatModel openAiChatModel, StreamingChatModel openAiStreamingChatModel) { return AiServices.builder(OpenApiChatAiService.class) .chatModel(openAiChatModel) .streamingChatModel(openAiStreamingChatModel) .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(100)) .toolProvider(buildToolProvider()) .build(); } private McpToolProvider buildToolProvider() { return McpToolProvider.builder() .failIfOneServerFails(true) .mcpClients(AMapHttpMcpToolProviderUtil.buildAMapHttpMcpToolProvider(), EdgeOnePageHttpMcpToolProviderUtil.buildEdgeOnePageHttpMcpToolProvider()) .build(); }
