【AI】Langchain4j开发学习笔记

学习笔记:

主要关注,api-key,base-url,model-name

熟练使用LangChain4j框架进行大语言模型应用开发,熟悉@AiService注解与聊天模型配置;

掌握多LLM模型集成方案,通过工厂模式实现OpenAI、通义千问(Qwen)等模型的灵活切换;

熟悉AI聊天应用开发,了解聊天记忆(ChatMemory)机制与持久化存储方案;

环境准备

<!--前后端分离的测试工具-->

<knife4j.version>4.4.0</knife4j.version>

<!--前后端分离的测试工具-->

<dependency>

<groupId>com.github.xiaoymin</groupId>

<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>

<version>{knife4j.version}</version>

</dependency>

<!--引入SpringBoot依赖管理清单-->

<spring-boot.version>3.2.6</spring-boot.version>

<dependencyManagement>

<dependencies>

<!--引|入SpringBoot依赖管理清单-->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-dependencies</artifactId>

<version>${spring-boot.version}</version>

<type>pom</type>

<scope>import</scope>

</dependency>

</dependencies>

</dependencyManagement>

<!--基于open-ai的 langchain4j接口:ChatGPT、deepseek都是open-ai 标准下的大模型-->

<langchain4j.version>1.15.0</langchain4j.version>

(二选一)

<dependency>

<groupId>dev.langchain4j</groupId>

<artifactId>langchain4j-open-ai</artifactId>

</dependency>

注意,这里如果引用错误会导致注入失效,无法创建bean

<dependency>

<groupId>dev.langchain4j</groupId>

<artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>

</dependency>

dependencyManagement>

<dependencies>

<dependency>

<groupId>dev.langchain4j</groupId>

<artifactId>langchain4j-bom</artifactId>

<version>${langchain4j.version}</version>

<type>pom</type>

<scope>import</scope>

</dependency>

</dependencies>

</dependencyManagement>

配置:application.yml

langchain4j:

open-ai:

chat-model:

api-key: demo

base-url: http://langchain4j.dev/demo/openai/v1

model-name: gpt-4o-mini

log-request: true

log-response: true

工厂模式集成多模态大模型

创建一个工厂LLMFactoy,

包含枚举项,接受枚举类型,选择调用大模型接口,返回接口类型

创建一个接口LLMService,一个方法use

创建实现类,openai实现类,qwen实现类

声明一个私有、不可变(final)的成员变量

构造器调用大模型,重写方法(传入prompt提示,返回回答)

等等

Aiservice智能代理

解决的问题:通过定义一个接口和几行注解,就能得到一个 "带持久化记忆、绑定指定模型、可直接调用" 的 AI 对话服务

直接在这里面定义所需要解决问题的对话方法即可

<!-- langchain4j高级功能 -->

<dependency>

<groupId>dev.langchain4j</groupId>

<artifactId>langchain4j-spring-boot-starter</artifactId>

@AiService(

wiringMode = EXPLICIT ,

chatModel = "qwenChatModel",

chatMemoryProvider = "chatMemoryMySqlProvider"

)

public interface Assistant {

String chat(String userMessage);

}

  • wiringMode = EXPLICIT 手动指定要用哪个 Bean,解决多模型冲突问题
  • chatModel = "qwenChatModel" 指定AI 模型(比如通义千问)。
  • chatMemoryProvider = "..." 指定对话记忆存储器(这里是存在 MySQL 里)。

聊天记忆(优化构建过程)

过程纲要:

从导入大模型,然后遇到问题,每次对话都是新的对话,然后去了解如何存储聊天记忆。

聊天记忆ChatMemorye(存储的会话是存在内存中的)

测试大模型有没有记忆功能

聊天记忆的简单实现

使用ChatMemory实现聊天记忆

通过AlService实现聊天记忆

ChatMemory高阶功能:

方法1:

收集

userMessage:用户给的信息

aiMessage:AI返回的信息

应用

第二次会话把第一次会话的信息全部给到第二次会话。

方法2:

通过AIservice智能代理

关键在于创建 MessageWindowChatMemory 存储会话

优化(简化了代码,使得代码更加清晰)

通过注解@AiService实现管理

构建一个config组件

这里使用的方法就是上面组件构建的

然后测试成功

会话隔离机制(对于不同的会话,进行存储)

主要就是通过MessageWindowChatMemory (消息窗口对话记忆)专门用来控制对话上下文长度的核心组件,来实现

可以决定是否存在在内存还是数据库中。

1.把chatMemory改成chatMemoryProvider

chat方法加入@MemoryId(用于标识对话的id),@UserMessage标识参数

2.创建SeparateAssistantConfig类,定义chatMemoryProvider方法,保证调用大模型时给大模型打上编号。

测试

前面那些会话都是存在内存里的,是十分有问题的,那么如何把他们存到数据库里面呢
1、首先导入相关依赖

<!-- MySQL 驱动(新版推荐) -->

<dependency>

<groupId>com.mysql</groupId>

<artifactId>mysql-connector-j</artifactId>

<version>8.0.33</version>

<scope>runtime</scope>

</dependency>

<!-- Hutool 工具包 -->

<dependency>

<groupId>cn.hutool</groupId>

<artifactId>hutool-all</artifactId>

<version>5.8.30</version>

</dependency>

</dependencies>

2、配置数据库

hutool的数据库要在db.setting里面配置

3、数据库建表

CREATE TABLE chat_msg(

id INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',

uid INT NOT NULL COMMENT '用户ID',

message VARCHAR(2048) NOT NULL COMMENT '聊天消息内容',

create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',

update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',

INDEX idx_uid (uid) COMMENT '用户ID索引,便于按用户查询聊天记录'

) COMMENT = '聊天记录表';

4、自定义ChatMessageStore实现,

ChatMemoryStore 就是专门用来「持久保存对话历史」的地方。

核心方法:

getMessages(Object memoryId)

updateMessages(Object memoryId, List<ChatMessage> messages)

deleteMessage(Object memoryId)

简单来讲:

  • 实现 ChatMemoryStore 接口 :自定义PersistentChatMemoryStore 处理数据库读写,使用Hutool的 Db 工具类简化JDBC操作
  • 依赖注入 :通过Spring的 @Bean 和 @Component 管理组件
  • 框架集成 :LangChain4j的 @AiService 自动调用 ChatMemoryProvider
  • 序列化机制 :使用LangChain4j自带的序列化工具处理消息

格式序列化 : messagesToJson(messages) 将消息列表转为JSON字符串

反序列化 : messagesFromJson(json) 将JSON字符串转回消息列表

5、自定义ChatMemoryProvider注册

ChatMemoryProvider 是一个 工厂接口 ,用于为每个会话(memoryId)创建独立的 ChatMemory 实例。

它就像一个"聊天记忆生成器",为每个用户创建专属的聊天记忆空间。

LangChain4j 默认提供的是 内存存储 ( InMemoryChatMemoryStore ),但你可能需要:

  • 持久化存储 :将聊天记录保存到 MySQL、Redis 等
  • 自定义策略 :控制保留多少条消息、如何组织消息等
  • 多租户隔离 :不同用户的数据独立存储

定义SeparateMySqlAssistant 的AI助手入口

6、SeparateAssistantConfig(配置类)

基于 MySQL 持久化的 ChatMemoryProvider

提示词prompt

核心三大注解

  • @UserMessage给 AI 看的提问模板
  • @V("xxx")把你代码 / 数据库里的数据,填进模板的 xxx 位置
  • @Description → 告诉 AI 这个字段存什么、是什么意思

注解总结

  • @UserMessage给 AI 看的提问模板
  • @V("xxx")把你代码 / 数据库里的数据,填进模板的 xxx 位置
  • @Description → 告诉 AI 这个字段存什么、是什么意思
  • @P = 给 AI 看的参数说明
  • @SystemMessage 是给 AI 的 "宪法",定全局规则

方法1

@SystemMessage 是给 AI 的 "宪法",定全局规则
@UserMessage是给用户的 "指令",定单次任务,会用户跟大模型每次会话的时候加角色的限制

问题1:

但是如果更改了系统提示词之后,ai记忆会出现错误,导致不记得前面的对话了。

问提2:如果没有提示大模型是不知道日期的

通过添加提示词{{current_date}}可以实现

通过模版文件去实现系统提示词的实现

注解SystemMessage里有个方法fromResource

通过用户在数据库中的已有信息实现对用户问题的精确回答

通过读取数据库中用户相关信息已实现回复的精确。

提取用户对话中的的相关信息并返回
1、对话中的数字
2、返回对象的数据

通过对话提取所要返回的对象数据的相关信息

3、自定义枚举类

通过对话判断是什么枚举类

案例1:基于提示词构建一个专业的法律咨询助手

1、创建相关的aiservice代理

@UserMessage("根据{{legal}}相关法律,回答问题:{{question}}")

这是用户提示词模板注解,作用是:

定义大模型收到的用户消息的固定格式

{{变量名}} 是占位符,会被方法参数动态替换

模板的内容,就是用户发给大模型的 "指令"

2、创建实体类决定要存储的信息

Tool外部能力集成

解决的问题:当面对一些因为大模型本身缺陷,计算能力不足时,可以使用此方式辅助大模型计算并输出

构建一个计算类,标注@Tool,可以在标注后给定一些描述如(计算两个整数的和,返回它们的加法结果),效果更好

优化:有时候大模型并能够完全理解工具方法的意思

则需要输入的参数也进行提示标注,以助大模型更好的理解

创建相关aiservice,实现计算类调用

案例2:基于提示词+函数调用实现+聊天记忆实现电商平台智能助手

技术要点:

  • 工具链设计 :实现商品库存查询、配送时间计算等自定义 Tool,通过@Tool注解封装业务逻辑,让大模型具备结构化数据查询能力;
  • 对话记忆优化:引入对话内存模块,维护用户上下文状态,支持多轮对话中历史意图的关联理解;
  • 提示词工程:设计场景化系统提示词,引导模型精准识别电商业务意图,按规范格式生成工具调用请求,降低误调用率;
  • 业务闭环实现:构建从用户提问→意图识别→工具调用→结果返回的完整对话流程,解决了传统客服无法处理复杂业务查询的痛点,提升用户咨询体验。

RAG检索增强生成

( Retrieval-Augmented Generation )

索引:

检索:

主要步骤:

引入相关依赖:

加载文档

langchain4j提供了加载解析文档的工具

文档加载器loader**= 读取文件(来源)**

文档解析器parser = 解析内容(格式)

分割文档(Splitter)

文本向量化(Embedding)

原始分割文档和文本向量化数据进行存储(EmbeddingStore)

自定义文档分割器,以免太大或太小
  • 重叠切块:块与块重叠 10%~20%,防止语义被一刀切断
  • 按语义切:优先按标题、段落、句号分割,别硬按字数砍
  • 小文档偏偏小切块 ,长篇大文偏偏大切块

自定义切割的实际意义在于:

  • 切太小碎话残缺,切太大杂讯干扰,按完整语义切中等块 + 少量重叠,RAG 效果最稳。
token计算
接入阿里百炼embedding向量模型

目的在于: 维度越高 → 语义细节越丰富 → 相似文本更容易被精准找到

向量数据库

pinecone

全球标杆,RAG 体验最好,但贵、数据出境敏感

1、导入相关依赖

强制指定Protobuf版本,解决Pinecone/Milvus向量数据库依赖版本冲突

pcsk_3r3hre_Pc7ejxiXQjwywean2s3F76Sa2yXJ9NVqQPNJim5ABnaaDhGkMX1Q3Fnvmxt7vVU

2、自定义向量数据库的配置

测试,把建立好索引的向量存入向量数据库中

3、通过提问去向量数据库中检索相似度最高的向量

意义:使得ai在回答问题的时候能够更加精准,更能够挖掘用户的意向,使得业务开展更加的顺利。

案例:仿京东外卖客服

解决的问题:使得客服回答能够更加贴合业务,而不会答非所问,充分利用公司所拥有的数据,提高业务效率

第一部分:

自定义一个文本分割器

获取文档路径

创建文档解析器

加载并解析文档

创建文档分割器

分割文档

向量化分割好的文档

将向量化的数据和分割的文本数据添加到向量存储空间中

第二部分:

检索实现

问题转换成向量数据

创建搜索向量的请求对象EmbeddingSearchRequest

向量数据库进行检索embeddingStore.search

获取检索结果(取出第0索引)search.matches().get(0)

第三部分:(RAG+大模型)

为了使得客服在回答例如快递发货什么时候能够到达时,不是前篇一律的2-7个工作日,我们就需要通过tool 让 AI 能调用外部功能实现结合实际时间推算送达日期。

1、创建AIService ,基于 LangChain4j 框架定义的一个 AI 服务接口,用来快速创建一个「带记忆、带工具、可对话」的京东客服机器人。

  • 之前的 AgentConfig 是配置「知识库检索器」
  • 而这个 JdAiService 是把这些东西整合起来的「入口」

你可以这样理解:

JdAiService 是 AI 的「大脑中枢」,它会根据用户的问题,自动决定要不要调用工具(比如日期计算)、要不要检索知识库,然后生成回答。

2、创建工具类DataCalculator, 给 AI 智能体调用的工具(Tool)

3、 创建并配置一个 ContentRetriever**(内容检索器)** Bean,给你的 RAG 智能体用

  • 这是 RAG(检索增强生成)流程里的关键一环
  • 后续你的 AiService 智能体,可以直接注入这个 ContentRetriever,调用它来:
    1. 把用户问题转成向量
    2. 去向量库找最相似的知识库片段
    3. 把片段作为上下文喂给大模型,生成基于知识库的回答

简单说,这个类就是给 AI 装好了 "从知识库找答案" 的能力。

4、测试成功

实现流程图

所遇到的问题

1、分段需要注意文本书写问题,否则就是这样

问题在于

是文本里藏了看不见的空格!

导致前面的切割方式出现了差错,因此修改成匹配:任意空白 + 2 次以上换行 + 任意空白 从而实现正确的切割

2、关于bean的默认名称

就是一个大小写问题

  • 类:DataCalculator
  • 默认 bean 名:dataCalculator
  • 你写的:datecalculator不匹配 → 报错

3、tool工具类老是调用不上

可能得原因以及解决办法

大模型认为不需要调用工具,问题可能不需要日期计算

工具描述不够清晰,改进 @Tool 描述,让大模型知道何时使用

参数类型解析问题 改用基本类型 int'

修改前

修改后

相关推荐
LuminousCPP1 小时前
数据结构 - 单链表第一篇:单链表基础操作
c语言·数据结构·经验分享·笔记·学习
沪漂阿龙1 小时前
LangChain 系列:Structured Output结构化输出与源码解析
java·人工智能·架构·langchain
她的男孩1 小时前
AI 自动化编写 SQL 脚本,更要守住 Flyway 版本管理的防线
人工智能·后端
wubba lubba dub dub7501 小时前
【无标题】
学习
虎符饼干1 小时前
内容SEO落地细则,依托质量撬动搜索自然流量
笔记
老金带你玩AI1 小时前
GLM-5.2来了,Claude Code百万上下文怎么配?
人工智能
AI_yangxi1 小时前
短视频矩阵系统供应商
大数据·人工智能·矩阵
Asize1 小时前
Prompt 驱动 NLP:从 ES6 模块化到文本推理实战
javascript·人工智能·机器学习
harykali1 小时前
Datawhale Hello-ROCm学习:初探Gemma4 #AMDev #Datawhale
人工智能·llm