写点什么

tidb+springAI:一个基于国产化分布式数据库的 RAG 项目实战

作者: du 拉松原文来源:https://tidb.net/blog/7dd0720a


随着人工智能技术的迅猛发展,大模型应用已广泛落地于各类场景。然而,所有智能应用的核心基础依然是“数据”。尤其在 RAG(Retrieval-Augmented Generation,检索增强生成)类任务中,既涉及结构化的关系数据,也包含大量非结构化的向量数据,因此如何选择合适的数据库成为构建智能系统的关键一环。


为了降低运维成本和开发复杂度,我们更倾向于选择一种既支持关系型数据,又具备原生向量检索能力的数据库。TiDB 自 v8.5 起引入了向量检索功能,为我们提供了统一的数据平台解决方案。


然而,在实际使用中我们常常会遇到这样一些问题:


  • 如何存储和使用向量类型数据?

  • 如何在 Java 项目中优雅地集成 TiDB 的向量能力?

  • 面对 Spring 技术栈,如何进行良好的架构拓展?

  • 常见 ORM 框架(如 MyBatis)中如何高效支持向量检索?


为了解答这些问题,我构建了一个基于 RAG 技术的示例项目,从实战角度出发,展示如何在 Java + Spring 环境下集成 TiDB 向量库,并实现端到端的智能问答流程。


接下来,我将围绕以下三个部分展开说明:


  1. RAG 执行流程解析

  2. 项目所采用的技术栈说明

  3. TiDB 向量库的使用方法与集成经验分享


希望本项目能够为你在构建智能应用、使用 TiDB 向量能力的过程中提供有价值的参考。

RAG 执行流程

检索增强生成(RAG)是一种优化大型语言模型(LLM)输出的架构。通过使用向量搜索,RAG 应用程序可以在数据库中存储向量嵌入,并在 LLM 生成回复时检索相关文档作为附加上下文,从而提高回复的质量和相关性。



上图分为两个流程:


  • 知识库维护:用户上传文档 –> 文档分块 –> 文本向量化 –> 向量存储到 tidb

  • 用户问答:用户提出问题 –> 问题向量化 –> 向量检索 –> 检索内容 rerank 重排序 –> 重新组织上下文 –> 大语言模型生成回答 –> 给用户响应。

项目技术栈

为了更直观地演示 TiDB 在 RAG 场景中的应用,我基于 Java 构建了一个简单的 Spring Boot 项目,实现了从文档解析、向量化、存储到问答交互的完整流程。项目中集成了主流的技术组件,包括 ORM 框架(mybatis)、模型服务、向量存储等,下面是本项目的技术栈及其作用说明:



springAI:Spring 官方推出的 AI 框架,简化了集成人工智能能力的开发流程。通过统一的抽象层支持多种模型和服务,避免繁琐的接入配置。这里不过多介绍,详情请看官网相关内容。


mybatis:一个流行的持久层框架,用于操作关系数据库。通过 MyBatis,我们可以无缝地访问 TiDB,包括向量存储的相关功能,无需引入额外插件。。


其他工具,比如 ollama 是用来本地化部署大语言模型和 embedding 模型的框架,文档分块服务:unstructured-api 可以帮我们快速的给文本分块,并且支持多种文本格式。


tidb:TiDB 是本项目的核心存储组件。它既支持传统关系数据,也原生支持向量索引与检索功能,简化了系统架构,避免依赖多种数据库,大大提升了开发效率和系统一致性。


技术细节请参考 github:https://github.com/geeklc/rag-tidb

库表准备

数据库部署

想要使用 tidb 中的向量索引,在部署 tidb 时需要部署 tiflash 组件,我在本地的测试中使用单机部署了一个简易集群,用来集成验证相关功能。


具体部署方式请参考官方文档,我这里只列出部署需要的拓扑文件:


global: user: "root" ssh_port: 22 deploy_dir: "/home/tidb-deploy" data_dir: "/home/tidb-data"monitored: node_exporter_port: 9100 blackbox_exporter_port: 9115server_configs: tidb:   instance.tidb_slow_log_threshold: 300 tikv:   readpool.storage.use-unified-pool: false   readpool.coprocessor.use-unified-pool: true pd:   replication.enable-placement-rules: true   replication.location-labels: ["host"] tiflash:   logger.level: "info"pd_servers: - host: 192.168.0.116tidb_servers: - host: 192.168.0.116tikv_servers: - host: 192.168.0.116   port: 20160   status_port: 20180   config:     server.labels: { host: "logic-host-1" } - host: 192.168.0.116   port: 20161   status_port: 20181   config:     server.labels: { host: "logic-host-2" } - host: 192.168.0.116   port: 20162   status_port: 20182   config:     server.labels: { host: "logic-host-3" }tiflash_servers: - host: 192.168.0.116monitoring_servers: - host: 192.168.0.116grafana_servers: - host: 192.168.0.116
复制代码

表创建

比如在项目中使用的文本分块表:


-- 创建文本分块表CREATE TABLE `doc_chunk` (     `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',     `kb_id` bigint DEFAULT NULL COMMENT '知识库id',     `doc_id` bigint DEFAULT NULL COMMENT '文档id',     `content` varchar(1000) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '分段内容',     `embedding` vector(1024) COMMENT '分段内容向量',     `sort` int DEFAULT NULL COMMENT '分段排序',     `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',     PRIMARY KEY (`id`)) AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='文档分段';
复制代码


其中 embedding 字段就是向量类型字段,字段的维度为 1024,因为我这里使用的 embedding 模型就是 1024 维度。


创建索引:


-- 首先设置表doc_chunk的tiflash副本数为1ALTER TABLE doc_chunk SET TIFLASH REPLICA 1;-- 创建向量索引,索引类型为HNSWCREATE VECTOR INDEX idx_doc_chunk_embedding ON doc_chunk ((VEC_COSINE_DISTANCE(embedding))) USING HNSW;
复制代码

向量的使用

添加依赖

连接 tidb 需要添加 mysql 的驱动,并添加 mybatis 的相关依赖:


注意本项目使用的是 springboot3.4.6,具体的版本请查阅 springboot 官网。


<dependency>    <groupId>org.mybatis.spring.boot</groupId>    <artifactId>mybatis-spring-boot-starter</artifactId>    <version>3.0.4</version></dependency><dependency>    <groupId>com.mysql</groupId>    <artifactId>mysql-connector-j</artifactId>    <scope>runtime</scope></dependency>
复制代码

配置信息

在 application.properties 配置文件中增加如下配置:


# 配置tidb的连接信息spring.datasource.url=jdbc:mysql://xxx.xxx.xxx.xxx:4000/rag_test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowMultiQueries=true&serverTimezone=Hongkongspring.datasource.username=xxxxspring.datasource.password=xxxxspring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver# 配置mybatismybatis.mapper-locations=classpath:mapper/*.xmlmybatis.type-aliases-package=com.df.rag.**.domainmybatis.configuration.map-underscore-to-camel-case=truemybatis.configuration.jdbc-type-for-null=nullmybatis.configuration.call-setters-on-nulls=truemybatis.configuration.lazy-loading-enabled=truemybatis.configuration.aggressive-lazy-loading=falsemybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
复制代码

定义文本分块内容的实体

定义实体时,注意向量类型的字段类型为 String


@Getter@Setterpublic class DocChunk {    private Long id;    /**     * 知识库id     */    private Long kbId;    /**     * 文档id     */    private Long docId;    /**     * 文本内容     */    private String content;    /**     * 向量内容,注意这里的向量类型是String类型     */    private String embedding;    /**     * 分块顺序     */    private Integer sort;    /**     * 创建时间     */    private Date createTime;    /**     * 语义距离     */    private Float distance;}
复制代码

定义对应的 mapper 信息

  1. 定义的 DocChunkMapper 如下:


batchInsert:是批量保存文档分块信息:


queryByVector:是根据向量和其他条件进行查询


  1. 定义的 DocChunkMapper.xml 如下:

逻辑处理

  1. 在 service 中保存分块信息的处理

  2. 在 service 中查询分块信息的处理

使用过程展示

  1. 准备一个 docx 的文档:



  1. 请求接口上传文档,作为知识库内容:



  1. 知识库问答接口请求


可以看到回答内容是根据文档中的信息生成:


总结与展望

在实际使用 TiDB 向量库的过程中,整体操作非常简洁易用。向量的存储方面,只需将 float[] 类型的向量通过 JSON 格式进行转换后存入数据库即可。查询时,需要特别注意 SQL 写法,主要使用了内置的 VEC_COSINE_DISTANCE() 函数进行向量相似度计算。除此之外,其余操作几乎与普通关系型查询一致,上手非常快速,学习成本低。


以上内容是我在实战中总结的一些配置方式和代码示例,希望能够为广大 TiDBer 在构建 RAG 系统或其他 AI 应用时,提供一些实际参考和技术借鉴。


我们期待 TiDB 的向量功能能够尽快实现 GA,同时希望其向量查询的 SQL 语法更加简洁友好,提升开发体验。


随着国产化数据库的发展和大模型应用的加速落地,TiDB 在融合结构化与非结构化数据处理方面具备巨大潜力。我们相信,TiDB 有望成为国产数据库与人工智能技术融合的中坚力量,持续推动智能时代的技术革新。


发布于: 刚刚阅读数: 4
用户头像

TiDB 社区官网:https://tidb.net/ 2021-12-15 加入

TiDB 社区干货传送门是由 TiDB 社区中布道师组委会自发组织的 TiDB 社区优质内容对外宣布的栏目,旨在加深 TiDBer 之间的交流和学习。一起构建有爱、互助、共创共建的 TiDB 社区 https://tidb.net/

评论

发布
暂无评论
tidb+springAI:一个基于国产化分布式数据库的RAG项目实战_8.x 实践_TiDB 社区干货传送门_InfoQ写作社区