阅读视图

LangChain4j RAG检索增强生成

未完待续

关于知识库以及RAG、向量相似度和向量数据库有关前置知识,已经在下面文章提到:

1.概述

LangChain4j中RAG过程分为两个不同的阶段:索引和检索

  • 索引 Indexing

    将文档切分为片段,并嵌入转换为向量,将文档片段和对应向量成对一并保存到向量数据库。

  • 检索 Retrieval

    当用户提交一个应该使用索引文档回答的问题时,将用户提问内容进行嵌入转换为向量,在将转换成的向量在向量数据库中检索出相似的片段,连同提示词一块发送给大模型。

2.API详解

LangChain4j为RAG提供的工具,主要包括以下几种概念和对应API

  • 文档 Document 一个文件,office,txt,pdf等等

  • 文档加载器 Document Loader 从本地或网络加载文档

  • 文档解析器 Document Parser 用于将文档(ms office, pdf)转换为纯文本数据的工具

  • 文档分割器 Document Splitter 用于将文档按照行,段落等转换为的纯文本

  • 向量模型 Embedding Model 将文本等数据转换为向量坐标

  • 向量数据库操作对象 Embedding Store 用于操作向量数据库

2.1 文档加载器

  • FileSystemDocumentLoader 从本地磁盘绝对路径加载
  • ClassPathDocumentLoader 从工程类路径加载
  • UrlDocumentLoader 从URL加载

2.2 文档解析器

  • TextDocumentParser 默认的解析器

  • ApachePdfBoxDocumentParser pdf解析器

    <dependency>    <groupId>dev.langchain4j</groupId>    <artifactId>langchain4j-document-parser-apache-pdfbox</artifactId></dependency>
  • ApacheTikaDocumentParser 几乎可以解析所有文档,但是可能解析PDF不专业

    <dependency>    <groupId>dev.langchain4j</groupId>    <artifactId>langchain4j-document-parser-apache-poi</artifactId></dependency>
  • ApachePoiDocumentParser 解析ms office文档,同样要引入langchain4j-document-parser-apache-poi

2.3 文档分割器

  • DocumentByParagraphSplitter 根据段落分割

  • DocumentByLineSplitter 根据行分割

  • DocumentBySentenceSplitter 根据句子分割

  • DocumentByWordSplitter 根据词分割

  • DocumentByCharacterSplitter 根据固定数量字符分割

  • DocumentByRegexSplitter 按照正则表达式分割

  • DocumentSplitters.recursive() 递归分割(默认分割器,单片段最大300字符)将文本按照段,行,句,词,字的优先级顺序分割


💡关于递归分割器:

将文本按照段,行,句,词,字顺序分割,能尽量多的截取信息。例如,如果限制截取最多300字符,要分割一个每段200字共两段的文档,使用段落分割器,会丢失整个的第二段。但如果使用递归分割器,可以尽可能将信息截取为第一段200字外加第二段的前几个行/句子组成的100字的形式。

如果默认的单片段最大300字符不能满足需求,还可以定制自己的递归分割器

DocumentSplitters.recursive(每片段最多字符, 两片段之间重叠字符个数);

2.4 向量模型

langchain4j中使用EmbeddingModel接口操作向量模型,前面的例子中,文本向量化使用的是EmbeddingStoreIngestor默认的向量模型BgeSmallEnV15QuantizedEmbeddingModel,但是它的功能有限,实际项目中应当替换为其他的向量模型,例如阿里巴巴千问text-embedding-v4,它兼容open-ai的API协议,因此和ChatModel一样进行配置

langchain4j:  open-ai:    embedding-model:      base-url: https://dashscope.aliyuncs.com/compatible-mode/v1      api-key: ${QWKEY}      model-name: text-embedding-v4      log-requests: true      log-responses: true      dimensions: 1024

配置类中注入即可

@Resourceprivate EmbeddingModel embeddingModel;

2.5 向量数据库

langchain4j操作向量数据库使用EmbeddingStore接口,默认会采用一个内存向量数据库,实际项目中数据的持久化存储,需要整合外置向量数据库,比如redis-stack,milvus, Qdrant等

以Qdrant为例,首先要引入Qdrant的依赖

<dependency>    <groupId>dev.langchain4j</groupId>    <artifactId>langchain4j-qdrant</artifactId></dependency>

用Docker启动一个Qdrant示例

docker run -d -p 6333:6333 -p 6334:6334 qdrant/qdrant

6333端口是浏览器访问控制台用的,6334是grpc协议,用于客户端连接。浏览器打开对应ip的6333端口,访问/dashboard,就可以看见Qdrant的控制台了

在Dashboard的Console中执行命令,创建一个test-qdrant库,这个步骤和ES还是很相似的

创建的向量库的维度size=1024,要和嵌入模型配置dimensions:1024一致,否则保存数据会出错

PUT /collections/test-qdrant{    "vectors": {      "size": 1024,      "distance": "Cosine"    }}

配置类中使用EmbeddingStore接口操作对应向量数据库QdrantEmbeddingStore

@Bean@Primarypublic EmbeddingStore<TextSegment> embeddingStore() {    return QdrantEmbeddingStore.builder()            .host("192.168.228.104")            .port(6334)            .collectionName("test-qdrant")            .build();}

3.实现案例

这里先用一个简单实现介绍LangChain4j RAG的大致用法,采用阿里巴巴text-embedding-v4向量模型和Qdrant向量数据库,使用过程大致步骤是:

  1. 文档经Document Loader加载为Document对象到内存中,通过解析器Document Parser解析为文本,通过分割器Document Splitter进一步转换为片段Text Segment,最终,经向量模型Embedding Model转换为向量Embedding

  2. 当用户询问问题,ContentRetriever会将问题向量化,从向量数据库查询出相关片段夹在提示词中一并发送给大模型。

要实现一个最简单的langchain4j rag功能,需要导入langchain4j-easy-rag依赖

<parent>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-parent</artifactId>    <version>3.5.4</version>    <relativePath/></parent><dependencyManagement>    <dependencies>        <dependency>            <groupId>dev.langchain4j</groupId>            <artifactId>langchain4j-bom</artifactId>            <version>1.8.0</version>            <type>pom</type>            <scope>import</scope>        </dependency>    </dependencies></dependencyManagement><dependencies>    <dependency>        <groupId>dev.langchain4j</groupId>        <artifactId>langchain4j-spring-boot-starter</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-web</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-test</artifactId>        <scope>test</scope>    </dependency>    <dependency>        <groupId>dev.langchain4j</groupId>        <artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>    </dependency>    <dependency>        <groupId>dev.langchain4j</groupId>        <artifactId>langchain4j-reactor</artifactId>    </dependency>    <dependency>        <groupId>dev.langchain4j</groupId>        <artifactId>langchain4j-easy-rag</artifactId>    </dependency></dependencies><repositories>    <repository>        <name>Central Portal Snapshots</name>        <id>central-portal-snapshots</id>        <url>https://central.sonatype.com/repository/maven-snapshots/</url>        <releases>            <enabled>false</enabled>        </releases>        <snapshots>            <enabled>true</enabled>        </snapshots>    </repository></repositories><build>    <plugins>        <plugin>            <groupId>org.apache.maven.plugins</groupId>            <artifactId>maven-compiler-plugin</artifactId>            <configuration>                <source>21</source>                <target>21</target>                <encoding>UTF-8</encoding>            </configuration>        </plugin>    </plugins></build>

application.yml配置基本的日志,嵌入模型等

server:  port: 8080logging:  level:    dev.langchain4j: debuglangchain4j:  open-ai:    embedding-model:      base-url: https://dashscope.aliyuncs.com/compatible-mode/v1      api-key: ${QWKEY}      model-name: text-embedding-v4      log-requests: true      log-responses: true      dimensions: 1024

新建配置类:

  1. EmbeddingStore<TextSegment>:向量数据库,设置为@Primary替换spring的自动装配
  2. EmbeddingStoreIngestor:用于分割文档,转换向量和存储
  3. ContentRetriever:用于检索与提示词相关的知识,一并发送给大模型,通过.dynamicFilter的配置回调方法在查找到相关知识片段后,还要根据某个元信息进行匹配,这里简单的将传过来的会话ID:chatMemoryId当作用户ID,用于区分用户,实际项目中可以实现根据chatMemoryId得到userId的方法,最终用userId进行查询。
package org.example.config;import dev.langchain4j.data.document.DocumentSplitter;import dev.langchain4j.data.document.splitter.*;import dev.langchain4j.data.segment.TextSegment;import dev.langchain4j.memory.ChatMemory;import dev.langchain4j.memory.chat.ChatMemoryProvider;import dev.langchain4j.memory.chat.MessageWindowChatMemory;import dev.langchain4j.model.chat.StreamingChatModel;import dev.langchain4j.model.embedding.EmbeddingModel;import dev.langchain4j.model.openai.OpenAiStreamingChatModel;import dev.langchain4j.rag.content.retriever.ContentRetriever;import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;import dev.langchain4j.store.embedding.EmbeddingStore;import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;import dev.langchain4j.store.embedding.filter.MetadataFilterBuilder;import dev.langchain4j.store.embedding.qdrant.QdrantEmbeddingStore;import dev.langchain4j.store.memory.chat.ChatMemoryStore;import dev.langchain4j.store.memory.chat.InMemoryChatMemoryStore;import jakarta.annotation.Resource;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;@Configurationpublic class RagConfig {    @Resource    private EmbeddingModel embeddingModel;    @Bean    public StreamingChatModel streamingChatModel() {        return OpenAiStreamingChatModel.builder()                .baseUrl("https://api.deepseek.com/")                .apiKey(System.getenv("DSKEY"))                .modelName("deepseek-reasoner")                .logRequests(true)                .logResponses(true)                .returnThinking(true)                .build();    }    @Bean    public ChatMemoryStore chatMemoryStore() {        return new InMemoryChatMemoryStore();    }    @Bean    public ChatMemoryProvider chatMemoryProvider () {        return new ChatMemoryProvider() {            @Override            public ChatMemory get(Object id) {                return MessageWindowChatMemory.builder()                        .id(id)                        .maxMessages(1000)                        .chatMemoryStore( chatMemoryStore() )                        .build();            }        };    }    @Bean    public EmbeddingStoreIngestor ingestor() {        return EmbeddingStoreIngestor.builder()                .embeddingStore(embeddingStore() )                .documentSplitter( DocumentSplitters.recursive(1000, 100) ) //指定分割器                .embeddingModel(embeddingModel)                .build();    }    @Bean    public ContentRetriever contentRetriever(EmbeddingStore embeddingStore) {        return EmbeddingStoreContentRetriever.builder()                .embeddingStore(embeddingStore)                .maxResults(7) //返回片段数                .minScore(0.5) //最小余弦相似度                .embeddingModel(embeddingModel) //使用自己定义的向量模型                .dynamicFilter(query -> {                    Object chatMemoryId = query.metadata().chatMemoryId();                    String userId = chatMemoryId.toString();                    return MetadataFilterBuilder.metadataKey("author").isEqualTo(userId);                })                .build();    }     @Bean    @Primary    public EmbeddingStore<TextSegment> embeddingStore() {        return QdrantEmbeddingStore.builder()                .host("192.168.228.104")                .port(6334)                .collectionName("test-qdrant")                .build();    }}

@AiService注解上新增属性值contentRetriever = "contentRetriever",使chat功能具备RAG能力

@AiService(        wiringMode = AiServiceWiringMode.EXPLICIT,        streamingChatModel = "streamingChatModel",        chatMemoryProvider = "chatMemoryProvider",        contentRetriever = "contentRetriever")public interface RagAssistance {    Flux<String> chat(@UserMessage String prompt, @MemoryId String msgId);}

新建一个测试类,将信息向量化,然后写入刚刚创建的向量数据库

实际项目中,这个过程就是用户自己上传文档到自己的知识库,供大模型参考,同时还附带了一些知识片段的元信息,主要用于标识知识片段,例如:用于区分所属用户。

package org.example.test;import dev.langchain4j.data.document.Document;import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;import dev.langchain4j.data.document.parser.apache.pdfbox.ApachePdfBoxDocumentParser;import dev.langchain4j.data.embedding.Embedding;import dev.langchain4j.data.segment.TextSegment;import dev.langchain4j.model.embedding.EmbeddingModel;import dev.langchain4j.model.output.Response;import dev.langchain4j.store.embedding.EmbeddingStore;import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;import jakarta.annotation.Resource;import org.example.Main;import org.junit.jupiter.api.Test;import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest(classes = Main.class)public class RagTest {    @Resource    private EmbeddingStore embeddingStore;    @Resource    private EmbeddingModel  embeddingModel;    @Resource    EmbeddingStoreIngestor ingestor;    //文字    @Test    public void index() {        String msg = """                户晨风,男,汉族,1998年10月11日出生,成都卖有回音文化传媒有限公司法定代表人, [7]网络视频博主,泛娱乐领域自媒体创作者,截至2025年9月,哔哩哔哩粉丝数达67.4万,抖音粉丝数达93.7万 [1-2],微博粉丝数达20万。2025年9月16日,话题 “户晨风疑似被封” 登上微博热搜,其微博、抖音、B站等多个社交平台的账号遭到封禁或功能限制。                户晨风成长于一个贫穷农村家庭,后随父母搬到城里。2023年11月27日,他发布了首个作品《100元人民币,在泰国首都曼谷的购买力到底有多强?》,从此开始进行自媒体创作。2024年6月21日,发布作品《新加坡街边卖艺,84 岁老人的一生》。2025年9月7日,发布作品《呼和浩特,真假乞讨?——户晨风全球真假乞讨系列》。                2025年,户晨风以“苹果安卓”为标签代指消费、学历上的鄙视链等言论引发巨大争议 [5]。9月16日,其微博、抖音、B站等多个社交平台的账号遭到封禁或功能限制 [3]。9月20日下午,有网友报料,网红“户晨风”在抖音、微博等多个平台账号已被封禁,其账号内容被清空 [5]。9月30日晚,极目新闻记者搜索发现,户晨风全网账号被彻底封禁,且无法通过搜索找到账号,账号主页已无法查看信息 [6]。11月5日,户晨风账号被封详情披露,从展示跨国消费差异到制造阶层对立,户晨风以“苹果、安卓论”收割流量,最终突破监管红线 [9]。                2025年12月消息,网络账号“户晨风”在多个平台长期编造所谓“安卓人”“苹果人”等煽动群体对立言论,各平台相关账号已关闭。                """;        Response<Embedding> response = embeddingModel.embed(msg);        Embedding embedding = response.content();        TextSegment segment = TextSegment.from(msg);        segment.metadata().put("author", "lzj");        segment.metadata().put("doc", "1.txt");        embeddingStore.add(embedding, segment);    }    //模拟pdf文档    @Test    public void pdf() {        Document document = FileSystemDocumentLoader.loadDocument("D:/毕业设计/装订/答辩.pdf", new ApachePdfBoxDocumentParser());        document.metadata().put("author", "lzj");        document.metadata().put("doc", "答辩.pdf");        ingestor.ingest(document);    }}

调用RagAssistance的方法,可以看到会在发送提示词给大模型前检索向量数据库查询相似片段进行拼接

GET http://127.0.0.1:8080/rag-chat/stream?msg=你知道安卓苹果相对论吗&msgId=lzj

package org.example.controller;import jakarta.annotation.Resource;import org.example.ai.RagAssistance;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import reactor.core.publisher.Flux;@RestController@RequestMapping("rag-chat")public class RagController {    @Resource    private RagAssistance assistance;        @GetMapping(value = "stream", produces = "text/html; charset=utf-8")    public Flux<String> stream(String msg, String msgId) {        return assistance.chat(msg, msgId);    }}
2025-12-14T15:06:25.110+08:00  INFO 14848 --- [nio-8080-exec-1] d.l.http.client.log.LoggingHttpClient    : HTTP request:- method: POST- url: https://api.deepseek.com/chat/completions- headers: [Authorization: Beare...07], [User-Agent: langchain4j-openai], [Content-Type: application/json]- body: {  "model" : "deepseek-reasoner",  "messages" : [ {    "role" : "user",    "content" : "你知道安卓苹果相对论吗吗\n\nAnswer using the following information:\n2025年12月消息,网络账号“户晨风”在多个平台长期编造所谓“安卓人”“苹果人”等煽动群体对立言论,各平台相关账号已关闭。\n\n2025年,户晨风以“苹果安卓”为标签代指消费、学历上的鄙视链等言论引发巨大争议 [5]。9月16日,其微博、抖音、B站等多个社交平台的账号遭到封禁或功能限制 [3]。9月20日下午,有网友报料,网红“户晨风”在抖音、微博等多个平台账号已被封禁,其账号内容被清空 [5]。9月30日晚,极目新闻记者搜索发现,户晨风全网账号被彻底封禁,且无法通过搜索找到账号,账号主页已无法查看信息 [6]。11月5日,户晨风账号被封详情披露,从展示跨国消费差异到制造阶层对立,户晨风以“苹果、安卓论”收割流量,最终突破监管红线 [9]。\n\n户晨风,男,汉族,1998年10月11日出生,成都卖有回音文化传媒有限公司法定代表人, [7]网络视频博主,泛娱乐领域自媒体创作者,截至2025年9月,哔哩哔哩粉丝数达67.4万,抖音粉丝数达93.7万 [1-2],微博粉丝数达20万。2025年9月16日,话题 “户晨风疑似被封” 登上微博热搜,其微博、抖音、B站等多个社交平台的账号遭到封禁或功能限制。\n\n户晨风成长于一个贫穷农村家庭,后随父母搬到城里。2023年11月27日,他发布了首个作品《100元人民币,在泰国首都曼谷的购买力到底有多强?》,从此开始进行自媒体创作。2024年6月21日,发布作品《新加坡街边卖艺,84 岁老人的一生》。2025年9月7日,发布作品《呼和浩特,真假乞讨?——户晨风全球真假乞讨系列》。"  } ],  "stream" : true,  "stream_options" : {    "include_usage" : true  }}

一个简单的RAG实现就完成了,实际项目中按照实际情况切换向量数据库和文档加载器和分割器即可。

  •  

Spring AI使用知识库增强对话功能

未完待续

1.引言

之前提到过,大模型的训练语料库和现实世界相比,往往滞后,比如当下一些热门的话题大模型通常会不了解,一种解决这种问题的方式是,在发消息时将实时的相关的数据一并发送给它,对大模型的知识储备进行补充。

但是,实时的数据是海量的,不能将内容整个全部发送大模型,而且Token的限制也不允许这样做,我们只需要检索出和问题相关的片段然后拆分出来发送即可。

如何检索数据呢?用ES?答案是否定的,因为ES是一种全文检索,不能完美实现相关性检索,例如我们想要和大模型聊一下最近有哪些“国际争端”之类的话题,“柬泰边境冲突”肯定算一件,但是如果以“国际争端” “争端”为关键词简单的全文检索,无法将这个话题有关的内容全部查询命中,因为这种场景的检索要求的不是文字的匹配而是语义的匹配,于是这里就引入了一个概念:向量相似度。

2.向量相似度

首先理解向量,向量就是数学中代表一个既有大小又有方向的量,物理上也称为矢量,例如平面直角坐标系上从(0, 0)点到任意一点构成的线段就是一个向量,向量相似度指的就是两个向量是否相似,通过欧氏距离和余弦距离都可判断相似度,欧氏距离越小,相似度越高,余弦距离越大,相似度越高

计算机中的数据都是以数字的形式进行存储,如果根据内容含义将文字数据转换成空间中的坐标,就成功把文字信息向量化了,含义相似的文本,转换为点的距离越近,通过对比向量相似度即可获得语义相近的内容。

3.嵌入(Embedding)模型

根据内容转换为向量的工作需要交由支持文本的嵌入模型来完成

嵌入(Embedding)是文本、图像或视频的数值表示,能够捕捉输入之间的关系,Embedding 通过将文本、图像和视频转换为称为向量(Vector)的浮点数数组来工作。这些向量旨在捕捉文本、图像和视频的含义,Embedding 数组的长度称为向量的维度。通过计算两个文本片段的向量表示之间的数值距离,应用程序可以确定用于生成嵌入向量的对象之间的相似性。

我用过常见的支持文本的嵌入模型有:

因为DeepSeek没有文本嵌入模型,因此这里采用阿里云百炼平台通义千问text-embedding-v4实现文本向量化。

基于jdk-21创建spring-boot项目,引入spring-boot依赖3.5.7,spring-ai依赖1.0.3,因为阿里云百炼平台兼容了OpenAI的协议,因此还需要引入spring-ai-starter-model-openai对接阿里云百炼平台

<parent>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-parent</artifactId>    <version>3.5.7</version></parent><dependencyManagement>    <dependencies>        <dependency>            <groupId>org.springframework.ai</groupId>            <artifactId>spring-ai-bom</artifactId>            <version>1.0.3</version>            <type>pom</type>            <scope>import</scope>        </dependency>    </dependencies></dependencyManagement><dependencies>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-web</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-test</artifactId>        <scope>test</scope>    </dependency>    <dependency>        <groupId>org.springframework.ai</groupId>        <artifactId>spring-ai-starter-model-openai</artifactId>    </dependency>    <!-- Lombok -->    <dependency>        <groupId>org.projectlombok</groupId>        <artifactId>lombok</artifactId>    </dependency></dependencies><build>    <plugins>        <plugin>            <groupId>org.apache.maven.plugins</groupId>            <artifactId>maven-compiler-plugin</artifactId>            <configuration>                <source>21</source>                <target>21</target>                <encoding>UTF-8</encoding>            </configuration>        </plugin>    </plugins></build>

application.yml中,将阿里云百炼text-embedding-v4配在openai下,而且URL后面的/v1必须去掉,否则无法连接成功

spring:  ai:    openai:      base-url: https://dashscope.aliyuncs.com/compatible-mode      api-key: sk-      embedding:        options:          model: text-embedding-v4          dimensions: 1024logging:  level:    org.springframework.ai: debug

测试一下文本转向量

package org.example.test;import jakarta.annotation.Resource;import org.example.Main;import org.junit.jupiter.api.Test;import org.springframework.ai.openai.OpenAiEmbeddingModel;import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest(classes = Main.class)public class TestEmbedding {    @Resource    private OpenAiEmbeddingModel embeddingModel;    @Test    public void test() {        String text = "今天是10月的最后一天";        float[] embed = embeddingModel.embed(text);        for (float v : embed) {            System.out.print(v+" ");        }    }}

得到System.out.print结果:

-0.03598024 -0.07856088 -0.023570947 -0.05446673 -0.016179034 0.028628573 0.006583633 -0.0021095797 0.012744679 0.011946459 0.0030872307 0.033162996 0.07281907 0.047088236 -0.02217574 0.017708397 -0.036033902 -0.061067134 -0.017466918 0.021961093 0.03321666 0.018821878 0.040943958 0.025355203 -0.036785167 0.00426276 -0.003155985 -0.031714126 0.0018714555 0.020539057 -0.0055271657 -0.028735897 -0.011765351 0.030587228 -0.04013903 0.0022303187 -0.04231233 0.07968778 0.0012048752 0.05672053 0.025288126 -0.015789986 -6.0411455E-4 0.004504238 0.009216415 0.044780776 0.012315384 -0.0024734738 0.009605463 0.008418196 0.01958656 -0.010101835 0.06536008 0.058115736 -0.015991218 -0.009887188 0.046041828 -0.0139789 -0.017909627 6.5064937E-4 -0.014891151 -0.014810658 -0.05677419 -0.07110189 0.007955363 -0.013220928 -0.04464662 -0.008082809 -0.016849807 -0.053930115 0.05731081 -0.006352216 -0.013173973 0.0062884926 -0.015025306 0.057632778 0.0033555396 0.067989506 -0.012536739 -0.103942916 -0.014448441 0.014770412 -0.0021229952 -0.013194096 0.0754485 0.030426243 -0.017627902 -0.0200561 0.019251173 0.057579115 0.01934508 -0.026696747 -0.0011495365 -0.043573387 -0.006570217 -0.031016523 -0.0570425 0.003638941 0.013871577 0.006305262 -6.152242E-5 0.06407219 -0.0048530395 -0.010195743 0.054627717 0.10490883 -0.04494176 0.019090187 0.003887127 -0.026066221 -0.044727113 -0.018419415 -0.0117452275 0.019559728 -0.011792182 0.061174456 -0.0058290134 0.025824744 0.0021162874 -0.0018446245 -0.012959326 0.024442952 -0.011282395 -0.044485636 0.009806694 -0.012825171 0.011161655 -0.015253368 0.05465455 0.012147691 0.016031465 -0.032599546 0.0017523933 -0.027743153 0.006915665 -0.0217062 0.01666199 0.027313858 -0.025033232 -0.0045780228 0.02766266 -0.0151728755 0.012496493 -0.013542898 -0.04247332 0.015937556 0.012147691 -0.06718458 -0.011926336 0.011322641 -0.008344411 -0.0033370934 -0.034933835 0.055432644 -0.018969448 -0.04005854 0.023101406 0.024939325 -0.025797913 0.018419415 0.03917312 0.017332762 0.03871699 0.010075004 0.031016523 -0.037080307 -0.025194218 -0.026844317 0.028896881 0.028789558 -0.010007926 0.042607475 -4.5151377E-4 0.0042862366 0.023839258 0.035175312 0.018687723 -0.029889625 0.0059933527 -0.008860906 0.04657845 -4.5235225E-4 0.0038904808 -0.014153301 -0.014971644 -0.014770412 0.0618184 -0.00426276 -0.05741813 -0.0048295623 2.8423988E-4 0.029835964 -7.8815775E-4 -0.004014574 0.015696079 -0.040300015 -0.038502347 0.043788034 0.0068888343 -0.013046526 0.015843648 0.03809988 0.0029027683 0.02067321 0.07303372 -0.019908529 0.0147435805 -0.0077407155 -0.013965485 -0.028574912 0.026978472 -0.014877736 0.012818464 -0.023409963 -0.038153544 0.031043354 -0.060852487 0.047893163 0.029513992 0.011181778 0.03364595 0.04220501 -0.021209829 -0.013884992 0.001418684 4.1105782E-4 -0.0018546862 0.047329713 -0.008941398 -0.00949814 0.0042795287 -0.026482102 -0.070565276 -0.02332947 -0.053983774 -0.0015067229 -0.0060268915 0.0076132687 -0.022309896 0.0060000606 0.013509359 0.022725774 8.9883525E-4 -0.009478017 -0.025355203 -0.018030366 -0.054332577 -0.060745165 -0.050361603 0.010282943 -0.024349043 -0.03235807 0.045880843 0.013952069 -0.011054331 -0.030748215 -0.035470452 0.013898407 0.0036490026 0.03834136 -0.014314286 0.02972864 -5.638682E-4 -0.041104943 -0.02530154 -0.024429537 0.030426243 0.06428684 -0.022846514 -0.013408744 -0.008418196 0.016836392 0.0109067615 -0.07893652 -0.046202812 0.032036096 -0.05076407 -0.006573571 -0.0034293248 0.014609426 -0.038475513 -0.017373009 -0.009571925 2.6555781E-5 -0.017641319 -0.020592717 -0.052856877 0.007143728 0.04641746 -0.0039038963 -0.027407767 0.012852002 -0.008062686 0.0014824073 0.040273186 -0.03335081 0.0540911 0.036677845 0.0097530335 0.0017507164 -0.053286172 -0.0029430147 -0.021035427 0.011175071 0.027421182 -0.009558509 -0.036892492 0.0724971 -0.024442952 -0.08891761 -0.03887798 -0.023758763 0.016031465 0.032250743 0.071745835 -0.012422708 -0.013730714 -0.02451003 0.00547015 -0.024335628 0.027582169 0.023946581 -0.03780474 -0.010859808 -0.0035618022 0.015132629 0.027877308 -0.025623512 0.013167266 -0.03149948 -0.04016586 0.008445026 -0.01471675 -0.0022101956 -9.407585E-4 -0.023248978 0.033458136 -0.017882796 -0.00967254 0.015991218 0.013113604 -0.043895356 0.008277333 0.045585703 0.0082236715 0.006684249 0.029970119 0.0042191595 -0.03217025 -0.001222483 0.007633392 0.012805048 0.044163667 0.01855357 -0.0058088903 -0.005282334 0.047624853 0.023020914 -0.04512958 0.027273612 -0.0013474143 -0.05049576 -0.008364534 0.008954814 0.03093603 -0.02094152 0.05347399 0.04987865 -0.0011704981 -0.021813523 0.05586194 -0.017453503 0.011731812 0.015239953 0.008740166 -0.014233794 0.026535762 0.0014245532 0.017708397 0.07534117 0.034907006 0.017238855 0.0029178606 -0.009109091 0.03783157 -0.0024298737 -0.021397645 -0.001357476 -0.003075492 -3.2532468E-4 -0.0055070426 -0.003152631 0.007096774 0.0079821935 0.05390328 -0.0042795287 -0.0202305 -0.0375096 0.016930299 -0.02822611 -0.047410205 0.005922922 -0.015803402 -0.0042594057 -0.001038859 -0.03869016 -0.03088237 0.021209829 -0.0076400996 0.020391487 0.0052186106 -0.057847425 0.074106954 0.0014438379 -0.03624855 1.7922204E-5 -0.007036404 -0.007774254 0.04075614 0.055808276 -0.035121653 0.009873772 0.033431303 -0.048644427 0.04842978 0.096698575 0.024684431 -0.03179462 -0.017681565 0.031901944 -0.011322641 -0.0019754253 0.025771081 0.014824074 0.057793763 -0.026280869 0.056613203 0.038851146 -0.044136833 0.0038737115 -0.013059942 0.034987498 -0.030184766 -0.004108482 -0.006191231 0.045075916 0.05586194 -0.0335118 -0.007090066 -0.023906333 0.052830048 -0.015937556 0.01560217 -9.608817E-4 0.0015553539 0.029809132 0.052776385 -0.001125221 -0.021397645 0.019532897 0.022631867 3.7709996E-4 -0.014045977 0.011792182 -0.009343862 0.045907672 -0.001883194 -0.014944812 0.056398556 0.007217513 0.007512653 -0.0023175192 0.056183908 -0.009679248 0.022336727 0.044378314 0.0079084085 0.061335444 0.001137798 0.069116406 0.017077869 0.001785932 -0.04220501 -0.009404232 -0.052481245 -0.044673454 0.015937556 -0.03302884 0.06128178 0.0030671076 0.018674308 -0.061442766 -0.034263063 -0.011718397 -0.016447343 0.011644612 -0.03123117 0.06273065 -0.008941398 -0.039360933 -0.035631437 0.017212024 -0.05108604 -0.007573022 0.036785167 0.016796146 -0.059296295 -0.011067747 -0.02852125 -0.031338494 0.021692785 -0.008787121 0.011416549 -0.013871577 0.024751507 0.003518202 0.019760959 -0.0030855539 -0.007230928 -0.010148789 -0.032841023 0.027877308 -0.007626684 0.050066464 0.006358924 -0.06466247 -0.043627046 0.010282943 0.062516004 -0.0027367522 0.02094152 -0.016447343 0.036972985 0.0123288 0.025838159 0.052266598 -0.007673638 -0.012657478 -0.018164521 -0.055808276 -0.03410208 0.038448684 -0.016621744 0.012134275 0.016568081 0.034611866 -0.033967923 0.015615585 -0.0070766504 -0.004316421 -0.011953167 -0.00802244 -0.015682662 0.0045880843 -0.011517165 -0.020485394 0.0040749433 0.05020062 0.01884871 0.012013537 -0.028279772 0.011631196 -0.004296298 0.023074577 -0.03450454 -0.015722908 -0.03388743 -0.038448684 -0.0037227878 0.03394109 -0.033967923 0.0036825414 0.0035114943 0.029192021 7.2946516E-4 -0.017855966 -0.033565458 0.014381364 0.06895542 -0.038824316 -0.0030771692 -0.011456795 -0.008881029 -0.019921945 -0.0099207265 -0.023155069 -0.001333999 -0.006436063 0.01265077 0.034746017 0.01660833 0.020606132 -0.030077443 0.026468685 0.028655404 -0.02822611 0.018808464 -0.028977375 0.029218853 -0.014730166 -0.026039392 -0.050388437 0.03353863 0.04598817 0.026388193 -0.04483444 0.0290847 -0.01621928 6.7370717E-4 -0.022524543 -0.004400268 -0.026589425 0.0084852725 -0.0109403 -0.0037529725 -0.019103603 -0.0023695042 -0.05390328 -0.0077541308 -0.010249405 -0.018030366 -0.009853649 0.02320873 -0.019251173 0.028628573 0.012724555 -0.018687723 -0.013777669 0.029594485 0.0066808946 0.018030366 0.04311726 -0.03147265 -0.011684858 -0.012234892 0.0052487953 -0.07185315 -0.0023393193 -0.05291054 -0.003102323 -0.0083913645 0.030855538 0.024496615 0.01144338 -0.031258002 -0.0024114274 -0.072014146 -0.02212208 0.026106467 0.0036121102 0.008364534 -0.04429782 0.017923042 0.03324349 0.040273186 0.046444293 0.014622842 -0.03149948 0.009243246 0.012053783 -0.04875175 0.015333861 -0.028896881 -0.04759802 0.012603817 -0.010490883 -0.033726446 -0.031633634 0.009598755 0.037375446 -0.06342825 -0.022658696 -0.026696747 0.0478395 0.028091954 -0.0057787057 -0.00426276 0.025824744 -0.010276236 0.006818403 0.03270687 0.061979383 0.018942617 0.026495516 -0.04547838 -0.007988901 -0.036436364 0.08151228 0.0067949262 0.018473076 -0.0026344592 -0.0217062 -0.010356728 0.0043398985 0.020659795 0.020109762 -0.052561738 0.007190682 -0.007438868 4.2761752E-4 -0.0850003 0.0050006094 -0.0049268245 -0.023557533 -0.019801207 -0.0014958228 0.03149948 -0.020445148 0.0035014327 -0.0356851 0.011798889 0.035443623 0.012852002 -0.013274589 -0.018634062 0.043492895 0.032492224 0.022846514 -0.02173303 -0.0043398985 -0.05494969 -0.0059061525 0.009618878 -0.009169461 0.06493078 0.0049268245 0.039012134 -0.007774254 -0.01315385 0.015763156 -0.06557473 -0.048483443 -5.299103E-4 4.33906E-4 0.023517286 0.010879931 -0.026656501 -0.019895114 -0.006137569 -0.03745594 -0.029353008 0.013569729 0.011181778 -0.001982133 0.107752904 0.04700774 0.008015732 -0.022055002 -0.06986767 0.035711933 0.022189157 -0.03300201 -0.019036526 0.012878833 -0.0139252385 -0.023959996 0.079634115 0.0098268185 -0.027474845 -0.055164337 0.016594913 -0.019278003 0.029513992 -0.0052420874 0.038260866 0.022403803 0.004500884 0.023839258 -0.0031844927 0.023718517 -0.031714126 -0.014636258 -0.0014119763 0.029916456 -0.01577657 -0.016326604 0.012053783 0.026817488 0.0070296964 -0.05972559 -0.036329042 0.026025975 -0.082263544 -0.0279578 0.013361789 0.024925908 0.04510275 -0.0040715896 0.028172448 -0.025288126 0.059832912 0.045290563 0.040917125 -0.031016523 -0.0013775992 -0.009310323 0.001955302 0.115265556 -0.017855966 -0.04247332 0.02347704 -0.035604607 0.07367766 -0.028279772 0.010430513 0.020539057 -0.04368071 0.011027501 0.019895114 -0.03262638 0.0088206595 3.9240194E-4 0.017963288 0.002003933 0.0064226473 -0.016541252 0.00426276 -2.3770503E-4 -0.011658027 -0.0043130675 0.0033639243 -0.00293463 -0.0147435805 -0.01120861 -0.010859808 0.01855357 0.0033656014 0.023101406 -0.043922186 0.010484175 0.032250743 0.0021531798 0.013804499 0.017762057 -0.0022940421 0.023383131 0.047061402 -0.003254924 0.014072808 0.0011218671 -0.009934141 0.013207512 -0.014019147 -0.02261845 -0.017708397 0.026830902 -0.016594913 -0.0033773398 -0.04928837 -0.028118785 -0.035819255 0.0012769833 -0.0342094 0.002465089 0.061120797 -0.020015853 0.0141667165 0.022578204 -0.030721383 0.040541492 0.006204646 0.008143179 -0.013489236 -0.0075663147 -0.008753582 0.004957009 0.0419367 -0.006110738 -0.01070553 0.042097688 0.034638695 0.11472894 -0.011919629 0.04005854 -0.027769985 -0.014528934 -0.02067321 0.0023057808 -0.041990362 -0.03895847 0.071745835 0.03061406 -9.935818E-4 -0.017466918 0.04365388 0.0046786387 -0.030184766 0.03694615 -0.02559668 0.0695457 0.027005304 -0.009759741 -0.052078784 0.03388743 0.008237087 0.0062147076 0.0039038963 0.018392583 0.035926577 0.015025306 -0.0045545455 -0.012483077 0.008310872 0.0040179277 -0.010926885 0.0058055366 -0.0060939686 -0.005590889 -0.028306602 -0.02377218 -0.009303615 -0.058115736 -0.015400938 -0.025180802 0.013817915 -0.008639551 0.02320873 -0.06986767 -4.8337548E-4 0.014448441 -0.030855538 0.004222513 0.028977375 -0.031982437 0.03305567 0.017077869 0.054600887 0.0019653635 0.043009937 -0.018982863 0.043519724 0.029889625 -0.010933593 0.010504298 -0.033726446 0.0075864377 0.0058357213 -0.012322092 0.06965302 -0.014327702 0.010168912 -0.03453137 -0.048000485 -0.007653515 0.04070248 0.015696079 0.017587656 0.011966582 0.010873224 -0.05827672 -0.01734618 -0.009102384 -0.014408194 0.0010044819 0.0076602227 0.027287029 0.03957558 0.021062259 0.010517714 -0.02471126 0.08231721 0.053071525 -0.0013633452 -0.01592414 -0.04131959 0.014032562 -0.035550945 0.03147265 -0.017641319 -5.18591E-4 -0.04875175 -0.03093603 -0.0014639611 -0.020887857 -0.013764253 -0.08033172 -0.023409963 0.0053997193 -0.14016463 -0.01949265 -0.048027314 -0.005798829 0.046229646 0.026374778 -0.028655404 -0.026924811 0.034021586 0.025234465 -0.009223123 -0.0021951033 -0.017279102 0.015857063 0.07399963 0.0077340077 0.0017373009 0.007834624 0.0055405814 -0.012825171 0.0570425 -0.014072808 0.027367521 -0.022940421 0.008163302 -0.013247758 -0.0064159394 0.014555764 -0.037482772 0.0077071767 -0.056076586 0.053581312 0.059242632 3.047823E-4 -0.05288371 0.0017339471 -0.0077943774 0.018956034 -0.007190682 0.011175071 0.004765839 0.040970787 -0.040621985 0.054037437 0.07421428 -0.023020914 

怎样知道这个嵌入模型转换的向量值准不准呢,做一个小测试:查询list中的每个话题和“体育赛事”这个话题的相似度,并将模型计算的结果进行欧氏距离判断,看看是不是话题越相似,距离越短。

@Testpublic void test() {    float[] embed1 = embeddingModel.embed("体育赛事");        List<String> list = Arrays.asList(        "中国河北发生滦河第一号洪水",        "菲律宾和中国就南海问题进行交涉",        "武大靖被韩国人在ins上谩骂",        "日本政府决定将核污染水进行排海",        "中华人民共和国全运会在天津开幕",        "在中国的调节下,沙特和伊朗和解",        "谷爱凌在2022北京冬奥会上获得滑雪冠军",        "缅甸曼德勒发生8.0级地震",        "无法忍受北约东扩,俄罗斯进攻乌克兰",        "湘潭大学周立人因投毒被判处死刑",        "全红婵在东京奥运会获得跳水金牌"    );    for (String s : list) {        float[] embed2 = embeddingModel.embed(s);        System.out.println(s +"=" +euclideanDistance(embed2, embed1));    }}/** * 计算欧氏距离 (Euclidean Distance) * @param vector1 第一个向量 * @param vector2 第二个向量 * @return 欧氏距离 */public static double euclideanDistance(float[] vector1, float[] vector2) {    if (vector1 == null || vector2 == null) {        throw new IllegalArgumentException("输入向量不能为null");    }    if (vector1.length != vector2.length) {        throw new IllegalArgumentException("向量维度必须相同");    }    if (vector1.length == 0) {        throw new IllegalArgumentException("向量不能为空");    }    double sum = 0.0;    for (int i = 0; i < vector1.length; i++) {        double diff = vector1[i] - vector2[i];        sum += diff * diff;    }    return Math.sqrt(sum);}

得到结果显示,“全红婵在东京奥运会获得跳水金牌”,“武大靖被韩国人在ins上谩骂”,“谷爱凌在北京冬奥会上获得滑雪冠军”,“中华人民共和国全运会在天津开幕”和关键词的距离都是1.0,1.1左右,小于其他的1.2!

中国河北发生滦河第一号洪水=1.2565409585119849菲律宾和中国就南海问题进行交涉=1.2780262570947603武大靖被韩国人在ins上谩骂=1.1504923215307303日本政府决定将核污染水进行排海=1.2980210701931219中华人民共和国全运会在天津开幕=1.0548370809772176在中国的调节下,沙特和伊朗和解=1.2655944458999424谷爱凌在2022北京冬奥会上获得滑雪冠军=1.1482314969126597缅甸曼德勒发生8.0级地震=1.2719576699963044无法忍受北约东扩,俄罗斯进攻乌克兰=1.273157362706503湘潭大学周立人因投毒被判处死刑=1.2694025438988223全红婵在东京奥运会获得跳水金牌=1.1600613375770383

4.向量数据库

之前提到,如果实时的数据是海量的,不能将内容整个全部发送大模型,而且Token的限制也不允许这样做,我们需要检索出和问题相关的片段然后拆分出来发送给大模型,而且是通过将文本转换成向量并根据向量相似度来进行匹配,这样,海量数据的储存和检索就需要向量数据库来完成。

Spring AI支持的向量数据库有很多,且对操作向量数据库制定了统一的接口标准org.springframework.ai.vectorstore.VectorStorehttps://docs.spring.io/spring-ai/reference/api/vectordbs.html#_vectorstore_implementations),这里就以支持向量的Redis (Redis Stack)为例

pom.xml

<dependency>    <groupId>org.springframework.ai</groupId>    <artifactId>spring-ai-starter-vector-store-redis</artifactId></dependency>

新增向量数据库的配置

spring:  ai:    vectorstore:      redis:        initialize-schema: false #不自动初始化索引结构,因为可能不能满足我们的查询要求        index-name: custom-index #向量库索引名        prefix: "doc:" #key前缀  data:    redis:      host: 192.168.228.104      port: 6379      database: 0

用Docker启动一个Redis Stack实例用于测试

docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest

手动设置redis-stack的custom-index索引结构,主要是为了将user_id设置为TAG,才能在Spring AI中进行==查询,当前版本Spring AI自动生成的索引是TEXT

FT.CREATE custom-index ON JSON PREFIX 1 "doc:" SCHEMA $.user_id AS user_id TAG $.content AS content TEXT $.embedding AS embedding VECTOR HNSW 6 TYPE FLOAT32 DIM 1024 DISTANCE_METRIC COSINE

新建测试类,可以直接注入并使用VectorStore操作向量数据库

package org.example.test;import jakarta.annotation.Resource;import org.example.Main;import org.junit.jupiter.api.Test;import org.springframework.ai.document.Document;import org.springframework.ai.vectorstore.VectorStore;import org.springframework.boot.test.context.SpringBootTest;import java.util.Arrays;@SpringBootTest(classes = Main.class)public class VectorStoreTest {    @Resource    private VectorStore vectorStore;    @Test    public void test() {        Document document = new Document("1", "一段测试信息", new HashMap<>());        vectorStore.add(Arrays.asList(document));    }}

打开8001端口的redis-stack管理页面,可以看到文本数据及转换后的向量数据保存到了redis-stack中

还可以将PDF文档向量化,保存进向量数据库,需要借助spring-ai-pdf-document-reader工具,这里以我的本科毕业答辩PPT转成PDF为例测试

 <dependency>    <groupId>org.springframework.ai</groupId>    <artifactId>spring-ai-pdf-document-reader</artifactId></dependency>
package org.example.test;import jakarta.annotation.Resource;import org.example.Main;import org.junit.jupiter.api.Test;import org.springframework.ai.document.Document;import org.springframework.ai.reader.ExtractedTextFormatter;import org.springframework.ai.reader.pdf.PagePdfDocumentReader;import org.springframework.ai.reader.pdf.config.PdfDocumentReaderConfig;import org.springframework.ai.vectorstore.VectorStore;import org.springframework.boot.test.context.SpringBootTest;import java.util.List;@SpringBootTest(classes = Main.class)public class VectorStoreTest {    @Resource    private VectorStore vectorStore;    @Test    public void test() {        PagePdfDocumentReader reader = new PagePdfDocumentReader(                "file:///C:/Users/lzj20/Desktop/答辩.pdf",                PdfDocumentReaderConfig.builder()                        .withPageExtractedTextFormatter(ExtractedTextFormatter.defaults())                        .withPagesPerDocument(1)                        .build()        );        List<Document> documents = reader.read();        for (Document document : documents) {            document.getMetadata().put("user_id", "001");        }        vectorStore.add(documents);    }}

数据保存成功

还可以搜索相关性高的内容

@Testpublic void search() {    SearchRequest request = SearchRequest.builder()            .query("服务器配置")            .topK(3) //相似度最高的前几名            //.filterExpression("user_id == '001'") //可以根据metadata中的内容过滤            .build();    List<Document> documents = vectorStore.similaritySearch(request);    for (Document document : documents) {        System.out.println(document.getText());        System.out.println(document.getScore());    }}

5.使用知识库增强对话功能(RAG)

最后一步,利用保存了我们自己上传了文档的向量数据库,作为大模型对话的知识库,对大模型尚未了解的内容进行补充,首先先将之前用过的对话模型DeepSeek的依赖和配置添加进去

spring:  ai:    deepseek:      base-url: https://api.deepseek.com      api-key: ${DEEPSEEK_KEY}
<dependency>    <groupId>org.springframework.ai</groupId>    <artifactId>spring-ai-starter-model-deepseek</artifactId></dependency>

再添加Spring AI对RAG功能支持的advisor

<dependency>    <groupId>org.springframework.ai</groupId>    <artifactId>spring-ai-advisors-vector-store</artifactId></dependency>

配置一个支持知识库自动检索的ChatClient,并关联向量数据库vectorStore

package org.example;import org.springframework.ai.chat.client.ChatClient;import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;import org.springframework.ai.chat.memory.ChatMemory;import org.springframework.ai.deepseek.DeepSeekChatModel;import org.springframework.ai.vectorstore.SearchRequest;import org.springframework.ai.vectorstore.VectorStore;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class ModelConfig {    @Bean    public ChatClient ragClient(DeepSeekChatModel model, ChatMemory chatMemory, VectorStore vectorStore) {        return ChatClient.builder(model)                .defaultAdvisors(                        SimpleLoggerAdvisor.builder().build(),                        MessageChatMemoryAdvisor.builder(chatMemory).build(),                        QuestionAnswerAdvisor.builder(vectorStore)                                .searchRequest(                                        SearchRequest.builder()                                        .similarityThreshold(0.6)                                        .topK(2)                                        .build()                                ).build()                    ).build();    }}

controller中使用ragClient,并使用advisor.param(QuestionAnswerAdvisor.FILTER_EXPRESSION, "user_id == '001'")区分不同用户的文档,实际项目中,用户ID应该从后端登录信息获得

package org.example.controller;import jakarta.annotation.Resource;import org.springframework.ai.chat.client.ChatClient;import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;import org.springframework.ai.chat.memory.ChatMemory;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import reactor.core.publisher.Flux;@RestController@RequestMapping("ai")public class ChatController {    @Resource    private ChatClient ragClient;    @GetMapping(value = "rag-stream", produces = "text/html;charset=utf-8")    public Flux<String> rag(String msg, String chatId) {        return ragClient.prompt()                .user(msg)                .advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, chatId))                .advisors(advisor -> advisor.param(QuestionAnswerAdvisor.FILTER_EXPRESSION, "user_id == '001'"))                .stream()                .content();    }}

通过测试,可以看到大模型回答它不知道的问题时,已经有检索知识库了

参考

  1. https://java2ai.com/docs/1.0.0-M6.1/concepts/?spm=4347728f.33449ac1.0.0.7b7d556bo6eN0q
  •  

简单理解AI智能体

一、智能体是什么

文章的开头,先来举一个身边最简单的例子,比如字节推出的云雀是大模型,而豆包和Coze就是智能体,豆包是一个实现了对话功能的智能体,而Coze是一个可以实现工作流编排的智能体。

1986年,智能体(AIAgent、人工智能代理)的概念最早由被誉为“AI之父”的马文·明斯基(Marvin Minsky)在《意识社会》(The society of Mind)中提出。

明斯基定义的智能体的核心要素:

  • 要素1:分布式智能体集合
  • 要素2:层级协作机制
  • 要素3:无中央控制

但是,明斯基对智能体的定义和现代的智能体定义有很大区别,直到2023年6月,OpenAl的元老翁丽莲在个人博客(https://lilianweng.github.io/posts/2023-06-23-agent/)中首次提出了现代AI Agent架构:智能体(AI Agent)是一种能够自主行动、感知环境、 做出决策并与环境交互的计算机系统或实体,通常依赖大型语言模型作为其核心决策和处理单元,具备独立思考、调用工具去逐步完成给定目标的能力。

二、智能体的核心要素

智能体有以下核心要素:

  • 核心要素1: 大模型(LLM)

    大模型作为“大脑”: 提供推理、规划和知识理解能力,是AIAgent的决策中枢。

  • 核心要素2: 记忆(Memory)

    • 长期记忆: 可以横跨多个任务或时间周期,可存储并调用核心知识,非即时任务。可以通过模型参数微调(固化知识),知识图谱(结构化语义网络)或向量数据库(相似性检索)方式实现。

    • 短期记忆:存储单次对话周期的上下文信息,属于临时信息存储机制。受限于模型的上下文窗口长度。

  • 核心要素3: 工具使用(Tool Use)

    调用外部工具(如API、数据库)扩展能力边界。

  • 核心要素4: 规划决策(Planning)

    通过任务分解、反思与自省框架实现复杂任务处理。例如,利用思维链(chain of Thought)将目标拆解为子任务,并通过反馈优化策略。

  • 核心要素5: 行动(Action)

    实际执行决策的模块,涵盖软件接口操作(如自动订票)和物理交互(如机器人执行搬运)。比如:检索、推理、编程等。

三、智能体的运用

智能体在PC,手机以及自动驾驶等方面都有广泛的应用。在单一智能体的基础上,多个智能体之间可以交互写作。

参考

  1. 0代码0基础,小白搭建智能体&知识库,尚硅谷,2025-03-17
  •