掘金 人工智能 9小时前
SpringAI(RAG+MCP)使用-未完结
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文详细介绍了如何使用Spring AI构建智能聊天机器人,重点阐述了集成OpenAI和DeepSeek模型、实现会话上下文记忆(包括JDBC持久化和自定义会话历史表)以及构建RAG(Retrieval-Augmented Generation)知识库的关键技术。文章提供了具体的依赖配置、Spring Bean的定义以及数据源和文档处理的示例,旨在帮助开发者快速上手Spring AI,打造功能更强大的AI应用。

✨ **模型集成与ChatClient配置**: Spring AI支持集成多种大语言模型,如OpenAI和DeepSeek。通过`ChatClient.builder()`可以方便地配置模型、系统提示(SystemPrompt)和拦截器(Advisors),实现与AI的灵活交互,并可为不同场景创建特定的ChatClient,如用于生成标题的`titleChatClient`。

🧠 **会话记忆持久化**: 文章详细介绍了两种会话记忆的实现方式:一是利用Spring AI自带的`JDBCChatMemoryRepository`,通过JDBC将对话记录持久化到`SPRING_AI_CHAT_MEMORY`表中,以`conversation_id`关联同一会话;二是创建自定义的`d_chat_type_history`表,用于存储不同用户或会话的额外信息,如标题和状态,并结合MyBatis-Plus进行管理,实现更精细化的会话追踪。

🛡️ **Advisors实现交互增强**: Spring AI的Advisors机制为聊天机器人提供了强大的扩展能力。文中展示了如何通过`SimpleLoggerAdvisor`进行日志记录,`ChatTypeHistoryAdvisor`记录会话ID到自定义表中,`ChatTypeTitleAdvisor`调用另一个ChatClient为会话生成标题,以及`MessageChatMemoryAdvisor`将对话保存到Spring AI的内存库中,这些Advisors通过责任链模式协同工作,增强了聊天机器人的功能。

📚 **RAG知识库构建**: 实现RAG(Retrieval-Augmented Generation)的关键在于向量数据库的接入和文档处理。文章以Pgvector为例,介绍了如何配置`PgVectorStore`,将其与PostgreSQL数据库和Embedding Model结合,用于存储和检索向量化的文档。同时,通过`MarkdownDocumentReader`等插件,可以将各种格式的文档转换为Spring AI的`Document`对象,为知识库的构建奠定基础。

🗄️ **多数据源管理**: 当项目同时使用MySQL和PostgreSQL时,为了避免`JdbcTemplate`的冲突,需要通过`@Configuration`和`@Bean`注解自定义数据源配置。文章提供了`DataSourceConfiguration`类,明确指定了MySQL为主数据源(`@Primary`),并为PostgreSQL创建了单独的数据源和`JdbcTemplate`实例,确保了多数据源的正确使用。

依赖

<properties>  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  <java.version>17</java.version>  <spring.boot.version>3.5.0</spring.boot.version>  <spring-ai.version>1.0.0</spring-ai.version>  <lombok.version>1.18.20</lombok.version>  <mybatis-plus.version>3.5.7</mybatis-plus.version>  <fastjson.version>1.2.83</fastjson.version>  <hutool.version>5.8.25</hutool.version></properties><dependency>  <groupId>org.springframework.ai</groupId>  <artifactId>spring-ai-starter-model-openai</artifactId></dependency><dependency>  <groupId>org.springframework.ai</groupId>  <artifactId>spring-ai-starter-model-deepseek</artifactId></dependency><!--持久化插件--><dependency>  <groupId>org.springframework.ai</groupId>  <artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId></dependency><!--rag相关--><dependency>  <groupId>org.springframework.ai</groupId>  <artifactId>spring-ai-rag</artifactId></dependency><!--向量化存储,这里用Postgresql--><dependency>  <groupId>org.postgresql</groupId>  <artifactId>postgresql</artifactId>  <version>42.7.5</version>  <scope>runtime</scope></dependency><dependency>  <groupId>org.springframework.ai</groupId>  <artifactId>spring-ai-pgvector-store</artifactId></dependency><!--    pg--><dependency>  <groupId>org.springframework.ai</groupId>  <artifactId>spring-ai-starter-vector-store-pgvector</artifactId></dependency><!-- JDBC--><dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency>  <groupId>com.mysql</groupId>  <artifactId>mysql-connector-j</artifactId></dependency><dependency>  <groupId>org.postgresql</groupId>  <artifactId>postgresql</artifactId>  <version>42.7.5</version>  <scope>runtime</scope></dependency><dependency>  <groupId>com.baomidou</groupId>  <artifactId>mybatis-plus-spring-boot3-starter</artifactId>  <version>${mybatis-plus.version}</version></dependency><!--文档解析插件--><dependency>  <groupId>org.springframework.ai</groupId>  <artifactId>spring-ai-markdown-document-reader</artifactId></dependency><dependency>  <groupId>org.springframework.ai</groupId>  <artifactId>spring-ai-pdf-document-reader</artifactId></dependency><!--其他--><dependency>  <groupId>cn.hutool</groupId>  <artifactId>hutool-all</artifactId>  <version>${hutool.version}</version></dependency><dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-log4j2</artifactId></dependency>

配置文件

spring:    datasource:    mysql:      jdbc-url: jdbc:mysql://localhost:3306/scai?serverTimezone=Asia/Shanghai&useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowPublicKeyRetrieval=true&allowMultiQueries=true&useServerPrepStmts=false      driver-class-name: com.mysql.cj.jdbc.Driver      username: root      password: root    postgres:      jdbc-url: jdbc:postgresql://localhost:5432/postgres      driver-class-name: org.postgresql.Driver  # 建议明确指定驱动类username: sats      password: 123456  ai:    vectorstore:      pgvector:#        datasource: postgresinde-type: HNSW        dimensions: 1024        distance-metric: COSINE_DISTANCE        max-document-batch-size: 10000    chat:      memory:        repository:        # jdbc持久方式,查找jdbcTemplate          jdbc:            initialize-schema: ALWAYS            platform: mariadb    deepseek:      api-key: ni DeepSeek Key      base-url: https://api.deepseek.com      chat:        options:          model: deepseek-chat    openai:      base-url: https://dashscope.aliyuncs.com/compatible-mode      api-key: 你的openaikey或者支持openai规范的也行,吧URL和model换了      chat:        options:          model: qwen-max-latest      embedding:        options:          model: text-embedding-v3          dimensions: 1024

数据源配置

 @Configuration@Slf4jpublic  class DataSourceConfiguration {    // 主数据源配置(MySQL) @Bean @Primary @ConfigurationProperties("spring.datasource.mysql")    public DataSource mysqlDataSource() {        return DataSourceBuilder.create().build();    }    // PostgreSQL数据源配置 @Bean @ConfigurationProperties(prefix = "spring.datasource.postgres")    public DataSource postgresqlDataSource() {        log.info("创建。。。。。。。 postgresql <UNK>");        return DataSourceBuilder.create().build();    }    @Bean @Primary  // mysql----> 直接当作name为jdbcTemplate public JdbcTemplate mysqlJdbcTemplate( @Qualifier("mysqlDataSource") DataSource dataSource) {        return  new JdbcTemplate(dataSource);    }    @Bean public JdbcTemplate postgresqlJdbcTemplate( @Qualifier("postgresqlDataSource") DataSource dataSource) {        log.info("创建。。。。。。。");        return  new JdbcTemplate(dataSource);    }}

持久化目标

    Spring AI自带持久化。记录会话内容

<dependency>  <groupId>org.springframework.ai</groupId>  <artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId></dependency>

docs.spring.io/spring-ai/r…

 @Beanpublic ChatMemory chatMemory(ChatMemoryRepository chatMemoryRepository) {    return MessageWindowChatMemory.builder()            .chatMemoryRepository(chatMemoryRepository)            .maxMessages(20)            .build();}
-- auto-generated definitioncreate  table SPRING_AI_CHAT_MEMORY(    conversation_id varchar(36)                         not  null,    content         mediumtext not  null,    type varchar(10)                         not  null,    timestamp  timestamp  default  CURRENT_TIMESTAMP  not  null  on  update  CURRENT_TIMESTAMP);

    给不同用户、不同新会话建立关联

做到常见的网页端效果

在SPRING_AI_CHAT_MEMORY基础上,新建一个表

-- 建数据库create database if not  exists scai character  set utf8mb4;-- 创建表CREATE  TABLE `d_chat_type_history` (                                       `id` bigint NOT  NULL AUTO_INCREMENT COMMENT '主键id',                                       `type` int  NOT  NULL COMMENT '会话类型,详见ChatType枚举',                                       `chat_id` varchar(225) NOT  NULL COMMENT '会话id',                                       `title` varchar(512) DEFAULT  NULL COMMENT '标题',                                       `create_time` datetime DEFAULT  NULL COMMENT '创建时间',                                       `edit_time` datetime DEFAULT  NULL COMMENT '编辑时间',                                       `status` tinyint(1) DEFAULT '1' COMMENT '1:正常 0:删除',                                       PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COMMENT='会话历史表';

图方便就没用userid了

模型AI交互的ChatClient

advisor就相当于和AI交互的拦截器

  @Bean public ChatClient chatClient ( @Qualifier("deepSeekChatModel") ChatModel deepSeekChatModel) { return ChatClient.builder(deepSeekChatModel).build();        } 

配置一个助手ChatClient

助手目标:

实现步骤

 @Beanpublic ChatClient loveChatClient( @Qualifier("deepSeekChatModel") ChatModel deepSeekChatModel, ChatMemory chatMemory,                                 ChatTypeHistoryService chatTypeHistoryService, @Qualifier("titleChatClient")ChatClient titleChatClient) {    return ChatClient.builder(deepSeekChatModel)            .defaultSystem(SystemPrompt.SYSTEM_PROMPT) // 角色            .defaultAdvisors(                    new SimpleLoggerAdvisor(),                    ChatTypeHistoryAdvisor.builder(chatTypeHistoryService).type(ChatType.ASSISTANT.getCode())                            .order(998).build(),                    ChatTypeTitleAdvisor.builder(chatTypeHistoryService).type(ChatType.ASSISTANT.getCode())                            .chatClient(titleChatClient).chatMemory(chatMemory).order(999).build(),                    MessageChatMemoryAdvisor.builder(chatMemory).order(1000).build()            ) // 拦截器            .build();}
 @Bean public ChatClient titleChatClient( @Qualifier("deepSeekChatModel") ChatModel deepSeekChatModel) {        return ChatClient                .builder(deepSeekChatModel)                .defaultAdvisors(                        new SimpleLoggerAdvisor()                )                .build();    }

advisor的实现

docs.spring.io/spring-ai/r…

    实现CallAdvisor, StreamAdvisor接口, 重写方法
public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {    this.logRequest(chatClientRequest);    ChatClientResponse chatClientResponse = callAdvisorChain.nextCall(chatClientRequest);    this.logResponse(chatClientResponse);    return chatClientResponse;}public Flux<ChatClientResponse> adviseStream(ChatClientRequest chatClientRequest, StreamAdvisorChain streamAdvisorChain) {    this.logRequest(chatClientRequest);    Flux<ChatClientResponse> chatClientResponses = streamAdvisorChain.nextStream(chatClientRequest);    return (new ChatClientMessageAggregator()).aggregateChatClientResponse(chatClientResponses, this::logResponse);}

2. 上面的ChatTypeHistoryAdvisor和ChatTypeTitleAdvisor,是基于BaseChatMemoryAdvisor实现的,BaseChatMemoryAdvisor继承BaseAdvisor继承了CallAdvisor, StreamAdvisor。

BaseChatMemoryAdvisor可以传递本次会话相关信息上下文,比如会话ID。定义了before,after方法适配链路

RAG向量知识库构建

Pgvector的接入: docs.spring.io/spring-ai/r…

VectorStore:docs.spring.io/spring-ai/r…

VectorStore

PgVectorStore的配置

 @Configurationpublic  class MyVectorStoreConfiguration {    @Bean public VectorStore pgVectorVectorStore( @Qualifier("postgresqlJdbcTemplate") JdbcTemplate postgresqlJdbcTemplate,                                           EmbeddingModel openAIEmbeddingModel) {        VectorStore vectorStore = PgVectorStore.builder(postgresqlJdbcTemplate, openAIEmbeddingModel)                .dimensions(1024)                    // Optional: defaults to model dimensions or 1024.distanceType(COSINE_DISTANCE)       // Optional: defaults to COSINE_DISTANCE.indexType(HNSW)                     // Optional: defaults to HNSW.initializeSchema(true)              // Optional: defaults to false.schemaName("public")                // Optional: defaults to "public".vectorTableName("vector_store")     // Optional: defaults to "vector_store".maxDocumentBatchSize(10000)         // Optional: defaults to 10000.build();        return vectorStore;    }}

向量数据库一旦创建,维度确定,更换维度需要重新创建。

文档处理

Spring AI提供了系列对应的文档处理插件,可以将知识处理成SpringAI 的 document

以处理markdown为例子

 @Component@Slf4jpublic  class MyDocumentLoader {    private  final ResourcePatternResolver resourcePatternResolver;    MyDocumentLoader(ResourcePatternResolver resourcePatternResolver) {        this.resourcePatternResolver = resourcePatternResolver;    }    public List<Document> loadMarkdowns() {        List<Document> allDocuments = new ArrayList<>();        try {            Resource[] resources = resourcePatternResolver.getResources("classpath:document/*.md");            for (Resource resource : resources) {                String fileName = resource.getFilename();                MarkdownDocumentReaderConfig config = MarkdownDocumentReaderConfig.builder()                        .withHorizontalRuleCreateDocument(true)                        .withIncludeCodeBlock(false)                        .withIncludeBlockquote(false)                        .withAdditionalMetadata("filename", fileName)                        .build();                MarkdownDocumentReader reader = new MarkdownDocumentReader(resource, config);                allDocuments.addAll(reader.get());            }        } catch (IOException e) {//            log.error("Markdown 文档加载失败", e);}        return allDocuments;    }}

待续

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Spring AI 聊天机器人 RAG 大模型 Java
相关文章