Files
ai-contract-platform/project-docs/V2-database-pgvector-hnsw-query-shape-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

188 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AI 合同平台 V2 数据库优化:pgvector HNSW 查询形态收口
日期:2026-06-24
## 本轮目标
按照《AI 合同平台 V2 重构版开发方案》和《AI 合同平台 V2 数据库优化重构方案》的 DB-5 性能基线推进,本轮聚焦 AI 中台知识库检索:
- 将知识库向量检索从可用的 pgvector 查询,收口为稳定命中 HNSW 索引的查询形态。
- 避免 pgvector 成功执行但返回空结果时回退到 Python 全量扫描。
- 保留合同系统和 AI 中台职责边界:合同系统只调用 AI 中台,知识库、向量检索、法条依据仍在 `yuqei-ai-platform` 内部完成。
## 已完成代码
### 1. pgvector 查询形态对齐 HNSW 表达式索引
文件:
- `yuqei-ai-platform/services/ai-platform-api/src/yuqei_ai_platform_api/repository.py`
核心变化:
- 默认 64 维 embedding 查询使用:
```sql
CAST(embedding AS vector(64)) <=> CAST(:query_vector AS vector(64))
```
- 与迁移中已创建的 HNSW 表达式索引保持一致:
```sql
CREATE INDEX ix_knowledge_chunks_embedding_hnsw
ON knowledge_chunks USING hnsw ((embedding::vector(64)) vector_cosine_ops)
WHERE embedding IS NOT NULL AND embedding_dimension = 64;
```
- 非默认维度仍保留兼容查询:
```sql
embedding <=> CAST(:query_vector AS vector)
```
这样做的原因是当前生产级默认 embedding 维度已经统一为 64,查询必须与表达式索引形态一致,PostgreSQL 才能使用 HNSW,而不是退化为顺序扫描。
### 2. pgvector 成功执行后不再误回退全量扫描
之前逻辑里,pgvector 查询返回空列表时会继续走 repository 的 Python 全量排序路径。真实数据量上这会造成慢查询,也会掩盖向量检索本身的问题。
已调整为:
- `None` 表示 pgvector 执行失败,可以回退。
- `[]` 表示 pgvector 成功执行但没有命中,不再回退全量扫描。
### 3. 向量候选池收口
新增候选池范围:
- 最小候选数:100
- 最大候选数:500
- 默认按 `limit * 25` 计算后夹紧
这样既保留混合排序的候选空间,又避免无限拉取候选造成接口变慢。
### 4. 中文法律核心词检索修正
文件:
- `yuqei-ai-platform/services/ai-platform-api/src/yuqei_ai_platform_api/embeddings.py`
- `yuqei-ai-platform/services/ai-platform-api/src/yuqei_ai_platform_api/main.py`
调整:
- 不再把“合同”“风险”作为 embedding 检索泛词过滤掉。
- 知识库检索接口现在可以正常搜索“合同”“风险”“押金”“租赁”。
- 法律问答层增加“泛合同问题”保护:例如“这个合同需要注意什么?”不强行召回某条不相干的劳动合同法,而是回到默认合同风险回答和民法典兜底依据。
这保持了两个目标:
- 知识库检索要能搜领域核心词。
- 法律问答不能因为泛词而错误引用不相关法条。
## 本地验证
命令:
```powershell
python -m pytest yuqei-ai-platform\services\ai-platform-api\tests -q
python -m compileall yuqei-ai-platform\services\ai-platform-api\src\yuqei_ai_platform_api\main.py yuqei-ai-platform\services\ai-platform-api\src\yuqei_ai_platform_api\repository.py yuqei-ai-platform\services\ai-platform-api\src\yuqei_ai_platform_api\embeddings.py
```
结果:
- `39 passed`
- compileall 通过
新增测试覆盖:
- 默认 64 维 pgvector 查询表达式必须包含 `CAST(embedding AS vector(64))` 和 `CAST(:query_vector AS vector(64))`。
- 非默认维度仍走兼容查询表达式。
- “合同”“风险”作为中文法律核心词必须可搜索。
## V2 smoke 部署验证
部署命令:
```powershell
$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-03-01`
远端数据库状态:
- `ai_platform_alembic_version = 20260624_0015`
- `knowledge_chunks = 60002`
- `embedding` 非空数量:`60002`
- `embedding_dimension`:最小 `64`,最大 `64`
- HNSW 索引存在:`ix_knowledge_chunks_embedding_hnsw`
## EXPLAIN ANALYZE 结果
验证 SQL:
```sql
EXPLAIN (ANALYZE, BUFFERS)
SELECT id
FROM knowledge_chunks
WHERE embedding IS NOT NULL
AND embedding_dimension = 64
ORDER BY CAST(embedding AS vector(64)) <=> CAST(:query_vector AS vector(64))
LIMIT 10;
```
关键结果:
```text
Index Scan using ix_knowledge_chunks_embedding_hnsw on knowledge_chunks
Execution Time: 1.668 ms
```
结论:
- 代码当前使用的 `CAST(embedding AS vector(64))` 可以命中迁移创建的 `embedding::vector(64)` HNSW 表达式索引。
- pgvector 查询已经从原来的顺序扫描风险,收口为 HNSW 索引扫描。
## API 抽样验证
远端 smoke 环境接口:
- `GET /api/v1/knowledge-chunks/search`
- `POST /api/v1/legal/qa`
知识库搜索抽样:
| 关键词 | 策略 | 结果数 | 首条分数 | 说明 |
| --- | --- | ---: | ---: | --- |
| 合同 | hybrid | 3 | 0.5711 | 中文核心词可检索 |
| 风险 | hybrid | 3 | 0.5365 | 中文核心词可检索 |
| 押金 | hybrid | 3 | 0.6326 | 租赁押金场景命中 |
| 租赁 | hybrid | 3 | 0.55 | 租赁场景命中 |
| deposit refund | vector | 3 | 0.2281 | 纯向量路径返回候选 |
法律问答抽样:
| 问题 | 首条依据 | 结果 |
| --- | --- | --- |
| 这个合同需要注意什么? | 中华人民共和国民法典 第五百零九条 | 泛合同问题回到默认兜底 |
| 房屋租赁合同押金退还应注意什么? | 租赁押金法条片段 | 具体问题走知识库命中 |
| 劳动合同试用期最长可以约定多久? | 知识库片段 | 具体问题走知识库命中 |
## 当前边界
- 本轮已经解决 pgvector/HNSW 查询形态,不继续扩大为全文检索重写。
- chunk 的 `title/reference/content` 已有 `pg_trgm` GIN 索引,但当前 SQL 使用 `lower(...) LIKE`,对部分泛词仍可能不是最优。后续如要继续压缩“合同/风险”这类超高频词耗时,可以单独做关键词 SQL 形态优化或生成列索引。
- 当前 smoke 数据中存在大量性能样本 chunk,返回的法条标题会带有测试数据特征。生产数据导入后需要结合真实法规库重新跑质量抽样。
## 下一步建议
DB-5 的 pgvector 查询形态已经收口。下一步更自然进入:
1. 关键词检索 SQL 形态优化:将 `lower(...) LIKE` 调整为更容易命中 trigram/tsvector 的查询方式,并做高频中文词基线。
2. RAG 可解释接口增强:在 explain 中显示是否走 HNSW、候选数量、关键词候选补充数量和最终混合排序原因。
3. 真实法规库导入质量验证:用民法典、劳动合同法、公司法等真实文本重新跑“主要依据”命中质量和延迟基线。