feat(agents): Phase 8.4-10.5 built-in plugins, bundled skills, coordinator
This commit is contained in:
157
development-doc/plan/rag-update/README.md
Normal file
157
development-doc/plan/rag-update/README.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# Jarvis RAG 升级计划索引
|
||||
|
||||
本目录用于存放 Jarvis RAG 系统的分阶段升级规划文档。
|
||||
|
||||
## 文档说明
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `README.md` | 总览、阶段关系、实施顺序 |
|
||||
| `phase-r-0-current-state.md` | 当前现状、问题、目标架构、VCPToolBox 借鉴 |
|
||||
| `phase-r-1-token-chunking.md` | Token 感知分块优化 |
|
||||
| `phase-r-2-multi-index.md` | 多索引架构 |
|
||||
| `phase-r-3-dynamic-weight.md` | 动态权重增强 |
|
||||
| `phase-r-4-advanced.md` | 高级特性(可选) |
|
||||
| `checklist.md` | 执行清单 |
|
||||
|
||||
## 推荐阅读顺序
|
||||
|
||||
1. 先读 `phase-r-0-current-state.md`
|
||||
2. 再按顺序阅读 phase r-1 ~ r-4
|
||||
3. 实施时严格按阶段推进,R.4 为可选
|
||||
4. 参考 `checklist.md` 进行任务追踪
|
||||
|
||||
---
|
||||
|
||||
## 总体升级原则
|
||||
|
||||
1. **Token 精确控制** - 使用 tiktoken 精确计数
|
||||
2. **多索引分层** - 按知识类型/重要性分离
|
||||
3. **动态适配** - 根据查询特性动态调整检索策略
|
||||
4. **测试优先** - 所有升级都要配套测试
|
||||
5. **可独立推进** - Phase R 可与 Agent Phase 1-5 并行
|
||||
|
||||
---
|
||||
|
||||
## 阶段总览图
|
||||
|
||||
```
|
||||
R.0 ──────────────────────────────────────────────────────────────┐
|
||||
│ 现状与目标 │
|
||||
│ - 当前架构分析 │
|
||||
│ - 短板识别 │
|
||||
│ - VCPToolBox TagMemo V6 借鉴 │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
R.1 ──────────────────────────────────────────────────────────────┐
|
||||
│ Token 感知分块优化 │
|
||||
│ - tiktoken 集成 │
|
||||
│ - 智能断句 │
|
||||
│ - 重叠分块 (10% overlap) │
|
||||
│ │
|
||||
│ 核心文件: services/chunker.py │
|
||||
│ 工作量: 3 天 │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
R.2 ──────────────────────────────────────────────────────────────┐
|
||||
│ 多索引架构 │
|
||||
│ - Collection 分离策略 │
|
||||
│ - 懒加载 + LRU TTL │
|
||||
│ - 重要性感知检索 │
|
||||
│ │
|
||||
│ 核心文件: services/multi_index.py │
|
||||
│ 依赖: R.1 │
|
||||
│ 工作量: 4 天 │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
R.3 ──────────────────────────────────────────────────────────────┐
|
||||
│ 动态权重增强 │
|
||||
│ - QueryAnalyzer │
|
||||
│ - DynamicReranker │
|
||||
│ - CoreTagAwareSearch │
|
||||
│ │
|
||||
│ 核心文件: services/query_analyzer.py, │
|
||||
│ services/dynamic_reranker.py, │
|
||||
│ services/core_tag_search.py │
|
||||
│ 依赖: R.1 │
|
||||
│ 工作量: 4.5 天 │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
R.4 ──────────────────────────────────────────────────────────────┐
|
||||
│ 高级特性 (可选) │
|
||||
│ - 语义去重 │
|
||||
│ - 语义分桶 │
|
||||
│ - EPA 分析设计 │
|
||||
│ │
|
||||
│ 状态: 可选 │
|
||||
│ 工作量: 4.5 天 │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## VCPToolBox TagMemo V6 核心借鉴
|
||||
|
||||
| 借鉴点 | 实现位置 | 难度 |
|
||||
|--------|---------|------|
|
||||
| Token 感知分块(85%+10% 重叠) | R.1 | 🟢 低 |
|
||||
| 多索引架构 | R.2 | 🟡 中 |
|
||||
| 懒加载 + LRU | R.2 | 🟡 中 |
|
||||
| TagBoost 动态权重 | R.3 | 🟡 中 |
|
||||
| 核心标签系统(1.33x 加权) | R.3 | 🟡 中 |
|
||||
| LIF 脉冲扩散 | R.4 | 🔴 高 |
|
||||
|
||||
---
|
||||
|
||||
## 实施顺序
|
||||
|
||||
```
|
||||
R.0 → R.1 → R.2 → R.3 → (R.4 可选)
|
||||
│ │ │ │
|
||||
│ │ │ └── 语义去重/分桶/PCA
|
||||
│ │ └── 多索引 + 懒加载
|
||||
│ └── Token感知分块
|
||||
└── 现状与目标
|
||||
```
|
||||
|
||||
**注意:** R.1 是基础,R.2 和 R.3 都依赖 R.1;R.4 可选。
|
||||
|
||||
---
|
||||
|
||||
## 文件变更追踪
|
||||
|
||||
| Phase | 新增文件 | 修改文件 |
|
||||
|-------|---------|---------|
|
||||
| R.1 | `services/chunker.py`, `tests/test_chunker.py` | `services/document_service.py` |
|
||||
| R.2 | `services/multi_index.py`, `tests/test_multi_index.py` | `services/knowledge_service.py`, `models/document.py` |
|
||||
| R.3 | `services/query_analyzer.py`, `services/dynamic_reranker.py`, `services/core_tag_search.py`, `tests/test_dynamic_reranker.py` | `services/knowledge_service.py`, `models/document.py` |
|
||||
| R.4 | `services/deduplicator.py`, `services/semantic_bucket.py` (可选) | - |
|
||||
|
||||
---
|
||||
|
||||
## 与 Agent Phase 1-5 的关系
|
||||
|
||||
| Agent Phase | RAG 协作内容 |
|
||||
|-------------|-------------|
|
||||
| Phase 1 | Task Schema 追踪 RAG 任务 |
|
||||
| Phase 2 | RAG 任务可分解给 Librarian Agent |
|
||||
| Phase 3 | 支持多索引动态选择 |
|
||||
| Phase 4 | RAG 检索过程可视化 |
|
||||
| Phase 5 | EPA 分析、语义分桶 |
|
||||
| **Phase R** | **独立 RAG 升级路径,可与 Phase 1-5 并行推进** |
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
| 注意事项 | 说明 |
|
||||
|---------|------|
|
||||
| R.1 是基础 | R.2 和 R.3 都依赖 R.1 的分块优化 |
|
||||
| Token 精确计数 | 使用 tiktoken,多版本验证 |
|
||||
| 索引分离 | 提供统一检索接口,隐藏内部逻辑 |
|
||||
| 动态权重 | 提供配置项,允许用户调整 |
|
||||
| EPA 高复杂度 | Phase R.4 可选,暂不实现 |
|
||||
327
development-doc/plan/rag-update/checklist.md
Normal file
327
development-doc/plan/rag-update/checklist.md
Normal file
@@ -0,0 +1,327 @@
|
||||
# Jarvis RAG 升级执行清单
|
||||
|
||||
日期:2026-04-03
|
||||
状态:执行清单
|
||||
借鉴来源:VCPToolBox TagMemo V6 架构
|
||||
|
||||
---
|
||||
|
||||
## 使用说明
|
||||
|
||||
- 完成前使用 `- [ ]`
|
||||
- 完成后改成 `- [x]`
|
||||
- Day R.2 默认依赖 Day R.1 的分块优化完成后再推进
|
||||
- Day R.3 默认依赖 Day R.1 的分块优化完成后再推进
|
||||
- Day R.4 为可选特性
|
||||
|
||||
---
|
||||
|
||||
## Day R.1:Token 感知分块优化(3天)
|
||||
|
||||
Day R.1 目标:解决跨块边界信息丢失问题,实现精确的 token 计数和重叠分块。
|
||||
|
||||
### Task R.1.1:集成 tiktoken
|
||||
|
||||
- [ ] 安装 tiktoken 依赖
|
||||
```bash
|
||||
uv add tiktoken
|
||||
```
|
||||
|
||||
- [ ] 新增 `backend/app/services/chunker.py`
|
||||
实现 `TokenAwareChunker` 类,支持 85% 安全边界
|
||||
|
||||
- [ ] 实现 `count_tokens()` 方法
|
||||
|
||||
### Task R.1.2:实现智能断句
|
||||
|
||||
- [ ] 实现 `find_best_breakpoint()` 函数
|
||||
在断点处(标点/空白)智能断开
|
||||
|
||||
- [ ] 实现 `_force_split_long_text()` 方法
|
||||
处理超长句子强制分割
|
||||
|
||||
### Task R.1.3:实现重叠分块
|
||||
|
||||
- [ ] 实现 `chunk_with_overlap()` 方法
|
||||
10% token 重叠,保证上下文连续性
|
||||
|
||||
- [ ] 实现 `_create_overlap()` 方法
|
||||
创建重叠部分
|
||||
|
||||
### Task R.1.4:集成到 DocumentService
|
||||
|
||||
- [ ] 修改 `backend/app/services/document_service.py`
|
||||
集成新的 TokenAwareChunker
|
||||
|
||||
- [ ] 替换原有的 `_build_chunks()` 方法
|
||||
|
||||
### Task R.1.5:补测试
|
||||
|
||||
- [ ] 新增 `backend/tests/services/test_chunker.py`
|
||||
|
||||
- [ ] 测试 Token 计数准确性
|
||||
|
||||
- [ ] 测试智能断句
|
||||
|
||||
- [ ] 测试重叠分块
|
||||
|
||||
### Day R.1 验收
|
||||
|
||||
- [ ] tiktoken 正确集成,token 计数误差 < 1%
|
||||
- [ ] 超长句子不在词汇中间断开
|
||||
- [ ] 重叠分块保证上下文连续性
|
||||
- [ ] 单元测试覆盖率 > 80%
|
||||
- [ ] 文档上传→分块→检索 集成测试通过
|
||||
|
||||
---
|
||||
|
||||
## Day R.2:多索引架构(4天)
|
||||
|
||||
Day R.2 目标:按知识类型/重要性分层,支持懒加载和 LRU 淘汰。
|
||||
|
||||
### Task R.2.1:设计 Collection 分离策略
|
||||
|
||||
- [ ] 新增 `backend/app/services/multi_index.py`
|
||||
|
||||
- [ ] 定义 `MultiIndexManager` 类
|
||||
|
||||
- [ ] 实现 `INDEX_STRATEGIES` 配置
|
||||
- default: 通用文档
|
||||
- important: 重要文档
|
||||
- code: 代码片段
|
||||
- meeting: 会议记录
|
||||
|
||||
- [ ] 实现 `get_collection()` 方法
|
||||
|
||||
### Task R.2.2:实现懒加载 + LRU TTL
|
||||
|
||||
- [ ] 实现 `LazyIndexLoader` 类
|
||||
|
||||
- [ ] 实现 `get_or_load()` 方法
|
||||
|
||||
- [ ] 实现 `sweep()` 方法
|
||||
2小时 TTL 淘汰机制
|
||||
|
||||
### Task R.2.3:实现重要性感知检索
|
||||
|
||||
- [ ] 实现 `retrieve_with_importance()` 方法
|
||||
|
||||
- [ ] important 索引加权 1.2x
|
||||
|
||||
### Task R.2.4:修改 Document 模型
|
||||
|
||||
- [ ] 修改 `backend/app/models/document.py`
|
||||
|
||||
- [ ] 增加 `importance` 字段(Float, default=0.5)
|
||||
|
||||
### Task R.2.5:集成到 KnowledgeService
|
||||
|
||||
- [ ] 修改 `backend/app/services/knowledge_service.py`
|
||||
|
||||
- [ ] 集成 MultiIndexManager
|
||||
|
||||
- [ ] 集成 LazyIndexLoader
|
||||
|
||||
- [ ] 根据 importance 选择索引
|
||||
|
||||
### Task R.2.6:补测试
|
||||
|
||||
- [ ] 新增 `backend/tests/services/test_multi_index.py`
|
||||
|
||||
- [ ] 测试多 Collection 创建
|
||||
|
||||
- [ ] 测试懒加载
|
||||
|
||||
- [ ] 测试 TTL 淘汰
|
||||
|
||||
### Day R.2 验收
|
||||
|
||||
- [ ] 多 Collection 创建成功
|
||||
- [ ] 懒加载索引生效
|
||||
- [ ] TTL 淘汰机制工作
|
||||
- [ ] 重要性感知检索加权生效
|
||||
- [ ] 单元测试覆盖率 > 80%
|
||||
|
||||
---
|
||||
|
||||
## Day R.3:动态权重增强(4.5天)
|
||||
|
||||
Day R.3 目标:根据查询特性动态调整检索策略,支持核心标签加权。
|
||||
|
||||
### Task R.3.1:实现查询特性分析
|
||||
|
||||
- [ ] 新增 `backend/app/services/query_analyzer.py`
|
||||
|
||||
- [ ] 定义 `QueryProfile` 数据类
|
||||
|
||||
- [ ] 实现 `QueryAnalyzer` 类
|
||||
|
||||
- [ ] 实现查询类型检测
|
||||
- 代码相关
|
||||
- 表格相关
|
||||
- 对话式
|
||||
|
||||
- [ ] 实现 `_calc_logic_depth()` 方法
|
||||
|
||||
### Task R.3.2:实现动态 Reranker
|
||||
|
||||
- [ ] 新增 `backend/app/services/dynamic_reranker.py`
|
||||
|
||||
- [ ] 实现 `DynamicReranker` 类
|
||||
|
||||
- [ ] 实现 `_get_weights()` 方法
|
||||
- 代码查询:关键词权重高
|
||||
- 表格查询:标题权重高
|
||||
- 对话式:语义权重高
|
||||
|
||||
- [ ] 实现 `_calc_beta()` 方法
|
||||
|
||||
- [ ] 实现 `rerank()` 方法
|
||||
|
||||
### Task R.3.3:实现核心标签系统
|
||||
|
||||
- [ ] 新增 `backend/app/services/core_tag_search.py`
|
||||
|
||||
- [ ] 实现 `CoreTagAwareSearch` 类
|
||||
|
||||
- [ ] 实现 `CORE_BOOST_FACTOR = 1.33`
|
||||
|
||||
- [ ] 实现 `search()` 方法
|
||||
|
||||
### Task R.3.4:修改 DocumentChunk 模型
|
||||
|
||||
- [ ] 修改 `backend/app/models/document.py`
|
||||
|
||||
- [ ] 增加 `tags` 字段(JSON, default=list)
|
||||
|
||||
- [ ] 增加 `is_core` 字段(Boolean, default=False)
|
||||
|
||||
### Task R.3.5:集成到 KnowledgeService
|
||||
|
||||
- [ ] 修改 `backend/app/services/knowledge_service.py`
|
||||
|
||||
- [ ] 集成 QueryAnalyzer
|
||||
|
||||
- [ ] 集成 DynamicReranker
|
||||
|
||||
- [ ] 集成 CoreTagAwareSearch
|
||||
|
||||
- [ ] 修改 `retrieve()` 方法支持动态权重
|
||||
|
||||
### Task R.3.6:补测试
|
||||
|
||||
- [ ] 新增 `backend/tests/services/test_dynamic_reranker.py`
|
||||
|
||||
- [ ] 测试查询特性分析
|
||||
|
||||
- [ ] 测试动态权重调整
|
||||
|
||||
- [ ] 测试核心标签加权
|
||||
|
||||
### Day R.3 验收
|
||||
|
||||
- [ ] 查询特性分析准确(代码/表格/对话式识别)
|
||||
- [ ] 动态权重根据查询类型调整
|
||||
- [ ] 核心标签检索加权 1.33x
|
||||
- [ ] Rerank 集成测试通过
|
||||
|
||||
---
|
||||
|
||||
## Day R.4:高级特性(可选)(4.5天)
|
||||
|
||||
Day R.4 目标:探索更高级的 RAG 增强技术。
|
||||
|
||||
### Task R.4.1:语义去重
|
||||
|
||||
- [ ] 新增 `backend/app/services/deduplicator.py`
|
||||
|
||||
- [ ] 实现 `SemanticDeduplicator` 类
|
||||
|
||||
- [ ] 实现 `_cosine_similarity()` 方法
|
||||
|
||||
- [ ] 实现 `deduplicate()` 方法
|
||||
|
||||
### Task R.4.2:语义分桶(可选)
|
||||
|
||||
- [ ] 新增 `backend/app/services/semantic_bucket.py`
|
||||
|
||||
- [ ] 实现 `SemanticBucketing` 类
|
||||
|
||||
- [ ] 实现 `bucket_by_topic()` 方法
|
||||
|
||||
### Task R.4.3:EPA 分析设计(可选探索)
|
||||
|
||||
- [ ] 设计 EPA 模块架构
|
||||
|
||||
- [ ] 定义 EPA 接口
|
||||
|
||||
- [ ] 实现残差金字塔算法(伪代码)
|
||||
|
||||
### Day R.4 验收(可选)
|
||||
|
||||
- [ ] 语义去重测试通过
|
||||
- [ ] 语义分桶原型完成(可选)
|
||||
- [ ] EPA 分析方案设计完成(可选实现)
|
||||
|
||||
---
|
||||
|
||||
## 总验收清单
|
||||
|
||||
### Phase R.1-R.3 必须完成
|
||||
|
||||
- [ ] Token 感知分块正常工作
|
||||
- [ ] 多索引架构正常工作
|
||||
- [ ] 动态权重增强正常工作
|
||||
- [ ] 单元测试覆盖率 > 80%
|
||||
- [ ] 集成测试通过
|
||||
- [ ] 原有检索功能无回退
|
||||
|
||||
### Phase R.4 可选完成
|
||||
|
||||
- [ ] 语义去重正常工作
|
||||
- [ ] 语义分桶正常工作(可选)
|
||||
- [ ] EPA 设计文档完成(可选)
|
||||
|
||||
---
|
||||
|
||||
## 总工作量估算
|
||||
|
||||
| Phase | 工作量 |
|
||||
|-------|--------|
|
||||
| R.1 Token 感知分块 | 3 天 |
|
||||
| R.2 多索引架构 | 4 天 |
|
||||
| R.3 动态权重增强 | 4.5 天 |
|
||||
| R.4 高级特性(可选) | 4.5 天 |
|
||||
| **R.1-R.3 必须** | **11.5 天** |
|
||||
| **R.1-R.4 含可选** | **16 天** |
|
||||
|
||||
---
|
||||
|
||||
## 产出清单
|
||||
|
||||
| 产出 | 对应 Phase |
|
||||
|------|-----------|
|
||||
| `services/chunker.py` | R.1 |
|
||||
| `services/multi_index.py` | R.2 |
|
||||
| `services/query_analyzer.py` | R.3 |
|
||||
| `services/dynamic_reranker.py` | R.3 |
|
||||
| `services/core_tag_search.py` | R.3 |
|
||||
| `services/deduplicator.py` | R.4 |
|
||||
| `services/semantic_bucket.py` | R.4(可选) |
|
||||
| `models/document.py` 更新 | R.2, R.3 |
|
||||
| 单元测试 > 80% | R.1, R.2, R.3 |
|
||||
| 集成测试通过 | R.1, R.2, R.3 |
|
||||
|
||||
---
|
||||
|
||||
## 与 Agent Phase 关系
|
||||
|
||||
| Agent Phase | RAG 协作内容 |
|
||||
|-------------|-------------|
|
||||
| Phase 1 | Task Schema 追踪 RAG 任务 |
|
||||
| Phase 2 | RAG 任务可分解给 Librarian Agent |
|
||||
| Phase 3 | 支持多索引动态选择 |
|
||||
| Phase 4 | RAG 检索过程可视化 |
|
||||
| Phase 5 | EPA 分析、语义分桶 |
|
||||
|
||||
**Phase R 可与 Agent Phase 1-5 并行推进。**
|
||||
156
development-doc/plan/rag-update/phase-r-0-current-state.md
Normal file
156
development-doc/plan/rag-update/phase-r-0-current-state.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# Phase R.0:RAG 现状与目标
|
||||
|
||||
日期:2026-04-03
|
||||
状态:已完成
|
||||
借鉴来源:VCPToolBox TagMemo V6 架构
|
||||
|
||||
---
|
||||
|
||||
## 1. 本阶段目的
|
||||
|
||||
本文件用于统一背景认知,明确:
|
||||
|
||||
- Jarvis 当前 RAG 架构处于什么水平
|
||||
- 主要短板是什么
|
||||
- 为什么要升级
|
||||
- 升级后的目标形态是什么
|
||||
- VCPToolBox 给我们什么启发
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前 Jarvis RAG 架构
|
||||
|
||||
### 2.1 核心流程
|
||||
|
||||
```
|
||||
用户上传文档 → DocumentService (解析/分块) → ChromaDB (向量存储) → KnowledgeService (检索)
|
||||
```
|
||||
|
||||
### 2.2 核心文件
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `backend/app/services/document_service.py` | 文档上传/解析/分块 |
|
||||
| `backend/app/services/knowledge_service.py` | ChromaDB 向量检索/混合检索 |
|
||||
| `backend/app/models/document.py` | Document/DocumentChunk 数据模型 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 当前能力矩阵
|
||||
|
||||
| 能力 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| 多格式文档解析 | ✅ | PDF/MD/TXT/DOCX/CSV/XLSX |
|
||||
| 结构化分块 | ✅ | 基于标题层级、表格、段落 |
|
||||
| 向量检索 | ✅ | ChromaDB 语义相似度 |
|
||||
| 关键词检索 | ✅ | SQL LIKE |
|
||||
| 混合检索 | ✅ | 向量 + 关键词加权 |
|
||||
| Rerank | ✅ | 语义分×0.7 + 关键词×0.2 + 标题×0.1 |
|
||||
| 上下文丰富 | ✅ | 自动获取前/后 chunk |
|
||||
|
||||
---
|
||||
|
||||
## 4. 当前短板
|
||||
|
||||
| 短板 | 严重程度 | 影响 |
|
||||
|------|----------|------|
|
||||
| 无重叠分块 | 🟡 中 | 跨块边界信息丢失 |
|
||||
| 单索引架构 | 🟡 中 | 无法按知识类型/重要性分层 |
|
||||
| 无动态权重 | 🟡 中 | 检索策略静态,不适配查询类型 |
|
||||
| 无 Tag/标签系统 | 🟡 中 | 无法利用语义标签增强检索 |
|
||||
| 无懒加载机制 | 🟢 低 | 大量文档时内存占用高 |
|
||||
| 无遗忘机制 | 🟢 低 | 存储无限增长 |
|
||||
|
||||
---
|
||||
|
||||
## 5. VCPToolBox TagMemo V6 核心借鉴
|
||||
|
||||
### 5.1 核心架构
|
||||
|
||||
```
|
||||
日记文件变化 → TextChunker(Token感知分块85%+10%重叠)
|
||||
→ EmbeddingUtils(并发批量向量化)
|
||||
→ SQLite(元数据) + VexusIndex(Rust HNSW向量索引)
|
||||
```
|
||||
|
||||
### 5.2 TagMemo V6 检索流程
|
||||
|
||||
```
|
||||
Query → EPA分析(逻辑深度L/共振R) → 残差金字塔 → TagBoost(β动态权重)
|
||||
→ LIF脉冲扩散(2跳) → 向量融合 → VexusIndex搜索
|
||||
```
|
||||
|
||||
### 5.3 核心模块
|
||||
|
||||
| 模块 | 功能 |
|
||||
|------|------|
|
||||
| TextChunker | Token 感知分块,85% 安全边界 + 10% 重叠 |
|
||||
| EPA | 语义空间投影分析,识别逻辑深度和跨域共振 |
|
||||
| Residual Pyramid | 残差金字塔,多级剥离捕获微弱信号 |
|
||||
| TagBoost | 动态权重增强,根据查询特性调整 |
|
||||
| LIF Spike | 脉冲扩散,2跳拓扑联想 |
|
||||
| VexusIndex | Rust HNSW 向量索引,高性能检索 |
|
||||
|
||||
### 5.4 关键设计理念
|
||||
|
||||
1. **TagMemo 不是搜索引擎,是记忆联想引擎** - 模拟人类大脑的感知→编码→巩固→检索→重构
|
||||
2. **动态适配** - 根据查询意图动态调整检索策略
|
||||
3. **拓扑涌现** - 基于共现矩阵的脉冲扩散,产生非直观联想
|
||||
|
||||
---
|
||||
|
||||
## 6. 目标架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ User Query │
|
||||
└─────────────────────────┬───────────────────────────────────┘
|
||||
│
|
||||
┌───────────┴───────────┐
|
||||
│ Query Analyzer │ ← R.3 新增
|
||||
│ (查询特性分析) │
|
||||
└───────────┬───────────┘
|
||||
│
|
||||
┌────────────────┼────────────────┐
|
||||
▼ ▼ ▼
|
||||
┌─────────┐ ┌───────────┐ ┌──────────────┐
|
||||
│ Default │ │ Important │ │ Code/Meeting │
|
||||
│ Collection│ │ Collection │ │ Collections │
|
||||
└────┬─────┘ └─────┬─────┘ └──────┬───────┘
|
||||
│ │ │
|
||||
└──────────────────┼─────────────────┘
|
||||
▼
|
||||
┌───────────────────────────┐
|
||||
│ Dynamic Reranker │ ← R.3 新增
|
||||
│ (Core Tag Boost + 动态权重)│
|
||||
└───────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────┐
|
||||
│ Search Result │
|
||||
└───────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 借鉴点映射
|
||||
|
||||
| VCPToolBox 借鉴点 | Jarvis 实现位置 | 优先级 |
|
||||
|-------------------|---------------|--------|
|
||||
| Token 感知分块(85%+10% 重叠) | `services/chunker.py` | 🟢 高 |
|
||||
| 多索引架构 | `services/multi_index.py` | 🟡 中 |
|
||||
| 懒加载 + LRU TTL | `services/multi_index.py` | 🟡 中 |
|
||||
| TagBoost 动态权重 | `services/dynamic_reranker.py` | 🟡 中 |
|
||||
| 核心标签系统(1.33x 加权) | `services/core_tag_search.py` | 🟡 中 |
|
||||
| 语义去重 | `services/deduplicator.py` | 🔴 低 |
|
||||
| 语义分桶 | `services/semantic_bucket.py` | 🔴 低 |
|
||||
| EPA 分析 | - | 🔴 探索 |
|
||||
| LIF 脉冲扩散 | - | 🔴 探索 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 本阶段产出要求
|
||||
|
||||
- [x] 团队对 Jarvis 当前 RAG 问题和目标方向达成一致
|
||||
- [x] VCPToolBox 借鉴点已映射到具体 Phase
|
||||
- [x] 后续 phase 文档能够在这个认知基础上展开
|
||||
188
development-doc/plan/rag-update/phase-r-1-token-chunking.md
Normal file
188
development-doc/plan/rag-update/phase-r-1-token-chunking.md
Normal file
@@ -0,0 +1,188 @@
|
||||
# Phase R.1:Token 感知分块优化
|
||||
|
||||
日期:2026-04-03
|
||||
状态:已规划
|
||||
依赖:R.0(现状与目标)
|
||||
工作量:3 天
|
||||
|
||||
---
|
||||
|
||||
## 1. 本阶段目的
|
||||
|
||||
解决跨块边界信息丢失问题,实现精确的 token 计数和重叠分块。
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心任务
|
||||
|
||||
### Task R.1.1:集成 tiktoken
|
||||
|
||||
**目标:** 使用 tiktoken 精确计算 token 数,85% 安全边界
|
||||
|
||||
**新增文件:** `backend/app/services/chunker.py`
|
||||
|
||||
```python
|
||||
import tiktoken
|
||||
|
||||
class TokenAwareChunker:
|
||||
"""Token 感知分块器,85% 安全边界 + 10% 重叠"""
|
||||
|
||||
def __init__(self, max_tokens: int = 8000, overlap_ratio: float = 0.1):
|
||||
self.encoding = tiktoken.get_encoding("cl100k_base")
|
||||
self.safe_max = int(max_tokens * 0.85)
|
||||
self.overlap_tokens = int(self.safe_max * overlap_ratio)
|
||||
|
||||
def count_tokens(self, text: str) -> int:
|
||||
return len(self.encoding.encode(text))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task R.1.2:实现智能断句
|
||||
|
||||
**目标:** 在断点处(标点/空白)智能断开,避免在词汇中间断开
|
||||
|
||||
```python
|
||||
BREAK_POINTS = ['\n', '。', '!', '?', ',', ';', ':', ' ', '\t']
|
||||
|
||||
def find_best_breakpoint(text: str, max_pos: int) -> int:
|
||||
"""在 max_pos 附近找到最佳断点(标点/空白处)"""
|
||||
for i in range(max_pos - 1, max(0, max_pos - 200), -1):
|
||||
if text[i] in BREAK_POINTS:
|
||||
return i + 1
|
||||
return max_pos
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task R.1.3:实现重叠分块
|
||||
|
||||
**目标:** 10% token 重叠,保证上下文连续性
|
||||
|
||||
```python
|
||||
def chunk_with_overlap(self, text: str) -> list[dict]:
|
||||
"""带重叠的分块器,上一块末尾作为下一块开头"""
|
||||
sentences = self._split_sentences(text)
|
||||
chunks = []
|
||||
current_chunk = ""
|
||||
current_tokens = 0
|
||||
|
||||
for sentence in sentences:
|
||||
sentence_tokens = self.count_tokens(sentence)
|
||||
|
||||
if sentence_tokens > self.safe_max:
|
||||
# 超长句子强制分割
|
||||
forced = self._force_split_long_text(sentence)
|
||||
chunks.extend(forced)
|
||||
continue
|
||||
|
||||
if current_tokens + sentence_tokens > self.safe_max:
|
||||
chunks.append({"content": current_chunk.strip()})
|
||||
# 创建重叠部分
|
||||
current_chunk = self._create_overlap(sentences, current_tokens)
|
||||
current_tokens = self.count_tokens(current_chunk)
|
||||
|
||||
current_chunk += sentence
|
||||
current_tokens += sentence_tokens
|
||||
|
||||
if current_chunk.strip():
|
||||
chunks.append({"content": current_chunk.strip()})
|
||||
|
||||
return chunks
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 修改现有文件
|
||||
|
||||
### `backend/app/services/document_service.py`
|
||||
|
||||
集成新的 TokenAwareChunker:
|
||||
|
||||
```python
|
||||
from app.services.chunker import TokenAwareChunker
|
||||
|
||||
class DocumentService:
|
||||
def __init__(self, ...):
|
||||
# ... existing init
|
||||
self.chunker = TokenAwareChunker()
|
||||
|
||||
def _build_chunks(self, parsed: ParsedDocument) -> list[dict]:
|
||||
# 原有逻辑替换为重叠分块
|
||||
chunks = self.chunker.chunk_with_overlap(parsed.summary)
|
||||
for node in parsed.nodes:
|
||||
node_chunks = self.chunker.chunk_with_overlap(node.text)
|
||||
for chunk in node_chunks:
|
||||
chunks.append(chunk)
|
||||
return chunks
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 新增测试
|
||||
|
||||
**新增文件:** `backend/tests/services/test_chunker.py`
|
||||
|
||||
```python
|
||||
import pytest
|
||||
from app.services.chunker import TokenAwareChunker, find_best_breakpoint
|
||||
|
||||
class TestTokenAwareChunker:
|
||||
def test_token_counting(self):
|
||||
chunker = TokenAwareChunker(max_tokens=100)
|
||||
text = "Hello, world!"
|
||||
assert chunker.count_tokens(text) > 0
|
||||
|
||||
def test_overlap_ratio(self):
|
||||
chunker = TokenAwareChunker(max_tokens=100, overlap_ratio=0.1)
|
||||
assert chunker.overlap_tokens == 10
|
||||
|
||||
def test_safe_max(self):
|
||||
chunker = TokenAwareChunker(max_tokens=100)
|
||||
assert chunker.safe_max == 85
|
||||
|
||||
class TestSmartBreakpoint:
|
||||
def test_find_breakpoint_at_punctuation(self):
|
||||
text = "Hello, world! How are you?"
|
||||
pos = find_best_breakpoint(text, 15)
|
||||
assert text[pos-1] in [',', '!', '?', '。', '!', '?']
|
||||
|
||||
class TestOverlappingChunker:
|
||||
def test_chunks_have_overlap(self):
|
||||
chunker = TokenAwareChunker(max_tokens=50, overlap_ratio=0.2)
|
||||
long_text = "A" * 200 + "." + "B" * 200
|
||||
chunks = chunker.chunk_with_overlap(long_text)
|
||||
assert len(chunks) >= 2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 验收标准
|
||||
|
||||
- [ ] tiktoken 正确集成,token 计数误差 < 1%
|
||||
- [ ] 超长句子不在词汇中间断开
|
||||
- [ ] 重叠分块保证上下文连续性
|
||||
- [ ] 单元测试覆盖率 > 80%
|
||||
- [ ] 集成测试通过(文档上传→分块→检索)
|
||||
|
||||
---
|
||||
|
||||
## 6. 变更文件清单
|
||||
|
||||
| 文件 | 操作 | 说明 |
|
||||
|------|------|------|
|
||||
| `backend/app/services/chunker.py` | 新增 | Token 感知分块器 |
|
||||
| `backend/app/services/document_service.py` | 修改 | 集成新的分块器 |
|
||||
| `backend/tests/services/test_chunker.py` | 新增 | 分块器单元测试 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 工作量估算
|
||||
|
||||
| 任务 | 估算 |
|
||||
|------|------|
|
||||
| R.1.1 tiktoken 集成 | 0.5 天 |
|
||||
| R.1.2 智能断句 | 0.5 天 |
|
||||
| R.1.3 重叠分块 | 1 天 |
|
||||
| 测试 + 调试 | 1 天 |
|
||||
| **R.1 总计** | **3 天** |
|
||||
244
development-doc/plan/rag-update/phase-r-2-multi-index.md
Normal file
244
development-doc/plan/rag-update/phase-r-2-multi-index.md
Normal file
@@ -0,0 +1,244 @@
|
||||
# Phase R.2:多索引架构
|
||||
|
||||
日期:2026-04-03
|
||||
状态:已规划
|
||||
依赖:R.1(Token 感知分块)
|
||||
工作量:4 天
|
||||
|
||||
---
|
||||
|
||||
## 1. 本阶段目的
|
||||
|
||||
按知识类型/重要性分层,支持懒加载和 LRU 淘汰。
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心任务
|
||||
|
||||
### Task R.2.1:设计 Collection 分离策略
|
||||
|
||||
**目标:** 按知识类型分离 ChromaDB Collection
|
||||
|
||||
**新增文件:** `backend/app/services/multi_index.py`
|
||||
|
||||
```python
|
||||
class MultiIndexManager:
|
||||
"""多索引管理器,按知识类型分离"""
|
||||
|
||||
INDEX_STRATEGIES = {
|
||||
"default": {
|
||||
"name": "user_{user_id}_default",
|
||||
"description": "通用文档"
|
||||
},
|
||||
"important": {
|
||||
"name": "user_{user_id}_important",
|
||||
"description": "重要文档(1.2x加权)"
|
||||
},
|
||||
"code": {
|
||||
"name": "user_{user_id}_code",
|
||||
"description": "代码片段"
|
||||
},
|
||||
"meeting": {
|
||||
"name": "user_{user_id}_meeting",
|
||||
"description": "会议记录"
|
||||
},
|
||||
}
|
||||
|
||||
def get_collection(self, user_id: str, index_type: str = "default"):
|
||||
name = self.INDEX_STRATEGIES[index_type]["name"].format(user_id=user_id)
|
||||
return self.chroma_client.get_or_create_collection(name=name)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task R.2.2:实现懒加载 + LRU TTL
|
||||
|
||||
**目标:** 2小时 TTL,访问时加载,不访问不加载
|
||||
|
||||
```python
|
||||
import time
|
||||
from threading import Lock
|
||||
|
||||
class LazyIndexLoader:
|
||||
"""懒加载索引,支持 TTL 淘汰"""
|
||||
|
||||
def __init__(self, ttl_seconds: int = 7200):
|
||||
self._cache = {}
|
||||
self._last_used = {}
|
||||
self._lock = Lock()
|
||||
self._ttl = ttl_seconds
|
||||
|
||||
def get_or_load(self, key: str, loader_fn) -> Any:
|
||||
with self._lock:
|
||||
if key in self._cache:
|
||||
self._last_used[key] = time.time()
|
||||
return self._cache[key]
|
||||
|
||||
value = loader_fn()
|
||||
self._cache[key] = value
|
||||
self._last_used[key] = time.time()
|
||||
return value
|
||||
|
||||
def sweep(self):
|
||||
"""清理过期索引"""
|
||||
now = time.time()
|
||||
expired = [
|
||||
k for k, t in self._last_used.items()
|
||||
if now - t > self._ttl
|
||||
]
|
||||
for k in expired:
|
||||
del self._cache[k]
|
||||
del self._last_used[k]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task R.2.3:实现重要性感知检索
|
||||
|
||||
**目标:** important 索引加权 1.2x
|
||||
|
||||
```python
|
||||
async def retrieve_with_importance(
|
||||
self,
|
||||
query: str,
|
||||
user_id: str,
|
||||
top_k: int = 5,
|
||||
) -> list[SearchResult]:
|
||||
"""重要性感知检索,优先返回高重要性文档"""
|
||||
|
||||
# 1. 从 default 索引检索
|
||||
default_results = await self.retrieve(query, user_id, top_k=top_k * 2)
|
||||
|
||||
# 2. 从 important 索引检索
|
||||
important_results = await self.retrieve(
|
||||
query, user_id,
|
||||
collection_name=f"user_{user_id}_important",
|
||||
top_k=top_k
|
||||
)
|
||||
|
||||
# 3. 合并,重要文档加权
|
||||
scored = []
|
||||
for r in default_results:
|
||||
scored.append((r.score * 0.8, r))
|
||||
for r in important_results:
|
||||
scored.append((r.score * 1.2, r)) # 重要文档 1.2x
|
||||
|
||||
scored.sort(key=lambda x: x[0], reverse=True)
|
||||
return [r for _, r in scored[:top_k]]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 修改现有文件
|
||||
|
||||
### `backend/app/models/document.py`
|
||||
|
||||
增加 `importance` 字段:
|
||||
|
||||
```python
|
||||
class Document(Base):
|
||||
# ... existing fields ...
|
||||
|
||||
importance = Column(Float, default=0.5) # 0.0 ~ 1.0, >0.8 进入 important 索引
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `backend/app/services/knowledge_service.py`
|
||||
|
||||
集成多索引支持:
|
||||
|
||||
```python
|
||||
from app.services.multi_index import MultiIndexManager, LazyIndexLoader
|
||||
|
||||
class KnowledgeService:
|
||||
def __init__(self, ...):
|
||||
# ... existing init
|
||||
self.multi_index = MultiIndexManager(self.chroma_client)
|
||||
self.lazy_loader = LazyIndexLoader(ttl_seconds=7200)
|
||||
|
||||
async def index_document(self, document_id: str, user_id: str, ...):
|
||||
# 根据 importance 选择索引
|
||||
doc = await self._get_document(document_id)
|
||||
if doc.importance >= 0.8:
|
||||
collection = self.multi_index.get_collection(user_id, "important")
|
||||
else:
|
||||
collection = self.multi_index.get_collection(user_id, "default")
|
||||
# ... rest of indexing
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 新增测试
|
||||
|
||||
**新增文件:** `backend/tests/services/test_multi_index.py`
|
||||
|
||||
```python
|
||||
import pytest
|
||||
from app.services.multi_index import MultiIndexManager, LazyIndexLoader
|
||||
|
||||
class TestMultiIndexManager:
|
||||
def test_get_collection_creates_if_not_exists(self):
|
||||
manager = MultiIndexManager(mock_chroma_client)
|
||||
col = manager.get_collection("user123", "default")
|
||||
assert col is not None
|
||||
|
||||
def test_collection_name_format(self):
|
||||
manager = MultiIndexManager(mock_chroma_client)
|
||||
name = manager.INDEX_STRATEGIES["important"]["name"].format(user_id="user123")
|
||||
assert name == "user_user123_important"
|
||||
|
||||
class TestLazyIndexLoader:
|
||||
def test_get_or_load_caches(self):
|
||||
loader = LazyIndexLoader()
|
||||
load_fn = lambda: {"data": "test"}
|
||||
|
||||
result1 = loader.get_or_load("key1", load_fn)
|
||||
result2 = loader.get_or_load("key1", load_fn)
|
||||
|
||||
# 第二次调用应该返回缓存的结果,而不是重新加载
|
||||
assert result1 is result2
|
||||
|
||||
def test_sweep_removes_expired(self):
|
||||
loader = LazyIndexLoader(ttl_seconds=1)
|
||||
loader.get_or_load("key1", lambda: "value1")
|
||||
|
||||
import time
|
||||
time.sleep(1.1) # 等待过期
|
||||
|
||||
loader.sweep()
|
||||
assert "key1" not in loader._cache
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 验收标准
|
||||
|
||||
- [ ] 多 Collection 创建成功
|
||||
- [ ] 懒加载索引生效(访问时加载,不访问不加载)
|
||||
- [ ] TTL 淘汰机制工作(2小时无访问自动卸载)
|
||||
- [ ] 重要性感知检索加权生效
|
||||
- [ ] 单元测试覆盖率 > 80%
|
||||
|
||||
---
|
||||
|
||||
## 6. 变更文件清单
|
||||
|
||||
| 文件 | 操作 | 说明 |
|
||||
|------|------|------|
|
||||
| `backend/app/services/multi_index.py` | 新增 | 多索引管理器 |
|
||||
| `backend/app/services/knowledge_service.py` | 修改 | 集成多索引支持 |
|
||||
| `backend/app/models/document.py` | 修改 | 增加 importance 字段 |
|
||||
| `backend/tests/services/test_multi_index.py` | 新增 | 多索引单元测试 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 工作量估算
|
||||
|
||||
| 任务 | 估算 |
|
||||
|------|------|
|
||||
| R.2.1 Collection 分离策略 | 1 天 |
|
||||
| R.2.2 懒加载 + LRU | 1 天 |
|
||||
| R.2.3 重要性感知检索 | 0.5 天 |
|
||||
| 测试 + 调试 | 1.5 天 |
|
||||
| **R.2 总计** | **4 天** |
|
||||
290
development-doc/plan/rag-update/phase-r-3-dynamic-weight.md
Normal file
290
development-doc/plan/rag-update/phase-r-3-dynamic-weight.md
Normal file
@@ -0,0 +1,290 @@
|
||||
# Phase R.3:动态权重增强
|
||||
|
||||
日期:2026-04-03
|
||||
状态:已规划
|
||||
依赖:R.1(Token 感知分块)
|
||||
工作量:4.5 天
|
||||
|
||||
---
|
||||
|
||||
## 1. 本阶段目的
|
||||
|
||||
根据查询特性动态调整检索策略,支持核心标签加权。
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心任务
|
||||
|
||||
### Task R.3.1:实现查询特性分析
|
||||
|
||||
**目标:** 分析查询类型(代码/表格/对话式)
|
||||
|
||||
**新增文件:** `backend/app/services/query_analyzer.py`
|
||||
|
||||
```python
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class QueryProfile:
|
||||
logic_depth: float # 逻辑深度 (0-1): 意图明确程度
|
||||
is_code_related: bool # 是否代码相关
|
||||
is_table_related: bool # 是否表格相关
|
||||
keyword_density: float # 关键词密度
|
||||
is_conversational: bool # 是否对话式查询
|
||||
|
||||
class QueryAnalyzer:
|
||||
CODE_KEYWORDS = {'code', 'function', 'class', 'api', 'python', 'js', 'bug', '函数', '代码'}
|
||||
TABLE_KEYWORDS = {'table', 'sheet', 'excel', 'csv', 'column', 'row', '数据', '统计', '表格', '列', '行'}
|
||||
|
||||
def analyze(self, query: str) -> QueryProfile:
|
||||
words = set(re.findall(r'\w+', query.lower()))
|
||||
|
||||
return QueryProfile(
|
||||
logic_depth=self._calc_logic_depth(query),
|
||||
is_code_related=bool(words & self.CODE_KEYWORDS),
|
||||
is_table_related=bool(words & self.TABLE_KEYWORDS),
|
||||
keyword_density=len(words) / max(len(query), 1),
|
||||
is_conversational=self._is_conversational(query),
|
||||
)
|
||||
|
||||
def _calc_logic_depth(self, query: str) -> float:
|
||||
"""计算逻辑深度:问句、具体名词越多越聚焦"""
|
||||
question_markers = ['how', 'why', 'what', 'which', '哪个', '如何', '为什么', '怎么']
|
||||
has_question = any(q in query.lower() for q in question_markers)
|
||||
has_specific_terms = len(re.findall(r'\w{5,}', query)) > 3
|
||||
return 0.8 if (has_question and has_specific_terms) else 0.5
|
||||
|
||||
def _is_conversational(self, query: str) -> bool:
|
||||
"""判断是否为对话式查询"""
|
||||
conversational_patterns = ['你', '我想', '能不能', '可以帮我', 'what do you think']
|
||||
return any(p in query for p in conversational_patterns)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task R.3.2:实现动态 Reranker
|
||||
|
||||
**目标:** 根据查询类型动态调整语义/关键词/标题权重
|
||||
|
||||
**新增文件:** `backend/app/services/dynamic_reranker.py`
|
||||
|
||||
```python
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
|
||||
class DynamicReranker:
|
||||
"""动态 Reranker,根据查询特性调整权重"""
|
||||
|
||||
def rerank(
|
||||
self,
|
||||
query: str,
|
||||
results: list[SearchResult],
|
||||
analyzer: QueryAnalyzer
|
||||
) -> list[SearchResult]:
|
||||
profile = analyzer.analyze(query)
|
||||
weights = self._get_weights(profile)
|
||||
beta = self._calc_beta(profile)
|
||||
|
||||
scored = []
|
||||
for r in results:
|
||||
score = r.score * weights["semantic"]
|
||||
score += self._keyword_score(query, r.content) * weights["keyword"]
|
||||
score += self._title_score(query, r.document_title) * weights["title"]
|
||||
|
||||
# 表格内容加分
|
||||
if profile.is_table_related:
|
||||
meta = json.loads(r.metadata_ or "{}")
|
||||
if meta.get("content_type") == "table_schema":
|
||||
score += 0.25
|
||||
elif meta.get("content_type") == "table_rows":
|
||||
score += 0.15
|
||||
|
||||
score *= beta
|
||||
scored.append((score, r))
|
||||
|
||||
scored.sort(key=lambda x: x[0], reverse=True)
|
||||
return [r for _, r in scored]
|
||||
|
||||
def _get_weights(self, profile: QueryProfile) -> dict:
|
||||
if profile.is_code_related:
|
||||
return {"semantic": 0.55, "keyword": 0.35, "title": 0.10}
|
||||
elif profile.is_table_related:
|
||||
return {"semantic": 0.50, "keyword": 0.30, "title": 0.20}
|
||||
elif profile.is_conversational:
|
||||
return {"semantic": 0.85, "keyword": 0.10, "title": 0.05}
|
||||
else:
|
||||
return {"semantic": 0.70, "keyword": 0.20, "title": 0.10}
|
||||
|
||||
def _calc_beta(self, profile: QueryProfile) -> float:
|
||||
"""计算动态 Beta:逻辑深度高时加大语义权重"""
|
||||
if profile.logic_depth > 0.7:
|
||||
return 1.2 # 意图明确,加大权重
|
||||
elif profile.logic_depth < 0.4:
|
||||
return 0.8 # 意图模糊,降低权重
|
||||
return 1.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task R.3.3:实现核心标签系统
|
||||
|
||||
**目标:** 核心标签 1.33x 加权
|
||||
|
||||
**新增文件:** `backend/app/services/core_tag_search.py`
|
||||
|
||||
```python
|
||||
class CoreTagAwareSearch:
|
||||
"""核心标签感知检索"""
|
||||
|
||||
CORE_BOOST_FACTOR = 1.33 # 33% 加权
|
||||
|
||||
async def search(
|
||||
self,
|
||||
query: str,
|
||||
user_id: str,
|
||||
core_tags: list[str] = None,
|
||||
base_search_fn: callable
|
||||
) -> list[SearchResult]:
|
||||
results = await base_search_fn(query, user_id)
|
||||
|
||||
if core_tags:
|
||||
for r in results:
|
||||
meta = json.loads(r.metadata_ or "{}")
|
||||
chunk_tags = meta.get("tags", [])
|
||||
|
||||
if any(tag in chunk_tags for tag in core_tags):
|
||||
r.score *= self.CORE_BOOST_FACTOR
|
||||
|
||||
return sorted(results, key=lambda x: x.score, reverse=True)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 修改现有文件
|
||||
|
||||
### `backend/app/models/document.py`
|
||||
|
||||
增加 `tags` 和 `is_core` 字段:
|
||||
|
||||
```python
|
||||
class DocumentChunk(Base):
|
||||
# ... existing fields ...
|
||||
|
||||
tags = Column(JSON, default=list) # ["重要", "代码", "架构"]
|
||||
is_core = Column(Boolean, default=False) # 是否核心切片
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `backend/app/services/knowledge_service.py`
|
||||
|
||||
集成动态权重:
|
||||
|
||||
```python
|
||||
from app.services.query_analyzer import QueryAnalyzer
|
||||
from app.services.dynamic_reranker import DynamicReranker
|
||||
from app.services.core_tag_search import CoreTagAwareSearch
|
||||
|
||||
class KnowledgeService:
|
||||
def __init__(self, ...):
|
||||
# ... existing init
|
||||
self.query_analyzer = QueryAnalyzer()
|
||||
self.dynamic_reranker = DynamicReranker()
|
||||
self.core_tag_search = CoreTagAwareSearch()
|
||||
|
||||
async def retrieve(self, query: str, user_id: str, ..., core_tags: list[str] = None) -> list[SearchResult]:
|
||||
# ... existing retrieval logic ...
|
||||
|
||||
# 动态 Rerank
|
||||
results = self.dynamic_reranker.rerank(
|
||||
query, results, self.query_analyzer
|
||||
)
|
||||
|
||||
# 核心标签加权
|
||||
if core_tags:
|
||||
results = await self.core_tag_search.search(
|
||||
query, user_id, core_tags,
|
||||
lambda q, u: results # 使用已检索的结果
|
||||
)
|
||||
|
||||
return results
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 新增测试
|
||||
|
||||
**新增文件:** `backend/tests/services/test_dynamic_reranker.py`
|
||||
|
||||
```python
|
||||
import pytest
|
||||
from app.services.query_analyzer import QueryAnalyzer, QueryProfile
|
||||
from app.services.dynamic_reranker import DynamicReranker
|
||||
|
||||
class TestQueryAnalyzer:
|
||||
def test_code_query_detection(self):
|
||||
analyzer = QueryAnalyzer()
|
||||
profile = analyzer.analyze("请解释这段 Python 代码")
|
||||
assert profile.is_code_related is True
|
||||
|
||||
def test_table_query_detection(self):
|
||||
analyzer = QueryAnalyzer()
|
||||
profile = analyzer.analyze("统计这个 Excel 表格的总和")
|
||||
assert profile.is_table_related is True
|
||||
|
||||
def test_conversational_detection(self):
|
||||
analyzer = QueryAnalyzer()
|
||||
profile = analyzer.analyze("我想了解一下")
|
||||
assert profile.is_conversational is True
|
||||
|
||||
class TestDynamicReranker:
|
||||
def test_code_query_weights(self):
|
||||
reranker = DynamicReranker()
|
||||
analyzer = QueryAnalyzer()
|
||||
|
||||
profile = QueryProfile(
|
||||
logic_depth=0.5,
|
||||
is_code_related=True,
|
||||
is_table_related=False,
|
||||
keyword_density=0.3,
|
||||
is_conversational=False
|
||||
)
|
||||
|
||||
weights = reranker._get_weights(profile)
|
||||
assert weights["keyword"] > weights["semantic"] * 0.5 # 代码查询关键词权重较高
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 验收标准
|
||||
|
||||
- [ ] 查询特性分析准确(代码/表格/对话式识别)
|
||||
- [ ] 动态权重根据查询类型调整
|
||||
- [ ] 核心标签检索加权 1.33x
|
||||
- [ ] Rerank 集成测试通过
|
||||
|
||||
---
|
||||
|
||||
## 6. 变更文件清单
|
||||
|
||||
| 文件 | 操作 | 说明 |
|
||||
|------|------|------|
|
||||
| `backend/app/services/query_analyzer.py` | 新增 | 查询特性分析 |
|
||||
| `backend/app/services/dynamic_reranker.py` | 新增 | 动态 Reranker |
|
||||
| `backend/app/services/core_tag_search.py` | 新增 | 核心标签检索 |
|
||||
| `backend/app/services/knowledge_service.py` | 修改 | 集成动态权重 |
|
||||
| `backend/app/models/document.py` | 修改 | 增加 tags/is_core 字段 |
|
||||
| `backend/tests/services/test_dynamic_reranker.py` | 新增 | 动态 Reranker 测试 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 工作量估算
|
||||
|
||||
| 任务 | 估算 |
|
||||
|------|------|
|
||||
| R.3.1 查询特性分析 | 1 天 |
|
||||
| R.3.2 动态 Reranker | 1 天 |
|
||||
| R.3.3 核心标签系统 | 1 天 |
|
||||
| 测试 + 调试 | 1.5 天 |
|
||||
| **R.3 总计** | **4.5 天** |
|
||||
255
development-doc/plan/rag-update/phase-r-4-advanced.md
Normal file
255
development-doc/plan/rag-update/phase-r-4-advanced.md
Normal file
@@ -0,0 +1,255 @@
|
||||
# Phase R.4:高级特性(可选)
|
||||
|
||||
日期:2026-04-03
|
||||
状态:已规划(可选)
|
||||
工作量:4.5 天
|
||||
|
||||
---
|
||||
|
||||
## 1. 本阶段目的
|
||||
|
||||
探索更高级的 RAG 增强技术。
|
||||
|
||||
> **注意:** 本阶段为可选特性,不影响核心功能。根据实际需求决定是否实施。
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心任务
|
||||
|
||||
### Task R.4.1:语义去重
|
||||
|
||||
**目标:** 消除冗余检索结果
|
||||
|
||||
**新增文件:** `backend/app/services/deduplicator.py`
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
|
||||
class SemanticDeduplicator:
|
||||
"""语义去重,消除冗余检索结果"""
|
||||
|
||||
DEDUP_THRESHOLD = 0.88 # 余弦相似度阈值
|
||||
|
||||
def deduplicate(
|
||||
self,
|
||||
results: list[SearchResult],
|
||||
embeddings: list[np.ndarray]
|
||||
) -> list[SearchResult]:
|
||||
if len(results) <= 1:
|
||||
return results
|
||||
|
||||
# 计算余弦相似度矩阵
|
||||
n = len(results)
|
||||
similarity_matrix = np.zeros((n, n))
|
||||
|
||||
for i in range(n):
|
||||
for j in range(i + 1, n):
|
||||
sim = self._cosine_similarity(embeddings[i], embeddings[j])
|
||||
similarity_matrix[i][j] = sim
|
||||
similarity_matrix[j][i] = sim
|
||||
|
||||
# 贪心去重
|
||||
keep = [True] * n
|
||||
for i in range(n):
|
||||
if not keep[i]:
|
||||
continue
|
||||
for j in range(i + 1, n):
|
||||
if keep[j] and similarity_matrix[i][j] > self.DEDUP_THRESHOLD:
|
||||
keep[j] = False
|
||||
|
||||
return [r for r, k in zip(results, keep) if k]
|
||||
|
||||
def _cosine_similarity(self, a: np.ndarray, b: np.ndarray) -> float:
|
||||
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task R.4.2:语义分桶(可选)
|
||||
|
||||
**目标:** 按主题自动组织检索结果
|
||||
|
||||
**新增文件:** `backend/app/services/semantic_bucket.py`
|
||||
|
||||
```python
|
||||
from collections import defaultdict
|
||||
import numpy as np
|
||||
|
||||
class SemanticBucketing:
|
||||
"""语义分桶,按主题自动组织检索结果"""
|
||||
|
||||
async def bucket_by_topic(
|
||||
self,
|
||||
results: list[SearchResult],
|
||||
embeddings: list[np.ndarray]
|
||||
) -> dict[str, list[SearchResult]]:
|
||||
# 使用层次聚类
|
||||
from sklearn.cluster import AgglomerativeClustering
|
||||
|
||||
n_clusters = min(5, len(results))
|
||||
if n_clusters < 2:
|
||||
return {"default": results}
|
||||
|
||||
clusterer = AgglomerativeClustering(n_clusters=n_clusters)
|
||||
labels = clusterer.fit_predict(np.array(embeddings))
|
||||
|
||||
buckets = defaultdict(list)
|
||||
for r, label in zip(results, labels):
|
||||
buckets[f"topic_{label}"].append(r)
|
||||
|
||||
# 按每个桶内最高分排序
|
||||
sorted_buckets = {}
|
||||
for name, items in buckets.items():
|
||||
sorted_items = sorted(items, key=lambda x: x.score, reverse=True)
|
||||
sorted_buckets[name] = sorted_items
|
||||
|
||||
return sorted_buckets
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task R.4.3:EPA 分析设计(探索)
|
||||
|
||||
**目标:** 语义空间投影分析方案设计
|
||||
|
||||
```python
|
||||
class EPAModule:
|
||||
"""
|
||||
EPA: Embedding Projection Analysis
|
||||
|
||||
分析向量在语义空间中的投影,识别:
|
||||
- 逻辑深度 (Logic Depth): 意图聚焦程度
|
||||
- 熵 (Entropy): 信息散乱程度
|
||||
- 共振 (Resonance): 跨域关联程度
|
||||
|
||||
注意:此模块为高级特性,复杂度高,建议后续探索。
|
||||
"""
|
||||
|
||||
def project(self, vector: np.ndarray) -> dict:
|
||||
"""
|
||||
返回语义投影结果:
|
||||
- logic_depth: 0~1, 高=意图聚焦
|
||||
- entropy: 0~1, 高=信息散乱
|
||||
- resonance: 跨域共振程度
|
||||
- dominant_axes: 主要语义轴
|
||||
"""
|
||||
raise NotImplementedError("EPA 模块探索中")
|
||||
|
||||
def detect_cross_domain_resonance(self, vector: np.ndarray) -> dict:
|
||||
"""
|
||||
检测跨域共振:
|
||||
- 当查询同时触及多个正交语义轴时触发
|
||||
- 返回共振强度和涉及的主要领域
|
||||
"""
|
||||
raise NotImplementedError("EPA 模块探索中")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 新增测试
|
||||
|
||||
```python
|
||||
# backend/tests/services/test_deduplicator.py
|
||||
|
||||
class TestSemanticDeduplicator:
|
||||
def test_deduplicate_similar_results(self):
|
||||
dedup = SemanticDeduplicator()
|
||||
|
||||
results = [
|
||||
SearchResult(chunk_id="1", score=0.9, ...),
|
||||
SearchResult(chunk_id="2", score=0.85, ...),
|
||||
SearchResult(chunk_id="3", score=0.8, ...),
|
||||
]
|
||||
embeddings = [
|
||||
np.array([0.1, 0.2, 0.3]),
|
||||
np.array([0.11, 0.21, 0.31]), # 与第一个高度相似
|
||||
np.array([0.9, 0.8, 0.7]), # 与第一个不相似
|
||||
]
|
||||
|
||||
deduped = dedup.deduplicate(results, embeddings)
|
||||
assert len(deduped) < len(results) # 应该去掉一些重复结果
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 验收标准
|
||||
|
||||
- [ ] 语义去重测试通过
|
||||
- [ ] 语义分桶原型完成(可选)
|
||||
- [ ] EPA 分析方案设计完成(可选实现)
|
||||
|
||||
---
|
||||
|
||||
## 5. 变更文件清单
|
||||
|
||||
| 文件 | 操作 | 说明 |
|
||||
|------|------|------|
|
||||
| `backend/app/services/deduplicator.py` | 新增 | 语义去重 |
|
||||
| `backend/app/services/semantic_bucket.py` | 新增(可选) | 语义分桶 |
|
||||
| `backend/tests/services/test_deduplicator.py` | 新增 | 去重测试 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 工作量估算
|
||||
|
||||
| 任务 | 估算 | 状态 |
|
||||
|------|------|------|
|
||||
| R.4.1 语义去重 | 1.5 天 | 必须 |
|
||||
| R.4.2 语义分桶 | 2 天 | 可选 |
|
||||
| R.4.3 EPA 设计 | 1 天 | 可选 |
|
||||
| **R.4 总计(必须)** | **1.5 天** | |
|
||||
| **R.4 总计(含可选)** | **4.5 天** | |
|
||||
|
||||
---
|
||||
|
||||
## 7. EPA 分析详细设计(供后续参考)
|
||||
|
||||
### 7.1 核心概念
|
||||
|
||||
EPA (Embedding Projection Analysis) 受 VCPToolBox TagMemo V6 启发,用于分析查询向量在语义空间中的投影特征。
|
||||
|
||||
### 7.2 关键指标
|
||||
|
||||
| 指标 | 定义 | 计算方式 |
|
||||
|------|------|----------|
|
||||
| Logic Depth | 意图聚焦程度 | 通过计算投影熵值判断 |
|
||||
| Entropy | 信息散乱程度 | 向量分布的熵 |
|
||||
| Resonance | 跨域共振 | 查询跨越多个语义轴的程度 |
|
||||
|
||||
### 7.3 动态 Beta 公式
|
||||
|
||||
```
|
||||
β = σ(L · log(1 + R) - S · noise_penalty)
|
||||
```
|
||||
|
||||
- L: Logic Depth
|
||||
- R: Resonance
|
||||
- S: 噪音程度
|
||||
- σ: 归一化函数
|
||||
|
||||
### 7.4 残差金字塔
|
||||
|
||||
对查询向量进行多级剥离:
|
||||
|
||||
1. 首轮匹配 → 获取主要语义
|
||||
2. 计算残差 → 提取被掩盖的微弱信号
|
||||
3. 递归搜索 → 直到 90% 能量被解释
|
||||
|
||||
### 7.5 LIF 脉冲扩散
|
||||
|
||||
模拟神经元的脉冲传导:
|
||||
|
||||
1. 种子节点激活
|
||||
2. 沿共现矩阵向外扩散(2跳限制)
|
||||
3. 阈值过滤噪音
|
||||
4. 涌现拓扑关联
|
||||
|
||||
---
|
||||
|
||||
## 8. 风险与注意事项
|
||||
|
||||
| 风险 | 影响 | 缓解措施 |
|
||||
|------|------|----------|
|
||||
| EPA 实现复杂度高 | 高 | Phase R.4 可选,暂不实现 |
|
||||
| 聚类计算开销 | 中 | 限制聚类数量,使用高效算法 |
|
||||
| 去重阈值调参 | 中 | 提供配置项,允许用户调整 |
|
||||
601
development-doc/plan/rag-update/phase-r-rag-upgrade.md
Normal file
601
development-doc/plan/rag-update/phase-r-rag-upgrade.md
Normal file
@@ -0,0 +1,601 @@
|
||||
# Phase R:RAG 系统升级专项
|
||||
|
||||
日期:2026-04-03
|
||||
状态:已规划
|
||||
借鉴来源:VCPToolBox TagMemo V6 架构
|
||||
|
||||
---
|
||||
|
||||
## R.0 当前现状与目标
|
||||
|
||||
### R.0.1 当前 Jarvis RAG 架构
|
||||
|
||||
```
|
||||
用户上传文档 → DocumentService (解析/分块) → ChromaDB (向量存储) → KnowledgeService (检索)
|
||||
```
|
||||
|
||||
**核心文件:**
|
||||
- `backend/app/services/document_service.py` - 文档上传/解析/分块
|
||||
- `backend/app/services/knowledge_service.py` - ChromaDB 向量检索/混合检索
|
||||
- `backend/app/models/document.py` - Document/DocumentChunk 数据模型
|
||||
|
||||
### R.0.2 当前能力矩阵
|
||||
|
||||
| 能力 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| 多格式文档解析 | ✅ | PDF/MD/TXT/DOCX/CSV/XLSX |
|
||||
| 结构化分块 | ✅ | 基于标题层级、表格、段落 |
|
||||
| 向量检索 | ✅ | ChromaDB 语义相似度 |
|
||||
| 关键词检索 | ✅ | SQL LIKE |
|
||||
| 混合检索 | ✅ | 向量 + 关键词加权 |
|
||||
| Rerank | ✅ | 语义分*0.7 + 关键词*0.2 + 标题*0.1 |
|
||||
| 上下文丰富 | ✅ | 自动获取前/后 chunk |
|
||||
|
||||
### R.0.3 当前短板
|
||||
|
||||
| 短板 | 严重程度 | 影响 |
|
||||
|------|----------|------|
|
||||
| 无重叠分块 | 🟡 中 | 跨块边界信息丢失 |
|
||||
| 单索引架构 | 🟡 中 | 无法按知识类型/重要性分层 |
|
||||
| 无动态权重 | 🟡 中 | 检索策略静态,不适配查询类型 |
|
||||
| 无 Tag/标签系统 | 🟡 中 | 无法利用语义标签增强检索 |
|
||||
| 无懒加载机制 | 🟢 低 | 大量文档时内存占用高 |
|
||||
| 无遗忘机制 | 🟢 低 | 存储无限增长 |
|
||||
|
||||
### R.0.4 VCPToolBox TagMemo 核心借鉴
|
||||
|
||||
```
|
||||
日记文件变化 → TextChunker(Token感知分块85%+10%重叠)
|
||||
→ EmbeddingUtils(并发批量向量化)
|
||||
→ SQLite(元数据) + VexusIndex(Rust HNSW向量索引)
|
||||
```
|
||||
|
||||
**TagMemo V6 检索流程:**
|
||||
```
|
||||
Query → EPA分析(逻辑深度L/共振R) → 残差金字塔 → TagBoost(β动态权重)
|
||||
→ LIF脉冲扩散(2跳) → 向量融合 → VexusIndex搜索
|
||||
```
|
||||
|
||||
### R.0.5 目标架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ User Query │
|
||||
└─────────────────────────┬───────────────────────────────────┘
|
||||
│
|
||||
┌───────────┴───────────┐
|
||||
│ Query Analyzer │ ← R.3 新增
|
||||
│ (查询特性分析) │
|
||||
└───────────┬───────────┘
|
||||
│
|
||||
┌────────────────┼────────────────┐
|
||||
▼ ▼ ▼
|
||||
┌─────────┐ ┌───────────┐ ┌──────────────┐
|
||||
│ Default │ │ Important │ │ Code/Meeting │
|
||||
│ Collection│ │ Collection │ │ Collections │
|
||||
└────┬─────┘ └─────┬─────┘ └──────┬───────┘
|
||||
│ │ │
|
||||
└──────────────────┼─────────────────┘
|
||||
▼
|
||||
┌───────────────────────────┐
|
||||
│ Dynamic Reranker │ ← R.3 新增
|
||||
│ (Core Tag Boost + 动态权重)│
|
||||
└───────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────┐
|
||||
│ Search Result │
|
||||
└───────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## R.1 Token 感知分块优化
|
||||
|
||||
**目标:** 解决跨块边界信息丢失问题,实现精确的 token 计数和重叠分块
|
||||
|
||||
### R.1.1 核心任务
|
||||
|
||||
#### Task R.1.1.1:集成 tiktoken
|
||||
|
||||
```python
|
||||
# services/chunker.py (新增)
|
||||
import tiktoken
|
||||
|
||||
class TokenAwareChunker:
|
||||
"""Token 感知分块器,85% 安全边界 + 10% 重叠"""
|
||||
|
||||
def __init__(self, max_tokens: int = 8000, overlap_ratio: float = 0.1):
|
||||
self.encoding = tiktoken.get_encoding("cl100k_base")
|
||||
self.safe_max = int(max_tokens * 0.85)
|
||||
self.overlap_tokens = int(self.safe_max * overlap_ratio)
|
||||
|
||||
def count_tokens(self, text: str) -> int:
|
||||
return len(self.encoding.encode(text))
|
||||
```
|
||||
|
||||
#### Task R.1.1.2:实现智能断句
|
||||
|
||||
```python
|
||||
BREAK_POINTS = ['\n', '。', '!', '?', ',', ';', ':', ' ', '\t']
|
||||
|
||||
def find_best_breakpoint(text: str, max_pos: int) -> int:
|
||||
"""在 max_pos 附近找到最佳断点(标点/空白处)"""
|
||||
for i in range(max_pos - 1, max(0, max_pos - 200), -1):
|
||||
if text[i] in BREAK_POINTS:
|
||||
return i + 1
|
||||
return max_pos
|
||||
```
|
||||
|
||||
#### Task R.1.1.3:实现重叠分块
|
||||
|
||||
```python
|
||||
def chunk_with_overlap(self, text: str) -> list[dict]:
|
||||
"""带重叠的分块器,上一块末尾作为下一块开头"""
|
||||
sentences = self._split_sentences(text)
|
||||
chunks = []
|
||||
current_chunk = ""
|
||||
current_tokens = 0
|
||||
|
||||
for sentence in sentences:
|
||||
sentence_tokens = self.count_tokens(sentence)
|
||||
|
||||
if sentence_tokens > self.safe_max:
|
||||
# 超长句子强制分割
|
||||
forced = self._force_split_long_text(sentence)
|
||||
chunks.extend(forced)
|
||||
continue
|
||||
|
||||
if current_tokens + sentence_tokens > self.safe_max:
|
||||
chunks.append({"content": current_chunk.strip()})
|
||||
# 创建重叠部分
|
||||
current_chunk = self._create_overlap(sentences, current_tokens)
|
||||
current_tokens = self.count_tokens(current_chunk)
|
||||
|
||||
current_chunk += sentence
|
||||
current_tokens += sentence_tokens
|
||||
|
||||
if current_chunk.strip():
|
||||
chunks.append({"content": current_chunk.strip()})
|
||||
|
||||
return chunks
|
||||
```
|
||||
|
||||
### R.1.2 验收标准
|
||||
|
||||
- [ ] tiktoken 正确集成,token 计数误差 < 1%
|
||||
- [ ] 超长句子不在词汇中间断开
|
||||
- [ ] 重叠分块保证上下文连续性
|
||||
- [ ] 单元测试覆盖率 > 80%
|
||||
|
||||
### R.1.3 变更文件
|
||||
|
||||
| 文件 | 操作 | 说明 |
|
||||
|------|------|------|
|
||||
| `services/chunker.py` | 新增 | Token 感知分块器 |
|
||||
| `services/document_service.py` | 修改 | 集成新的分块器 |
|
||||
| `tests/test_chunker.py` | 新增 | 分块器单元测试 |
|
||||
|
||||
### R.1.4 工作量估算
|
||||
|
||||
| 任务 | 估算 |
|
||||
|------|------|
|
||||
| R.1.1.1 tiktoken 集成 | 0.5 天 |
|
||||
| R.1.1.2 智能断句 | 0.5 天 |
|
||||
| R.1.1.3 重叠分块 | 1 天 |
|
||||
| 测试 + 调试 | 1 天 |
|
||||
| **R.1 总计** | **3 天** |
|
||||
|
||||
---
|
||||
|
||||
## R.2 多索引架构
|
||||
|
||||
**目标:** 按知识类型/重要性分层,支持懒加载和 LRU 淘汰
|
||||
|
||||
### R.2.1 核心任务
|
||||
|
||||
#### Task R.2.1.1:设计 Collection 分离策略
|
||||
|
||||
```python
|
||||
# services/multi_index.py (新增)
|
||||
class MultiIndexManager:
|
||||
"""多索引管理器,按知识类型分离"""
|
||||
|
||||
INDEX_STRATEGIES = {
|
||||
"default": {"name": "user_{user_id}_default", "description": "通用文档"},
|
||||
"important": {"name": "user_{user_id}_important", "description": "重要文档(1.2x加权)"},
|
||||
"code": {"name": "user_{user_id}_code", "description": "代码片段"},
|
||||
"meeting": {"name": "user_{user_id}_meeting", "description": "会议记录"},
|
||||
}
|
||||
|
||||
def get_collection(self, user_id: str, index_type: str = "default"):
|
||||
name = self.INDEX_STRATEGIES[index_type]["name"].format(user_id=user_id)
|
||||
return self.chroma_client.get_or_create_collection(name=name)
|
||||
```
|
||||
|
||||
#### Task R.2.1.2:实现懒加载 + LRU TTL
|
||||
|
||||
```python
|
||||
import time
|
||||
from threading import Lock
|
||||
|
||||
class LazyIndexLoader:
|
||||
"""懒加载索引,支持 TTL 淘汰"""
|
||||
|
||||
def __init__(self, ttl_seconds: int = 7200):
|
||||
self._cache = {}
|
||||
self._last_used = {}
|
||||
self._lock = Lock()
|
||||
self._ttl = ttl_seconds
|
||||
|
||||
def get_or_load(self, key: str, loader_fn) -> Any:
|
||||
with self._lock:
|
||||
if key in self._cache:
|
||||
self._last_used[key] = time.time()
|
||||
return self._cache[key]
|
||||
|
||||
value = loader_fn()
|
||||
self._cache[key] = value
|
||||
self._last_used[key] = time.time()
|
||||
return value
|
||||
|
||||
def sweep(self):
|
||||
"""清理过期索引"""
|
||||
now = time.time()
|
||||
expired = [k for k, t in self._last_used.items() if now - t > self._ttl]
|
||||
for k in expired:
|
||||
del self._cache[k]
|
||||
del self._last_used[k]
|
||||
```
|
||||
|
||||
#### Task R.2.1.3:实现重要性感知检索
|
||||
|
||||
```python
|
||||
async def retrieve_with_importance(
|
||||
self,
|
||||
query: str,
|
||||
user_id: str,
|
||||
importance_threshold: float = 0.0,
|
||||
top_k: int = 5,
|
||||
) -> list[SearchResult]:
|
||||
"""重要性感知检索,优先返回高重要性文档"""
|
||||
|
||||
# 1. 从 default 索引检索
|
||||
default_results = await self.retrieve(query, user_id, top_k=top_k * 2)
|
||||
|
||||
# 2. 从 important 索引检索
|
||||
important_results = await self.retrieve(
|
||||
query, user_id,
|
||||
collection_name=f"user_{user_id}_important",
|
||||
top_k=top_k
|
||||
)
|
||||
|
||||
# 3. 合并,重要文档加权
|
||||
scored = []
|
||||
for r in default_results:
|
||||
scored.append((r.score * 0.8, r))
|
||||
for r in important_results:
|
||||
scored.append((r.score * 1.2, r)) # 重要文档 1.2x
|
||||
|
||||
scored.sort(key=lambda x: x[0], reverse=True)
|
||||
return [r for _, r in scored[:top_k]]
|
||||
```
|
||||
|
||||
### R.2.2 验收标准
|
||||
|
||||
- [ ] 多 Collection 创建成功
|
||||
- [ ] 懒加载索引生效(访问时加载,不访问不加载)
|
||||
- [ ] TTL 淘汰机制工作(2小时无访问自动卸载)
|
||||
- [ ] 重要性感知检索加权生效
|
||||
|
||||
### R.2.3 变更文件
|
||||
|
||||
| 文件 | 操作 | 说明 |
|
||||
|------|------|------|
|
||||
| `services/multi_index.py` | 新增 | 多索引管理器 |
|
||||
| `services/knowledge_service.py` | 修改 | 集成多索引支持 |
|
||||
| `models/document.py` | 修改 | 增加 importance 字段 |
|
||||
| `tests/test_multi_index.py` | 新增 | 多索引单元测试 |
|
||||
|
||||
### R.2.4 工作量估算
|
||||
|
||||
| 任务 | 估算 |
|
||||
|------|------|
|
||||
| R.2.1.1 Collection 分离策略 | 1 天 |
|
||||
| R.2.1.2 懒加载 + LRU | 1 天 |
|
||||
| R.2.1.3 重要性感知检索 | 0.5 天 |
|
||||
| 测试 + 调试 | 1.5 天 |
|
||||
| **R.2 总计** | **4 天** |
|
||||
|
||||
---
|
||||
|
||||
## R.3 动态权重增强
|
||||
|
||||
**目标:** 根据查询特性动态调整检索策略,支持核心标签加权
|
||||
|
||||
### R.3.1 核心任务
|
||||
|
||||
#### Task R.3.1.1:实现查询特性分析
|
||||
|
||||
```python
|
||||
# services/query_analyzer.py (新增)
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class QueryProfile:
|
||||
logic_depth: float # 逻辑深度 (0-1): 意图明确程度
|
||||
is_code_related: bool # 是否代码相关
|
||||
is_table_related: bool # 是否表格相关
|
||||
keyword_density: float # 关键词密度
|
||||
is_conversational: bool # 是否对话式查询
|
||||
|
||||
class QueryAnalyzer:
|
||||
CODE_KEYWORDS = {'code', 'function', 'class', 'api', 'python', 'js', 'bug'}
|
||||
TABLE_KEYWORDS = {'table', 'sheet', 'excel', 'csv', 'column', 'row', '数据', '统计'}
|
||||
|
||||
def analyze(self, query: str) -> QueryProfile:
|
||||
words = set(re.findall(r'\w+', query.lower()))
|
||||
|
||||
return QueryProfile(
|
||||
logic_depth=self._calc_logic_depth(query),
|
||||
is_code_related=bool(words & self.CODE_KEYWORDS),
|
||||
is_table_related=bool(words & self.TABLE_KEYWORDS),
|
||||
keyword_density=len(words) / max(len(query), 1),
|
||||
is_conversational=self._is_conversational(query),
|
||||
)
|
||||
```
|
||||
|
||||
#### Task R.3.1.2:实现动态 Reranker
|
||||
|
||||
```python
|
||||
# services/dynamic_reranker.py (新增)
|
||||
class DynamicReranker:
|
||||
def rerank(self, query: str, results: list[SearchResult]) -> list[SearchResult]:
|
||||
profile = QueryAnalyzer().analyze(query)
|
||||
|
||||
# 根据查询类型调整权重
|
||||
weights = self._get_weights(profile)
|
||||
beta = self._calc_beta(profile)
|
||||
|
||||
scored = []
|
||||
for r in results:
|
||||
score = r.score * weights["semantic"]
|
||||
score += self._keyword_score(query, r.content) * weights["keyword"]
|
||||
score += self._title_score(query, r.document_title) * weights["title"]
|
||||
|
||||
# 表格内容加分
|
||||
if profile.is_table_related:
|
||||
meta = json.loads(r.metadata_ or "{}")
|
||||
if meta.get("content_type") == "table_schema":
|
||||
score += 0.25
|
||||
|
||||
score *= beta
|
||||
scored.append((score, r))
|
||||
|
||||
scored.sort(key=lambda x: x[0], reverse=True)
|
||||
return [r for _, r in scored]
|
||||
|
||||
def _get_weights(self, profile: QueryProfile) -> dict:
|
||||
if profile.is_code_related:
|
||||
return {"semantic": 0.55, "keyword": 0.35, "title": 0.10}
|
||||
elif profile.is_table_related:
|
||||
return {"semantic": 0.50, "keyword": 0.30, "title": 0.20}
|
||||
elif profile.is_conversational:
|
||||
return {"semantic": 0.85, "keyword": 0.10, "title": 0.05}
|
||||
else:
|
||||
return {"semantic": 0.70, "keyword": 0.20, "title": 0.10}
|
||||
```
|
||||
|
||||
#### Task R.3.1.3:实现核心标签系统
|
||||
|
||||
```python
|
||||
# 在 models/document.py 中增加 tags 字段
|
||||
class DocumentChunk(Base):
|
||||
tags = Column(JSON, default=list) # ["重要", "代码", "架构"]
|
||||
is_core = Column(Boolean, default=False) # 是否核心切片
|
||||
|
||||
# services/core_tag_search.py (新增)
|
||||
class CoreTagAwareSearch:
|
||||
CORE_BOOST_FACTOR = 1.33 # 33% 加权
|
||||
|
||||
async def search(self, query: str, user_id: str,
|
||||
core_tags: list[str] = None) -> list[SearchResult]:
|
||||
results = await self.base_search(query, user_id)
|
||||
|
||||
if core_tags:
|
||||
for r in results:
|
||||
meta = json.loads(r.metadata_ or "{}")
|
||||
chunk_tags = meta.get("tags", [])
|
||||
|
||||
if any(tag in chunk_tags for tag in core_tags):
|
||||
r.score *= self.CORE_BOOST_FACTOR
|
||||
|
||||
return sorted(results, key=lambda x: x.score, reverse=True)
|
||||
```
|
||||
|
||||
### R.3.2 验收标准
|
||||
|
||||
- [ ] 查询特性分析准确(代码/表格/对话式识别)
|
||||
- [ ] 动态权重根据查询类型调整
|
||||
- [ ] 核心标签检索加权 1.33x
|
||||
- [ ] Rerank 集成测试通过
|
||||
|
||||
### R.3.3 变更文件
|
||||
|
||||
| 文件 | 操作 | 说明 |
|
||||
|------|------|------|
|
||||
| `services/query_analyzer.py` | 新增 | 查询特性分析 |
|
||||
| `services/dynamic_reranker.py` | 新增 | 动态 Reranker |
|
||||
| `services/core_tag_search.py` | 新增 | 核心标签检索 |
|
||||
| `services/knowledge_service.py` | 修改 | 集成动态权重 |
|
||||
| `models/document.py` | 修改 | 增加 tags/is_core 字段 |
|
||||
| `tests/test_dynamic_reranker.py` | 新增 | 动态 Reranker 测试 |
|
||||
|
||||
### R.3.4 工作量估算
|
||||
|
||||
| 任务 | 估算 |
|
||||
|------|------|
|
||||
| R.3.1.1 查询特性分析 | 1 天 |
|
||||
| R.3.1.2 动态 Reranker | 1 天 |
|
||||
| R.3.1.3 核心标签系统 | 1 天 |
|
||||
| 测试 + 调试 | 1.5 天 |
|
||||
| **R.3 总计** | **4.5 天** |
|
||||
|
||||
---
|
||||
|
||||
## R.4 高级特性(可选)
|
||||
|
||||
**目标:** 探索更高级的 RAG 增强技术
|
||||
|
||||
### R.4.1 Task R.4.1.1:语义去重
|
||||
|
||||
```python
|
||||
class SemanticDeduplicator:
|
||||
DEDUP_THRESHOLD = 0.88
|
||||
|
||||
def deduplicate(self, results, embeddings) -> list:
|
||||
"""消除冗余检索结果"""
|
||||
# 计算余弦相似度矩阵
|
||||
# 贪心去重
|
||||
...
|
||||
```
|
||||
|
||||
### R.4.2 Task R.4.2.1:语义分桶
|
||||
|
||||
```python
|
||||
class SemanticBucketing:
|
||||
async def bucket_by_topic(self, results, embeddings) -> dict:
|
||||
"""按主题自动组织检索结果"""
|
||||
# 使用聚类算法
|
||||
...
|
||||
```
|
||||
|
||||
### R.4.3 Task R.4.3.1:EPA 分析(探索)
|
||||
|
||||
```python
|
||||
class EPAModule:
|
||||
"""
|
||||
EPA: Embedding Projection Analysis
|
||||
高复杂度,Phase R.4 探索
|
||||
"""
|
||||
pass # 暂不实现
|
||||
```
|
||||
|
||||
### R.4.4 验收标准
|
||||
|
||||
- [ ] 语义去重测试通过
|
||||
- [ ] 语义分桶原型完成
|
||||
- [ ] EPA 分析方案设计完成(可选实现)
|
||||
|
||||
### R.4.5 工作量估算
|
||||
|
||||
| 任务 | 估算 |
|
||||
|------|------|
|
||||
| R.4.1.1 语义去重 | 1.5 天 |
|
||||
| R.4.2.1 语义分桶 | 2 天 |
|
||||
| R.4.3.1 EPA 设计 | 1 天 |
|
||||
| **R.4 总计(可选)** | **4.5 天** |
|
||||
|
||||
---
|
||||
|
||||
## R.5 阶段总结与产出
|
||||
|
||||
### R.5.1 完整实施路径
|
||||
|
||||
```
|
||||
R.0 ──────────────────────────────────────────────────────────────┐
|
||||
│ 现状与目标 │
|
||||
│ - 当前架构分析 │
|
||||
│ - 短板识别 │
|
||||
│ - VCPToolBox 借鉴点 │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
R.1 ──────────────────────────────────────────────────────────────┐
|
||||
│ Token 感知分块优化 │
|
||||
│ - tiktoken 集成 │
|
||||
│ - 智能断句 │
|
||||
│ - 重叠分块 │
|
||||
│ │
|
||||
│ 工作量: 3 天 │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
R.2 ──────────────────────────────────────────────────────────────┐
|
||||
│ 多索引架构 │
|
||||
│ - Collection 分离策略 │
|
||||
│ - 懒加载 + LRU TTL │
|
||||
│ - 重要性感知检索 │
|
||||
│ │
|
||||
│ 依赖: R.1 │
|
||||
│ 工作量: 4 天 │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
R.3 ──────────────────────────────────────────────────────────────┐
|
||||
│ 动态权重增强 │
|
||||
│ - QueryAnalyzer │
|
||||
│ - DynamicReranker │
|
||||
│ - CoreTagAwareSearch │
|
||||
│ │
|
||||
│ 依赖: R.1 │
|
||||
│ 工作量: 4.5 天 │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
R.4 ──────────────────────────────────────────────────────────────┐
|
||||
│ 高级特性 (可选) │
|
||||
│ - 语义去重 │
|
||||
│ - 语义分桶 │
|
||||
│ - EPA 分析设计 │
|
||||
│ │
|
||||
│ 工作量: 4.5 天(可选) │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### R.5.2 总工作量估算
|
||||
|
||||
| Phase | 工作量 |
|
||||
|-------|--------|
|
||||
| R.1 | 3 天 |
|
||||
| R.2 | 4 天 |
|
||||
| R.3 | 4.5 天 |
|
||||
| R.4(可选) | 4.5 天 |
|
||||
| **R.1-R.3 必须** | **11.5 天** |
|
||||
| **R.1-R.4 含可选** | **16 天** |
|
||||
|
||||
### R.5.3 产出清单
|
||||
|
||||
| 产出 | 对应 Phase |
|
||||
|------|-----------|
|
||||
| `services/chunker.py` | R.1 |
|
||||
| `services/multi_index.py` | R.2 |
|
||||
| `services/query_analyzer.py` | R.3 |
|
||||
| `services/dynamic_reranker.py` | R.3 |
|
||||
| `services/core_tag_search.py` | R.3 |
|
||||
| `models/document.py` 更新 | R.2, R.3 |
|
||||
| 单元测试 > 80% | R.1, R.2, R.3 |
|
||||
| 集成测试通过 | R.1, R.2, R.3 |
|
||||
|
||||
### R.5.4 与 Phase 1-5 的关系
|
||||
|
||||
| Phase | RAG 协作内容 |
|
||||
|-------|-------------|
|
||||
| Phase 1 | 基础加固:Task Schema 追踪 RAG 任务 |
|
||||
| Phase 2 | 协作:RAG 任务可分解给 Librarian Agent |
|
||||
| Phase 3 | 动态:支持多索引动态选择 |
|
||||
| Phase 4 | 可视化:RAG 检索过程可视化 |
|
||||
| Phase 5 | 高级:EPA 分析、语义分桶 |
|
||||
| **Phase R** | **独立 RAG 升级路径,可与 Phase 1-5 并行推进** |
|
||||
|
||||
---
|
||||
|
||||
## R.6 风险与注意事项
|
||||
|
||||
| 风险 | 影响 | 缓解措施 |
|
||||
|------|------|----------|
|
||||
| Token 计数不准确 | 低 | 使用 tiktoken 精确计数,多次验证 |
|
||||
| 索引分离后检索复杂 | 中 | 提供统一检索接口,隐藏内部逻辑 |
|
||||
| 动态权重调参困难 | 中 | 提供配置项,允许用户调整 |
|
||||
| EPA 实现复杂度高 | 高 | Phase R.4 可选,暂不实现 |
|
||||
Reference in New Issue
Block a user