PageIndex 实战

RAG 搜索优化 - PageIndex 实战

前言

在上一篇文章中解决记忆冗余与检索截断,我们探讨了如何通过 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 系统。

评论


暂无评论

* 登录后即可评论