• 热门
  • 基础
  • 数据库
  • 安全
  • 大数据
  • 人工智能
  • 混合云
  • 开发与运维
  • 企业应用

应用服务

行业引擎

全部文档
当前文档

暂无内容

如果没有找到您期望的内容,请尝试其他搜索词

文档中心

RAG on Relyt 实践指南

最近更新时间:2024-11-26 19:57:04

RAG概述

数据集的概念

在 RAG 中,数据集(Collection)是用户管理和组织信息的基本单元,每个数据集可以包含多种类型的数据,如论文、Excel、PPT 等。这种灵活性使得用户能够根据自己的需求对数据进行归类和整理。每次用户的一次chat,都会关联一个或多个数据集进行 RAG 查询。这种查询方式能够根据用户的问题,从相关的数据集中检索出答案,提高了查询的准确性和效率。

Chunk 的概念

Chunk是指从源文件中提取出的较小的数据片段或文本块。这些块通常是经过特定处理后得到,旨在提高信息检索和处理的效率。一个源文件(例如 PDF 论文)可以被划分为多个 Chunk,每个 Chunk 包含一定数量的文本或信息。

对于数据集内的每个源文件(以 PDF 论文为例),其每个源文件被输入到处理系统中时,会经过多个处理管道(Pipeline),负责对源文件进行不同的处理步骤,包括:

  • 文本提取:从 PDF 中提取文本内容,去除格式和冗余信息。

  • 分块处理:将提取的文本划分为多个 Chunk,通常基于段落、句子或固定字数等标准。

  • 语义嵌入生成:为每个 Chunk 生成相应的语义 Embedding。这些 Embedding 是通过深度学习模型(如 BERT 或其他自然语言处理模型)计算得出,能够捕捉 Chunk的语义信息。

召回流程

当用户对某个 Collection 发起 Chat 时,作为一个 RAG 召回系统,目标是从当前 Collection 对应的所有 Chunk中召回相关信息。这个过程通常包括如下步骤:

  1. Query embedding:调用模型,对用户 Query 文本进行 Embedding,产出 Query 向量。

  2. 向量召回:Query 向量与 Chunk Embedding 向量进行近似计算,从数据库中检索出多个相关 Chunk。这些 Chunk 是与用户查询最相关的文本片段,可能来自于多个源文件,但都属于同一个数据集。

  3. 重排序:系统对召回的 Chunk 进行重排序,以确定哪些 Chunk 最能满足用户的查询需求。重排序通常基于 Chunk 的相关性、质量和其它因素进行评估。

  4. 作为问题上下文:经过重排序后,选定的 Chunk 将作为问题上下文(Question Context)返回给用户。这些上下文信息将帮助用户更好地理解查询结果,并提供更准确的答案。

表 Schema 设计

业务字段需求

字段名

含义

字段类型

collection_id

唯一表示一个数据集的 ID。可以是用户数据集,也可能是公开数据集。

text

id

唯一表示数据集内的一条记录。

text

datasource_id

每个源文件(例如 PDF 文件)对应的 ID。

text

text

原始文本,用于作为 Context 返回给 LLM。

text

embedding

文本的 Dense Embedding 向量,与 Text 一一对应,包含了文本经过模型 Embed 后生成的语义信息,用于语义匹配。

vecf16(1024)

sparse_embedding

文本的 Sparse Embedding 向量,与 Text 一一对应,包含了文本的关键词信息和权重,用于关键词匹配。

svector(30052)

meta

该 Chunk 的 Metadata 信息,包含作者、来源、引用等。

jsonb

text_type

该条记录对应的原文类型,可能是 Chunk、Summary 等,从 meta 中提取,用于点查过滤。

text

index_name

该条记录对应的索引类型,从 meta 中提取,专门用于点查过滤。

text

2. 分区键的设计和选择

在设计表 Schema 时,如何有效地管理和查询不同数量的数据集是一个重要的考虑因素。为了在表的数量和查询性能之间取得平衡,选择以用户的数据集 ID (collection_id) 作为分区键(partition by range(collection_id))。其中,collection_id 以日期为前缀,保证按时间有序。这种设计策略能够有效地应对用户私有数据集和平台公开数据集的数量差异,同时保证高效的查询性能。

以带时间前缀的 collection_id 作为分区键具有以下几个优点:

  • 自然的时间序列性:由于collection_id 按时间有序,使用它作为分区键可以确保数据按照访问的冷热特性存放 layout,实现冷热数据分离的效果。

  • 避免元数据膨胀:相比最朴素的做法——为每个用户私有数据创建一个独立的表,随着用户数量和数据集数量的增加,系统元数据将迅速膨胀,导致管理和维护的复杂性增加,影响整体系统性能表现。因此使用单一大表并通过 collection_id 进行分区,可以有效减少表的数量,简化数据库结构。

  • 便于针对特殊数据集做定制:当有特殊的数据集需要定制时,比如希望与其它数据集隔离,或者单独定制索引策略时,仅需要增加一个子分区表达式即可,不需要业务重新建表做路由。

以日期为前缀的 collection_id 示例:

  • "20240803-clzqw6zd70iv501l1k9cmhb5p"

  • "20240804-clzqw6zd70iv501l1k9cmsfhw"

  • "20240805-clzqw6zd70iv501l1k9cmvert"

按月创建分区表的示例:

CREATE TABLE llama_index_vectors_1024_part_202407 partition of llama_index_vectors_1024 FOR VALUES FROM ('20240701') TO ('20240801');
CREATE TABLE llama_index_vectors_1024_part_202408 partition of llama_index_vectors_1024 FOR VALUES FROM ('20240801') TO ('20240901');

按日创建分区表的示例:

CREATE TABLE llama_index_vectors_1024_part_20240701 partition of llama_index_vectors_1024 FOR VALUES FROM ('20240701') TO ('20240702');
CREATE TABLE llama_index_vectors_1024_part_2024702 partition of llama_index_vectors_1024 FOR VALUES FROM ('20240702') TO ('20240703');

根据数据增量大小和访问 Pattern不同,建议采用按月或按日的分区策略,保证分区表数量与查询性能之间的平衡。

3. 分布列设计

在实际使用中,大多数查询都会带上类似 collection_id = '20240803-xxx' 的等值条件,且每个为保证更好的 QPS(每个查询最会使用一个计算节点),我们选择使用 collection_id 作为分布列。这种设计的缺点是 RT 会受一些影响。

4. 索引的设计

4.1 向量索引

对于 Embedding 向量字段 ,使⽤ HNSW 索引 ,在保证⾼召回率前提下实现毫秒级的查询延迟。

4.2 点查索引

对于 index_nametext_type 等常⽤于 filter 的字段 ,构建 B-Tree ⼆级索引(collection_id, index_name),对于⾼筛选率的查询有⾮常好的加速效果。

5. 完整建表语句

5.1 创建主表

CREATE TABLE llama_index_vectors_1024
(
    id            text not null,
    collection_id text not null, --以日期字符串为前缀,例: "20240803-clzqw6zd70iv501l1k9cmhb5p"
    datasource_id text,
    index_name    text,
    text_type     text
    text          text,
    embedding     vecf16(1024),
    sparse_embedding svector(30052),
    meta          jsonb,
    primary key (collection_id, id)
)
DISTRIBUTED BY (collection_id) 
PARTITION BY range(collection_id);


-- 设置为plain存储
ALTER TABLE llama_index_vectors_1024 
    ALTER COLUMN id set storage plain, 
    ALTER COLUMN collection_id set storage plain, 
    ALTER COLUMN index_name set storage plain, 
    ALTER COLUMN text_type set storage plain, 
    ALTER COLUMN embedding set storage plain,
    ALTER COLUMN datasource_id set storage plain;

5.2 按月创建分区表

-- 按月建分区
CREATE TABLE llama_index_vectors_1024_part_202407 partition of llama_index_vectors_1024 FOR VALUES FROM ('20240701') TO ('20240801');
CREATE TABLE llama_index_vectors_1024_part_202408 partition of llama_index_vectors_1024 FOR VALUES FROM ('20240801') TO ('20240901');

-- 默认分区,当collection_id没有命中时间前缀的分区表,会路由到默认分区表
CREATE TABLE llama_index_vectors_1024_part_default partition of llama_index_vectors_1024 default;

5.3 向量索引

CREATE INDEX llama_index_vectors_1024_embedding on
llama_index_vectors_1024
USING vectors(embedding vecf16_cos_ops
    WITH (options = $$
    optimizing.optimizing_threads = 10
    segment.max_growing_segment_size = 2000
    segment.max_sealed_segment_size = 30000000
    [indexing.hnsw]
    m=30
    ef_construction=500
    $$);

5.4 点查索引

CREATE INDEX llama_index_vectors_1024_index_name on
llama_index_vectors_1024 using btree(collection_id, index_name);
CREATE INDEX llama_index_vectors_1024_text_type on
llama_index_vectors_1024 using btree(collection_id, text_type);
CREATE INDEX llama_index_vectors_1024_datasource_id on
llama_index_vectors_1024 using btree(collection_id, datasource_id);
CREATE INDEX llama_index_vectors_1024_meta on llama_index_vectors_1024
USING gin((collection_id::tsvector), meta);

6. 查询语句

SELECT id, text, meta,
embedding <-> '[0.1,0.3, ...., 0.6]'  -- 1024维query向量数组
as distance
FROM llama_index_vectors_1024
WHERE collection_id = '20240803-clzqw6zd70iv501l1k9cmhb5p'
AND index_name = 'EMBEDDING'
ORDER BY distance --按点积距离排序 LIMIT 16

纯净模式常规模式

纯净模式

点击可全屏预览文档内容

鼠标选中内容,快速反馈问题

如果在文档使用中出现问题,可选中有问题的部分进行快速反馈,我们将跟进处理。
不再提示
好的,我知道了

聆听反馈