a3bb78e06b
Security Quality Gate / npm audit, Bandit, pip-audit, detect-secrets (pull_request) Failing after 16m35s
6.8 KiB
6.8 KiB
AI 合同平台 V2 数据库优化:关键词检索 SQL 形态收口
日期:2026-06-24
本轮目标
延续 DB-5 性能基线,本轮专门处理 AI 中台知识库混合检索中的关键词候选补充慢查询。
上一轮已经把向量检索收口到 pgvector HNSW。本轮目标是压缩高频中文词,例如“合同”“风险”,避免 lower(...) LIKE '%词%' 在 knowledge_chunks 上顺序扫描。
现状问题
原关键词候选 SQL:
SELECT id, ...
FROM knowledge_chunks
WHERE embedding IS NOT NULL
AND (
lower(title) LIKE :term
OR lower(reference) LIKE :term
OR lower(content) LIKE :term
)
ORDER BY created_at DESC
LIMIT :limit;
问题:
- 现有 trigram 索引建在原字段
title/reference/content上。 - 查询使用
lower(...) LIKE,无法稳定使用这些原字段索引。 - 对“合同”“风险”这类高频词,即使改为 raw
LIKE或 trigram%,也无法稳定改善。
远端 smoke 库验证:
| 查询形态 | 关键词 | 查询计划 | 耗时 |
|---|---|---|---|
lower(...) LIKE '%合同%' |
合同 | Seq Scan | 约 948 ms |
raw LIKE '%合同%' |
合同 | Parallel Seq Scan | 约 1135 ms |
trigram % '合同' |
合同 | Bitmap Index Scan,但相似度阈值导致 0 行且回表重 | 约 8308 ms |
表达式 trigram 索引 + lower(...) LIKE |
合同 | 仍 Seq Scan | 约 475 ms |
结论:不能简单把 LIKE 替换成 %,也不能只补表达式 trigram 索引。
已完成代码
1. 新增独立迁移 20260624_0016
文件:
yuqei-ai-platform/services/ai-platform-api/migrations/versions/20260624_0016_add_knowledge_chunk_search_vector.py
迁移内容:
ALTER TABLE knowledge_chunks ADD COLUMN IF NOT EXISTS search_vector tsvector;
UPDATE knowledge_chunks
SET search_vector = to_tsvector(
'simple',
coalesce(title, '') || ' ' || coalesce(reference, '') || ' ' || coalesce(content, '')
)
WHERE search_vector IS NULL;
CREATE INDEX IF NOT EXISTS ix_knowledge_chunks_search_vector_gin
ON knowledge_chunks USING gin (search_vector);
CREATE INDEX IF NOT EXISTS ix_knowledge_chunks_embedding_created_desc
ON knowledge_chunks (created_at DESC, id)
WHERE embedding IS NOT NULL;
说明:
0015在 smoke 库已经执行过,因此本轮没有继续修改已应用迁移,而是新增0016增量迁移。search_vector用simple配置,适合当前中文法规片段以词/短语为主的最小可用检索。created_at DESC局部索引用于高频词场景,让数据库可以按最新片段顺序快速停止。
2. repository 查询改造
文件:
yuqei-ai-platform/services/ai-platform-api/src/yuqei_ai_platform_api/repository.py
改造点:
- 混合检索仍先走 pgvector HNSW 获取全局语义候选。
- 关键词候选补充优先使用:
search_vector @@ plainto_tsquery('simple', :keyword_query)
- 查询继续按
created_at DESC LIMIT :remaining_limit,用于快速补足混合候选。 - 如果目标库没有
search_vector列,或者 SQL 执行失败,自动回退到旧LIKE逻辑,保证兼容。 - 新增/重建 chunk 后自动刷新对应行的
search_vector。
3. 中文核心词保持可搜索
延续上一轮修复:
- “合同”“风险”不再作为 embedding 泛词过滤。
- 法律问答层仍保留泛问题保护,避免“这个合同需要注意什么?”误召回不相关专门法条。
本地验证
命令:
python -m pytest yuqei-ai-platform\services\ai-platform-api\tests -q
python -m compileall yuqei-ai-platform\services\ai-platform-api\migrations\versions\20260624_0016_add_knowledge_chunk_search_vector.py yuqei-ai-platform\services\ai-platform-api\src\yuqei_ai_platform_api\repository.py
结果:
40 passed- compileall 通过
新增测试:
_to_tsquery_text(["合同", "", " 风险 "]) == "合同 风险"
V2 smoke 部署验证
部署命令:
$env:SSHPASS='YOUR_REMOTE_PASSWORD'
powershell.exe -NoProfile -ExecutionPolicy Bypass -File yuqei-infra\scripts\deploy-v2-smoke-docker.ps1 -UseSshpass -AllowDirtyWorktree -SkipSmoke
最终结果:
V2_SMOKE_DOCKER_DEPLOY_PASS- 产物目录:
project-docs/qa-artifacts/v2-smoke-docker-deploy/2026-06-24T21-35-08
中间情况:
- 第一次失败:拉取
node:22-alpine元数据时 Docker 镜像代理 TLS 超时。 - 第二次失败:Postgres 因上次中断进入 crash recovery/fsync,healthcheck 时间不够。
- 等待 Postgres 恢复后手动
docker compose up -d成功。 - 第三次完整部署通过。
远端数据库状态:
ai_platform_alembic_version = 20260624_0016knowledge_chunks = 60002search_vector非空数量:60002- 索引存在:
ix_knowledge_chunks_embedding_hnswix_knowledge_chunks_search_vector_ginix_knowledge_chunks_embedding_created_desc
EXPLAIN ANALYZE 结果
验证 SQL:
EXPLAIN (ANALYZE, BUFFERS)
SELECT id
FROM knowledge_chunks
WHERE embedding IS NOT NULL
AND search_vector @@ plainto_tsquery('simple', '合同')
ORDER BY created_at DESC
LIMIT 100;
关键结果:
Index Scan using ix_knowledge_chunks_embedding_created_desc on knowledge_chunks
Execution Time: 0.396 ms
对比:
| 查询 | 优化前 | 优化后 |
|---|---|---|
| 高频词“合同”候选查询 | 约 475-948 ms | 约 0.396 ms |
API 抽样验证
接口:
GET /api/v1/knowledge-chunks/search?strategy=hybrid&limit=3
warm 抽样结果:
| 关键词 | 5 次耗时 ms | 中位数 |
|---|---|---|
| 合同 | 70.41, 17.32, 14.51, 14.77, 15.51 | 15.51 ms |
| 风险 | 15.45, 14.85, 31.43, 14.13, 14.99 | 14.99 ms |
| 押金 | 16.69, 15.39, 26.76, 59.36, 17.49 | 17.49 ms |
| 租赁 | 15.55, 30.02, 14.56, 15.61, 28.65 | 15.61 ms |
说明:
- 首次请求可能包含服务/缓存预热成本。
- 连续访问热路径稳定在约 15-17 ms。
- 返回仍包含匹配词,检索语义没有丢失。
当前边界
- 本轮不是完整中文分词搜索引擎改造,而是 AI 中台知识库候选查询的数据库执行层优化。
simpletsvector 对长中文连续文本的切词能力有限;当前法规片段和测试数据以短词/短语为主,可以满足 MVP 性能闭环。- 后续真实法规库导入后,应继续用真实问题集验证“法条依据命中率”,必要时再引入专门中文分词、jieba/SCWS 或向量+BM25 服务。
下一步建议
DB-5 关键词 SQL 形态已经收口。下一步更自然进入:
- RAG explain 增强:显示本次检索是否命中 HNSW、是否使用 search_vector、关键词候选数量、向量候选数量和最终排序原因。
- 真实法规库质量基线:用真实法律问题集验证主要依据命中率和错误引用率。
- 数据库运维基线:为迁移耗时、索引大小、autovacuum、备份恢复演练补记录。