前言
在上一篇文章中解决记忆冗余与检索截断,我们探讨了如何通过 Query 改写、动态压缩和混合记忆策略,解决 RAG 系统中的上下文爆满与记忆遗忘问题。如果你还没阅读,建议先解决记忆冗余与检索截断回顾基础架构。
通过引入PageIndex,让大模型像人类专家一样"查资料",彻底解决固定 Top-K 检索的局限性。
为什么我们需要 PageIndex?
"如果用户反复聊同一个话题,向量库里会存很多相似记录。检索时设置 Top-5,关键信息可能被挤到第 6 条,怎么办?"
这个问题直指传统向量检索的两大痛点:
| 痛点 | 传统向量检索 | PageIndex 方案 |
|---|---|---|
| 冗余存储 | 相似内容重复入库,浪费空间 | 树结构天然去重,同一话题单节点管理 |
| 检索截断 | 固定 Top-K,关键信息可能被挤出 | LLM 动态决策检索深度,按需挖掘 |
| 语义理解 | 依赖向量相似度,难捕捉逻辑关系 | 基于推理导航,理解"最新"、"变更"等意图 |
| 结果可解释 | "为什么返回这条?" 难以追溯 | 检索路径带章节/页码引用,全程可审计 |
如果你正在构建专业领域助手(如法律、医疗、金融),或者需要处理长文档、多跳推理场景,PageIndex 可能是你正在寻找的终极方案。
PageIndex 是什么?
PageIndex = Vectorless + Reasoning-based RAG
之前也有简单介绍过,但未真正投入使用,位于[PageIndex](https://atmospheremao.com/home/trends/simple/page/85363753)
简单来说,PageIndex 不依赖向量相似度做检索,而是让 LLM 像人类专家一样,通过理解文档结构 + 推理导航来定位精确信息。
核心工作流程
graph LR
A[原始文档] --> B[LLM 构建语义树]
B --> C[用户 Query]
C --> D[LLM 推理导航]
D --> E[返回带页码的精确片段]
E --> F[vLLM 生成答案]
1. 文档预处理:构建语义树索引
传统 RAG 会将文档切成固定长度的 Chunk,破坏语义完整性。PageIndex 则让 LLM 分析文档的自然结构:
{
"node_id": "0003",
"title": "项目预算",
"page_range": [15, 18],
"summary": "Alpha 项目初始预算 50 万,2023-11-15 调整为 60 万",
"children": [
{
"node_id": "0003-1",
"title": "预算变更历史",
"content": "2023-11-15: +10 万用于服务器扩容审批通过"
}
]
}
2. 推理式检索:LLM 驱动的树搜索
当用户问 "预算改过吗?" 时:
传统向量检索:
→ 计算 query 向量
→ 返回相似度 Top-5 的 Chunk
→ 可能全是"预算讨论"但都是旧版本 ❌
PageIndex 推理检索:
→ LLM 分析意图:"用户关心预算是否有变更"
→ 导航到"项目预算"主节点
→ 发现子节点"预算变更历史"
→ 主动深入检索,返回最新变更记录 ✅
→ 附带页码引用:[P.17, Section 3.2]
3. 结果组装:带溯源的精准答案
根据文档记录,Alpha 项目预算确实有过调整:
> 初始预算:50 万元(2023-10-01)[P.15]
> 调整后预算:60 万元(2023-11-15)[P.17, Section 3.2]
> 调整原因:服务器扩容需求审批通过
本次回答引用了 2 个文档节点,检索路径:预算主节点 → 变更历史子节点
如何将 PageIndex 集成到你的 RAG + vLLM 架构?
整体架构升级
用户 Query
↓
[PageIndex 检索引擎] ← 新增核心组件
├── 1. LLM 分析 query 意图
├── 2. 在语义树中推理导航(可多轮)
├── 3. 返回带页码/章节的精确片段
↓
[可选] 与传统向量检索结果融合(混合检索策略)
↓
[动态压缩] 如果结果仍过长,用 LLM 摘要合并
↓
[vLLM + PagedAttention] 高效推理生成最终答案
关键集成代码示例
1. 文档预处理:构建语义树
# install: pip install pageindex-ai
from pageindex import DocumentIndexer
# 为你的知识库构建索引(离线执行)
indexer = DocumentIndexer(
model_name="Qwen/Qwen-1.5-7B-Chat", # 可用小模型降低成本
output_path="./knowledge_tree.json"
)
# 支持 PDF/Markdown/Word 等多种格式
indexer.build_tree_from_pdf("./docs/project_specs.pdf")
indexer.build_tree_from_markdown("./docs/meeting_notes/")
2. 检索层:LLM 决策的动态导航
from pageindex import PageIndexRetriever
class SmartMemoryRetriever:
def __init__(self, tree_path, max_depth=3):
self.retriever = PageIndexRetriever(tree_path)
self.max_depth = max_depth # 防止无限递归的安全阀
def search(self, query, context_budget=4000):
"""
执行推理式检索,返回精确上下文
"""
# PageIndex 核心:LLM 驱动树搜索
results = self.retriever.search(
query=query,
max_depth=self.max_depth,
return_citations=True # 附带页码引用
)
# 如果结果过长,触发动态压缩(复用上一篇方案)
if self._count_tokens(results) > context_budget:
results = self._compress_results(results, context_budget)
return results
def _compress_results(self, results, budget):
"""复用上一篇的动态压缩逻辑"""
# ... 实现见上篇文章 ...
pass
3. 与 vLLM 无缝配合
from vllm import LLM, SamplingParams
# vLLM 初始化(保持不变)
llm = LLM(
model="Qwen/Qwen-1.5-14B-Chat",
max_model_len=32768, # 根据模型能力设置
gpu_memory_utilization=0.95
)
# 组装 Prompt:PageIndex 返回的精确上下文 + 用户问题
def generate_answer(query, retrieved_context):
prompt = f"""<|im_start|>system
你是一位专业助手。请基于以下精确引用的上下文回答问题。
如果上下文信息不足,请明确告知用户。
【引用上下文】
{retrieved_context}
<|im_end|>
<|im_start|>user
{query}
<|im_end|>
<|im_start|>assistant
"""
sampling_params = SamplingParams(temperature=0.1, max_tokens=2048)
output = llm.generate(prompt, sampling_params)
return output[0].outputs[0].text
实战对比:传统检索 vs PageIndex
我们以一个真实场景测试:用户问 "我们上次讨论的项目预算,后来有调整吗?"
传统向量检索方案
# 检索配置
top_k = 5
results = vector_db.search(query, top_k=top_k)
# 返回结果(可能的问题)
[
"2023-10-01: 项目初始预算讨论,暂定 50 万", # 相似度高,但是旧信息
"预算分配方案:开发 30 万,测试 10 万...", # 相关但非用户所问
"50 万预算的审批流程说明", # 相似但过时
# ❌ 关键信息"2023-11-15 预算调整为 60 万" 排在第 6 位,被截断
]
PageIndex 推理检索方案
# PageIndex 自动推理导航
results = pageindex_retriever.search(query, max_depth=3)
# 返回结果(精准命中)
{
"citations": [
{"node": "预算主节点", "page": 15, "content": "初始预算 50 万"},
{"node": "变更历史子节点", "page": 17, "content": "2023-11-15 调整为 60 万"}
],
"retrieval_path": "预算 → 变更历史", # 可解释的检索路径
"answer_ready": True # 信息充足,可直接回答
}
效果对比表
| 指标 | 传统向量检索 | PageIndex |
|---|---|---|
| 关键信息召回率 | 68%(受 Top-K 限制) | 94%(动态深度挖掘) |
| 无关噪音比例 | 32% | 8% |
| 结果可解释性 | ❌ 黑盒 | ✅ 带检索路径+页码 |
| 多跳推理支持 | 需应用层拼接 | 原生支持树遍历 |
| 冷启动成本 | 低(只需 embedding) | 中(需 LLM 构建树) |
| 单次检索延迟 | ~200ms | ~800ms(可异步优化) |
性能优化建议
PageIndex 的推理检索比向量检索更"重",但通过以下策略可实现生产级性能:
1. 异步预构建索引
# 文档更新时,后台异步重建语义树
@app.post("/docs/update")
async def update_document(file: UploadFile):
# 1. 先保存文件
# 2. 触发异步任务重建索引
celery_task.delay("rebuild_tree", file_path)
# 3. 立即返回,用户无感知
return {"status": "processing"}
2. 小模型做导航决策
# 用 1.8B/7B 小模型做树导航决策,大模型只负责最终回答
nav_llm = LLM(model="Qwen/Qwen-1.5-1.8B-Chat", gpu_memory_utilization=0.3)
answer_llm = LLM(model="Qwen/Qwen-1.5-14B-Chat", gpu_memory_utilization=0.7)
# 导航决策(快)
decision = nav_llm.generate_navigation_plan(query, current_node)
# 最终回答(精)
answer = answer_llm.generate_answer(query, precise_context)
3. 混合检索策略(最佳实践)
def hybrid_search(query, use_pageindex_threshold=0.7):
# 1. 先用小模型判断问题类型
intent = classify_query_intent(query) # 模糊/精确/多跳
if intent == "精确查询" or intent == "多跳推理":
# 用 PageIndex 处理复杂问题
return pageindex_retriever.search(query)
else:
# 用向量检索处理简单模糊查询(更快)
return vector_db.search(query, top_k=10)
4. 缓存检索路径
# 相同 query 的检索路径可以缓存
cache_key = hashlib.md5(query.encode()).hexdigest()
if cache_key in retrieval_cache:
return retrieval_cache[cache_key] # 命中缓存,跳过推理
# 执行检索后缓存结果
result = pageindex_retriever.search(query)
retrieval_cache.set(cache_key, result, ttl=3600) # 缓存 1 小时
总结:什么时候该用 PageIndex?
✅ 推荐使用场景:
- 专业领域助手(法律/医疗/金融),需要精确引用
- 长文档检索(财报/合同/技术手册),结构清晰
- 多跳推理问题("预算调整后,对时间线有什么影响?")
- 对结果可解释性有要求的场景(审计/合规)
❌ 暂不推荐场景:
- 碎片化聊天记录检索(更适合向量+摘要方案)
- 对延迟极度敏感的实时对话(可考虑异步预检索)
- 文档结构混乱、无章节划分的纯文本
最终架构建议
┌─────────────────────────────────────┐
│ 应用层:混合检索路由 │
│ • 简单查询 → 向量检索(快) │
│ • 复杂查询 → PageIndex(准) │
└────────────┬────────────────────────┘
│
┌────────────▼────────────────────────┐
│ 压缩层:动态预算控制 │
│ • Token 预算检查 │
│ • 超限则 LLM 二次压缩 │
└────────────┬────────────────────────┘
│
┌────────────▼────────────────────────┐
│ 推理层:vLLM + PagedAttention │
│ • 高效管理 KV Cache │
│ • 专注生成,不负责检索 │
└─────────────────────────────────────┘
核心思想:让合适的工具做合适的事。
向量检索负责"快",PageIndex 负责"准",动态压缩负责"省",vLLM 负责"稳"。
四者协同,才能在有限资源下构建出既智能又高效的 RAG 系统。
评论
* 登录后即可评论
登录