a3bb78e06b
Security Quality Gate / npm audit, Bandit, pip-audit, detect-secrets (pull_request) Failing after 16m35s
188 lines
6.8 KiB
Markdown
188 lines
6.8 KiB
Markdown
# 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. 真实法规库导入质量验证:用民法典、劳动合同法、公司法等真实文本重新跑“主要依据”命中质量和延迟基线。
|