Files
ai-contract-platform/project-docs/V2-database-keyword-search-sql-optimization-2026-06-24.md
T
yuqei a3bb78e06b
Security Quality Gate / npm audit, Bandit, pip-audit, detect-secrets (pull_request) Failing after 16m35s
release: freeze V2 release candidate
2026-07-12 15:32:24 +08:00

6.8 KiB
Raw Blame History

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_0016
  • knowledge_chunks = 60002
  • search_vector 非空数量:60002
  • 索引存在:
    • ix_knowledge_chunks_embedding_hnsw
    • ix_knowledge_chunks_search_vector_gin
    • ix_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 中台知识库候选查询的数据库执行层优化。
  • simple tsvector 对长中文连续文本的切词能力有限;当前法规片段和测试数据以短词/短语为主,可以满足 MVP 性能闭环。
  • 后续真实法规库导入后,应继续用真实问题集验证“法条依据命中率”,必要时再引入专门中文分词、jieba/SCWS 或向量+BM25 服务。

下一步建议

DB-5 关键词 SQL 形态已经收口。下一步更自然进入:

  1. RAG explain 增强:显示本次检索是否命中 HNSW、是否使用 search_vector、关键词候选数量、向量候选数量和最终排序原因。
  2. 真实法规库质量基线:用真实法律问题集验证主要依据命中率和错误引用率。
  3. 数据库运维基线:为迁移耗时、索引大小、autovacuum、备份恢复演练补记录。