先做精确搜索基线
向量库不是把文章扔进去就自动得到可靠答案。必须保留文本、来源、页码、分块策略和Embedding版本,查询与文档使用同一套模型。本教程用PostgreSQL 16、pgvector及BGE-M3的1024维稠密表示,先不创建近似索引,让最小链路可检查。
下列数据库只用于本机试验,不连接网站生产数据库。密码为占位符,请生成专用强密码;不要直接使用示例字符串上线。前提是BGE-M3服务已经运行在127.0.0.1:8020。
建库与表结构
docker volume create rag-pg-data
docker run -d --name rag-pg \
-e POSTGRES_USER=rag -e POSTGRES_DB=rag \
-e POSTGRES_PASSWORD='REPLACE_WITH_STRONG_PASSWORD' \
-p 127.0.0.1:5434:5432 \
-v rag-pg-data:/var/lib/postgresql/data \
pgvector/pgvector:pg16
docker exec -it rag-pg psql -U rag -d ragCREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE chunks (
id bigserial PRIMARY KEY,
source_id text NOT NULL,
page_no integer,
model_revision text NOT NULL,
body text NOT NULL,
embedding vector(1024) NOT NULL
);
CREATE INDEX chunks_revision ON chunks(model_revision);pg16标签也会更新,成功验证后固定镜像摘要并保存扩展版本。页面编号必须对应可查看的原文;网页资料没有页码时可使用章节或段落编号,不能伪造PDF页号。生产表需增加租户、权限和删除标记,本文仅展示最小结构。
将真实向量与原文一起写入
pip install 'psycopg[binary]' pgvector requests
export RAG_DATABASE_URL='postgresql://rag:REPLACE_WITH_STRONG_PASSWORD@127.0.0.1:5434/rag'import os, requests, psycopg, numpy as np
from pgvector.psycopg import register_vector
samples=[('demo-manual',1,'权重显存之外还需要为KV缓存预留空间。'),
('demo-manual',2,'冷却方案需要检查机房环境与供电条件。')]
revision='bge-m3-your-actual-revision'
r=requests.post('http://127.0.0.1:8020/embed',
json={'texts':[x[2] for x in samples]},timeout=120)
r.raise_for_status()
vectors=r.json()['vectors']
with psycopg.connect(os.environ['RAG_DATABASE_URL']) as conn:
register_vector(conn)
for (source,page,body),vector in zip(samples,vectors,strict=True):
assert len(vector)==1024
conn.execute('INSERT INTO chunks(source_id,page_no,model_revision,body,embedding) VALUES (%s,%s,%s,%s,%s)',
(source,page,revision,body,np.asarray(vector,dtype=np.float32)))
conn.commit()
q=requests.post('http://127.0.0.1:8020/embed',
json={'texts':['模型部署为什么不能把显存用满?']},timeout=120)
q.raise_for_status()
query=np.asarray(q.json()['vectors'][0],dtype=np.float32)
rows=conn.execute('SELECT source_id,page_no,body,embedding <=> %s AS distance FROM chunks WHERE model_revision=%s ORDER BY distance LIMIT 5',
(query,revision)).fetchall()
print(rows)示例是有明确内容的两段演示资料,不冒充真实文档。重复运行会追加重复行,正式导入应以来源、分块ID、版本和文本哈希实现幂等。SQL使用绑定参数,不能把用户提问直接拼接到查询字符串。
距离、召回与近似索引
<=>为余弦距离,越小越接近;它不是回答正确率。先用100个标注问题检查精确检索Recall@10,记录遗漏段落。若切块把定义与条件拆开,向量模型再好也可能找不到完整答案。重叠范围、表格处理和标题附加应作为独立变量测试。
数据量扩大后再考虑HNSW或IVFFlat,并比较索引前后召回与延迟。近似搜索叠加过滤时可能返回较少结果,需按所选pgvector版本检查迭代扫描与查询参数,不简单增大LIMIT掩盖问题。带租户权限的过滤必须在候选检索阶段生效,不能先召回他人资料再仅隐藏显示。
故障、备份与升级
维度错误检查服务是否真为1024维;向量含NaN拒绝入库;所有结果不相关先检查模型版本、文本清洗与截断;扩展不存在检查镜像和CREATE EXTENSION执行结果。数据库连接密码别写入代码仓库,日志不要输出完整连接串。
模型升级新建revision索引,全量重算并验证后切换。数据库备份与原文文件备份同时保留,恢复后抽查向量数量、页码与实际文件。测试结束停止rag-pg,但保留rag-pg-data卷;回滚使用上一版索引,不删除原文。依据:pgvector官方文档。
分块规则应该怎样落到真实资料
对普通技术文章,先以标题和段落作为边界,再按实际Tokenizer估算长度。不要直接每五百个字符截一刀:命令、条件和注意事项可能被拆到不同块。表格按行组保留表头,代码保留语言与对应说明,跨页段落记录起止页。给每个块稳定编号,后续纠错才能定位到具体片段。
文档清洗保留型号、数字、单位和否定词。过度删除符号可能把版本号、接口路径或GPU型号变成另一种含义。用十份代表资料人工对照解析结果,再批量处理。每次导入记录源文件哈希、解析器版本、分块策略和模型版本,重复导入应更新同一版本或建立新版本,而非不断追加重复内容。
权限与关键词检索的配合
企业文档按部门或项目过滤,过滤条件来自登录身份,不来自用户可修改的请求字段。数据库查询使用服务端确定的权限范围,返回前再次核对来源权限。导出、图片和PDF下载也要保持同一边界,不能只保护文本检索接口。
型号、编号和错误码适合关键词精确匹配。向量召回擅长语义,但可能把两个相近型号混在一起。可以分别获取关键词与向量候选,按稳定规则融合,再重排;两组分数口径不同,不直接相加。只有原始证据中存在的型号和条件才能进入最终回答,排名靠前不表示事实已验证。
数据库容量与维护计划
一百万条一千零二十四维单精度向量的原始数值约四点一GB十进制空间,文本、行开销、索引和备份另计。上线前用实际文档抽样测每块平均占用,预估导入期间的临时空间。不要把原始向量大小当成磁盘最终需求,也不把数据库缓存设置到主机内存上限而挤压模型服务。
导入采用受控批次和事务,监测磁盘、连接数与慢查询。更新大量块后按数据库建议维护统计信息,并观察索引体积。备份恢复验收不仅检查进程启动,还要核对文档数量、权限过滤和十条固定查询。若模型升级后召回退步,切回旧版本索引,保留新结果供分析,不在没有备份时大范围删除重建。
导入时特别检查页码与原文件的对应关系,封面、目录与印刷页号可能不同。给用户展示时明确采用PDF实际页序还是文档印刷页号,并提供可直接打开的证据入口。原文件替换后重新核对哈希和页码,不能沿用旧定位。检索结果超过访问范围时返回空候选并记录权限原因,不让生成模型自行推断被隐藏的内容。
来源:网昱算力学院 · 技术编辑。第三方内容版权归原作者或发布机构所有;本站仅在许可证或明确授权允许时提供本地原文。
- 原文语言
- ZH
- 原文更新时间
- 未提供
- 许可证
- 原创工程内容
