FastGPT 向量数据库集成测试:工厂模式驱动的多库兼容性验证指南
FastGPT 的核心能力之一是 RAG 检索,而向量数据库(VectorDB)正是承载知识库向量与召回的核心存储层。为了保证在不同向量库(PGVector、Milvus、OceanBase、OpenGauss、SeekDB 等)之上行为一致、稳定可迁移,仓库在 packages/service/test/integrations/vectorDB 目录下维护了一套基于工厂模式的向量库集成测试。本指南将带你理解这套测试的组织方式、环境变量与运行方法,逐条拆解其测试用例的断言意图,并结合源码说明如何低成本地接入一个新的向量库。
为什么需要「真实环境」的向量库集成测试
FastGPT 的向量检索链路涉及多条路径:向量写入、按时间轮询数据、向量召回(embRecall)、全文检索(BM25)以及删除与计数。单测可以用 mock 覆盖控制器的行为,却无法验证:
- 不同向量库的
insert/delete/embRecall语义是否一致; - Milvus 这类分布式库的 growing segment 写入后索引可见性(存在秒级延迟);
- BM25 能力门禁在真实 Milvus 元数据下是否能通过;
- 主键
id是否随召回结果返回——如 testSuites.ts 注释所强调的,下游反查indexes.dataId依赖id,SDK 只解析output_fields指定字段,缺id会被吞成空召回。
因此仓库将这类测试独立为 integration 模式,要求连接真实运行的向量库实例,用一套数据、一套用例驱动多个库的测试,保证向量相关操作兼容与稳定。
整体结构:测试数据、测试套件、驱动入口三层分离
从 README.md 与目录结构可以看到,这套测试被刻意拆成三个角色:
| 文件 | 职责 |
|---|---|
| testData.ts | 统一测试数据:1536 维 TEST_VECTORS、查询向量、collection id、动态生成的 teamId/datasetId,所有向量库共用 |
| testSuites.ts | 工厂函数 createVectorDBTestSuite(vectorCtrl),同一套用例供各驱动复用 |
*/index.integration.test.ts |
各向量库的测试入口,实例化对应控制器并调用工厂,按环境变量决定是否跳过 |
这种"数据 + 用例 + 入口"三层结构就是工厂模式的落点:新增向量库时,不需要重写任何用例,只需新增一个入口文件并复用 testData.ts 和 testSuites.ts。
统一测试数据(testData.ts)
testData.ts 定义了跨库通用的数据基准:
VECTOR_DIM = 1536:与主流 Embedding 模型(如text-embedding-ada-002)输出维度一致的常量;baseVector:通过((index % 10) + 1) / 100生成确定性向量,保证多次运行结果可复现;TEST_VECTORS:3 条向量——基准向量、0.7 倍缩放、0.3 倍缩放,用于制造可区分的相似度梯度;QUERY_VECTOR = baseVector:查询向量即基准向量本身,确保召回必然命中最高相似度项;TEST_COLLECTION_IDS = ['col_1', 'col_2', 'col_3']:三个固定的 collection id;createTestIds():用Date.now()+ 随机后缀生成唯一的 datasetId,避免并行运行时互相污染。
工厂函数(testSuites.ts)
createVectorDBTestSuite(vectorCtrl: VectorControllerType) 接受一个符合统一接口的控制器实例,内部用 describe.sequential 串行注册用例,beforeAll 中先调用 vectorCtrl.init()(初始化表结构与索引),随后依次执行 6 个用例,每个用例结束都会清理本次写入的数据。
环境变量与启动配置
环境变量注入链路
测试环境变量由 test/.env.test.local 提供(该文件不提交到 git)。首次使用时复制模板并填写:
cp test/.env.example test/.env.test.local
# 编辑 test/.env.test.local,填入 PG_URL 等
启动时由 test/setup.ts 调用 test/utils/env.ts 的 loadVectorDBEnv({ envFileNames: ['.env.test.local'] }) 读取并注入 process.env,因此各驱动入口才能直接判断 process.env.PG_URL 等变量是否存在。
支持的变量与对应驱动
| 变量 | 说明 | 适用驱动入口 |
|---|---|---|
PG_URL |
PostgreSQL + pgvector 连接串 | pg/ → PgVectorCtrl |
OCEANBASE_URL |
Oceanbase 连接串 | oceanbase/ → ObVectorCtrl |
MILVUS_ADDRESS |
Milvus 地址 | milvus/ → MilvusCtrl |
OPENGAUSS_URL |
OpenGauss 连接串 | opengauss/ → OpenGaussVectorCtrl |
SEEKDB_URL |
SeekDB 连接串(兼容 MySQL 协议) | seekdb/ → SeekVectorCtrl |
关键行为:未设置对应环境变量时,该驱动的集成测试会整体跳过,不会报错。 这一语义由入口文件中的 describe.skipIf(!isEnabled) 实现,例如 pg/index.integration.test.ts:
const isEnabled = Boolean(process.env.PG_URL);
describe.skipIf(!isEnabled)('PG Vector Integration', () => {
const vectorCtrl = new PgVectorCtrl();
createVectorDBTestSuite(vectorCtrl);
});
运行方式
在项目根目录执行(pnpm workspace 环境):
# 仅运行 workspace 默认测试(不包含 integration)
pnpm test
# 运行 service 的 integration 测试(含 vectorDB 集成测试)
FASTGPT_TEST_MODE=integration pnpm test
FASTGPT_TEST_MODE=integration 是进入集成模式的开关。未设置时集成测试不会被执行;设置后,凡是配好了对应连接串的向量库都会跑同一套 createVectorDBTestSuite。
六条核心用例逐条拆解
统一用例定义在 testSuites.ts,以下按执行顺序说明其验证目标:
1. insert and count:写入与计数
并行向 3 个 collection 各写入 1 条向量,然后分别断言:
getVectorCount({ teamId, datasetId })返回 3(等于TEST_VECTORS.length);getVectorCount({ teamId, datasetId, collectionId: TEST_COLLECTION_IDS[0] })返回 1,验证按 collection 粒度的过滤计数。
写入后固定等待 500ms,为部分向量库的索引异步可见性留出时间。
2. embRecall returns results:基础向量召回
以 QUERY_VECTOR 发起召回(limit: 3),断言:
- 结果非空;
- 每条结果的
collectionId都落在TEST_COLLECTION_IDS内; - 每条结果都带
id且属于本次插入的 id——这是整条链路的关键契约,见前文"主键必返"注释。
3. embRecall respects forbidCollectionIdList:排除指定集合
召回时传入 forbidCollectionIdList: [TEST_COLLECTION_IDS[0]],断言返回结果中不包含被排除的 collection。这是知识库检索中"跳过某目录/集合"能力的基础。
4. embRecall respects filterCollectionIdList:限定指定集合
与上一条相对,传入 filterCollectionIdList: [TEST_COLLECTION_IDS[1]],断言所有结果都来自该 collection,验证白名单过滤语义。
5. getVectorDataByTime returns data:按时间窗轮询
以 new Date(0) 为起点、Date.now() + 600_000 为终点查询时间窗内的向量数据,过滤出本次 team/dataset 的记录,断言其 id 集合包含本次插入的全部 id。这条用例验证的是增量同步类任务(如定时构建索引、统计回收)依赖的时间窗查询接口。
6. delete by idList removes vectors:按 id 删除
先插入 3 条,再按 idList 删除前 2 条,随后断言 getVectorCount 变为 1,验证精确删除的幂等性与计数一致性。
每个用例结束后统一调用 cleanupTestVectors,按 teamId + datasetIds 整库清理,避免测试数据残留影响后续运行。
控制器统一接口:工厂模式的契约基础
所有向量库控制器都实现同一个接口 VectorControllerType,定义于 packages/service/common/vectorDB/type.ts#L87-L117:
export interface VectorControllerType {
init(): Promise<void>; // 建表、建索引等初始化
insert(props): Promise<InsertVectorResponseType>;
delete(props): Promise<void>; // 支持按 datasetIds 或 idList 删除
embRecall(props): Promise<EmbeddingRecallResponseType>;
getVectorDataByTime(start, end): Promise<GetVectorDataByTimeResponseType>;
getVectorCount(props): Promise<number>;
}
六个方法正好对应六条核心用例,这正是"一套用例驱动多个库"能够成立的前提——各库入口只需 new 对应的控制器实现(如 PgVectorCtrl、MilvusCtrl、ObVectorCtrl({ type: 'oceanbase' })、OpenGaussVectorCtrl、SeekVectorCtrl({ type: 'seekdb' })),传入工厂即可获得全部用例。
值得注意的是各入口都先执行 vi.unmock('@fastgpt/service/common/vectorDB/xxx') 与 vi.unmock('@fastgpt/service/common/vectorDB/constants'),确保集成测试中使用真实控制器实现而非仓库默认的 mock。
Milvus 专属:BM25 全文检索集成测试
除通用用例外,Milvus 还配有独立的全文检索测试 milvus/fullText.integration.test.ts,覆盖 4 个场景:
| 用例 | 验证内容 |
|---|---|
| TC-FT-0 | 从真实 collection 元数据探测 BM25 能力(describeCollection 的 FunctionType 在不同 SDK/服务端组合下可能返回数字 1 或字符串 BM25,该用例确保能力门禁不误报) |
| TC-FT-1 | BM25 全文检索命中后,通过 indexes.dataId 反查 dataset_data 返回正确记录(results[0].dataId === String(doc._id)) |
| TC-FT-2 | filterCollectionIdList 把全文结果收窄到指定 collection |
| TC-FT-3 | 不存在的词返回空结果 |
该测试体现了两个重要的实现事实:
- Milvus 单表方案要求每条向量在
insert时携带 BM25 文本(texts字段),其他 provider 会忽略该字段——这正是 testSuites.ts 中texts: ['integration-test-${index}']注释所说明的; - 全文后端跟随实际向量库:provider 为 milvus 时恒为 BM25(
modeldata_v2单表),无独立引擎开关;且 Milvus 最低版本要求 2.5(推荐 2.5.16+),由应用启动版本门禁保证。
测试写入后调用 waitVisible()(默认 800ms)等待 Milvus 对刚写入的 growing segment 建立 sparse 索引并可见——这是分布式向量库测试中典型的时序处理。
本地环境搭建:docker-compose 一键起库
仓库提供了完整的测试依赖编排文件 packages/service/test/integrations/vectorDB/yml/docker-compose.yml,包含:
- pgTest:
fastgpt/pgvector:0.8.0-pg15(PostgreSQL 15 + pgvector 0.8.0),映射宿主机6001:5432,带pg_isready健康检查; - milvus-test:
milvusdb/milvus:v2.5.16standalone 模式,映射6002:19530,配套 etcd(v3.5.5)与 MinIO(2023-03-20 版本)两个依赖服务,security_opt: seccomp:unconfined,带/healthz健康检查; - ob-test:
oceanbase/oceanbase-ce:4.3.5-lts,映射6005:2881,MODE=MINI节省资源,并通过 config 注入初始化 SQLALTER SYSTEM SET ob_vector_memory_limit_percentage = 30;调整向量内存上限; - seekdb-test:
oceanbase/seekdb:1.0.1.0-100000392025122619,映射6003:2881、6004:2886,兼容 MySQL 协议,用mysqladmin ping做健康检查。
启动后按上文映射关系填写 test/.env.test.local 即可,例如:
PG_URL=postgresql://username:password@127.0.0.1:6001/postgres
MILVUS_ADDRESS=127.0.0.1:6002
OCEANBASE_URL=...
OPENGAUSS_URL=...
SEEKDB_URL=...
注意 docker-compose 中的账号密码等配置只有首次运行生效,修改后需删除持久化数据目录再重启。
新增一个向量库的接入步骤
结合 README 与源码实现,接入新向量库只需四步:
- 在 packages/service/common/vectorDB 下实现符合
VectorControllerType接口的控制器(若为 MySQL 协议兼容库可参考seekdb入口的new SeekVectorCtrl({ type: 'xxx' })写法); - 新建
packages/service/test/integrations/vectorDB/<name>/index.integration.test.ts,模式为:vi.unmock真实控制器 → 依据环境变量构造isEnabled→describe.skipIf(!isEnabled)→ 实例化控制器 →createVectorDBTestSuite(vectorCtrl); - 在
test/.env.test.local中补充对应连接串变量; - 可选:若需要 docker 环境,在
yml/docker-compose.yml中追加对应服务。
完成后执行 FASTGPT_TEST_MODE=integration pnpm test,新库即可与 PG、Milvus 等库共享同一套 6 条核心用例,无需编写任何重复断言。
小结
这套集成测试的价值在于:把"向量库可插拔"从架构承诺变成了可执行验证。通过统一测试数据、统一用例工厂、按环境变量启停的驱动入口,FastGPT 得以在多向量库之间保持稳定的插入、召回、过滤、删除与计数语义;Milvus BM25 专项用例又针对分布式库的索引可见性与反查链路做了真实环境校准。无论你是要新增一种向量库支持,还是排查某个库的召回异常,都可以从这组集成测试入手:先看 testData.ts 理解数据基准,再看 testSuites.ts 对照接口契约,最后用 docker-compose 拉起目标库复现问题。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00