Files
AIRegulation-DocAnalysis/docs/superpowers/specs/2026-05-20-rag-dialogue-optimization-design.md

146 lines
5.7 KiB
Markdown
Raw Normal View History

# 法规对话模块优化设计文档
**日期**: 2026-05-20
**状态**: 已批准,实施中
---
## 背景
当前法规对话模块存在以下问题:
1. `compliance.py` `/compliance/chat/{segment_id}` 返回硬编码 Mock 数据
2. `rag.py` `/rag/chat` 返回硬编码 Mock 数据(前端实际调用 `/agent/chat/stream`,此路由可统一)
3. 前端快速问题列表硬编码在 `rag.ts`,未调用后端
4. 仅有 Dense 向量检索COSINE无 BM25 混合检索与精排
5. LLM 输出的 `[1][2]` 引用编号未在前端内联高亮
6. 会话存储仅为内存100条上限30分钟过期
---
## 方案分层优先4个阶段
### Phase 1 — 接入真实服务Week 1
**目标**:消灭所有 Mock让系统真正可用。
#### 后端变更
**`backend/app/schemas/compliance.py`**
- `ComplianceChatRequest` 新增 `segment_context: str | None = None`
**`backend/app/api/routes/compliance.py`**
- 移除 `get_mock_compliance_chat_response` 导入
- `/compliance/chat/{segment_id}` 接入 `get_agent_conversation_service().stream_chat()`
-`segment_context` 拼接到 query 前缀作为上下文
- 将 agent `content` 事件翻译为 `{"type":"chunk","text":"..."}` 格式,保持前端兼容
**`backend/app/api/routes/rag.py`**
- 移除 `get_mock_retrieval``get_mock_rag_answer` 导入
- `/rag/chat` 接入 `get_agent_conversation_service().stream_chat()`
- 翻译 agent 事件为 rag 格式(`retrieved`/`chunk`/`done`
**`backend/app/schemas/rag.py`**
- `RagChatRequest` 新增 `session_id: str | None = None``filters: str | None = None`
#### 前端变更
**`frontend/src/api/rag.ts`**
- `getQuickQuestions()` 改为真实调用 `GET /api/v1/rag/quick-questions`,失败时降级为本地数组
**`frontend/src/api/compliance.ts`**
- `complianceChat()` 新增第三个参数 `segmentContext: string | undefined`,传入 request body
**`frontend/src/pages/Compliance/CompliancePage.tsx`**
- `sendChatMessage()` 中构建 `segmentContext`intent + content 摘要 + 法规名)
- 传给 `complianceChat()`
---
### Phase 2 — 混合检索 + RerankingWeek 2-3
**目标**提升召回质量BM25 + dense RRF融合+ 精排。
#### 2a — Cross-Encoder Reranking优先无需 schema 变更)
- 新增端口 `backend/app/domain/retrieval/ports.py`: `Reranker` ABC
- 新增适配器 `backend/app/infrastructure/vectorstore/cross_encoder_reranker.py`
- 调用 OpenAI-compatible reranker APIBAAI/bge-reranker-v2-m3
- 修改 `KnowledgeRetrievalService`:先 retrieve top-20再 rerank 到 top-5
- 新增 settings: `reranker_enabled: bool = False``reranker_model: str``reranker_top_k: int = 5`
#### 2b — Milvus Sparse BM25需 schema 迁移)
- Milvus collection 新增 `sparse_embedding SPARSE_FLOAT_VECTOR` 字段
- 新增端口 `SparseEmbeddingProvider`sparse embed 接口)
- 适配器优先使用 BGE-M3 API同时输出 dense + sparse不可用时降级为 TF-IDF keyword weights
- `MilvusVectorIndex.upsert()` 同时写入 sparse 向量
- `MilvusVectorIndex.search()` 改为 hybrid search`WeightedRanker``RRFRanker`
- 提供一次性迁移脚本dump 所有 chunks → recreate collection → re-embed → re-insert
---
### Phase 3 — 引用溯源 + 筛选 UIWeek 4
**目标**:答案文本中 `[1][2]` 可点击跳转原文片段;法规类型/版本可筛选。
#### 引用内联解析
- 新增 React 组件 `CitedAnswer`:接受 `text` + `sources[]`
- 用正则 `/\[(\d+)\]/g` 拆分文本,将 `[N]` 渲染为可点击 `<cite>` 元素
- 点击高亮/滚动到来源面板对应条目
- `RagChatPage.tsx` 将 assistant 消息改用 `CitedAnswer` 组件渲染
- 来源面板各条目加 `id="source-N"` 属性
#### 法规筛选 UI
- `RagChatPage.tsx` 在输入框上方加筛选栏:`regulation_type` 下拉 + `version` 输入
- 筛选值作为 `filters` 参数传到 `/agent/chat/stream`
- `MilvusVectorIndex.search()` 解析 `filters` 字符串为 Milvus expr`regulation_type == "GB"`
#### 快速问题动态化
- 后端 `/rag/quick-questions` 改为从 settings 或配置文件加载,不硬编码
- 前端已在 Phase 1 中改为调用后端
---
### Phase 4 — 会话持久化 + 上下文压缩Week 5
**目标**:长对话不丢失;上下文过长时智能压缩。
#### PostgreSQL 会话存储
- 新增 `backend/app/infrastructure/session/postgres_conversation_store.py`
- 实现 `ConversationStore` port
- 复用现有 PostgreSQL 连接
- DDL: `conversation_sessions` + `conversation_messages` 两张表
- 新增 settings: `conversation_store_backend: str = "memory"`
- `bootstrap.py` 按 settings 切换实现
#### 上下文压缩
-`AgentConversationService` 中,当对话轮数 > N 时,将早期消息用 LLM 摘要
- 摘要替换为一条 `system` 消息 `"对话摘要:..."`
- 新增 `AnswerGenerator.summarize(messages) -> str` 方法
---
## 关键设计决策
| 决策 | 选择 | 原因 |
|------|------|------|
| BM25 实现 | Milvus sparse vectors | 不引入新服务Milvus 2.4+ 原生支持 |
| Reranking | API-based cross-encoder | 复用现有 OpenAI-compatible 接口 |
| 会话持久化 | PostgreSQL | 复用现有 PostgreSQL 基础设施 |
| 引用解析 | 前端纯客户端 | LLM 已输出 [N] 编号,无需后端改动 |
| segment_context | 前端构建传后端 | 合规结果存在前端状态,无需后端持久化 |
---
## 验证标准
- Phase 1: 合规对话面板返回真实法规检索结果,不再固定响应
- Phase 2: 相同 query 在 hybrid 模式下比 dense-only 召回更相关结果
- Phase 3: 点击答案中的 `[1]` 能跳转到来源面板第一条
- Phase 4: 刷新页面后历史对话仍可恢复