Practical notes: PageIndex: Фреймворк RAG, заменивший векторные базы данных
Пошаговое руководство по Practical notes: PageIndex: Фреймворк RAG, заменивший векторные базы данных: контракты, проверки и готовые блоки кода для команд, использующих эту модель.
Используйте это как переработанную версию идей из статьи «PageIndex: The RAG Framework That Threw Out Vector Databases and Still Hit 98.7% Accuracy», ориентированную на операторов: четкие этапы, упорядоченные блоки кода и записи о восстановлении, сохраняющиеся при передаче задач.
Как система поиска на основе логики от VectifyAI тихо разрушает самое укоренившееся предположение в промышленных решениях типа RAG
Этап работы системы VectifyAI, основанной на логике, наилучшим образом функционирует, когда он рассматривается как измеримая структура. Сначала зафиксируйте один идеальный пример работы, один случай сбоя и записи о возврате к предыдущему состоянию, прежде чем расширять объем работ. Записывайте время выполнения и стоимость токенов или запросов вместе с функциональными результатами. Отслеживание затрат на раннем этапе предотвращает неожиданные счета при переходе от демо-версии к общедоступным средам. Определите лимит токенов на один ход и на сессию. Инструменты агентов активно расширяют контекст; жесткие ограничения не позволяют демо-версиям превращаться в неожиданные счета.
Проблему, которую мы продолжаем игнорировать
Подход «Проблема — Этап» работает наилучшим образом, когда его рассматривают как измеримую поверхность. Соберите один идеальный пример работы, один случай сбоя и запись о возврате к предыдущему состоянию перед расширением объёма работ. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь код. Разделяйте политику разбиения на части и политику извлечения данных. Изменение одной из них не должно приводить к переписыванию другой при изменении показателей качества.
Что на самом деле такое PageIndex
Этап «Что на самом деле представляет собой PageIndex» работает лучше всего, если рассматривать его как измеримую характеристику. Соберите один идеальный пример работы, один случай сбоя и запись о возврате к предыдущему состоянию перед расширением объёма исследования. Документируйте одновременно успешный сценарий работы и сценарий восстановления. Повторные попытки, проверки человеком и обработка неработающих сообщений являются частью продукта, а не этапом последующей доработки. Разделяйте политику разбиения данных на части и политику их извлечения; изменение одной не должно принуждать к переписыванию другой при изменении показателей качества.
Шаг 1: Создание иерархического древовидного индекса
Шаг 1 «Создание сцены» работает наилучшим образом, если рассматривать его как измеримую поверхность. Соберите один идеальный пример выполнения, один случай сбоя и записку о возврате к предыдущему состоянию перед расширением объёма работы. Предпочитайте небольшие, тестируемые единицы кода вместо обширных скриптов. Когда происходит сбой на каком-либо шаге, причина должна указывать на конкретную ответственность, а не на запутанную цепочку операций. Разделяйте политику разбиения на части и политику извлечения данных. Изменение одной из них не должно приводить к переписыванию другой при изменении показателей качества.
{
"node_id": "0006",
"title": "Financial Stability",
"start_index": 21,
"end_index": 22,
"summary": "Covers the Federal Reserve's financial stability oversight...",
"sub_nodes": [
{
"node_id": "0007",
"title": "Monitoring Financial Vulnerabilities",
"start_index": 22,
"end_index": 28,
"summary": "Describes the Fed's vulnerability monitoring framework..."
},
{
"node_id": "0008",
"title": "Domestic and International Cooperation",
"start_index": 28,
"end_index": 31,
"summary": "Federal Reserve collaboration with international bodies..."
}
]
}
Шаг 2: Поиск в дереве на основе логики
Этап «Дерево на основе рассуждений, шаг 2» работает наилучшим образом, когда его рассматривают как измеримую структуру. Соберите один идеальный пример работы, один случай сбоя и записку о возврате к предыдущему состоянию перед расширением объёма работ. Рассматривайте этот этап как контракт между входными данными и проверенными результатами. Дайте названия всем элементам, определите критерии успеха и не соглашайтесь на молчаливое частичное выполнение задач. Установите лимиты на количество токенов за ход и за сессию. Инструменты агентов активно расширяют контекст; строгие ограничения предотвращают появление неожиданных счетов. Этап «Дерево на основе рассуждений, шаг 2» работает наилучшим образом, когда его рассматривают как измеримую структуру. Соберите один идеальный пример работы, один случай сбоя и записку о возврате к предыдущему состоянию перед расширением объёма работ. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, которое операторы могут проверять, не читая весь граф структур.
Почему это действительно работает: пример из Приложения G
На этапе «Почему это действительно работает» необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Необходимо одновременно задокументировать успешный сценарий работы и сценарий восстановления. Повторные попытки, проверки человеком и обработка неработоспособных сообщений являются частью продукта, а не последующими улучшениями. Указывайте те фрагменты текста, которые легли в основу ответа. Без цитат операторы не смогут отличить галлюцинации от пробелов в индексации.
Реализация на Python: полноценный RAG без векторов
Для реализации на Python в режиме End-to-End без использования векторов необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг, исходя из известной точки контроля, без необходимости угадывания скрытого состояния. Лучше использовать небольшие, тестируемые модули вместо обширных скриптов. При сбое шага причина должна быть связана с конкретной функцией, а не с запутанной структурой обработки данных. Необходимо разделить процесс создания клиента от цикла обработки сообщений, чтобы можно было заменять поставщики услуг без переписывания машины состояний диалога.
Установка
На этапе установки необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед внесением изменений в код. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рассматривайте этот этап как контракт между входными данными и проверенными результатами. Укажите названия результатов работы, определите критерии успеха и не допускайте молчаливого частичного завершения задачи. Цитируйте те фрагменты, которые фактически легли в основу ответа. Без цитат операторы не смогут отличить галлюцинации от пробелов в индексации.
pip install pageindex openai
Настройка
На этапе настройки необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рядом с функциональными результатами следует записывать время выполнения и стоимость токенов или запросов. Отображение стоимости заранее предотвращает неожиданные счета при переходе с демо-среды в общедоступные среды. Указывайте те фрагменты текста, которые легли в основу ответа. Без цитат операторы не смогут отличить галлюцинации от пробелов в индексации.
import os
import json
import asyncio
from pageindex import PageIndexClient
from openai import AsyncOpenAI
# Grab an API key from https://dash.pageindex.ai/api-keys
PAGEINDEX_API_KEY = os.environ["PAGEINDEX_API_KEY"]
OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]pi_client = PageIndexClient(api_key=PAGEINDEX_API_KEY)
openai_client = AsyncOpenAI(api_key=OPENAI_API_KEY)
Загрузка документа и построение дерева
Для операций «Прием документа» и «Подготовка к обработке» необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии системы. Конфигурацию следует хранить отдельно от кода приложения. Файлы среды, хранилища конфиденциальных данных и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь кодовый граф. Указывайте те участки текста, которые легли в основу ответа. Без цитат операторы не смогут отличить галлюцинации от пробелов в индексации.
import pageindex.utils as utils
# Upload a PDF; PageIndex handles the tree generation
doc = pi_client.upload("annual_report_2024.pdf")
doc_id = doc["doc_id"]# Tree generation takes a bit, so we poll
while not pi_client.is_retrieval_ready(doc_id):
print("Still indexing...")
import time; time.sleep(5)# Grab the tree and take a look
tree = pi_client.get_tree(doc_id, node_summary=True)["result"]
print("Document Tree:")
utils.print_tree(tree)
Основа: поиск в дереве с использованием LLM
Для этапа The Core LLM-Driven Tree необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Необходимо одновременно задокументировать успешный сценарий выполнения и сценарий восстановления. Повторные попытки, проверки человеком и обработка неработоспособных сообщений являются частью продукта, а не этапом последующей доработки. При следующем шаге, представляющем собой код или вызов инструмента, следует отдавать предпочтение структурированным выводам с проверкой по шаблону перед свободным текстом.
async def find_relevant_nodes(tree: dict, query: str) -> list:
"""LLM reasons over tree structure to identify relevant nodes."""
# Strip raw text to save tokens; the LLM only needs titles and summaries
tree_without_text = utils.remove_fields(
tree.copy(), fields=["text"]
) search_prompt = f"""
You are a document retrieval expert. Given a question and
a hierarchical tree structure of a document, identify all
nodes likely to contain the answer. Each node has a node_id, title, and summary.
Follow cross-references if a section mentions another. Question: {query} Document tree structure:
{json.dumps(tree_without_text, indent=2)} Reply in this JSON format only:
{{
"thinking": "<reasoning about which nodes are relevant>",
"node_list": ["node_id_1", "node_id_2"]
}}
""" response = await openai_client.chat.completions.create(
model="gpt-4.1",
messages=[{"role": "user", "content": search_prompt}],
temperature=0,
response_format={"type": "json_object"},
) result = json.loads(response.choices[0].message.content)
print(f"LLM reasoning: {result['thinking']}")
return result["node_list"]
Получение контента и генерация ответа
На этапе извлечения контента и генерации необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не пытаясь угадать скрытое состояние. Лучше использовать небольшие, тестируемые модули вместо обширных скриптов. При сбое шага причина должна быть связана с конкретной функцией, а не с запутанной структурой обработки. Указывайте те части текста, на которых основан ответ. Без цитат операторы не смогут отличить вымысел от проблем с индексацией.
def collect_node_content(tree: dict, node_ids: list) -> str:
"""Pull raw text from the nodes the LLM selected."""
all_nodes = utils.flatten_tree(tree)
context_parts = []
for node in all_nodes:
if node["node_id"] in node_ids:
title = node.get("title", "Untitled")
pages = f"pages {node.get('start_index', '?')}-{node.get('end_index', '?')}"
text = node.get("text", "")
context_parts.append(
f"[{title} | {pages}]\n{text}"
)
return "\n\n---\n\n".join(context_parts)
async def answer_query(tree: dict, query: str) -> dict:
"""Full vectorless RAG pipeline: tree search + answer generation.""" # Step 1: LLM picks the nodes
node_ids = await find_relevant_nodes(tree, query) # Step 2: Fetch content from those nodes
context = collect_node_content(tree, node_ids) # Step 3: Generate answer with citations
answer_prompt = f"""
Answer the question using only the provided context.
Cite specific pages and sections in your answer. Context:
{context} Question: {query}
""" response = await openai_client.chat.completions.create(
model="gpt-4.1",
messages=[{"role": "user", "content": answer_prompt}],
temperature=0,
) return {
"answer": response.choices[0].message.content,
"retrieved_nodes": node_ids,
"context_length": len(context),
}
# Run it
query = "What was the total value of deferred assets in 2023?"
result = asyncio.run(answer_query(tree, query))
print(result["answer"])
print(f"Nodes used: {result['retrieved_nodes']}")
Бонус: интеграция с MCP
На этапе интеграции MCP в рамках бонуса необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед внесением изменений в код. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рассматривайте этот этап как контракт между входными данными и проверенными выходными результатами. Укажите названия элементов, определите критерии успеха и не допускайте молчаливого частичного завершения работы. Цитируйте те фрагменты, которые фактически легли в основу ответа. Без цитат операторы не смогут отличить галлюцинации от пробелов в индексации. На этапе интеграции MCP в рамках бонуса необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед внесением изменений в код. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, которое операторы могут проверять, не читая весь кодовый граф.
{
"mcpServers": {
"pageindex": {
"type": "http",
"url": "https://api.pageindex.ai/mcp",
"headers": {
"Authorization": "Bearer your_api_key"
}
}
}
}
{
"mcpServers": {
"pageindex": {
"command": "npx",
"args": ["-y", "@pageindex/mcp"]
}
}
}
Цифры критериев оценки (в контексте)
При работе над этапом «Цифры критериев оценки в контексте» сначала запишите условия контракта: необходимые входные данные, сигнал успешного выполнения и действия при частичной неудаче. Такой список поможет сохранять честность при последующих изменениях кода. Документируйте одновременно успешный сценарий работы и сценарий восстановления. Повторные попытки, проверки человеком и обработка неработоспособных сообщений являются частью продукта, а не элементами последующей доработки. Измеряйте уровень воспроизведения ответов на фиксированном наборе вопросов перед настройкой подсказок. Частая смена подсказок редко помогает улучшить качество поиска.
В чём проявляются недостатки PageIndex (и действительно проявляются)
При работе над этапом «Где не хватает PageIndex» сначала запишите условия работы: требуемые входные данные, сигнал о успешном выполнении и что происходит при частичной неудаче. Такой список поможет сохранять честность при последующих изменениях кода. Лучше использовать небольшие, тестируемые модули вместо обширных скриптов. Когда какой-то шаг терпит неудачу, она должна указывать на конкретную ответственность, а не на запутанную цепочку операций. Оцените точность воспроизведения ответов на фиксированном наборе вопросов перед настройкой подсказок. Частая смена подсказок редко помогает улучшить качество поиска.
Так когда же стоит использовать это?
При работе над этапом «So When Should You» сначала запишите условия соглашения: необходимые входные данные, сигнал успешного выполнения и действия при частичной неудаче. Такой чек-лист помогает сохранять честность при последующих изменениях кода. Рассматривайте этот этап как соглашение между входными данными и проверенными выходными результатами. Дайте названия создаваемым элементам, определите критерии успешности и не допускайте безответственного частичного выполнения задач. Оцените уровень воспроизводимости на фиксированном наборе вопросов перед настройкой подсказок. Частая смена подсказок редко помогает улучшить качество поиска. При работе над этапом «So When Should You» сначала запишите условия соглашения: необходимые входные данные, сигнал успешного выполнения и действия при частичной неудаче. Такой чек-лист помогает сохранять честность при последующих изменениях кода. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, которое операторы могут проверять без необходимости просматривать весь код.
Что произошло с момента запуска (последние разработки)
Этап «Что произошло» работает наилучшим образом, если рассматривать его как измеримую поверхность. Соберите один идеальный пример работы, один случай сбоя и запись о возврате к предыдущему состоянию перед расширением объема работ. Документируйте одновременно успешный сценарий выполнения и сценарий восстановления. Повторные попытки, проверки человеком и обработка неработающих сообщений являются частью продукта, а не этапом последующей доработки. Разделяйте политику разбиения на части и политику извлечения данных. Изменение одной из них не должно приводить к переписыванию другой при изменении показателей качества.
pip install openai-agents
python3 examples/agentic_vectorless_rag_demo.py
Более широкая картина
Модель «Большая картина» работает наилучшим образом, если рассматривать её как измеримую поверхность. Соберите один идеальный пример выполнения, один случай сбоя и записку о возврате к предыдущему состоянию перед расширением объёма работы. Предпочитайте небольшие, тестируемые единицы вместо обширных скриптов. Когда какой-то шаг сбивается, причина сбоя должна указывать на конкретную ответственность, а не на запутанную цепочку операций. Разделяйте политику разбиения на части и политику извлечения данных. Изменение одной из них не должно приводить к переписыванию другой при изменении показателей качества.
Начало работы
Этап «Начало работы» работает наилучшим образом, когда его рассматривают как измеримую основу. Соберите один идеальный пример работы, один случай сбоя и запись о возврате к предыдущему состоянию перед расширением объёма работ. Рассматривайте этот этап как контракт между входными данными и проверенными результатами. Дайте названия создаваемым элементам, определите критерии успеха и не соглашайтесь на молчаливое частичное выполнение задач. Разделяйте политику разбиения на части и политику извлечения данных. Изменение одной из них не должно приводить к переписыванию другой при изменении показателей качества. Этап «Начало работы» работает наилучшим образом, когда его рассматривают как измеримую основу. Соберите один идеальный пример работы, один случай сбоя и запись о возврате к предыдущему состоянию перед расширением объёма работ. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, которое операторы могут проверять, не читая весь код.
Чек-лист операционной деятельности
При работе над этапом операционного чек-листа сначала запишите условия контракта: необходимые входные данные, сигнал успешного выполнения и действия при частичной неудаче. Такой чек-лист помогает сохранять честность при последующих изменениях кода.
Рядом с функциональными результатами записывайте временные показатели и стоимость токенов или запросов. Отслеживание затрат на раннем этапе предотвращает неожиданные счета при переходе от демо-среды к общедоступным средам.
Оцените точность воспроизведения ответов на фиксированный набор вопросов перед настройкой подсказок. Изменение подсказок редко помогает улучшить качество поиска.
Зафиксируйте версии зависимостей и сохраните хэш изображения, использованного для демонстрации. Воспроизводимость важнее устного опыта команды.
Храните конфигурацию отдельно от кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, которое операторы могут проверять, не читая весь кодовый граф.
Оцените уровень воспроизведения ответов на фиксированном наборе вопросов перед настройкой подсказок. Частая смена подсказок редко помогает улучшить качество поиска информации.
Перед внедрением обновлений заморозьте существующие версии, сохраните эталонный вариант транскрипта для критически важных этапов и убедитесь, что предусмотрены шаги для возврата к прежнему состоянию. В совместных средах необходимы ограничения на частоту запросов, проверки прав доступа и четко определенный ответственный за обновление секретов. Лучше добиваться простой надежности, чем создавать креативные, но единоразовые демонстрации.
Примечание для d194e0549478: не храните ключи поставщиков в репозитории, установите лимит токенов на каждую сессию и сохраняйте транскрипты рядом с элементами для оценки, чтобы последующие замены моделей оставались сопоставимыми.
При работе над этапом 0 записки по укреплению безопасности сначала запишите условия работы: необходимые входные данные, сигнал о успешном выполнении и действия при частичной неудаче. Такой чек-лист поможет сохранять честность при последующих изменениях кода. Лучше использовать небольшие, тестируемые модули вместо обширных скриптов. Если какой-то шаг не сработает, причина неудачи должна указывать на конкретную ответственность, а не на запутанную цепочку операций.
Деталь укрепления безопасности 0/751: измерьте время выполнения, класс ошибки и расход токенов для этой записки, затем решите, следует ли сохранять изменение, опираясь на заранее определенный набор критериев, а не на устные оценки.
Этап 1 записки по укреплению безопасности работает лучше всего, когда его рассматривают как измеримую область. Соберите один идеальный пример работы, один случай сбоя и записку о возврате к предыдущему состоянию перед расширением объема работ. Записывайте временные показатели и стоимость токенов или запросов рядом с функциональными результатами. Отслеживание затрат на раннем этапе предотвращает неожиданные расходы при переходе с демо-среды в общедоступные среды.
Подробности усиления безопасности 1/751: измерьте время работы стены, класс ошибки и расход токенов для этой записи, затем решите, следует ли сохранять изменения на основе фиксированного набора вопросов, а не на основе единичных примеров.