Практические замечания: Я перестал использовать векторные базы данных для RAG : PageIndex
Пошаговое руководство по практическим заметкам: я перестал использовать векторные базы данных для RAG : PageIndex: контракты, проверки и готовые блоки кода для команд, внедряющих эту схему.
В следующих примечаниях описывается практический подход к реализации концепции «Я перестал использовать векторные базы данных для RAG: PageIndex Vectorless RAG». Основное внимание уделяется контрактам, проверкам и местам для вставки кода, а не мотивирующим формулировкам. На этапе обзора сначала запишите контракт: необходимые входные данные, сигнал успешного выполнения и действия при частичной неудаче. Такой список поможет сохранять честность при последующих изменениях кода. Документируйте как успешный, так и восстановительный пути работы. Повторные попытки, проверки человеком и обработка ошибок являются частью продукта, а не этапом доработки позже.
Что вообще такое PageIndex?
Этап What Even Is PageIndex работает наилучшим образом, если рассматривать его как измеримую сферу. Соберите один идеальный пример работы, один случай сбоя и записку о возврате к предыдущему состоянию перед расширением объёма работ. Предпочитайте небольшие, тестируемые единицы кода вместо обширных скриптов. Когда какой-то шаг терпит неудачу, причина сбоя должна указывать на конкретную ответственность, а не на запутанную цепочку операций. Разделяйте политику разбиения на части и политику извлечения данных. Изменение одной из них не должно приводить к переписыванию другой при изменении показателей качества.
Проблемы с Vector RAG (о которых недостаточно говорят)
Проблема этапа Vector работает наилучшим образом, когда его рассматривают как измеримую поверхность. Соберите один идеальный пример, один случай сбоя и запись о возврате к предыдущему состоянию перед расширением объёма работ. Рассматривайте этот этап как контракт между входными данными и проверенными выходными результатами. Дайте названия создаваемым элементам, определите критерии успеха и не соглашайтесь на молчаливое частичное выполнение задачи. Разделяйте политику разбиения на части и политику извлечения данных. Изменение одной из них не должно приводить к переписыванию другой при изменении показателей качества.
Введите PageIndex — извлечение на основе логических рассуждений
Метод Enter PageIndex- Retrieval by stage работает наилучшим образом, когда его рассматривают как измеримую систему. Соберите один идеальный пример работы, один случай сбоя и запись о возврате к предыдущему состоянию перед расширением объема работ. Записывайте время выполнения операций, а также стоимость токенов или запросов рядом с функциональными результатами. Отслеживание затрат на раннем этапе предотвращает неожиданные счета при переходе от демо-среды к общедоступным средам. Установите лимиты на количество токенов за одну попытку и за сессию. Инструменты типа агентов активно расширяют объем обрабатываемой информации; жесткие ограничения не позволяют демо-версиям превращаться в неожиданные счета. Метод Enter PageIndex- Retrieval by stage работает наилучшим образом, когда его рассматривают как измеримую систему. Соберите один идеальный пример работы, один случай сбоя и запись о возврате к предыдущему состоянию перед расширением объема работ. Документируйте как успешный, так и восстановительный пути работы. Повторные попытки, проверки человеком и обработка неработающих сообщений являются частью продукта, а не элементами последующей доработки.
Как это на самом деле работает
На этапе «Как это на самом деле работает» необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Лучше использовать небольшие, тестируемые единицы кода вместо обширных скриптов. При сбое шага причина должна быть связана с конкретной функцией, а не с запутанной структурой обработки. Указывайте те фрагменты текста, на которых основан ответ. Без цитат операторы не смогут отличить вымысел от проблем с индексацией.
Annual Report 2023
├── Business Overview
│ ├── Products and Services
│ └── Market Position
├── Risk Factors
│ ├── Financial Risks
│ └── Operational Risks
├── Financial Statements
│ ├── Balance Sheet
│ │ ├── Assets
│ │ └── Liabilities
│ └── Income Statement
└── Notes to Financial Statements
├── Note 1: Accounting Policies
└── Note 12: Long-term Debt
Архитектура PageIndex
Для этапа архитектуры PageIndex необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рассматривайте этот этап как контракт между входными данными и проверенными выходными результатами. Укажите названия результатов работы, определите критерии успеха и не допускайте молчаливого частичного завершения задачи. Цитируйте те фрагменты, которые фактически легли в основу ответа. Без цитат операторы не смогут отличить галлюцинации от пробелов в индексации.
Давайте писать код
На этапе Let s Code необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рядом с функциональными результатами следует записывать время выполнения и стоимость токенов или запросов. Отображение стоимости заранее предотвращает неожиданные счета при переходе от демо-среды к общедоступным средам. Указывайте те фрагменты текста, которые легли в основу ответа. Без цитат операторы не смогут отличить галлюцинации от пробелов в индексации.
Шаг 1: Анализ документа
На этапе 1 необходимо проанализировать структуру, определить входные данные, ответственного за выполнение шага и критерии завершения перед внесением изменений в код. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Конфигурацию следует хранить отдельно от кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь граф. Указывайте те участки текста, которые фактически легли в основу ответа. Без цитат операторы не смогут отличить галлюцинации от пробелов в индексации.
import fitz # pip install pymupdf
def parse_pdf(pdf_path: str) -> list[dict]:
doc = fitz.open(pdf_path)
pages = []
for i, page in enumerate(doc):
text = page.get_text().strip()
if text:
pages.append({"page_num": i + 1, "text": text})
doc.close()
return pages
def group_pages_into_sections(pages, per_section=3):
sections = []
for i in range(0, len(pages), per_section):
batch = pages[i : i + per_section]
section_id = f"S{str(i // per_section + 1).zfill(3)}"
combined_text = "\n\n".join(p["text"] for p in batch)
sections.append({
"section_id": section_id,
"start_page": batch[0]["page_num"],
"end_page": batch[-1]["page_num"],
"text": combined_text,
})
return sections
Шаг 2: Создание индекса дерева (с использованием LLM)
На втором этапе создания этапа необходимо определить входные данные, ответственного за выполнение этапа и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить этап с известной точки контроля, не догадываясь о скрытом состоянии. Необходимо задокументировать как успешный, так и восстановительный сценарии работы. Повторные попытки, проверки человеком и обработка неработоспособных сообщений являются частью продукта, а не элементами последующей доработки. При следующем этапе, представляющем собой написание кода или вызов инструмента, следует предпочитать структурированные выводы с проверкой по шаблону вместо свободного текста.
from google import genai
from google.genai import types
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
def index_section(section: dict) -> dict:
preview = section["text"][:1500]
prompt = f"""Read this section from a document and summarize it.
Section pages: {section['start_page']} to {section['end_page']}
Text:
{preview}
Respond with ONLY valid JSON:
{{
"title": "short descriptive title (5-8 words)",
"summary": "2-3 sentence summary of what this section covers",
"key_topics": ["topic1", "topic2", "topic3"]
}}"""
response = client.models.generate_content(
model="gemini-2.0-flash",
contents=prompt,
config=types.GenerateContentConfig(temperature=0.0),
)
parsed = json.loads(response.text.strip())
return {
"node_id": section["section_id"],
"title": parsed["title"],
"pages": f"{section['start_page']}-{section['end_page']}",
"summary": parsed["summary"],
"key_topics": parsed["key_topics"],
}
def build_tree_index(sections):
nodes = [index_section(s) for s in sections]
return {
"title": "Your Document Title",
"total_sections": len(nodes),
"nodes": nodes,
}
# Save for reuse
with open("tree.json", "w") as f:
json.dump(tree, f, indent=2)
Этап 3: Поиск в дереве (мышление, а не сходство)
На этапе поиска в дереве шага 3 необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг, исходя из известной точки контроля, без необходимости угадывать скрытое состояние. Лучше использовать небольшие, тестируемые единицы кода вместо обширных скриптов. При сбое шага он должен указывать на конкретную причину, а не на сложную взаимосвязь между компонентами. При следующем шаге, представляющем собой код или вызов инструмента, целесообразно использовать структурированные выходные данные с проверкой соответствия шаблону вместо свободного текста.
def retrieve_sections(tree: dict, query: str) -> dict:
# Build a compact text representation of the tree
tree_text = f"Document: {tree['title']}\n\n"
for node in tree["nodes"]:
tree_text += f"[{node['node_id']}] Pages {node['pages']} | {node['title']}\n"
tree_text += f" Summary: {node['summary']}\n"
tree_text += f" Topics: {', '.join(node['key_topics'])}\n\n"
prompt = f"""You are a document retrieval expert.
Given this document tree, identify which sections most likely answer the question.
Think step by step about where a human expert would look.
{tree_text}
QUESTION: {query}
Respond with ONLY valid JSON:
{{
"reasoning": "your step-by-step reasoning about where to look",
"selected_ids": ["S001", "S004"],
"confidence": "high/medium/low"
}}"""
response = client.models.generate_content(
model="gemini-2.0-flash",
contents=prompt,
config=types.GenerateContentConfig(temperature=0.0),
)
return json.loads(response.text.strip())
Шаг 4: Получение контента
На этапе извлечения контента шага 4 необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед внесением изменений в код. Операторы должны иметь возможность перезапустить шаг, исходя из известной точки контроля, без необходимости угадывания скрытого состояния. Рассматривайте этот этап как контракт между входными данными и проверенными результатами. Укажите названия элементов, определите критерии успеха и не допускайте молчаливого частичного завершения работы. Цитируйте те фрагменты, которые действительно легли в основу ответа. Без цитат операторы не смогут отличить галлюцинации от пробелов в индексации.
def retrieve_content(selected_ids: list, sections: list) -> str:
section_map = {s["section_id"]: s for s in sections}
context_parts = []
for sid in selected_ids:
if sid in section_map:
sec = section_map[sid]
context_parts.append(
f"--- Pages {sec['start_page']}-{sec['end_page']} ---\n"
+ sec["text"][:3000]
)
return "\n\n".join(context_parts)
Шаг 5: Генерация ответа
На этапе генерации ответов на шаге 5 необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Записывайте время выполнения и стоимость токенов или запросов рядом с функциональными результатами. Отображение стоимости заранее предотвращает неожиданные счета при переходе с демо-среды в общедоступные среды. Указывайте конкретные фрагменты текста, на которых основан ответ. Без цитат операторы не смогут отличить галлюцинации от пробелов в индексации. На этапе генерации ответов на шаге 5 необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Документируйте как успешный, так и восстановительный пути выполнения. Повторные попытки, проверки человеком и обработка неработающих сообщений являются частью продукта, а не последующими улучшениями.
def generate_answer(query: str, context: str) -> str:
prompt = f"""Answer the question using only the provided context.
Be specific. Include exact numbers, technical terms, and cite page numbers.CONTEXT:
{context}
QUESTION: {query}
ANSWER:"""
response = client.models.generate_content(
model="gemini-2.0-flash",
contents=prompt,
config=types.GenerateContentConfig(temperature=0.1),
)
return response.text.strip()
PageIndex против традиционного векторного RAG
При работе над этапом сравнения PageIndex и традиционного векторного подхода сначала запишите условия работы: необходимые входные данные, сигнал о успешном выполнении и последствия частичной неудачи. Такой список поможет избежать ошибок при последующих изменениях кода. Лучше использовать небольшие, тестируемые модули вместо обширных скриптов. Если какой-то шаг не сработает, проблема должна быть связана с конкретной функцией, а не с запутанной цепочкой операций. Оцените уровень воспроизведения информации на фиксированном наборе вопросов перед настройкой подсказок. Частая смена подсказок редко помогает улучшить качество поиска.
Когда следует использовать PageIndex?
При работе над этапом «Когда следует использовать» сначала запишите условия контракта: необходимые входные данные, сигнал успешного выполнения и действия при частичной неудаче. Такой чек-лист поможет сохранять честность при последующих изменениях кода. Рассматривайте этот этап как контракт между входными данными и проверенными выходными результатами. Дайте названия элементам, определите критерии успеха и не допускайте молчаливого частичного выполнения задачи. Оцените уровень воспроизводимости на фиксированном наборе вопросов перед настройкой подсказок. Частая смена подсказок редко помогает улучшить качество поиска.
Заключение
На этапе «Заключительная мысль» сначала запишите условия работы системы: необходимые входные данные, сигнал о успешном выполнении и действия при частичной неудаче. Такой список помогает сохранять честность при последующих изменениях кода. Рядом с результатами работы запишите время выполнения и стоимость использования токенов или запросов. Отображение затрат с самого начала предотвращает неожиданные счета при переходе от демо-среды к общедоступным средам. Перед настройкой подсказок измерьте точность воспроизведения ответов на фиксированный набор вопросов. Частая смена подсказок редко помогает улучшить качество поиска. На этапе «Заключительная мысль» сначала запишите условия работы системы: необходимые входные данные, сигнал о успешном выполнении и действия при частичной неудаче. Такой список помогает сохранять честность при последующих изменениях кода. Одновременно задокументируйте успешный сценарий работы и сценарий восстановления. Повторные попытки, проверка человеком и обработка неработающих сообщений являются частью продукта, а не элементами последующей доработки.
Начало работы
Этап «Начало работы» работает наилучшим образом, если рассматривать его как измеримую основу. Соберите один идеальный пример работы, один случай сбоя и запись о возврате к предыдущему состоянию перед расширением объема работ. Предпочитайте небольшие, тестируемые единицы кода вместо обширных скриптов. Когда какой-то шаг сбивается, причина сбоя должна указывать на конкретную ответственность, а не на запутанную цепочку операций. Разделяйте политику разбиения на части и политику извлечения данных. Изменение одной из них не должно вынуждать переписывать другую при изменении показателей качества.
Чек-лист операций
На этапе чек-листа операций определите входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность повторно выполнить шаг, исходя из известной точки контроля, без необходимости угадывать скрытое состояние системы.
Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, которое операторы могут проверять, не читая весь кодовый граф.
Укажите те фрагменты текста, которые действительно легли в основу ответа. Без цитат операторы не смогут отличить галлюцинации от пробелов в индексации.
Напишите краткий руководство: как обновлять ключи, как опустошать очередь, как откатывать последнюю загрузку данных.
Задокументируйте как успешный, так и восстановительный сценарии работы. Повторные попытки, проверки человеком и обработка неработающих сообщений являются частью продукта, а не последующими улучшениями.
Укажите те фрагменты текста, которые действительно легли в основу ответа. Без цитат операторы не смогут отличить галлюцинации от пробелов в индексации.
Перед повышением версии стека заморозьте существующие версии, сохраните эталонный отчет для критического сценария работы и убедитесь в правильности шагов отката. В совместных средах необходимы ограничения на частоту запросов, проверки принадлежности ресурсов и четко определенный ответственный за обновление секретов. Лучше предпочесть простую надежность сложным одноразовым демонстрациям.
Примечание к пакету e54dedbe364e: не храните ключи поставщиков в репозитории, установите лимит токенов на сессию и сохраняйте транскрипции рядом с фиксами для оценки, чтобы последующие замены моделей оставались сопоставимыми.