首页 / 文章 / 实用提示:我扔掉了自己的向量数据库,RAG的效果好多了。

实用提示:我扔掉了自己的向量数据库,RAG的效果好多了。

《实用笔记》操作指南:我抛弃了向量数据库。借助合同、校验机制以及为采用该模式的团队准备的即用代码模块,RAG性能有了显著提升。

2297 词

以下笔记梳理了一条基于“我放弃了向量数据库,使用PageIndex后RAG性能大幅提升”这一经验的实用路径。重点在于契约、校验以及可直接插入的代码占位符,而非激励性表述。 在完成概览阶段时,首先明确契约内容:所需输入、成功标志以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 同时记录正常流程与异常恢复路径。重试机制、人工审核环节以及死信处理都是产品本身的一部分,而非后续的优化工作。

向量RAG的核心误区

将舞台作品的核心理念视为可测量的对象来处理效果最佳。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。 优先选择小型且可测试的单元,而非庞大的脚本。当某个步骤出现故障时,故障应指向单一的责任主体,而非复杂的流程链。 将分块策略与检索策略分开。当质量指标发生变化时,修改其中一项不应迫使重新编写另一项。

PageIndex: 无需向量数据库的RAG

在没有该阶段的情况下,PageIndex RAG作为可度量的处理对象时效果最佳。在扩大范围之前,先收集一份理想的文本样本、一个失败案例以及回滚说明。 将此阶段视为输入与经过验证的输出之间的契约。为相关成果命名,明确成功标准,绝不允许出现无声的半完成状态。 将分块策略与检索策略分开。当质量指标发生变化时,修改其中一项不应强制要求重新编写另一项。

PageIndex的工作原理:两步流程

PageIndex 的工作原理:将该阶段视为可测量的对象时,其效果最佳。在扩大范围之前,需记录一份理想的处理结果、一个失败案例以及回滚说明。 在功能结果旁同时记录处理时间以及令牌或查询成本。提前了解成本情况,可避免在系统从演示环境过渡到共享环境时出现意外费用。 应将分块策略与检索策略分开。当质量指标发生变化时,调整其中一个不应迫使重新编写另一个。 PageIndex 的工作原理:将该阶段视为可测量的对象时,其效果最佳。在扩大范围之前,需记录一份理想的处理结果、一个失败案例以及回滚说明。 需同时记录正常流程和恢复流程。重试机制、人工审核环节以及死信处理都是产品本身的组成部分,而非后续需要补充的功能。

入门指南:在本地运行 PageIndex

在“开始运行 PageIndex”阶段,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 优先选择小型、可测试的单元,而非冗长的脚本。当某个步骤失败时,故障应指向单一责任点,而非复杂的流程链。 需引用实际作为答案依据的段落。没有引用的话,操作人员就无法区分是幻觉还是索引缺失。

git clone https://github.com/VectifyAI/PageIndex.git
cd PageIndex
pip3 install --upgrade -r requirements.txt
CHATGPT_API_KEY=your_openai_api_key_here
python3 run_pageindex.py --pdf_path /path/to/annual_report.pdf
python3 run_pageindex.py --md_path /path/to/technical_spec.md

索引的实际结构

在“索引的实际功能”阶段,应在修改代码之前明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 将此阶段视为输入与经过验证的输出之间的契约。为相关成果命名,定义成功判定标准,并拒绝默许的半完成状态。 需引用实际作为答案依据的段落。没有引用的话,操作人员就无法区分幻觉内容与索引缺失的问题。

{
  "document": "Apple Inc. Annual Report 2023",
  "index": {
    "title": "Apple Inc. Annual Report 2023",
    "summary": "Comprehensive financial and operational report covering revenue, product segments, risks, and strategic outlook",
    "children": [
      {
        "title": "Business Overview",
        "summary": "Company description, product lines, and market position",
        "pages": [1, 8],
        "children": [...]
      },
      {
        "title": "Financial Results",
        "summary": "Revenue, operating income, EPS, and segment performance for fiscal 2023",
        "pages": [45, 72],
        "children": [
          {
            "title": "Revenue by Product Category",
            "summary": "iPhone, Mac, iPad, Wearables, and Services revenue breakdown",
            "pages": [46, 52]
          },
          {
            "title": "Geographic Revenue Distribution",
            "summary": "Americas, Europe, Greater China, Japan, Rest of Asia Pacific",
            "pages": [53, 58]
          }
        ]
      },
      {
        "title": "Risk Factors",
        "summary": "Operational, market, regulatory, and competitive risks",
        "pages": [89, 110]
      }
    ]
  }
}

查询索引:内部机制

在“查询索引”阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 应在功能结果旁记录执行时间以及令牌或查询成本。提前显示成本可避免在流程从演示环境切换到共享环境时出现意外费用。 需注明实际用于得出答案的相关内容。没有引用依据的话,操作人员就无法区分是幻觉结果还是索引缺失导致的错误。 在“查询索引”阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及错误处理措施都是产品本身的组成部分,而非后续需要补充的内容。

import json
from openai import OpenAI

client = OpenAI()
def navigate_index(query: str, index_node: dict, depth: int = 0) -> list[dict]:
    """
    Recursively navigate the document index using LLM reasoning.
    Returns list of relevant leaf nodes with page references.
    """
    children = index_node.get("children", [])
    if not children:
        # Leaf node: return this section as relevant
        return [index_node]
    # Ask the LLM which branches are relevant to the query
    children_summary = "\n".join([
        f"[{i}] {child['title']}: {child['summary']}"
        for i, child in enumerate(children)
    ])
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {
                "role": "system",
                "content": (
                    "You are navigating a document index to find sections relevant "
                    "to a query. Select the index numbers of sections that are likely "
                    "to contain the answer. Return a JSON array of selected indices."
                )
            },
            {
                "role": "user",
                "content": (
                    f"Query: {query}\n\n"
                    f"Available sections:\n{children_summary}\n\n"
                    f"Which sections should I look into? Return JSON array of indices only."
                )
            }
        ],
        temperature=0,
        response_format={"type": "json_object"}
    )
    selected = json.loads(response.choices[0].message.content).get("indices", [])
    relevant_nodes = []
    for idx in selected:
        if idx             # Recurse into selected branches
            relevant_nodes.extend(
                navigate_index(query, children[idx], depth + 1)
            )
    return relevant_nodes

def answer_with_pageindex(query: str, index: dict, document_pages: dict) -> str:
    """
    Full PageIndex retrieval and answer generation.
    """
    # Navigate the index to find relevant sections
    relevant_nodes = navigate_index(query, index)
    # Retrieve full text from identified pages
    context_parts = []
    citations = []
    for node in relevant_nodes:
        pages = node.get("pages", [])
        if pages:
            page_start, page_end = pages[0], pages[1]
            for page_num in range(page_start, page_end + 1):
                if page_num in document_pages:
                    context_parts.append(document_pages[page_num])
                    citations.append(f"p.{page_num}")
    context = "\n\n".join(context_parts)
    # Generate answer with full, unchunked context
    answer_response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {
                "role": "system",
                "content": (
                    "Answer the question based on the provided document sections. "
                    "Be precise. If the answer involves numbers or dates, quote them exactly."
                )
            },
            {
                "role": "user",
                "content": f"Document sections:\n{context}\n\nQuestion: {query}"
            }
        ],
        temperature=0
    )
    answer = answer_response.choices[0].message.content
    citation_str = ", ".join(set(citations))
    return f"{answer}\n\n**Source:** {citation_str}"

让我格外关注的FinanceBench测试结果

在处理FinanceBench测试结果的阶段时,首先需明确合同的各项要求:所需输入、成功标志以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 建议采用小型、可测试的单元而非庞大的脚本。当某个步骤失败时,故障应指向单一责任模块,而非复杂的流程链。 在调整提示词之前,先使用固定的问题集来衡量检索的准确率。仅仅更换提示词往往无法解决检索效果不佳的问题。

何时选择PageIndex而非传统RAG

在处理“何时使用PageIndex”这一阶段时,首先需明确相关约定:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改始终符合约定。 将这一阶段视为输入与经过验证的输出之间的契约。为相关成果命名,定义成功判定标准,杜绝无声的半完成状态。 在调整提示词之前,先使用固定的问题集来测试召回率。仅仅更换提示词往往无法改善较差的检索效果。

使用PageIndex云API

在完成“使用 PageIndex Cloud”阶段时,首先需明确相关约定:所需输入、成功信号以及部分失败时的处理方式。这份清单能确保后续的代码修改始终符合预期。 在功能结果旁记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。 在调整提示词之前,先使用固定的问题集测试召回率。仅仅更换提示词往往无法改善较差的检索效果。 在完成“使用 PageIndex Cloud”阶段时,首先需明确相关约定:所需输入、成功信号以及部分失败时的处理方式。这份清单能确保后续的代码修改始终符合预期。 需同时记录正常流程和故障恢复流程。重试机制、人工审核环节以及错误处理措施都是产品本身的组成部分,而非后续需要补充的功能。

import requests

PAGEINDEX_API_KEY = "your_api_key"
BASE_URL = "https://api.pageindex.ai/v1"
def upload_document(file_path: str) -> str:
    """Upload a document and get back a document_id."""
    with open(file_path, "rb") as f:
        response = requests.post(
            f"{BASE_URL}/documents",
            headers={"Authorization": f"Bearer {PAGEINDEX_API_KEY}"},
            files={"file": f}
        )
    return response.json()["document_id"]

def query_document(document_id: str, question: str) -> dict:
    """Query an indexed document and get a cited answer."""
    response = requests.post(
        f"{BASE_URL}/query",
        headers={
            "Authorization": f"Bearer {PAGEINDEX_API_KEY}",
            "Content-Type": "application/json"
        },
        json={
            "document_id": document_id,
            "question": question
        }
    )
    return response.json()

# Example usage
doc_id = upload_document("q3_earnings_report.pdf")
result = query_document(doc_id, "What was total revenue in Q3?")
print(result["answer"])
print(f"Sources: {result['citations']}")

这所代表的更深层次变革

将这一阶段视为可测量的层面来处理最为有效。在扩大范围之前,先记录一份优秀的测试案例、一个失败案例以及回滚说明。 优先选择小型且可测试的单元,而非庞大的脚本。当某个步骤失败时,故障应指向单一责任模块,而非复杂的流程链。 将分块策略与检索策略分开。当质量指标发生变化时,修改其中一项不应迫使重新编写另一项。

如果您正在开发文档人工智能,这意味着什么

若将“这意味着什么”阶段视为可度量的流程层面,其效果最佳。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。 将此阶段视为输入与经过验证的输出之间的契约。为相关成果命名,明确成功标准,绝不允许出现无声的、不完整的处理。 将分块策略与检索策略分开。当质量指标发生变化时,修改其中一项不应强制要求重新编写另一项。

让我们继续一起学习

将“让我们持续学习”阶段视为可度量的对象来管理效果最佳。在扩大范围之前,需记录一份理想的处理结果、一个失败案例以及回滚说明。 在功能测试结果旁同时记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在系统从演示环境过渡到共享环境时出现意外费用。 应将分块策略与检索策略分开。当质量指标发生变化时,调整其中一个不应迫使重新编写另一个。 将“让我们持续学习”阶段视为可度量的对象来管理效果最佳。在扩大范围之前,需记录一份理想的处理结果、一个失败案例以及回滚说明。 需同时记录正常流程与故障恢复流程。重试机制、人工审核环节以及死信处理都是产品本身的组成部分,而非后续需要补充的功能。

资源

在资源准备阶段,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。相比冗长的脚本,更应采用小型且可测试的单元。当某个步骤失败时,故障原因应能指向单一责任模块,而非复杂的流程链。必须引用实际作为答案依据的段落;没有引用的话,操作人员就无法区分是虚假信息还是索引缺失所致。

操作检查清单

在操作检查清单阶段,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。

将配置信息置于应用程序代码之外。环境文件、密钥存储以及功能开关应集中存放于一处,以便操作员无需查看全部内容即可进行审计。

需引用实际作为答案依据的段落。没有引用的话,操作员就无法区分是虚假信息还是索引缺失导致的错误。

编写简短的操作手册:说明如何轮换密钥、如何清空队列、以及如何回滚上一次的数据导入操作。

同时记录正常流程和故障恢复流程。重试机制、人工审核环节以及死信处理都是产品功能的一部分,而非后续需要补充的内容。

需引用实际作为答案依据的段落。没有引用的话,操作员就无法区分是虚假信息还是索引缺失导致的错误。

在推广该技术栈之前,应先冻结版本,为关键流程记录标准输出日志,并明确回滚步骤。共享环境需要设置速率限制、租户验证机制,以及负责密钥轮换的明确责任人。与其展示花哨的一次性演示,不如注重扎实的可靠性。

关于888b75aac33b的批处理说明:请将提供商密钥移出代码仓库,设定单会话令牌上限,并将日志存储在评估用示例文件旁,以便后续模型更换时保持数据可比性。