Solon v4.0.2

rag - 文档向量知识库(Repository)

</> markdown
2026年6月10日 下午7:23:31

文档知识库,一般是基于向量数据库封装的并提供文档存储与相似搜索的接口(所以也叫,文档向量知识库)。其实,不基于向量数据库封装也行,比如基于本文搜索。

1、内置的适配(可以根据业务,按需定制)

知识库所在插件描述
InMemoryRepositorysolon-ai内存知识库(数据在 map 里)
WebSearchRepositorysolon-ai联网搜索知识库
ChromaRepositorysolon-ai-repo-chromaChroma 矢量存储知识库
DashVectorRepositorysolon-ai-repo-dashvectorDashVector 矢量存储知识库
ElasticsearchRepositorysolon-ai-repo-elasticsearchElasticSearch 矢量存储知识库
MilvusRepositorysolon-ai-repo-milvusMilvus 矢量存储知识库
QdrantRepositorysolon-ai-repo-qdrantQdrant 矢量存储知识库
RedisRepositorysolon-ai-repo-redisRedis Search 矢量存储知识库
TcVectorDbRepositorysolon-ai-repo-tcvectordbTcVectorDb 腾讯云矢量存储知识库

相关配置,参考具体插件介绍。

2、Repository 接口

只读知识库接口。比如封装网络搜索,只读接口即可

package org.noear.solon.ai.rag;

import org.noear.solon.ai.chat.message.ChatMessage;
import org.noear.solon.ai.rag.util.QueryCondition;
import org.noear.solon.lang.Preview;

import java.io.IOException;
import java.util.List;

/**
 * 知识库(可检索)
 */
public interface Repository {
    /**
     * 检索
     *
     * @param query 查询字符串
     */
    default List<Document> search(String query) throws IOException {
        return search(new QueryCondition(query));
    }

    /**
     * 检索
     *
     * @param condition 查询条件
     */
    List<Document> search(QueryCondition condition) throws IOException;

    /**
     * 提示词简单增强
     */
    default ChatMessage promptAugment(String query) throws IOException {
        List<Document> context = search(query);
        return ChatMessage.ofUserAugment(query, context);
    }
}

可写知识库接口(可读,可写)

package org.noear.solon.ai.rag;

import org.noear.solon.core.util.RunUtil;
import org.noear.solon.lang.Preview;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;

/**
 * 可存储的知识库(可存储)
 */
public interface RepositoryStorable extends Repository {
    /**
     * 异步保存文档
     *
     * @param documents        文档集
     * @param progressCallback 进度回调
     * @since 3.5
     */
    default CompletableFuture<Void> asyncSave(List<Document> documents, BiConsumer<Integer, Integer> progressCallback) {
        CompletableFuture<Void> future = new CompletableFuture<>();

        RunUtil.async(() -> {
            try {
                save(documents, progressCallback);
                future.complete(null);
            } catch (Throwable ex) {
                future.completeExceptionally(ex);
            }
        });

        return future;
    }

    /**
     * 保存文档
     *
     * @param documents        文档集
     * @param progressCallback 进度回调
     * @since 3.5
     */
    void save(List<Document> documents, BiConsumer<Integer, Integer> progressCallback) throws IOException;


    /**
     * 保存文档
     *
     * @param documents 文档集
     * @since 3.5
     */
    default void save(List<Document> documents) throws IOException {
        save(documents, null);
    }

    /**
     * 保存文档
     *
     * @param documents 文档集
     * @since 3.5
     */
    default void save(Document... documents) throws IOException {
        save(Arrays.asList(documents));
    }

    /**
     * 删除文档
     *
     * @param ids 文档IDs
     * @since 3.5
     */
    void deleteById(String... ids) throws IOException;

    /**
     * 是否存在文档
     *
     * @param id 文档ID
     * @since 3.5
     */
    boolean existsById(String id) throws IOException;
}

3、简单示例

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.save(documents); 
}