当前位置: 首页 > news >正文

LangChain4j的初步学习【逐步添加中】

LangChain4j 是一个专为 Java 开发者设计的、受 Python LangChain 启发而构建的框架。它的核心目标是简化大型语言模型(LLM)的集成过程,让开发者能够轻松地将强大的 AI 功能构建到自己的 Java 应用程序中。

1. 什么是 LangChain4j?

  • 定义:一个用于 Java 应用程序的 AI 集成框架。

  • 灵感来源:Python 的 LangChain 项目,但并非其直接端口,而是根据 Java 生态的特点(如强类型、丰富的企业级库)重新设计和实现。

  • 核心价值:提供一套抽象、工具和预构建组件,将 LLM(如 OpenAI 的 GPT-4, Google 的 Gemini, 本地的 Mixtral)与外部数据和系统连接起来,从而创建功能强大、上下文感知的 AI 应用。

  • 设计哲学约定优于配置,旨在通过简洁、富有表现力且遵循 Java 习惯的 API 来降低开发复杂度。

LangChain4j 中文文档https://docs.langchain4j.info/

2. 核心概念与功能

LangChain4j 的核心是围绕一系列“概念”构建的,这些概念是构建复杂 LLM 应用的基础模块。

  1. Model(模型)

    • 抽象了与各种 LLM 提供商的交互。你可以通过更改配置轻松切换模型,而无需重写业务逻辑。

    • 支持提供商:OpenAI, Azure OpenAI, Google Gemini, Anthropic Claude, Hugging Face, Ollama(本地模型), LocalAI 等。

  2. ChatMemory(聊天记忆)

    • 管理对话的上下文历史。这是实现多轮对话的关键。

    • 类型:支持多种存储方式,如简单的内存存储、基于 Redis 或数据库的持久化存储,以确保应用重启后对话状态不丢失。

  3. Tools(工具)

    • 允许 LLM 按需执行外部动作或获取外部数据。这是将 LLM 能力与现有系统和 API 连接起来的超级能力。

    • 示例:一个“获取天气”的工具,一个“执行数据库查询”的工具,一个“调用内部 HTTP API”的工具。

    • LLM 可以根据用户请求决定调用哪个工具,并将工具执行结果融入其回复中。

  4. PromptTemplates(提示模板)

    • 帮助动态生成高质量的提示(Prompt)。它将用户输入、上下文、对话历史等变量注入到预定义的模板中,形成最终发送给 LLM 的指令。

    • 好处:提高提示词的一致性、可维护性和复用性。

  5. OutputParsers(输出解析器)

    • LLM 的输出是非结构化的文本。OutputParsers 负责将这些文本解析成结构化、可编程的对象(如 Java 对象、JSON 等)。

    • 示例:让 LLM 生成一个 Person 对象的 JSON 字符串,然后用 JsonOutputParser 将其自动反序列化为 Java 的 Person 类实例。

  6. Embeddings(嵌入) & VectorStores(向量数据库)

    • Embeddings:将文本(如文档、问答)转换为数值向量( embeddings)。

    • VectorStores:存储和检索这些向量。通过计算向量之间的相似度,可以实现语义搜索。

    • 应用场景检索增强生成(RAG)。这是 LangChain4j 最强大的功能之一。它允许你将自己的数据(PDF、Word、数据库)转换为向量并存入向量数据库。当用户提问时,系统会先从你的数据中检索最相关的信息,然后将这些信息作为上下文提供给 LLM,从而让 LLM 生成基于你自身知识的、更准确的回答,避免“幻觉”。

  7. Chains(链)

    • 将上述多个组件按顺序链接起来,形成一个复杂的工作流。一个链的输出可以作为下一个链的输入。

    • 示例RetrievalChain 就是一个预定义的链,它自动完成了“检索相关文档 -> 构建提示 -> 调用模型 -> 解析输出”的整个 RAG 流程。

  8. AI Services(AI 服务)

    • 这是 LangChain4j 中一个非常强大且具有创新性的抽象。它允许你通过定义一个 Java 接口 来创建一个 AI 应用。

    • 你可以在接口方法上使用注解(如 @SystemMessage@UserMessage@MemoryId@V)来定义行为、提示和记忆。

    • 框架会在运行时自动生成这个接口的代理实现,处理所有与 LLM 的交互、工具调用、记忆管理等复杂细节。这让代码变得极其简洁和声明式。

3.代码初步

相关背景知识

开始之前需要准备一个空的SpringBoot项目。

相关依赖

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-spring-boot-starter</artifactId><version>1.4.0-beta10</version></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai-spring-boot-starter</artifactId><version>1.3.0-beta9</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.5.18</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai</artifactId><version>1.3.0</version></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-reactor</artifactId><version>1.0.1-beta6</version></dependency>

(1)入门程序

import dev.langchain4j.model.openai.OpenAiChatModel;public class App
{public static void main( String[] args ){OpenAiChatModel chatModel = OpenAiChatModel.builder().baseUrl("your_baseUrl")//这个需要去调用的模型的提供者的文档查看.modelName("your_modelName")//这是你调用的模型名字.apiKey("your_apiKey")//这是你的api秘钥.logRequests(true)//打印请求.logResponses(true)//打印回复.build();String result = chatModel.chat("你是谁?");//向大模型请求的问题System.out.println(result);}
}

(2)配置yml文件

langchain4j:open-ai:chat-model: #普通调用base-url: your_baseUrlapi-key: your_apiKeymodel-name: your_modelNamelog-requests: true #开启请求打印log-responses: true #开启回复打印streaming-chat-model: #流式调用base-url: your_baseUrlapi-key: your_apiKeymodel-name: your_modelNamelog-requests: true #开启请求打印log-responses: true #开启回复打印
logging:level:dev.langchain4j: debug

(3)入门接口

a.创建service相关类
import org.springframework.stereotype.Service;@Service
public interface ConsultantService {String chat(String message);
}
b.创建controller相关类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class ChatController {@Autowiredprivate ConsultantService consultantService;@RequestMapping("/chat")public String chat (String message){return consultantService.chat(message);}
}
c.创建config相关类
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class CommonConfig {@Autowiredprivate OpenAiChatModel model;//通过你的yml文件自动创建的@Beanpublic ConsultantService consultantService(){return AiServices.builder(ConsultantService.class).chatModel(model).build();}
}
d.使用Apifox测试

e.声明式使用

不使用config类,直接在service相关方法上加上注解@AiService@AiService 是来自 LangChain4j 框架的一个注解,用于将一个接口自动装配为一个 AI 服务(即代理实现),使得你可以像调用普通方法一样调用 AI 模型。

import dev.langchain4j.service.spring.AiService;
import dev.langchain4j.service.spring.AiServiceWiringMode;@AiService(wiringMode = AiServiceWiringMode.EXPLICIT,//自动装配chatModel = "openAiChatModel"//模型Bean名,这里是LangChain4j通过yml文件自动创建的默认名字
)
public interface ConsultantService {String chat(String message);
}

wiringMode = AiServiceWiringMode.EXPLICIT

作用:

这个参数控制 Spring 如何为 @AiService 接口创建实现类(代理)。

可选值:

  • AiServiceWiringMode.EXPLICIT(显式装配)

  • AiServiceWiringMode.AUTO(自动装配)

EXPLICIT 的含义:

  • 表示你需要 手动明确地配置 所使用的 ChatModelTokenizer 等组件。

  • Spring 不会自动从上下文中查找默认的 ChatModel

  • 你必须通过 chatModel = "..." 显式指定 Spring 容器中某个 bean 的名称。

AUTO的含义:

  • LangChain4j 会尝试自动从 Spring 容器中查找 唯一的 ChatModel bean

  • 如果有多个 ChatModel bean,会报错。

  • 不需要写 chatModel = "..."

chatModel = "openAiChatModel"

作用:

指定该 AI 服务应使用 Spring 容器中名为 "openAiChatModel"ChatModel bean。

openAiChatModel 是什么?

它是 LangChain4j 为 OpenAI 类型模型 自动创建的默认 Bean 名称

当你在 application.yml 中配置了相关参数(base-url、api-key、model-name),

LangChain4j 的自动配置(LangChain4jAutoConfiguration)就会:

  • 创建一个 OpenAiChatModel 实例(或兼容 OpenAI 的客户端)。

  • 使用你提供的 base-urlapi-keymodel-name 等参数。

  • 将这个实例注册为 Spring 容器中的一个 Bean,名字叫 openAiChatModel

如果想使用多个model,你可以不使用LangChain4j自动创建Bean,在config类中写类似如下代码:

@Bean("name")//自己取个名字
public ChatModel qwenChatModel() {return OpenAiChatModel.builder().baseUrl("your_base-url").apiKey(System.getenv("your_API_KEY")).modelName("your_model_name").build();
}

在注解处写成对应名字即可使用对应模型。

(4)流式调用

a.修改service相关类
import dev.langchain4j.service.spring.AiService;
import dev.langchain4j.service.spring.AiServiceWiringMode;
import reactor.core.publisher.Flux;@AiService(wiringMode = AiServiceWiringMode.EXPLICIT,chatModel = "openAiChatModel",streamingChatModel = "openAiStreamingChatModel"//h和上面类似,是Langchain4j自动注册的Bean
)
public interface ConsultantService {String chat(String message);Flux<String> fluxChat(String message);
}
b.修改新的接口
    @RequestMapping("/fluxChat")public Flux<String> fluxChat(String message){return consultantService.fluxChat(message);}

(5)会话功能

a.消息来源和意图

@SystemMessage@UserMessage@AssistantMessageLangChain4j 提供的用于在 @AiService 接口中定义对话角色(message roles)的注解。它们的作用是帮助你结构化地组织对话内容,让 AI 模型清楚每条消息的来源和意图。

什么是消息角色(Message Roles)?

在大模型(如 GPT、Qwen、Claude 等)中,对话通常由三种角色构成:

角色

说明

system

系统提示词,用于设定 AI 的行为、身份、语气等(例如:“你是一个专业的客服助手”)

user

用户输入的问题或请求

assistant

AI 的回复(历史对话中的)

LangChain4j 使用这些注解让你在接口方法中清晰地标注每段文本属于哪个角色。

@SystemMessage

用于定义 系统提示词(System Prompt),通常只在对话开始时设置一次。

    @SystemMessage("你是一个性格活泼有趣的代码助手,只能回答用户关于代码的相关问题,其他的都需要拒绝。")String chat(String message);
  • 效果:每次调用 chat(...) 时,都会自动加上这条系统消息。

  • 常用于:设定 AI 的角色、语气、输出格式等。

也可以通过参数从资源文件夹的文件读取消息

    @SystemMessage(fromResource="promptWord/system.txt")String chat(String message);
@UserMessage

标注哪一部分是 用户输入的消息

  • {{it}} 是模板变量,表示参数值(当只有一个参数时可用 {{it}},只能用it!!!)。

  • 如果有多个参数,可以用@V注解。

    @UserMessage("我想说:{{it}}。")Flux<String> fluxChat(String message);@UserMessage("请你比较{{msg1}}和{{msg2}}的区别。")Flux<String> fluxChat(@V("msg1")String message1,@V("msg2") String message2);
@AssistantMessage

用于提供 AI 之前的回复,模拟历史对话,实现多轮对话上下文。

@SystemMessage("你是一个旅游顾问")
@UserMessage("推荐一些国内适合夏天旅游的地方")
@AssistantMessage("你可以考虑去云南大理、贵州贵阳或青海湖,气候凉爽。")
@UserMessage("有没有更适合带小孩去的?")
String recommendWithMemory();
  • 这种写法相当于“硬编码”了一段对话历史。

  • 适用于测试或固定流程的引导对话。

  • 实际项目中更常用的是通过 Memory(如 ChatMemory)来动态管理历史。

b.会话记忆入门

大模型是不具备记忆能力的,要想让大模型记住之前聊天的内容,唯一的办法就是把之前聊天的内容与新的提示词一起发给大模型。

修改config类
@Configuration
public class CommonConfig {/*@Autowiredprivate OpenAiChatModel model;@Beanpublic ConsultantService consultantService(){return AiServices.builder(ConsultantService.class).chatModel(model).build();}*/@Beanpublic ChatMemory chatMemory(){//这里的10是最大储存消息数return MessageWindowChatMemory.builder().maxMessages(10).build();}
}
修改service类
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;
import dev.langchain4j.service.spring.AiService;
import dev.langchain4j.service.spring.AiServiceWiringMode;
import reactor.core.publisher.Flux;@AiService(wiringMode = AiServiceWiringMode.EXPLICIT,chatModel = "openAiChatModel",streamingChatModel = "openAiStreamingChatModel",chatMemory = "chatMemory"//自动找的chatMemory的bean
)
public interface ConsultantService {@SystemMessage("你是一个性格活泼有趣的代码助手,只能回答用户关于代码的相关问题,其他的都需要拒绝。")String chat(String message);@UserMessage("我想说:{{it}}。")Flux<String> fluxChat(String message);@UserMessage("请你比较{{msg1}}和{{msg2}}的区别。")Flux<String> fluxChat(@V("msg1")String message1,@V("msg2") String message2);
}
c.会话记忆隔离

刚才我们做的会话记忆,所有会话使用的是同一个记忆存储对象,因此不同会话之间的记忆并没有做到隔离。

在config中创建记忆对象提供者
    @Beanpublic ChatMemoryProvider chatMemoryProvider(){return new ChatMemoryProvider() {@Overridepublic ChatMemory get(Object memoryId) {return MessageWindowChatMemory.builder().id(memoryId).maxMessages(10).build();}};}
修改service类的注解参数
@AiService(wiringMode = AiServiceWiringMode.EXPLICIT,chatModel = "openAiChatModel",streamingChatModel = "openAiStreamingChatModel",/*chatMemory = "chatMemory"*/chatMemoryProvider = "chatMemoryProvider"
)
修改service类相关方法

@UserMessage:显式标注哪个参数是用户输入

@MemoryId:区分不同用户的对话记忆(会话隔离)

如果不加 @UserMessage 会怎样?

LangChain4j 会尝试自动推断哪个参数是用户输入。
但如果参数多于一个(比如还有 @MemoryId),它就无法确定,可能导致错误或忽略参数

 所以:当有多个参数时,必须用 @UserMessage 明确标注用户输入参数

public interface ConsultantService {@SystemMessage("你是一个性格活泼有趣的代码助手,只能回答用户关于代码的相关问题,其他的都需要拒绝。")String chat(@MemoryId String memoryId, @UserMessage String message);@UserMessage("我想说:{{it}}。")Flux<String> fluxChat(@MemoryId String memoryId, @UserMessage String message);@UserMessage("请你比较{{msg1}}和{{msg2}}的区别。")Flux<String> fluxChat(@MemoryId String memoryId, @V("msg1") String message1, @V("msg2") String message2);
}
修改接口
import com.learning.learning.service.ConsultantService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;@RestController
public class ChatController {@Autowiredprivate ConsultantService consultantService;@RequestMapping("/chat")public String chat(String memoryId,String message) {return consultantService.chat(memoryId,message);}@RequestMapping("/fluxChat")public Flux<String> fluxChat(String memoryId, String message) {return consultantService.fluxChat(memoryId, message);}@RequestMapping("/fluxChat1")public Flux<String> fluxChat1(String memoryId, String message1, String message2) {return consultantService.fluxChat(memoryId, message1, message2);}
}

http://www.xdnf.cn/news/1459387.html

相关文章:

  • 强化学习DQN解决Cart_Pole问题
  • claude code route 使用教程|命令大全
  • linux中的awk使用详解
  • 深度解读《实施“人工智能+”行动的意见》:一场由场景、数据与价值链共同定义的产业升级
  • 【8】C#上位机---泛型、委托delegate与多线程Task
  • 2025年代理IP服务深度评测:三大平台横评,谁是最强业务助手?
  • 检查数据集格式(77)
  • 计算机二级C语言操作题(填空、修改、设计题)——真题库(16)附解析答案
  • C++基础——模板进阶
  • 【C++题解】关联容器
  • Linux的权限详解
  • 一次死锁的排查
  • 激活函数:神经网络的“灵魂开关”
  • 阅读论文神奇Zotero下载安装教程以及划词翻译(Translate for Zotero)的配置
  • 动态内存管理柔性数组
  • Vue 中绑定样式的几种方式
  • Process Explorer 学习笔记(第三章3.1.1):度量 CPU 的使用情况详解
  • 【Unity知识分享】Unity接入dll调用Window系统接口
  • 无限时长视频生成新突破!复旦联合微软、腾讯混元推出StableAvatar,仅需1张照片+1段音频实现真人说话视频
  • hutool的EnumUtil工具类实践【持续更新】
  • 揭秘23种设计模式的艺术与技巧之行为型
  • 美联储计划召开稳定币和代币化创新会议
  • 大数据框架Doris全面解析
  • 期权平仓后权利金去哪了?
  • 基于STM32的智能家居语音控制系统设计
  • Pycharm终端pip install的包都在C:\Users\\AppData\Roaming\Python\解决办法
  • 手写Spring框架
  • 前端跨域终极指南:3 种优雅解决方案 + 可运行 Demo
  • 解密注意力机制:为何它能在Transformer中实现高效并行计算?
  • STM32G4 速度环开环,电流环闭环 IF模式建模