主流 AI 框架 + RAG 落地实战

SpringAI 框架完整使用

SpringAI 基础定位

通俗定位

SpringAI 是 Spring 官方推出的 AI 开发框架,专门简化 Java 对接各大公有大模型的开发流程,底层自动封装 HTTP 网络请求、请求报文组装、流式数据解析、异常处理,不用手写 OpenFeign、WebClient 那一套重复代码。

生活化类比

原生 OpenFeign/WebClient 调用大模型 = 自己买菜、洗切、调味、开火全套手动操作,每换一道菜都要重复工序;

SpringAI = 预制料理包,调料、加工步骤全部封装完毕,你只需要告知需求,直接出成品,大幅减少重复开发工作。

SpringAI 基础配置与核心对象 ChatClient

项目依赖引入

只需要引入 SpringAI starter,无需手动定义请求实体、请求头拦截器:

XML 复制代码
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-alibaba-starter</artifactId>
</dependency>

yml 统一配置(全局管理密钥、模型地址)

所有模型信息写配置文件,不用硬编码、不用自定义拦截器携带密钥:

XML 复制代码
spring:
  ai:
    dashscope:
      api-key: sk-xxxx你的密钥
      base-url: https://dashscope.aliyuncs.com

核心对象:ChatClient

框架核心入口对象,自动读取 yml 配置,内置同步、流式两种调用能力,直接注入使用:

java 复制代码
@Autowired
private ChatClient chatClient;
ChatClient 是什么?
通俗定义

ChatClient 是 SpringAI 对外提供最高阶、业务层首选 的对话客户端门面,底层封装 ChatModel,把 HTTP 请求、报文组装、流式解析、异常、重试、提示词管理全部屏蔽,提供链式流畅 API,专门用来和各大 LLM 对话(通义千问、GLM、DeepSeek 等)。

生活化类比

原生 OpenFeign / WebClient = 自己买菜、洗菜、切肉、调酱汁、开火全套手动操作,每换一个菜品就要重写全套代码;

ChatClient = 全自动智能料理机:只需要告诉机器你想要什么菜(用户需求),机器自动处理食材、火候、调味,不用管底层机械运转(HTTP 通信、模型 API 差异)。

整体分层流程图
ChatClient 和底层 ChatModel 区别对比表
对象 层级 使用场景 优点 缺点
ChatModel 底层 API 深度自定义、自研复杂中间件 底层完全可控 需要手动拼接 Prompt、处理分段、异常,代码量大
ChatClient 上层门面(推荐) 90% 业务开发(短信生成、问答) 链式简洁、内置提示词 / 流式 / 重试 / Advisor 拦截 底层网络改造灵活性略低
ChatClient 两种创建方式(项目标准写法)

SpringAI 自动装配 ChatClient.Builder优先用 Builder 构建,不用手动 new。

方式 1:Controller/Service 构造注入(最简单测试)

Spring 自动注入 Builder,调用.build()生成实例

java 复制代码
@RestController
public class AiSmsController {
    // 自动注入Builder原型Bean
    private final ChatClient chatClient;

    // 构造器注入
    public AiSmsController(ChatClient.Builder chatClientBuilder) {
        // 构建客户端
        this.chatClient = chatClient.build();
    }
}
方式 2:配置类全局统一创建(生产推荐,全局系统提示词)

统一设置项目固定角色(合规短信专员),所有调用自动携带系统指令,不用每次写 system ()

java 复制代码
@Configuration
public class AiChatConfig {
    @Bean
    public ChatClient smsChatClient(ChatClient.Builder builder) {
        return builder
                // 全局固定系统提示词:只写一次,全局生效
                .defaultSystem("你是运营商合规短信文案专员,输出40-60字,禁用极限词")
                // 全局默认模型参数:降低随机性
                .defaultOptions(OpenAiChatOptions.builder().temperature(0.2).build())
                .build();
    }
}

创建核心要点

  1. ChatClient.Builder 是原型 Bean(每次注入都是新 Builder),可生成多个不同配置的 ChatClient;
  2. defaultSystem() 全局系统提示词,所有对话自动带上,减少重复编码;
  3. defaultOptions() 统一全局 temperature、model 名称。
ChatClient 两大核心调用模式(call 同步 /stream 流式)
同步调用 .call()(后台批量、管理后台)

一次性接收完整文本,阻塞执行,返回字符串。

完整短信生成示例

java 复制代码
@GetMapping("/sms/sync")
public String syncGenSms(String activity) {
    return chatClient.prompt()
            // 用户单次需求
            .user("写两条"+activity+"家电营销短信")
            // 同步发送,等待完整结果
            .call()
            // 提取AI返回纯文本
            .content();
}
流式调用 .stream()(前端实时打字预览 SSE)

返回 Flux<String>,分段逐字推送,前端实现打字效果,非阻塞。

SSE 流式接口标准代码

java 复制代码
// 必须声明媒体类型text/event-stream
@GetMapping(value = "/sms/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamSms(String activity) {
    return chatClient.prompt()
            .user("写两条"+activity+"家电营销短信")
            // 开启分段流式输出
            .stream()
            .content();
}

两种调用模式对比表格

调用方法 返回值 底层特性 适用短信项目场景 用户体验
call() String 同步阻塞,一次性全量返回 定时批量生成、后台管理页 页面长时间空白加载
stream() Flux<String> 异步非阻塞,分段推送 前端 AI 实时预览页面 逐字弹出,无空白等待
关键 API 流程文字框架
java 复制代码
chatClient.prompt()  // 开启一次对话构造
    .system(临时角色) // 单次覆盖全局system(可选)
    .user(用户需求)   // 业务提问
    .options(单次参数) // 临时覆盖全局temperature
    .call() / .stream() // 选择同步/流式
    .content() // 提取纯文本
ChatClient 五大核心内置能力
1. 统一管理提示词(全局 + 单次)
  • 全局defaultSystem():项目通用合规规则,所有对话自带;
  • 单次.system():仅当前请求临时修改角色,互不干扰;
  • 支持 PromptTemplate 模板,可带占位符动态填充活动名称。
2. 原生流式 SSE 封装,不用手写 WebClient

底层自动封装 WebClient、Flux 分段解析,无需处理 SSE chunk 分割逻辑,几行代码实现实时预览。

3. 内置全局超时、重试机制

yml 统一配置超时、重试次数,Builder 可自定义重试 Advisor,不用手动整合 Spring Retry。

4. Advisor 拦截器体系(扩展能力)

内置多种拦截器,开箱即用:

  • ChatMemoryAdvisor:自动维护多轮对话上下文;
  • PromptAdvisor:统一处理提示词模板;
  • LoggingAdvisor:打印输入输出日志;
  • 自定义 Advisor:做输出校验、Prompt 注入过滤。
5. 多模型无缝切换

仅修改 yml 的api-keybase-url,业务ChatClient代码一行不用改动,适配通义、GLM、DeepSeek 等任意厂商模型。

ChatClient 处理返回结构(返回对象说明)
  1. call() 返回 ChatResponseSpec
    • content():直接拿纯文本(日常短信生成最常用);
    • chatResponse():完整响应对象,可读取 token 消耗、finish_reason、错误码;
  2. stream() 返回 StreamResponseSpec
    • content():分段字符串 Flux,逐字返回;
ChatClient 生产环境优势总结
  1. 屏蔽各家大模型 API 差异,一套代码切换多款模型;
  2. 链式 API 极简,省去手动封装请求头、JSON 实体、WebClient 流式代码;
  3. 全局统一配置系统提示词、temperature、超时、重试;
  4. 内置 Advisor 拦截体系,轻松实现多轮记忆、日志、内容校验、安全过滤;
  5. 同步 / 流式两套 API 统一入口,项目同步批量、前端预览两套场景全覆盖。
ChatClient 核心常用方法 / 字段功能对照表
一、ChatClient.Builder 构建器核心方法(全局配置,创建客户端时使用)
方法名 作用说明 项目使用场景(短信 AI 平台)
defaultSystem(String text) 设置全局固定系统提示词,所有对话自动携带,全局永久生效 统一规定:合规短信专员、禁用极限词、字数限制,不用每次调用重复写
defaultOptions(ChatOptions) 全局统一模型参数(temperature、topP、模型名称) 全局设置 temperature=0.2,保证文案风格稳定统一
defaultAdvisors(Advisor...) 全局注册拦截器(日志、记忆、安全过滤) 全局开启日志打印、Prompt 注入过滤、多轮对话记忆
build() 根据前面配置,生成最终可用 ChatClient 实例 配置完成后执行,交给 Spring 注入业务使用
二、chatClient.prompt () 对话构建链方法(单次请求内链式调用)
链式方法 作用说明 使用示例
prompt() 开启一次全新对话,返回 PromptSpec 对象,所有单次配置从这里开始 chatClient.prompt()
system(String text) 单次临时系统提示词,仅覆盖当前这一次请求,不影响全局 .system ("只生成家电活动短信,50 字左右")
user(String text) 传入用户业务提问、活动需求,必写方法 .user ("写两条 618 冰箱促销短信")
options(ChatOptions) 单次请求临时覆盖模型参数,优先级高于全局 defaultOptions .options(OpenAiChatOptions.builder().temperature(0.5).build())
三、发起请求核心执行方法(二选一:同步 / 流式)
执行方法 返回对象 功能作用 适用场景
call() ChatResponseSpec 同步阻塞调用,一次性接收完整 AI 返回内容 后台批量生成短信、管理后台同步接口
stream() StreamResponseSpec 异步分段流式调用,返回 Flux 分段数据流 前端实时打字预览 SSE 页面
四、响应解析通用方法(call/stream 后提取数据)
方法 归属对象 作用
content() ChatResponseSpec / StreamResponseSpec 直接提取 AI 生成的纯文本(日常开发最常用)
chatResponse() ChatResponseSpec 获取完整原始响应对象,可读取 token 消耗、结束标识、错误信息
五、配套常用配置字段(yml 全局配置字段)
yml 配置字段 含义 业务价值
spring.ai.dashscope.api-key 大模型鉴权密钥 统一管理密钥,不硬编码在代码中
spring.ai.dashscope.base-url 模型 API 基础地址 切换测试 / 生产环境只改配置,无需改代码
spring.ai.retry.max-attempts 最大重试次数 5xx 服务故障自动重试,减少人工报错
spring.ai.retry.timeout 请求全局超时时间 防止大模型响应过慢阻塞服务线程
六、补充关键字段 / 对象说明
对象 / 字段名称 类型 核心作用
ChatClient.Builder 构建器类 模板化统一创建带全局配置的 ChatClient,单项目可创建多个不同配置客户端
ChatOptions 参数实体 存放 temperature、model 名称、top_p 等模型生成控制参数
Advisor 拦截器接口 切面扩展,可在请求前过滤恶意 Prompt、响应后校验文案合规性
Flux<String> 流式返回类型 WebFlux 数据流,用于 SSE 逐字推送给前端
简易执行流程图

引入依赖 → yml 配置模型密钥地址 → 自动装配 ChatClient → 调用同步 / 流式接口生成文案

SpringAI 两种调用模式:同步问答 + SSE 流式问答

调用模式 底层特性 适用业务场景(你的短信项目) 用户体验
同步问答 一次性接收完整返回数据,同步阻塞 后台批量生成短信、运营管理后台单次生成文案 页面等待全部生成完成才展示,会有空白加载
SSE 流式问答 分段推送数据,支持逐字输出 前端 AI 文案实时预览页面 文字逐行弹出打字效果,无长时间空白

SpringAI 五大核心功能

五大核心功能:

  1. 单次普通对话同步调用
  2. SSE 流式问答接口开发
  3. 可复用提示词模板封装
  4. 全局超时、重试、统一异常捕获
  5. 接口降级兜底逻辑开发

整体类比:原生 OpenFeign/WebClient 是手工做饭;SpringAI 五大功能等于一套全自动厨房五件套,覆盖业务所有 AI 生成场景。

普通单次同步对话调用

通俗定义

基于 ChatClient.call () 实现一次性完整获取 AI 返回文本,同步阻塞执行,无需处理分段数据流,适合后台批量、管理后台同步生成文案。

生活化类比

点外卖一次性送餐,商家全部做好打包统一送达,你全程等待完整商品。

核心作用
  1. 一行链式代码完成对话,不用手动组装 JSON 请求体、请求头;
  2. 自动拼接 system 系统提示词 + user 用户需求,底层封装 messages 数组;
  3. 直接提取纯文本 content,省去手动解析返回 JSON。
短信项目代码示例
java 复制代码
@RestController
@RequestMapping("/ai/sms")
public class AiSmsController {
    private final ChatClient chatClient;
    // 构造注入全局配置好的ChatClient
    public AiSmsController(ChatClient.Builder builder) {
        this.chatClient = builder
                .defaultSystem("你是合规短信文案专员,40-60字,禁用极限词")
                .build();
    }

    // 同步单次对话接口
    @GetMapping("/sync")
    public String syncGen(String activityName) {
        return chatClient.prompt()
                .user("生成2条"+activityName+"营销短信")
                .call() // 同步调用
                .content(); // 直接拿文案文本
    }
}
适用场景
  1. 定时任务批量生成短信模板;
  2. 运营后台同步生成文案;
  3. 不需要实时打字预览的页面。
优缺点

优点:代码极简、调试简单、无响应式学习成本;

缺点:同步阻塞,大批量并发会占用 Tomcat 线程。

SSE 流式问答接口开发

通俗定义

基于 ChatClient.stream () 返回 Flux<String>分段数据流,实现 SSE 服务端单向推送,前端逐字打字预览效果,SpringAI 底层自动封装 WebClient、分段解析逻辑。

生活化类比

奶茶分次出杯,做好一小杯立刻递给顾客,不用等全部制作完成。

核心作用
  1. 内置流式协议解析,不用手写 WebClient 响应式 Flux 代码;
  2. 非阻塞,不长期占用 Tomcat 工作线程,高并发预览不卡顿;
  3. 天然适配 text/event-stream SSE 协议,前端开箱即用。
短信项目代码示例
java 复制代码
// produces指定SSE媒体类型
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamSms(String activityName) {
    return chatClient.prompt()
            .user("生成2条夏季空调促销短信")
            .stream() // 开启流式分段输出
            .content();
}
适用场景

前端 AI 文案实时预览页面,消除页面长时间空白加载。

关键优势对比原生 WebClient

原生 WebClient 需要手动配置连接池、超时、分段分割逻辑;SpringAI 一行 stream () 封装全部底层细节。

自定义可复用提示词模板封装

通俗定义

SpringAI 提供 PromptTemplate 统一抽取固定系统角色、合规约束、通用句式,支持占位符动态填充业务参数,全局多处业务复用,减少重复 Prompt 硬编码。

生活化类比

打印预制填空模板,活动名称、优惠金额动态填入,不用每次重新写完整文案要求。

核心作用
  1. 统一管理合规规则(禁用极限词、字数限制),修改只改一处;
  2. 支持 ${变量} 占位符,运行时动态填充活动、商品类型;
  3. 区分全局默认 system 模板、单次临时业务模板。
代码示例(模板复用)
java 复制代码
// 1. 定义公共模板字符串
String smsPromptTemplate = """
    你是合规短信专员,输出2条40-60字短信,禁用极限词。
    活动:${activity},优惠:${discount}
""";

// 2. 填充占位符
PromptTemplate template = new PromptTemplate(smsPromptTemplate);
Map<String,Object> params = Map.of("activity","618家电","discount","满2000减300");
Prompt prompt = template.create(params);

// 3. 传入ChatClient调用
String result = chatClient.prompt(prompt).call().content();
项目价值
  1. 避免在多个 Controller/Service 重复写大段系统提示词;
  2. 统一管控短信合规规则,后期运营商规则变更只修改模板;
  3. 大幅减少 Token 浪费,提升开发效率。

全局超时、重试、异常统一捕获

通俗定义

SpringAI 原生支持 yml 全局配置超时时间、最大重试次数,内置统一异常拦截,自动区分 4xx 客户端错误、5xx 服务端故障,无需手动整合 Spring Retry、手动写超时逻辑。

生活化类比

外卖系统内置规则,商家超时自动重试 2 次,地址填错直接提示不重复派送。

1)yml 全局配置(开箱即用)
XML 复制代码
spring:
  ai:
    dashscope:
      api-key: sk-xxx
    retry:
      max-attempts: 3 # 最大重试3次
      timeout: 30s # 全局读取超时30秒
2)自动异常分类处理
错误类型 框架处理逻辑 业务规则
4xx(密钥错误、参数非法、额度耗尽) 不重试,直接抛出业务异常 客户端问题,重试无意义
5xx(模型服务宕机、过载) 自动间隔重试,达到 max-attempts 停止 服务临时故障,有限重试提升成功率
核心价值
  1. 全局统一管控,所有 ChatClient 调用共享一套超时、重试规则;
  2. 底层自动捕获网络异常、模型返回错误,统一封装异常信息;
  3. 不用手动给 OpenFeign/WebClient 写重试、超时配置,减少大量模板代码。

接口降级兜底逻辑编写

通俗定义

当大模型服务超时、宕机、调用失败时,自动切换返回本地预设标准短信模板,保障营销业务不中断,属于生产环境高可用必备能力。

生活化类比

奶茶店设备故障,立刻提供提前备好的瓶装饮料兜底,不让顾客空手离开。

两种降级实现方式
方式 1:Builder 全局统一兜底(推荐)

可自定义兜底返回文案,当大模型服务宕机、超时无响应时,自动返回预设标准短信模板,保障营销业务不中断。

java 复制代码
@Bean
public ChatClient smsChatClient(ChatClient.Builder builder){
    return builder
            .defaultSystem("合规短信专员")
            // 自定义Advisor,捕获异常返回兜底文案
            .defaultAdvisors((request, next) -> {
                try {
                    return next.stream();
                } catch (Exception e) {
                    // 兜底预设短信
                    return Flux.just("【家电特惠】门店家电限时满减,欢迎到店选购");
                }
            })
            .build();
}
方式 2:业务层手动 try-catch 降级
java 复制代码
public String genSms(String activity){
    try {
        return chatClient.prompt().user(activity).call().content();
    }catch (Exception e){
        // 模型服务不可用,返回兜底模板
        return "门店优惠活动火热进行中,进店享专属折扣";
    }
}
项目核心价值
  1. 避免大模型第三方服务故障导致营销功能完全不可用;
  2. 提升系统稳定性,商户不会因为 AI 接口挂掉无法下发短信;
  3. 分层兜底:全局统一兜底 + 业务自定义兜底双重保障。

五大功能汇总对比速查表

功能序号 功能名称 核心 API 解决什么痛点
1 单次同步对话 call() 批量后台生成文案,简化同步调用代码
2 SSE 流式输出 stream() 前端实时打字预览,替代手写 WebClient
3 提示词模板封装 PromptTemplate 复用 Prompt、统一合规规则,减少重复代码
4 全局超时重试异常 yml retry 配置 不用手动整合重试、超时、异常区分逻辑
5 服务降级兜底 Advisor/try-catch 第三方模型故障时业务不中断,高可用保障

表格对比:SpringAI VS 原生 HTTP 调用

OpenFeign/WebClient/RestTemplate对比

对比维度 原生 HTTP 调用(OpenFeign/WebClient) SpringAI 框架
代码冗余度 高,需手动写请求实体、请求头、JSON 解析、分段处理流式数据 极低,底层全部封装,仅传入业务提示词即可
多模型切换 切换通义千问 / ChatGLM 需要修改接口、实体、请求地址,改动量大 仅修改 yml 配置,一行配置切换模型,无业务代码改动
流式开发难度 WebClient 需要掌握 Flux 响应式语法,学习成本高 内置流式封装,少量代码实现 SSE,无需理解底层数据流
异常 & 重试 手动区分 4xx/5xx 错误,自行整合重试组件 全局统一捕获异常,配置文件直接开启重试
底层自定义能力 极高,HTTP 请求全流程可自由改造 封装程度高,底层网络逻辑自定义较麻烦

LangChain4j 基础入门

LangChain4j 定位

通俗定位

LangChain4j 是 Java 生态主流 AI 编排框架,核心不只是简单调用大模型,而是串联整套 AI 业务流程:多轮对话记忆、提示词模板、工具调用、文档知识库、RAG 链路编排,适合复杂长流程 AI 业务。

SpringAI 侧重「快速简单调用大模型」;LangChain4j 侧重「复杂 AI 业务流水线组装」。

生活化类比

SpringAI = 家用微波炉,一键热饭,简单需求够用;

LangChain4j = 完整中央厨房流水线,具备食材存储、配方模板、辅助工具、批量加工全套能力,适合复杂、多步骤业务。

两者适用场景简易区分表
框架 核心擅长场景 短信项目使用场景
SpringAI 单次、简单文案生成,同步 / 流式快速对接模型 普通活动短信单次生成、前端实时预览
LangChain4j 多轮连续对话、AI 工具调用、知识库 RAG 完整流程 运营连续多次修改短信需求、AI 自动校验商户额度

三大核心组件:ChatMemory、PromptTemplate、Tools 工具调用

组件 1:ChatMemory 对话记忆

通俗解释

自动存储多轮对话历史,每次请求自动拼接过往问答,实现连续上下文对话,不用开发者手动拼接 messages。

生活化类比

微信聊天记录,每次发消息自动带上之前全部对话,AI 能记住你上一轮提的约束(比如 "短信不能用极限词、控制 50 字")。

常见实现类
  • InMemoryChatMemory:内存存储,服务重启丢失,测试、临时对话使用;
  • PersistentChatMemory:持久化(Redis / 数据库),线上生产存储多轮会话。
使用方式

配置类注册成 Bean,AiService 绑定,前端传 sessionId 即可自动隔离记忆,全程不需要手动存聊天记录。

项目痛点解决

运营连续调整需求:先要求家电短信、再要求加优惠券、再删减字数。

若无 ChatMemory,每次调用都要重复粘贴全部历史需求;ChatMemory 自动携带上下文。

组件 2:PromptTemplate 提示词模板

通俗解释

统一封装固定角色、合规约束,支持占位符动态填充业务参数,全局多处复用提示词。

和 SpringAI 模板共性与差异

相同:都支持占位符、统一管理系统规则;

差异:LangChain4j 的模板深度集成记忆、工具、RAG 链路,可一键嵌入完整流程。

实际可用工具

框架提供PromptTemplate实体类,内置方法:

  • 加载模板文本
  • 填充占位符参数
  • 生成完整 Prompt 对象传给大模型
使用方式

定义全局静态模板常量,组装 AiService 时全局绑定,所有对话自动携带统一合规规则;也可单次请求单独创建临时模板。

示例模板

你是合规短信文案专员,严格遵守规则:${rule}

请根据活动生成短信:${activityName}

组件 3:Tools 工具调用基础认知

通俗解释

允许大模型主动调用你后端写的 Java 业务接口,AI 判断需要外部数据时自动发起调用,拿到结果再生成文案。

生活化类比

导购不清楚门店剩余优惠券库存,主动去后台库存系统查询,再给顾客推荐。

实际可用工具

框架提供全套注解、工具注册逻辑:

  • @Tool 注解标记业务方法
  • ToolProvider 统一管理所有工具
  • AiService 自动识别并交给大模型调用
使用方式

给后端业务方法加@Tool注解,组装 AiService 时注册工具集,AI 会自主判断是否调用该接口(比如查询商户短信额度、活动有效期)。

短信项目落地场景

AI 生成短信前,主动调用工具接口查询:商户剩余 Token 额度、是否开启短信通道、活动有效期;

若额度不足,AI 直接输出提示,不生成文案。

核心价值

打破大模型 "无外部业务数据" 的局限,让 AI 联动现有业务系统。

汇总对比表格

组件名称 是否单纯概念 是否可编码使用 本质
ChatMemory 否,不是纯概念 是,可直接实例化、注入 会话存储工具类
PromptTemplate 否,不是纯概念 是,提供实体类与操作方法 提示词封装工具
Tools 工具体系 否,不是纯概念 是,整套注解 + 管理组件 AI 调用外部接口工具框架

LangChain4j 基础实操流程

场景需求:运营连续多次调整短信文案规则,框架自动记忆历史对话,不用每次手动拼接全部上下

1.前置初始化

导入依赖 + yml 配置大模型密钥,框架自动加载通义千问 ChatLanguageModel 大模型对象

引入 LangChain4j 依赖
XML 复制代码
<!-- LangChain4j 核心依赖 -->
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-spring-boot-starter</artifactId>
    <version>0.34.0</version>
</dependency>
<!-- 通义千问模型对接依赖 -->
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-dashscope-spring-boot-starter</artifactId>
    <version>0.34.0</version>
</dependency>

解释

  1. 核心 starter 提供 ChatMemory、PromptTemplate、AiService 全套组件;
  2. dashscope 专用依赖,用来对接阿里通义千问大模型;
  3. SpringBoot 项目自动装配模型客户端,无需手动创建 HTTP 请求。
yml 配置模型密钥(全局统一配置)
XML 复制代码
langchain4j:
  dashscope:
    api-key: sk-xxxxxxxxxxxx你的密钥
    model-name: qwen-turbo

解释

将鉴权密钥、模型名称配置在配置文件,硬编码,框架自动读取配置构建模型实例。

2.创建 ChatMemory 存储会话上下文

ChatMemory = 对话存储器,开辟独立会话存储空间,按照 sessionId 隔离每个运营的聊天记录,限制最大消息数防止上下文过长

作用:

  1. 根据会话 ID(sessionId)区分不同运营人员,每人拥有独立聊天记录;
  2. 自动保存每一轮问答内容;
  3. 每次请求自动拼接历史上下文,无需手动拼接字符串;
  4. 设置消息上限,防止对话过多超出大模型上下文窗口,引发报错。
  5. 测试环境使用内存存储,服务重启记录清空;生产环境替换为 Redis 持久化存储。

该组件写在 Spring 配置类中,提供会话内存生成规则:

java 复制代码
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class LangChain4jConfig {

    /**
     * 会话内存提供者
     * 根据传入的sessionId,分配独立的对话存储空间
     */
    @Bean
    public ChatMemoryProvider chatMemoryProvider() {
        // maxMessages = 10:最多保存10轮一问一答,超出会自动丢弃最早的记录
        return sessionId -> MessageWindowChatMemory.withMaxMessages(10);
    }
}

解释

ChatMemoryProvider:框架规定的会话内存生成接口;

sessionId -> MessageWindowChatMemory.withMaxMessages(10)

  • 入参 sessionId:会话唯一标识,用来隔离不同用户对话;
  • MessageWindowChatMemory:窗口式内存存储,最常用;
  • withMaxMessages(10):限定最大存储消息条数,控制上下文长度,避免 token 溢出;

底层默认存储在 JVM 内存,重启服务所有对话记录全部消失。

LangChain4j ChatMemory 持久化存储到 Redis 完整教程

默认 MessageWindowChatMemory 使用 InMemoryChatMemoryStore,数据存在 JVM 内存,服务重启、多实例部署会话全部丢失,只适合本地测试。

LangChain4j 设计了顶层抽象接口 ChatMemoryStore,所有存储实现都要实现它,包含 3 个核心方法:

  • getMessages():根据 sessionId 读取历史对话
  • updateMessages():更新 / 写入最新一轮对话
  • deleteMessages():清空会话记录

持久化改造思路:自定义 RedisChatMemoryStore 实现 ChatMemoryStore,用 Redis 存储序列化后的对话 JSON;再替换掉默认内存存储,让 ChatMemory 走 Redis 读写。

整体实现步骤

步骤 1:引入全部 Maven 依赖

1)LangChain4j 核心、通义千问模型 2)Spring Redis 客户端

XML 复制代码
<!-- LangChain4j核心 -->
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-spring-boot-starter</artifactId>
    <version>0.34.0</version>
</dependency>
<!-- 通义千问模型 -->
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-dashscope-spring-boot-starter</artifactId>
    <version>0.34.0</version>
</dependency>
<!-- Spring Redis 操作模板 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
步骤 2:application.yml 配置 Redis 连接 + 大模型密钥
XML 复制代码
# Redis连接配置
spring:
  data:
    redis:
      host: 127.0.0.1
      port: 6379
      password: ""
      database: 0
# 通义千问密钥
langchain4j:
  dashscope:
    api-key: sk-xxxxxxxxxxxx
    model-name: qwen-turbo
步骤 3:自定义 RedisChatMemoryStore(实现 ChatMemoryStore)

核心作用:完成 ChatMessage 与 JSON 序列化、Redis 读写、会话过期自动清理

java 复制代码
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.ChatMessageDeserializer;
import dev.langchain4j.data.message.ChatMessageSerializer;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.time.Duration;
import java.util.List;

@Repository
public class RedisChatMemoryStore implements ChatMemoryStore {

    // Redis key前缀,区分业务数据
    private static final String KEY_PREFIX = "sms:chat_memory:";
    // 会话自动过期:3天无访问自动删除
    private static final Duration SESSION_TTL = Duration.ofDays(3);

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 根据sessionId读取该会话全部历史对话
     */
    @Override
    public List<ChatMessage> getMessages(Object memoryId) {
        String redisKey = KEY_PREFIX + memoryId;
        String json = stringRedisTemplate.opsForValue().get(redisKey);
        // 不存在会话返回空集合
        if (json == null || json.isBlank()) {
            return List.of();
        }
        // JSON反序列化为对话消息列表
        return ChatMessageDeserializer.messagesFromJson(json);
    }

    /**
     * 新增/更新一轮对话,写入Redis并刷新过期时间
     */
    @Override
    public void updateMessages(Object memoryId, List<ChatMessage> messages) {
        String redisKey = KEY_PREFIX + memoryId;
        // 对话列表序列化为JSON字符串
        String json = ChatMessageSerializer.messagesToJson(messages);
        // 写入Redis,同时设置过期时间
        stringRedisTemplate.opsForValue().set(redisKey, json, SESSION_TTL);
    }

    /**
     * 手动清空某个会话全部历史
     */
    @Override
    public void deleteMessages(Object memoryId) {
        String redisKey = KEY_PREFIX + memoryId;
        stringRedisTemplate.delete(redisKey);
    }
}

代码逐段解释

  1. ChatMessageSerializer / ChatMessageDeserializer:LangChain4j 内置工具,专门负责对话消息序列化,不用自己写 Jackson 转换逻辑;
  2. StringRedisTemplate:使用字符串模板,直接存 JSON 文本,避免复杂对象序列化报错;
  3. SESSION_TTL:会话自动过期,避免 Redis 堆积大量无效长期未使用会话;
  4. KEY_PREFIX:给所有会话 key 加业务前缀,和系统其他 Redis 数据隔离。
步骤 4:修改 LangChain4j 配置,替换内存存储为 Redis 存储

修改之前的ChatMemoryProvider,将自定义 Redis 存储注入,全局所有会话走 Redis 持久化:

java 复制代码
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.service.AiServices;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;

@Configuration
public class LangChain4jConfig {

    // 注入Redis持久化存储实现
    @Resource
    private RedisChatMemoryStore redisChatMemoryStore;

    // 步骤2:构建会话内存提供者(替换原内存存储)
    @Bean
    public ChatMemoryProvider chatMemoryProvider() {
        // memoryId:前端传入的会话ID
        return memoryId -> MessageWindowChatMemory.builder()
                // 指定使用Redis存储,不再使用默认InMemory
                .chatMemoryStore(redisChatMemoryStore)
                // 限制单会话最多保存10轮问答,防止上下文超长溢出token
                .maxMessages(10)
                .build();
    }

    // 步骤3:全局短信提示词模板(不变)
    public static final PromptTemplate SMS_RULE_TEMPLATE = PromptTemplate.from("""
            你是运营商合规短信文案专员,请严格遵守以下规范:
            1. 单条短信字数控制在40~60字之间;
            2. 严禁使用极限词、夸大宣传词汇;
            3. 只输出短信正文,不要多余话术解释;
            用户本次需求:{{userContent}}
            """);

    // 步骤4:组装顶层AiService(无需改动,自动复用Redis记忆)
    @Bean
    public SmsAiService smsAiService(ChatLanguageModel chatModel, ChatMemoryProvider memoryProvider) {
        return AiServices.builder(SmsAiService.class)
                .chatLanguageModel(chatModel)
                .chatMemoryProvider(memoryProvider)
                .systemPromptTemplate(SMS_RULE_TEMPLATE)
                .build();
    }
}

关键改动说明

MessageWindowChatMemory.builder().chatMemoryStore(redisChatMemoryStore)

  • 不指定chatMemoryStore:默认使用内存存储InMemoryChatMemoryStore
  • 传入自定义RedisChatMemoryStore:所有读写对话全部转发 Redis,实现持久化。
步骤 5:原有 Controller、AiService 接口完全不用修改,直接复用
java 复制代码
// 业务接口不变
public interface SmsAiService {
    String createSmsText(@MemoryId String sessionId, @UserMessage String userDemand);
}

// Controller 完全不变
@RestController
public class SmsChatController {
    private final SmsAiService smsAiService;
    public SmsChatController(SmsAiService smsAiService) {
        this.smsAiService = smsAiService;
    }
    @GetMapping("/langchain/sms/chat")
    public String smsChat(@RequestParam String sessionId, @RequestParam String demand) {
        return smsAiService.createSmsText(sessionId, demand);
    }
}
运行验证效果
  1. 第一次调用接口 sessionId=op001,对话存入 Redis sms:chat_memory:op001
  2. 重启 SpringBoot 服务,再次使用相同 sessionId 请求,历史对话完整保留(内存存储会丢失,Redis 持久化不会);
  3. 多台服务集群部署,共享同一 Redis,不同实例可读取同一会话上下文;
  4. 超过 3 天无访问,Redis 自动清除该会话 key,释放存储空间。
内存存储 VS Redis 持久化对比表
对比项 默认 InMemory 内存存储 Redis 持久化存储
数据存放 JVM 堆内存 Redis 独立缓存服务
服务重启 会话全部丢失 会话永久保存,过期自动清理
分布式集群 多实例会话隔离,无法共享 全实例共享同一会话记录
内存占用 会话越多堆内存越大,易 OOM 不占用应用服务内存
适用场景 本地测试、单机临时演示 线上生产环境、分布式项目

3.定义 PromptTemplate 封装短信合规规则

PromptTemplate = 可复用提示词模板,统一固化短信合规要求:字数、禁用极限词、角色定位,全局复用

  1. 将短信固定规则(角色、字数、禁用极限词)统一封装;
  2. 支持固定文本 + 动态用户需求拼接;
  3. 一处修改全局生效,不用在每一次调用时重复抄写合规文案,统一管控运营商短信规范。

代码追加至上方配置类

java 复制代码
import dev.langchain4j.prompt.PromptTemplate;

@Configuration
public class LangChain4jConfig {

    // 步骤2 会话内存Bean 此处省略,保留原有代码

    /**
     * 全局短信合规提示词模板
     */
    public static final PromptTemplate SMS_RULE_TEMPLATE = PromptTemplate.from("""
            你是运营商合规短信文案专员,请严格遵守以下规范:
            1. 单条短信字数控制在40~60字之间;
            2. 严禁使用极限词、夸大宣传词汇;
            3. 只输出短信正文,不要多余话术解释;
            用户本次需求:{{userContent}}
            """);
}

解释

  1. PromptTemplate.from("文本"):创建固定提示词模板;
  2. 三重引号:Java 文本块,方便多行文本编写,无需手动换行转义;
  3. {``{userContent}}:模板占位符,后续会填充运营的活动需求;
  4. 定义为常量,全局任意位置都可以复用这套短信规则。

构建 AiService(框架顶层封装对象,整合记忆、模板、模型、工具);

AiService 是 LangChain4j 最高层门面对象,将「大模型 + 对话记忆 + 提示词模板」三者绑定在一起,生成可直接调用的 AI 代理服务

一次性整合四大核心内容: 大模型客户端 + ChatMemory 对话记忆 + PromptTemplate 提示词模板 + Tools 工具调用

开发者无需手动拼接消息、组装请求,直接调用自定义接口方法即可发起对话。

完整代码(分为两部分:业务接口 + 配置类组装 Bean)

① 先定义 AI 业务接口
java 复制代码
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.UserMessage;

/**
 * 短信AI生成服务接口
 */
public interface SmsAiService {

    /**
     * 生成营销短信
     * @param sessionId 会话ID:绑定专属对话记忆
     * @param userDemand 用户本次活动需求
     * @return AI生成的短信文案
     */
    String createSmsText(
            @MemoryId String sessionId,
            @UserMessage String userDemand
    );
}
② 在配置类中组装生成 AiService 实例
java 复制代码
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.service.AiServices;

@Configuration
public class LangChain4jConfig {

    // 步骤2、步骤3代码保留不变

    /**
     * 组装顶层AiService,整合模型、记忆、提示词模板
     */
    @Bean
    public SmsAiService smsAiService(
            ChatLanguageModel chatModel,    // yml自动装配的通义千问大模型
            ChatMemoryProvider memoryProvider  // 步骤2创建的会话内存
    ) {
        return AiServices.builder(SmsAiService.class)
                // 绑定对接的大模型
                .chatLanguageModel(chatModel)
                // 绑定会话记忆管理器
                .chatMemoryProvider(memoryProvider)
                // 绑定全局短信系统提示词模板
                .systemPromptTemplate(SMS_RULE_TEMPLATE)
                .build();
    }
}

代码注解详解

  1. @MemoryId:标识该参数为会话 ID,框架根据此 ID 调取对应的历史聊天记录;
  2. @UserMessage:标识该参数为用户输入的业务需求;
  3. ChatLanguageModel:项目读取 yml 配置自动创建的通义千问客户端,负责底层 HTTP 调用大模型接口;
  4. AiServices.builder():框架提供的构建器,一站式整合所有组件;
  5. .build():生成接口代理实现类,交由 Spring 容器管理,可直接注入使用。

多次连续调用方法,自动携带历史对话,实现多轮对话。

编写 Controller 接收前端请求,使用同一个 sessionId 反复调用接口;

框架自动完成:读取历史对话 → 拼接系统模板 + 历史消息 + 新需求 → 请求大模型 → 保存本轮问答记录,实现连贯对话。

java 复制代码
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SmsChatController {

    // 注入组装完成的AI服务
    private final SmsAiService smsAiService;

    public SmsChatController(SmsAiService smsAiService) {
        this.smsAiService = smsAiService;
    }

    /**
     * 多轮短信生成接口
     * @param sessionId 会话标识
     * @param demand 用户需求
     * @return 短信文案
     */
    @GetMapping("/langchain/sms/chat")
    public String smsChat(
            @RequestParam String sessionId,
            @RequestParam String demand
    ) {
        return smsAiService.createSmsText(sessionId, demand);
    }
}
多轮调用测试演示
第一轮请求

地址:/langchain/sms/chat?sessionId=op001&demand=写2条618家电促销短信

返回:两条合规家电短信,系统自动保存本次问答记录。

第二轮请求(同一个 sessionId)

地址:/langchain/sms/chat?sessionId=op001&demand=加上优惠券福利,每条控制50字以内

框架自动读取上一轮对话历史,AI 记住 618 家电活动,叠加新需求,不会丢失前置约束。

更换 sessionId=op002

全新空白会话,无任何历史记录,对话从头开始。

内部执行流转图

LangChain4j 多轮对话完整业务流程图

区分 SpringAI 与 LangChain4j 适用场景对比表

一、综合对比总表

对比维度 SpringAI LangChain4j
核心定位 Spring 生态轻量级大模型调用框架,主打快速对接 LLM Java 端 AI 全链路编排框架,主打复杂 AI 流程组装
底层适配 深度整合 SpringBoot、WebFlux,无缝融入现有 Spring 微服务 无强 Spring 绑定,普通 Java 项目、Spring 项目均可使用
上手难度 极低,链式 API 简洁,开箱即用 中等,组件多、流程复杂,需要理解整套编排思想
核心优势 同步 / 流式一行代码、内置重试 / 超时 / 降级、多模型一键切换 原生支持对话记忆、工具调用、完整 RAG 流水线、多步骤任务
多轮对话支持 需手动拼接历史消息,无内置记忆组件 内置 ChatMemory,自动维护上下文,开箱即用
工具调用 无原生工具链,自定义实现复杂 原生 Tools 体系,简单注解即可让 AI 调用后端业务接口
RAG 能力 仅基础文本问答,无完整分片 / 向量库链路封装 全套 RAG 组件:文档加载、文本切分、向量化、向量检索一体化
流式 SSE 原生封装 stream (),适配前端实时预览 流式支持较弱,需要自行适配 WebFlux 做 SSE
项目改动成本 极小,现有 Spring 项目快速接入 中等,复杂流程需要改造业务分层

二、短信业务场景细分对照表

业务场景 推荐框架 选择理由
单次同步生成营销短信(后台管理) SpringAI 代码极简,内置超时重试,不需要复杂上下文
前端实时打字预览 SSE 页面 SpringAI 原生封装流式输出,不用手动处理 Flux 分段逻辑
运营连续多轮调整文案(多次修改需求、叠加约束) LangChain4j ChatMemory 自动保存历史对话,不用手动拼接所有历史 Prompt
AI 自动校验商户额度、活动有效期后再生成文案(AI 调用业务接口) LangChain4j 原生 Tools 工具调用能力,AI 自主查询外部业务数据
接入企业合规知识库,检索规则后生成短信(完整 RAG 流程) LangChain4j 内置全套文档切片、向量入库、相似度检索组件
项目仅简单对接大模型,无复杂 AI 流水线 SpringAI 轻量无冗余组件,学习、维护成本低
后期需要拓展复杂 AI 多步骤任务(总结、检索、工具联动) LangChain4j 流程编排能力强,扩展性更好

选型背诵总结

  1. 追求快速开发、流式预览、简单单次问答、原生 Spring 适配 → 选 SpringAI;
  2. 需要多轮记忆对话、AI 工具调用、完整 RAG 知识库、复杂多步骤 AI 流程 → 选 LangChain4j。

RAG 检索增强生成

RAG 是什么、解决什么痛点

通俗定义

RAG 全称Retrieval-Augmented Generation 检索增强生成

简单说:先检索企业私有文档资料,把真实业务规则塞给大模型,让 AI 基于内部真实数据回答,根治模型幻觉

生活化类比

普通大模型 = 实习员工,只懂网上公开信息,公司内部短信合规规则完全不知道,容易乱编规定,导致商户短信违规封号;

RAG = 员工上岗前先翻阅公司《短信运营合规手册》,回答问题只允许参考手册内容,不能自己瞎编规则。

两大核心痛点(不用 RAG 会出现的问题)

  1. 模型幻觉:AI 凭空编造不存在的运营商合规要求、活动规则;
  2. 无法读取企业私有数据:公有大模型训练数据不包含你公司内部文档、专属业务规范。

RAG 核心价值

  1. 大幅减少 AI 虚假编造内容;
  2. 支持企业内部私有知识库(合规文档、活动模板、行业规则);
  3. 减少超长 Prompt,降低 Token 消耗,节约调用成本。

RAG 完整七步业务流程

文档加载 → 文本分片切片 → 文本向量化 → 向量存入向量库 → 用户提问向量化 → 相似度召回文档片段 → 拼接检索文档 + 提示词传入大模型 → AI 生成合规回答

RAG 全环节分步

RAG 分为两大阶段:

  1. 知识库构建阶段(离线一次性执行:步骤 1~4):提前把企业文档处理成向量存入数据库,后台定时 / 项目启动执行
  2. 用户问答检索阶段(线上实时执行:步骤 5~7):用户提问时实时执行,每次 AI 生成文案都要走这套流程

步骤 1:文档加载

举例比喻

公司有一堆合规手册(PDF、txt、md),就像一摞纸质规章制度;这一步相当于把所有文件全部拆开、扫描,把里面所有文字提取出来,丢掉图片、空白、乱码,只保留能用的业务规则。

核心作用

读取企业私有本地文档,提取纯文本,作为知识库原始素材;

支持文件:txt、markdown、PDF(短信合规规范、商户协议、活动规则)

执行时机

项目首次启动全量加载;凌晨定时任务增量更新新增文档。

流程图

本地文档文件(PDF/txt/md) → 文档解析器 → 清洗过滤空白/乱码 → 完整纯文本Document对象

完整代码 + 逐行注释
java 复制代码
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.document.parser.apache.pdfbox.ApachePdfBoxDocumentParser;
import java.nio.file.Path;

public class Step1_DocumentLoad {
    public static void main(String[] args) {
        // 1.指定合规PDF文件路径
        Path pdfPath = Path.of("doc/短信运营合规手册.pdf");
        // 2.创建PDF专用解析器
        ApachePdfBoxDocumentParser pdfParser = new ApachePdfBoxDocumentParser();
        // 3.加载文件并自动提取全部文字
        Document document = FileSystemDocumentLoader.loadDocument(pdfPath, pdfParser);
        // 4.获取清洗后的完整文本
        String fullText = document.text();
        System.out.println("提取后的文档原文:\n" + fullText);
    }
}
关键点总结

只负责读取文字,不做拆分;文本过长会导致后续向量化、大模型调用 Token 超标,必须交给步骤 2 分片。

步骤 2:文本分片切片

举例比喻

一本几百页的规则书不能整本书拿去计算;把书裁剪成一张张纸条,相邻两张纸条保留一小段重复文字

防止一条完整规则被一刀切断,一半在第一张、一半在第二张,检索时只能匹配半句话,丢失完整规则。

两种分片策略对比表
策略 配置方式 作用 不做的后果
固定长度分割 设置单段最大字符(例 800 字) 限制单段文本长度,避免 Token 溢出 单段文字过长,向量精度下降、调用成本高
重叠分片 设置重叠字符(例 200 字) 保证跨分片的完整语义不被截断 规则句子被拆分,检索不到完整合规条款
环节流程图

完整长文本 → 递归分片器分割 → 相邻片段预留重叠文字 → 生成多个独立 TextSegment 文本块

完整代码 + 注释
java 复制代码
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.segment.TextSegment;
import java.util.List;

public class Step2_SplitText {
    public static void main(String[] args) {
        // 步骤1加载出来的完整文档
        Document document = Document.from("超长短信合规规则原文......");

        // 分片器:单段最大800字,两段重叠200字
        var splitter = DocumentSplitters.recursive(800, 200);
        // 对长文本切片,得到多个小块
        List<TextSegment> segmentList = splitter.split(document);

        // 遍历打印所有分片
        for (int i = 0; i < segmentList.size(); i++) {
            System.out.println("分片" + (i+1) + ":" + segmentList.get(i).text());
        }
    }
}
常见踩坑

分片太小:单块只有零散词语,语义破碎,检索无意义; 分片太大:单块 Token 过多,向量相似度计算不准,浪费费用。

步骤 3:文本向量化Embedding

举例比喻

机器看不懂中文文字,只能识别数字;

每一张分片纸条,送入专门翻译器(Embedding 向量模型),翻译成一串多维数字编码。

语义相近的文字,数字编码在坐标系里距离很近;语义无关,距离很远。

例:「短信禁止最低价」和「营销不能用极限词」向量距离很近。

核心硬性规则

离线文档、用户提问必须使用同一个 Embedding 模型,向量维度完全一致,否则相似度计算完全失效。

流程图

分片文本 TextSegment → Embedding 向量化模型 → 多维浮点数组 Embedding 向量

完整代码(通义千问 Embedding)
java 复制代码
import dev.langchain4j.model.dashscope.DashScopeEmbeddingModel;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;

public class Step3_Embedding {
    public static void main(String[] args) {
        // 初始化通义千问向量化模型
        DashScopeEmbeddingModel embeddingModel = DashScopeEmbeddingModel.builder()
                .apiKey("sk-你的大模型密钥")
                .modelName("text-embedding-v2")
                .build();

        // 步骤2拆分好的文本片段
        TextSegment seg = TextSegment.from("短信禁止使用最高、第一、最低价等极限宣传词汇");
        // 文本转向量
        Embedding embedding = embeddingModel.embed(seg.text()).content();
        // 打印数字向量数组
        System.out.println("文本对应向量数组:" + embedding.vector());
    }
}

步骤 4:向量存入向量数据库

举例比喻

把「纸条原文 + 对应的数字编码」成对存入专用档案柜(向量数据库),永久保存。后续用户提问时,拿着提问的数字编码,快速在档案柜匹配相近纸条。

PgVector vs Milvus 精简选型表
向量库 适用场景 运维成本
PgVector 中小型项目、百万向量内,已有 Postgres 极低,复用现有数据库
Milvus 海量知识库、百万 / 亿级向量、高并发集群 高,独立部署维护集群
存储每条数据结构

唯一 ID + 原始文本分片 TextSegment + 多维向量 Embedding

流程图

分片文本 + 对应向量 → 批量写入向量库表 → 持久化存储,建立向量索引加速检索

Maven 依赖
java 复制代码
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-store-pgvector</artifactId>
    <version>0.34.0</version>
</dependency>
入库完整代码
java 复制代码
import dev.langchain4j.store.embedding.pgvector.PgVectorEmbeddingStore;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.model.dashscope.DashScopeEmbeddingModel;

public class Step4_SaveVector {
    public static void main(String[] args) {
        // 1.初始化PgVector向量存储
        PgVectorEmbeddingStore store = PgVectorEmbeddingStore.builder()
                .host("127.0.0.1")
                .port(5432)
                .database("sms_knowledge_db")
                .user("postgres")
                .password("123456")
                .table("sms_rule_knowledge") // 存储合规规则的表
                .dimension(1536) // 和Embedding模型向量维度保持一致
                .build();

        // 2.构造分片文本+向量(来自步骤2、3)
        TextSegment seg = TextSegment.from("短信禁止极限宣传词");
        DashScopeEmbeddingModel embeddingModel = DashScopeEmbeddingModel.builder().apiKey("sk-xxx").build();
        Embedding embedding = embeddingModel.embed(seg.text()).content();

        // 3.向量+原文存入数据库
        store.add(embedding, seg);
        System.out.println("向量入库完成");
    }
}

以上 4 步 = 离线知识库构建,一次性执行完成

步骤 5:用户提问向量化

举例比喻

运营提问:"短信可以写最低价吗?"

把用户这句话,用和文档完全相同的翻译器,生成一串数字编码,用来和库里档案对比。

流程图

用户原始问句 → 同一个 Embedding 模型 → 提问向量

极简代码
java 复制代码
// 用户输入问题
String userQuestion = "营销短信能不能使用最低价宣传?";
// 复用步骤3同一个向量化模型
DashScopeEmbeddingModel embeddingModel = DashScopeEmbeddingModel.builder().apiKey("sk-xxx").build();
Embedding questionEmbedding = embeddingModel.embed(userQuestion).content();

步骤 6:相似度召回匹配文档片段

举例比喻

拿着用户提问的数字编码,遍历档案柜里所有编码,计算距离;

设置门槛(相似度阈值 0.7),只保留匹配度 70% 以上的纸条,无关内容直接过滤丢弃。

阈值参数说明
  • minScore=0.7(通用生产推荐)
  • 阈值过高 0.9:召回内容太少,缺少参考规则,回答不完整
  • 阈值过低 0.5:大量无关文档混入,干扰 AI、浪费 Token 费用
流程图

提问向量 → 向量库索引快速相似度计算 → 过滤低于阈值数据 → 返回匹配的业务文档片段

检索完整代码
java 复制代码
import dev.langchain4j.store.embedding.EmbeddingSearchRequest;
import dev.langchain4j.store.embedding.EmbeddingSearchResult;
import dev.langchain4j.store.embedding.pgvector.PgVectorEmbeddingStore;

public class Step6_SearchVector {
    public static void main(String[] args) {
        // 初始化向量库
        PgVectorEmbeddingStore store = PgVectorEmbeddingStore.builder()
                .host("127.0.0.1")
                .port(5432)
                .database("sms_knowledge_db")
                .user("postgres")
                .password("123456")
                .table("sms_rule_knowledge")
                .dimension(1536)
                .build();

        // 步骤5生成的提问向量
        Embedding questionEmbedding = ...;

        // 构建检索条件
        EmbeddingSearchRequest searchReq = EmbeddingSearchRequest.builder()
                .queryEmbedding(questionEmbedding)
                .maxResults(3) // 最多返回3条匹配文档
                .minScore(0.7) // 相似度门槛0.7
                .build();

        // 执行检索
        EmbeddingSearchResult result = store.search(searchReq);

        // 遍历打印匹配到的合规规则
        result.matches().forEach(match -> {
            System.out.println("匹配相似度分数:" + match.score());
            System.out.println("合规原文:" + match.embedded().text());
        });
    }
}

步骤 7:拼接上下文交给大模型生成答案

举例比喻

把检索到的几条合规纸条、用户问题、系统角色约束打包交给 AI;

强制 AI 只能参考纸条上真实企业规则作答,不允许凭空编造运营商规范,从根源解决模型幻觉。

流程图

检索到的知识库片段 + 系统角色约束 + 用户提问 → 拼接完整 Prompt → 送入 LLM 大模型 → 返回精准合规答案

标准 Prompt 模板

你是短信合规审核专员,只能依据下方【企业内部合规资料】回答,严禁编造不存在的规则。

【企业内部合规资料】

{检索出来的多条知识库文本}

用户问题:{用户原始提问}

请简洁准确给出合规答复。

业务整合代码(结合 ChatClient)
java 复制代码
import org.springframework.ai.chat.client.ChatClient;

public class Step7_GenerateAnswer {
    public static void main(String[] args) {
        // 1.拼接所有检索到的知识库内容
        StringBuilder knowledgeText = new StringBuilder();
        EmbeddingSearchResult result = ...; // 步骤6检索结果
        result.matches().forEach(match -> knowledgeText.append(match.embedded().text()).append("\n"));

        String userQuestion = "营销短信能不能使用最低价宣传?";

        // 2.组装完整提示词
        String prompt = """
                你是短信合规审核专员,只能依据下方【企业内部合规资料】回答,严禁编造不存在的规则。
                【企业内部合规资料】
                %s
                
                用户问题:%s
                """.formatted(knowledgeText, userQuestion);

        // 3.调用SpringAI ChatClient生成回答
        ChatClient chatClient = ChatClient.builder().build();
        String answer = chatClient.prompt()
                .user(prompt)
                .call()
                .content();
        System.out.println("AI最终合规回答:" + answer);
    }
}

7 步完整汇总总表

阶段 步骤 执行时机 核心产出 解决什么问题
离线知识库构建 1 文档加载 项目启动 / 定时任务 完整文档纯文本 读取企业私有内部资料
离线知识库构建 2 文本分片切片 离线一次性 多个短文本片段 控制文本长度,防止语义被切断
离线知识库构建 3 文本向量化 离线一次性 文本对应数字向量 实现语义相似度计算,让机器看懂文字含义
离线知识库构建 4 向量入库 离线一次性 向量 + 文本持久化存储 建立可快速检索的私有知识库
在线实时问答 5 提问向量化 用户每次提问实时执行 用户问句向量 和库内文档向量做相似度匹配
在线实时问答 6 相似度召回 用户每次提问实时执行 高匹配度业务文档片段 筛选和用户问题相关的内部规则
在线实时问答 7 拼接 Prompt 生成答案 用户每次提问实时执行 无幻觉、贴合内部资料的回答 限制 AI 只能基于真实企业文档输出,根治模型幻觉

向量数据库对比表:PgVector VS Milvus

对比维度 PgVector Milvus
底层本质 PostgreSQL 数据库的扩展插件,依附 Postgres 运行 独立、原生分布式专用向量数据库,计算存储分离架构
适配向量数据量级 中小规模,500 万条以内向量最优,超过千万性能明显下滑 海量场景,千万~百亿级向量,支持持续扩容Zilliz
部署与运维成本 极低,无需新增中间件;复用现有 Postgres 备份、主从、监控、权限体系 高,需要单独部署集群、维护多组件(Proxy/QueryNode/IndexNode),学习运维成本高
查询性能 百万级延迟 50~200ms;不支持 GPU 加速;高并发 QPS 上限低 延迟稳定<50ms,支持 GPU 加速、多核并行,高并发吞吐是 PgVector 数倍
混合查询能力(向量 + 业务字段) 原生支持 SQL,向量检索可 JOIN 业务表、事务 ACID、多条件过滤,非常适合业务系统 RAG 只专注纯向量检索,结构化业务过滤能力弱,向量和业务数据需分库存储,多库联调复杂
技术栈学习成本 极低,会 SQL 就能直接使用向量检索,不用学习新 API 高,有独立 SDK、集合管理、分区索引专属语法,需要单独学习
内存占用 HNSW 索引占用内存高,高维向量容易内存溢出 内置向量压缩算法,内存管控更精细,海量数据内存压力更小
扩容扩展能力 横向分片扩展困难,依赖 Postgres 原生主从,向量检索无法水平分摊压力 原生分布式,支持弹性扩容、冷热数据分离、集群负载均衡
短信平台适配场景 中小型短信系统,合规文档、规则库总量几万~百万条,已有 Postgres 业务库 大型政企短信平台,百万级以上合规文件、海量商户知识库,高并发检索
生活化类比 家用厨房加装空气炸锅,现有厨具复用,不用额外空间 专业商用中央后厨,专门处理大批量订单,需要独立场地维护

优缺点拆分表

PgVector

✅ 优点

  1. 不用新增中间件,复用项目现有 PostgreSQL,架构轻量化;
  2. 向量数据和商户、活动、合规业务数据同库,支持事务、联表查询;
  3. 上手简单,标准 SQL 操作向量,开发速度快;
  4. 中小项目运维零额外成本,备份、监控复用现有方案。

❌ 缺点

  1. 向量超过 500 万后查询延迟暴涨,并发能力不足;
  2. 不支持 GPU 加速、冷热分离等高级向量特性;
  3. 分布式分片复杂,无法支撑超大规模知识库。
Milvus

✅ 优点

  1. 底层专为向量检索优化,海量数据下低延迟、高并发;
  2. 分布式弹性扩容,支持 GPU 加速、多种向量索引;
  3. 支持百亿级向量存储检索,适配超大知识库 RAG 场景。

❌ 缺点

  1. 新增一套独立中间件,运维、部署、监控成本大幅上升;
  2. 无法和业务库做 SQL 联查,业务数据与向量数据分离,开发复杂度提升;
  3. 团队需要额外学习 Milvus 专属 API、集群调优知识。

项目选型决策(短信 AI RAG 场景)

  1. 中小型短信营销平台(推荐 PgVector) 知识库文档几十万条以内,已有 PostgreSQL 业务库,追求少组件、易维护、快速开发,选 PgVector。

  2. 大型政企 / 多租户短信平台(推荐 Milvus) 合规文档、商户知识库超百万条,并发检索量大,需要极低查询延迟、支持水平扩容,选 Milvus。

  3. 通用演进方案:初期使用 PgVector 快速落地 RAG,后续业务数据量上涨、性能不足时,平滑迁移至 Milvus。

RAG 落地优化方案

  • 调整分片大小:分片过小语义断裂,分片过大 Token 消耗高,测试找到平衡值;
  • 合理设置相似度阈值:阈值过高召回太少,过低带入无关文档干扰 AI;
  • 增加重排序:召回多条文档后,二次筛选相关性最高片段;
  • 后端二次校验:AI 输出文案后,敏感词、极限词拦截双重兜底,进一步规避违规。
相关推荐
哈__1 小时前
面向AI智能体的数据库专业技能包:将DBA工程经验封装为可调用能力
数据库·人工智能·dba
leisoo80971 小时前
财报数据怎么排雷本地化Python构建财务异常预警系统
人工智能·python·算法
u0103055271 小时前
长株潭AI节能应用构建核心方案
人工智能
ACP广源盛139246256731 小时前
蚂蚁百灵 Ling‑3.0‑flash 开源 + 昇腾 0‑Day 原生适配@ACP#GSV9001E 在国产算力矩阵中的机会与落地场景
大数据·人工智能·分布式·单片机·嵌入式硬件
小柯南敲键盘1 小时前
跨马翻译:跨境电商批量图片翻译与视频字幕一站式工具
人工智能·python·音视频
微硬创新1 小时前
老旧产线改造:耐达讯自动化16路4‑20mA转PROFINET的工程实践
人工智能·网络协议·自动化·信息与通信
小智GEO观察2 小时前
衡量标准之变:企业传播的价值评估正在经历一次静默重构
人工智能·重构
天工开户012 小时前
2026 Facebook投放正在发生的7个变化
人工智能·经验分享·facebook
Henry-SAP2 小时前
SAP S/4HANA引领物流ERP新生态
人工智能·云原生·sap·erp