Solon v3.2.0

rag - 检索文档与知识库(Repository)

</> markdown

RAG 里最重要的一个工作,就是“检索”。就是在一个“知识库(Repository)”里找“文档(Document)”。

1、概念

检索的内容,可称为文档(Document)。提供文档索检服务的对象,就是知识库(Repository)了。知识库可分为:

  • 只读知识库,只用于检索的
  • 可存储知识库,可用于检索,同时提供存储管理

可存储知识库,还会用到两个重要的工具:

  • DocumentLoader,文档加载器
  • DocumentSplitter,文档分割器

比如有个 pdf 文档,需要通过 DocumentLoader 加载,转为 Document 列表,然后存入 Repository。有时候 Document 太大,可能还要用 DocumentSplitter 分割成多个小 Document。

接口描述
Document文档
DocumentLoader文档加载器
DocumentSplitter文档分割器
Repository知识库
RepositoryStorable可存储知识库
EmbeddingModel嵌入模型(知识库在存储或检索时使用)

2、DocumentLoader 及内置的适配

  • 基础接口
public interface DocumentLoader {
    //附加元数据
    DocumentLoader additionalMetadata(String key, Object value);

    //附加元数据
    DocumentLoader additionalMetadata(Map<String, Object> metadata);

    //加载文档
    List<Document> load() throws IOException;
}
  • 内置的适配(可以根据业务,按需定制)
加载器所在插件描述
TextLoadersolon-ai纯文本加载器
ExcelLoadersolon-ai-load-excelexel 文件加载器
HtmlSimpleLoadersolon-ai-load-htmlhtml 简单加载器(一般要根据文页特征定制为好)
MarkdownLoadersolon-ai-load-markdownmd 文件加载器
PdfLoadersolon-ai-load-pdfpdf 文件加载器
PptLoadersolon-ai-load-pptppt 文件加载器
WordLoadersolon-ai-load-wordword 文件加载器

3、DocumentSplitter 及内置的适配

  • 基础接口
public interface DocumentSplitter {
    //分割
    default List<Document> split(String text) {
        return split(Arrays.asList(new Document(text)));
    }

   //分割
    List<Document> split(List<Document> documents);
}
  • 内置的适配(可以根据业务,按需定制)
分割器所在插件描述
JsonSplittersolon-ai根据 json 格式分割(把 array 格式的 json,分成多个文档)
RegexTextSplittersolon-ai根据 正则表达式分割内容
TokenSizeTextSplittersolon-ai根据 token 限制大小分割内容
SplitterPipelinesolon-ai分割器管道(把一批分割器,串起来用)
  • 使用示例
private void load(RepositoryStorable repository, String file) throws IOException {
    //加载器
    HtmlSimpleLoader loader = new HtmlSimpleLoader(new File(file));
    
    //加载后再分割(按需)
    List<Document> documents = new SplitterPipeline() 
            .next(new RegexTextSplitter("\n\n"))
            .next(new TokenSizeTextSplitter(500))
            .split(loader.load());
    
    //入库
    repository.insert(documents); 
}

4、Repository 及内置的适配

基础接口

public interface Repository {
    //检索
    default List<Document> search(String query) throws IOException {
        return search(new QueryCondition(query));
    }

    //检索
    List<Document> search(QueryCondition condition) throws IOException;
}

public interface RepositoryStorable extends Repository {
    //插入
    void insert(List<Document> documents) throws IOException;

    //删除
    void delete(String... ids) throws IOException;

    //是否存在 
    boolean exists(String id) throws IOException;
}
  • 内置的适配(可以根据业务,按需定制)
知识库所在插件描述
InMemoryRepositorysolon-ai内存知识库(数据在 map 里)
WebSearchRepositorysolon-ai联网搜索知识库
ChromaRepositorysolon-ai-repo-chromaChroma 矢量存储知识库
ElasticsearchRepositorysolon-ai-repo-elasticsearchElasticSearch 矢量存储知识库
MilvusRepositorysolon-ai-repo-milvusMilvus 矢量存储知识库
QdrantRepositorysolon-ai-repo-qdrantQdrant 矢量存储知识库
RedisRepositorysolon-ai-repo-redisRedis Search 矢量存储知识库
TcVectorDbRepositorysolon-ai-repo-tcvectordbTcVectorDb 腾讯云矢量存储知识库

7、RAG 要素联合演示

知识库准备

//构建 embeddingModel
EmbeddingModel embeddingModel = EmbeddingModel.of(embedding_apiUrl)
        .apiKey(embedding_apiKey)
        .model(embedding_model)
        .build();

//构建 repository
InMemoryRepository repository = new InMemoryRepository(embeddingModel);

//加载文档并存储
PdfLoader loader = new PdfLoader(new File("ticket.pdf")).additionalMetadata("file", "ticket.pdf");

//再次(按需)组合切割
List<Document> documents = new SplitterPipeline() //2.分割文档(确保不超过 max-token-size)
                .next(new RegexTextSplitter("\n\n"))
                .next(new TokenSizeTextSplitter(500))
                .split(loader.load());

//存储仓库
repository.insert(documents);

检索应用

//构建 chatModel
ChatModel chatModel = ChatModel.of(chat_apiUrl)
                .provider(provider)
                .model(model)
                .build();

//用户输入消息
String message = "刘德华今年有几场演唱会?";

//1. 检索
List<Document> context = repository.search(message);

//2. 消息增强
ChatMessage chatMessage = ChatMessage.augment(message, context);

//3. 提交大模型
ChatResponse resp = chatModel.prompt(chatMessage).call();

//打印结果
System.out.println(resp.getMessage());