WeKnora 向量数据库集成指南:从 RetrieveEngine 接口到 Doris 与 Tencent VectorDB 实战
【免费下载链接】WeKnoraOpen-source LLM knowledge platform: turn raw documents into a queryable RAG, an autonomous reasoning agent, and a self-maintaining Wiki.项目地址: https://gitcode.com/GitHub_Trending/we/WeKnora
本文是面向开发者的 WeKnora 向量检索引擎扩展指南。WeKnora 将 RAG 检索、Agent 推理与 Wiki 等能力建立在可插拔的检索引擎之上,通过一组标准化接口支持 PostgreSQL、Elasticsearch、Qdrant、Milvus、Weaviate、Doris、SQLite、OpenSearch 与 Tencent VectorDB 等不同后端。读完本文,你将掌握 WeKnora 检索引擎的三层接口抽象、接入新向量数据库的六步完整流程,并深入理解 Apache Doris 4.1 与 Tencent VectorDB 两个内置适配器的实现细节(双通道协议、维度分表、倒排/ANN 索引、Stream Load 批量更新、sparse vector 关键词检索等),可直接在此基础上集成自定义向量数据库。
WeKnora 的检索引擎抽象:三层接口体系
在动手集成之前,先理解 WeKnora 如何抽象检索能力。检索引擎相关的接口统一定义在 internal/types/interfaces/retriever.go,共分为三层:基础检索引擎、存储层与服务层。
1. 基础检索引擎接口 RetrieveEngine
最底层是RetrieveEngine,定义检索引擎的核心检索能力,仅含三个方法:
type RetrieveEngine interface { // EngineType 返回检索引擎的类型标识 EngineType() types.RetrieverEngineType // Retrieve 执行检索操作,返回匹配结果 Retrieve(ctx context.Context, params types.RetrieveParams) ([]*types.RetrieveResult, error) // Support 返回该引擎支持的检索类型列表 Support() []types.RetrieverType }EngineType():引擎类型标识,对应 internal/types/retriever.go 中定义的RetrieverEngineType字符串常量;Retrieve():核心检索入口,入参为types.RetrieveParams,返回[]*types.RetrieveResult;Support():声明引擎支持的检索类型。RetrieverType目前有三类:keywords(关键词检索)、vector(向量检索)、websearch(网络搜索),见 internal/types/retriever.go#L33-L37。
检索入参RetrieveParams覆盖了查询文本、查询向量(Embedding []float32)、知识库/知识/分块 ID 过滤、Tag 过滤、TopK、相似度阈值Threshold、KnowledgeType(如faq、manual,决定使用哪个索引)以及可扩展的AdditionalParams等字段。返回的RetrieveResult内嵌IndexWithScore结构,包含ChunkID、KnowledgeID、KnowledgeBaseID、TagID、Score与匹配类型等信息。
2. 存储层接口 RetrieveEngineRepository
RetrieveEngineRepository在基础检索能力之上扩展了索引管理能力,同样定义在 internal/types/interfaces/retriever.go#L22-L65:
type RetrieveEngineRepository interface { Save(ctx context.Context, indexInfo *types.IndexInfo, params map[string]any) error BatchSave(ctx context.Context, indexInfoList []*types.IndexInfo, params map[string]any) error EstimateStorageSize(ctx context.Context, indexInfoList []*types.IndexInfo, params map[string]any) int64 DeleteByChunkIDList(ctx context.Context, indexIDList []string, dimension int, knowledgeType string) error DeleteBySourceIDList(ctx context.Context, sourceIDList []string, dimension int, knowledgeType string) error CopyIndices(ctx context.Context, sourceKnowledgeBaseID string, sourceToTargetKBIDMap map[string]string, sourceToTargetChunkIDMap map[string]string, targetKnowledgeBaseID string, dimension int, knowledgeType string) error DeleteByKnowledgeIDList(ctx context.Context, knowledgeIDList []string, dimension int, knowledgeType string) error BatchUpdateChunkEnabledStatus(ctx context.Context, chunkStatusMap map[string]bool) error BatchUpdateChunkTagID(ctx context.Context, chunkTagMap map[string]string) error RetrieveEngine }对比原文档中的版本,当前仓库的接口还新增了两个批量更新方法:BatchUpdateChunkEnabledStatus(批量启停分块)与BatchUpdateChunkTagID(批量设置分块 Tag),这是后续 Doris 走 Stream Load partial update 实现的功能基础。CopyIndices用于知识库复制场景,直接复制索引数据以避免重新计算嵌入向量。
3. 服务层接口 RetrieveEngineService
RetrieveEngineService负责索引创建与管理的业务编排,除了继承存储层能力,还引入了embedding.Embedder参数,在写索引时完成向量化:
type RetrieveEngineService interface { Index(ctx context.Context, embedder embedding.Embedder, indexInfo *types.IndexInfo, retrieverTypes []types.RetrieverType) error BatchIndex(ctx context.Context, embedder embedding.Embedder, indexInfoList []*types.IndexInfo, retrieverTypes []types.RetrieverType) error EstimateStorageSize(ctx context.Context, embedder embedding.Embedder, indexInfoList []*types.IndexInfo, retrieverTypes []types.RetrieverType) int64 CopyIndices(...) error DeleteByChunkIDList(...) error DeleteBySourceIDList(...) error DeleteByKnowledgeIDList(...) error BatchUpdateChunkEnabledStatus(...) error BatchUpdateChunkTagID(...) error RetrieveEngine }此外,internal/types/interfaces/retriever.go#L67-L99 定义了RetrieveEngineRegistry(注册表)接口,支持Register/GetRetrieveEngineService/GetAllRetrieveEngineServices,以及按 store ID 获取引擎的GetByStoreID与GetOrLoadByStoreID。从源码注释可以看到,多实例部署下引擎注册表是进程内的,某实例注册的引擎在其他实例上缺失,GetOrLoadByStoreID允许按需从数据库重建,且按租户范围查询,作为防跨租户越权的纵深防御。
六步集成流程:接入一个新向量数据库
WeKnora 为接入新向量数据库规定了固定流程,核心是"接口实现 + 注册 + DI 依赖注入"。以下每一步都有对应的源码佐证。
第 1 步:实现基础检索引擎接口
按上文RetrieveEngine接口实现三个方法。通常你的仓库实现会通过内嵌一个通用混合检索引擎来减少重复代码——参考现有实现均使用retriever.NewKVHybridRetrieveEngine(repo, engineType)将存储层 repo 包装为同时支持 keyword/vector 的混合引擎。
第 2 步:实现存储层接口
实现RetrieveEngineRepository的全部方法:Save/BatchSave(保存索引)、EstimateStorageSize(估算存储空间)、DeleteByChunkIDList/DeleteByKnowledgeIDList/DeleteBySourceIDList(删除索引)、CopyIndices(复制索引,避免重算向量)、BatchUpdateChunkEnabledStatus/BatchUpdateChunkTagID(批量更新)。注意删除方法均携带dimension与knowledgeType参数,因为 WeKnora 按向量维度分表/分集合,删除时必须定位到具体维度。
第 3 步:实现服务层接口
创建RetrieveEngineService实现,负责索引创建与管理的业务逻辑。一般直接由NewKVHybridRetrieveEngine包装存储层 repo 即可获得完整的服务能力。
第 4 步:添加环境变量配置
在RETRIEVE_DRIVER中加入新驱动名(逗号分隔,支持多驱动并存),并补充连接参数:
# 多个驱动用逗号分隔,可同时启用多个检索引擎 RETRIEVE_DRIVER=postgres,elasticsearch_v8,your_database # 新数据库的连接参数 YOUR_DATABASE_ADDR=your_database_host:port YOUR_DATABASE_USERNAME=username YOUR_DATABASE_PASSWORD=password # 其他必要的连接参数...第 5 步:注册检索引擎
在 internal/container/container.go 的initRetrieveEngineRegistry函数(定义于 container.go#L1117)中添加初始化与注册逻辑。函数签名如下:
func initRetrieveEngineRegistry( db *gorm.DB, cfg *config.Config, auditSvc interfaces.AuditLogService, storeRepo interfaces.VectorStoreRepository, engineFactory interfaces.EngineFactory, ) (interfaces.RetrieveEngineRegistry, error) { registry := retriever.NewRetrieveEngineRegistry(storeRepo, engineFactory) retrieveDriver := strings.Split(os.Getenv("RETRIEVE_DRIVER"), ",") // 通过 slices.Contains(retrieveDriver, "your_database") 判断是否启用 // ... return registry, nil }新增驱动的注册代码模式如下:
if slices.Contains(retrieveDriver, "your_database") { client, err := your_database.NewClient(your_database.Config{ Addresses: []string{os.Getenv("YOUR_DATABASE_ADDR")}, Username: os.Getenv("YOUR_DATABASE_USERNAME"), Password: os.Getenv("YOUR_DATABASE_PASSWORD"), }) if err != nil { log.Errorf("Create your_database client failed: %v", err) } else { yourDatabaseRepo := your_database.NewYourDatabaseRepository(client, cfg) if err := registry.Register( retriever.NewKVHybridRetrieveEngine( yourDatabaseRepo, types.YourDatabaseRetrieverEngineType, ), ); err != nil { log.Errorf("Register your_database retrieve engine failed: %v", err) } else { log.Infof("Register your_database retrieve engine success") } } }initRetrieveEngineRegistry通过 DI 容器container.Provide注入(见 container.go#L131),注册完成后的 registry 还会同时暴露为StoreRegistry,供上层按 store ID 解析引擎。
第 6 步:定义检索引擎类型常量
在 internal/types/retriever.go 中追加新的引擎类型常量:
const ( PostgresRetrieverEngineType RetrieverEngineType = "postgres" ElasticsearchRetrieverEngineType RetrieverEngineType = "elasticsearch" DorisRetrieverEngineType RetrieverEngineType = "doris" TencentVectorDBRetrieverEngineType RetrieverEngineType = "tencent_vectordb" // 新增: YourDatabaseRetrieverEngineType RetrieverEngineType = "your_database" )当前仓库已注册的引擎类型包括postgres、elasticsearch、infinity、elasticfaiss、qdrant、milvus、weaviate、doris、sqlite、tencent_vectordb与opensearch(见 internal/types/retriever.go#L7-L27)。
参考实现:以现有驱动为模板
仓库提供了多个完整的驱动实现,可作为开发模板:
- PostgreSQL:
internal/application/repository/retriever/postgres/ - Elasticsearch V7:
internal/application/repository/retriever/elasticsearch/v7/ - Elasticsearch V8:
internal/application/repository/retriever/elasticsearch/v8/ - Apache Doris 4.1:
internal/application/repository/retriever/doris/ - Tencent VectorDB:
internal/application/repository/retriever/tencentvectordb/
其中 Doris 与 Tencent VectorDB 是仓库中较新的适配器,实现文件包含repository.go、structs.go、move.go(索引迁移)等,并配有repository_test.go测试。Doris 目录下还有schema.go(表结构管理)、streamload.go(批量更新)、query.go(检索查询)、compat.go(兼容模式)等文件,下文详细展开。
Apache Doris 4.1 集成要点
Doris 是 MPP 风格的分析型 SQL 数据库,其接入方式与 NoSQL 向量库(Qdrant/Milvus/Weaviate)有显著差异,WeKnora 适配器做了针对性设计,实现在internal/application/repository/retriever/doris/。
双通道协议
| 通道 | 端口 | 用途 |
|---|---|---|
| MySQL 协议 | FE 9030 | 主链路 CRUD、ANN 检索、全文检索 |
| HTTP API | FE 8030 / BE 8040 | Stream Load partial update |
WeKnora 通过database/sql + go-sql-driver/mysql调用 MySQL 协议;通过net/http调用 Stream Load。两条通道复用同一份用户名/密码。注册代码位于 container.go#L1346-L1392,DSN 构造为username:password@tcp(addr)/database?charset=utf8mb4&parseTime=true&loc=Local&interpolateParams=true,连接池设置为最大 20 个连接、最大空闲 5 个、单连接最长 1 小时;HTTP 基础地址则由 FE 地址主机名拼接DORIS_HTTP_PORT(默认 8030)得到。
表结构与维度分表
每个 embedding 维度对应一张物理表<DORIS_TABLE_PREFIX>_<dim>(如weknora_embeddings_768),getTableName实现于 schema.go#L28-L30。表关键属性:
ENGINE=OLAP UNIQUE KEY(id) DISTRIBUTED BY HASH(id) BUCKETS 10 PROPERTIES( "replication_num"="1", "enable_unique_key_merge_on_write"="true" );enable_unique_key_merge_on_write=true是 Stream Load partial update 的前提条件。从 schema.go#L14-L22 可见,WeKnora 将默认桶数设为 10、副本数设为 1,这是对单机/小集群更友好的保守值。表创建结果缓存在initializedTables中,同一进程内同一维度只会真正执行一次SHOW TABLES+ DDL。
索引设计
- 倒排索引(INVERTED):
chunk_id / knowledge_id / knowledge_base_id / source_id / tag_id / is_enabled等过滤字段建索引;content字段使用parser=chinese,在数据库端完成中文分词与全文检索。 - ANN 索引:在
embedding ARRAY<FLOAT>列上构建 HNSW + cosine_distance。
关键细节:Doris 的 ANN 索引在建表后是异步构建的,索引未就绪期间查询会退化为 brute-force(结果正确但速度慢)。WeKnora 在ensureTable中于后台 goroutine 轮询SHOW INDEX FROM <table>等待idx_emb进入FINISHED/NORMAL状态,超时上限 30 秒、轮询间隔 1 秒(见 schema.go#L37-L80)。这样写入路径不被阻塞——索引未就绪时检索退化为 brute-force,比让首批写入卡 30 秒更可接受。
分数语义
向量检索使用:
1 - cosine_distance_approximate(embedding, <vec>) AS score将 distance 翻转为 similarity,与 Qdrant cosine 相似度方向一致:值越大越相似。threshold 比较使用HAVING score >= ?、排序使用ORDER BY score DESC LIMIT ?。
关键词检索
依赖 Doris 内建的MATCH_ANY与chineseparser,无需在 Go 端做 jieba 分词。跨维度的多张表会逐表查询并合并取 topK,与 Milvus/Weaviate 的现状一致。
批量字段更新:Stream Load partial update
BatchUpdateChunkEnabledStatus与BatchUpdateChunkTagID通过 Stream Load partial update 实现(见internal/application/repository/retriever/doris/streamload.go):
- HTTP PUT
http://<fe_http>/api/<db>/<table>/_stream_load - Headers:
partial_columns: true、columns: id,is_enabled、merge_type: APPEND、format: json、strip_outer_array: true - Body:
[{"id": "...", "is_enabled": true}, ...]
每批 ≤ 1MiB 自动拆批,请求体通过bytes.Reader+req.GetBody闭包构造,确保 FE → BE 的 307 redirect 时可以重发 Body。
环境变量与本地启动
RETRIEVE_DRIVER=doris DORIS_ADDR=doris-fe:9030 # FE MySQL 协议地址 DORIS_HTTP_PORT=8030 # FE HTTP 端口(Stream Load) DORIS_DATABASE=weknora # 目标库 DORIS_USERNAME=root DORIS_PASSWORD= DORIS_TABLE_PREFIX=weknora_embeddings注意各环境变量在注册代码中都有默认值:DORIS_ADDR默认doris-fe:9030(docker-compose 服务名)、DORIS_DATABASE默认weknora、DORIS_USERNAME默认root、DORIS_HTTP_PORT默认 8030(见 container.go#L1346-L1366)。
本地启动 Doris:
docker compose --profile doris up -d docker exec -it WeKnora-doris-fe mysql -h 127.0.0.1 -P 9030 -uroot \ -e "CREATE DATABASE IF NOT EXISTS weknora;"仓库根目录的 docker-compose.yml(第 713 行起)为--profile doris定义了 FE + BE 单实例 standalone 部署:FE 使用apache/doris:fe-4.1.0镜像、BE 使用apache/doris:be-4.1.0镜像,数据持久化在doris_fe_meta/doris_fe_log/doris_be_storage/doris_be_log四个命名卷中。启动后运行 WeKnora 后端,知识库写入即会按维度自动建表。
Tencent VectorDB 集成解析
WeKnora 内置 Tencent VectorDB 适配器,驱动名为tencent_vectordb,实现在internal/application/repository/retriever/tencentvectordb/。该适配器支持向量检索、基于 BM25 sparse vector 的关键词检索和索引管理,可参与 WeKnora 上层混合检索。
环境变量
RETRIEVE_DRIVER=tencent_vectordb TENCENT_VECTORDB_ADDR=http://your-instance.tencentvectordb.com TENCENT_VECTORDB_USERNAME=root TENCENT_VECTORDB_API_KEY=your_tencent_vectordb_api_key TENCENT_VECTORDB_DATABASE=weknora TENCENT_VECTORDB_COLLECTION=weknora_embeddings TENCENT_VECTORDB_REPLICA_NUMBER=1从注册代码(container.go#L1393-L1423)可以看到,客户端通过tcvectordb.NewRpcClient(addr, username, apiKey, ...)创建 RPC 客户端,并配置了ReadConsistency: EventualConsistency(最终一致性读)与 10 秒超时;ADDR、USERNAME、API_KEY三者缺一不可,缺失会直接报 "Missing Tencent VectorDB configuration"。
维度分集合与副本数
TENCENT_VECTORDB_COLLECTION是集合名前缀。WeKnora 会按向量维度创建实际集合,例如weknora_embeddings_768,用于隔离不同 embedding 模型维度的数据。集合名解析逻辑在 repository.go#L38-L46:优先使用IndexConfig中的配置,否则读取环境变量,再回退到默认值。
TENCENT_VECTORDB_REPLICA_NUMBER是创建集合时使用的副本数。resolveReplicaNumber的解析优先级为:IndexConfig.ReplicaNumber> 环境变量(需 ≥ 0)> 默认值 1(见 repository.go#L49-L60)。单节点 QA 环境可设为0,生产环境可按 Tencent VectorDB 集群规模调整。此外还有分片数shardsNum,默认 1,同样可通过IndexConfig覆盖。
sparse vector 关键词检索
关键词检索依赖 Tencent VectorDB 的 sparse vector 索引:新建集合会自动创建sparse_vector索引,适配器的Support()返回[keywords, vector](见 repository.go#L66-L68)。注意:旧版本已创建的向量集合如果没有该索引,需要重建集合并重新导入知识库数据后才能启用关键词检索。写入时,BatchSave会按 embedding 维度分组,同时构造 dense vector 与 BM25 sparse vector 数据写入集合。
扩展开发模式的延伸阅读
向量数据库集成遵循的"接口实现 + 注册 + DI 依赖注入"模式,在 WeKnora 中同样适用于网络搜索引擎等扩展点:
- 添加网络搜索引擎 — 同类扩展开发模式(接口实现 + 注册 + DI)
- 知识图谱 — 知识图谱功能依赖 Neo4j 而非向量数据库
- 常见问题 — Embedding 模型配置与向量维度相关
- 版本路线图 — 路线图中的检索能力扩展方向
- 开发指南 — 本地开发环境与调试方式
- Home — Wiki 首页导航
掌握了本指南的接口体系与六步流程,即可将任意具备向量检索能力的数据源接入 WeKnora 的 RAG、Agent 与 Wiki 检索链路,享受上层混合检索、维度分表、索引管理等开箱即用的能力。
【免费下载链接】WeKnoraOpen-source LLM knowledge platform: turn raw documents into a queryable RAG, an autonomous reasoning agent, and a self-maintaining Wiki.项目地址: https://gitcode.com/GitHub_Trending/we/WeKnora
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考