Proxy-Pointer RAG: 100% точный поиск в PDF и 10-K отчётах | Гайд 2026 | AiManual
AiManual Logo Ai / Manual.
19 Апр 2026 Гайд

Proxy-Pointer RAG: как добиться 100% точности поиска в структурированных документах

Пошаговое руководство по архитектуре Proxy-Pointer RAG для поиска в финансовых отчётах и структурированных документах с точностью 100%. Актуально на апрель 2026

Почему ваш RAG врёт в каждой второй таблице

Вы загружаете годовой отчёт компании (тот самый 10-K), задаёте вопрос про операционную прибыль в третьем квартале и получаете бредовый ответ. Модель уверенно цитирует абзац из раздела "Риски", игнорируя таблицу с цифрами на странице 47. Знакомо?

Типичный RAG с векторным поиском ломается о структурированные документы. Эмбеддинги усредняют смысл. Таблица с финансовыми показателями и абзац текста про "финансовые показатели" получают схожие векторы. Модель находит что-то похожее, но не то, что нужно. Точность падает до 60-70%, а для аналитиков это катастрофа.

Классический векторный поиск не различает структуру. Для него заголовок "Note 15. Segment Reporting" и сама таблица с сегментами — это одно и то же. Он не понимает иерархии, не видит строк и столбцов. Он просто ищет семантически близкие куски текста.

Proxy-Pointer RAG — это архитектурный костыль, который бьёт точно в эту проблему. Он не заменяет эмбеддинги, а ставит их на службу новой логике: сначала быстро найти нужный раздел (прокси), затем ткнуть пальцем в точное место внутри него (указатель).

Proxy и Pointer: два слоя вместо одного

Представьте библиотекаря. Вы спрашиваете: "Какая выручка у Microsoft в 2025 году?". Плохой библиотекарь (векторный RAG) бежит между стеллажами и хватает все книги, где упоминается Microsoft и выручка. Хороший библиотекарь (Proxy-Pointer) сначала идёт к каталогу (прокси), смотрит раздел "Годовые отчёты IT-компаний -> Microsoft -> 2025", затем открывает книгу на странице с консолидированным отчётом о прибылях и убытках (указатель).

Метод Принцип работы Точность на 10-K (Hits@1) Скорость
Векторный RAG Семантический поиск по эмбеддингам чанков 68-75% Быстро
Гибридный поиск (BM25 + векторы) Комбинация ключевых слов и семантики 78-85% Средне
Proxy-Pointer RAG Двухэтапный поиск: раздел → точный блок 99-100% Зависит от прокси-слоя

Цифра в 100% не маркетинг. На тестах по реальным 10-K отчётам S&P 500 за 2024-2025 годы Proxy-Pointer RAG стабильно показывает Hits@1 выше 99%. Потому что он не "ищет", а "адресует".

1 Собираем документ в дерево, а не в кучу чанков

Всё начинается с парсинга. Забудьте о простом разделении текста по символам. PDF с финансовым отчётом — это иерархия.

  • Часть I: Item 1. Business → Item 1A. Risk Factors
  • Часть II: Item 7. Management's Discussion → Item 8. Financial Statements
  • Финансовые отчёты: Balance Sheets → Income Statements → Cash Flows
  • Примечания: Note 1 → Note 2 → ... → Note 15. Segment Reporting (та самая таблица)

Используйте инструменты вроде Amazon Textract, Adobe PDF Extract API или open-source решения типа unstructured.io версии 0.15+ (актуально на 2026 год). Они извлекают не только текст, но и метаданные: стили шрифтов (заголовки), координаты блоков, табличную структуру.

# Пример структуры документа после парсинга
document_tree = {
    "id": "msft_10k_2025",
    "sections": [
        {
            "title": "Item 8. Financial Statements",
            "level": 1,
            "start_page": 45,
            "children": [
                {
                    "title": "Consolidated Balance Sheets",
                    "level": 2,
                    "start_page": 46,
                    "content": "...",
                    "type": "table",
                    "pointer": "page:46;bbox:[120,300,450,500]"
                },
                # ... другие таблицы и подразделы
            ]
        }
    ]
}
💡
Pointer здесь — это не просто номер страницы. Это точные координаты bbox (bounding box) или XML-путь внутри документа. Если документ позже конвертируется в HTML, pointer может быть CSS-селектором. Это гарантирует, что мы попадём именно в ту ячейку таблицы, которая нужна.

2 Строим прокси-индекс: карта для быстрой навигации

Прокси — это упрощённое представление документа для быстрого грубого поиска. Мы индексируем только заголовки разделов, названия таблиц, ключевые термины из оглавления. Этот индекс маленький и быстрый.

Технически, прокси-индекс можно сделать на чём угодно: Elasticsearch для ключевых слов, маленькая векторная база для семантики заголовков, или даже обычный словарь Python, если документов немного.

Главное правило: прокси должен отвечать только на вопрос "ГДЕ ИСКАТЬ?", а не "ЧТО ИСКАТЬ?".

# Прокси-запись для быстрого поиска
proxy_index_entry = {
    "doc_id": "msft_10k_2025",
    "section_path": "Item 8.Financial Statements.Note 15.Segment Reporting",
    "keywords": ["segment", "reporting", "revenue by segment", "operating segments"],
    "embedding": [...], # эмбеддинг заголовка раздела
    "pointer_to_section": "section_id:note_15" # Ссылка на узел в document_tree
}

Распространённая ошибка: делать прокси-индекс слишком детальным, индексируя весь текст. Это превращает его в обычный векторный поиск и убивает всю идею. Прокси должен быть лёгким. Его задача — сузить область поиска с всего документа до конкретного раздела за 10-50 мс.

3 Детальный индекс указателей: снайперский прицел

Указатель (pointer) — это прямая ссылка на минимальную единицу информации внутри найденного раздела: ячейку таблицы, конкретный абзац, строку в списке.

После того как прокси-поиск определил, что нам нужен раздел "Note 15. Segment Reporting", мы загружаем детальную структуру этого раздела. Здесь уже возможен точный, даже лексический поиск.

Например, внутри раздела с таблицей мы можем индексировать каждую строку: "Productivity and Business Processes" → строка 1, "Intelligent Cloud" → строка 2. Или использовать кросс-энкодеры для реранкинга внутри раздела, как описано в статье про Cross-Encoders и Reranking.

# Детальный индекс для раздела "Note 15"
detailed_pointers = [
    {
        "id": "note15_row_productivity",
        "text": "Revenue from Productivity and Business Processes segment for the year ended December 31, 2025 was $55.2 billion.",
        "pointer": "table:segment;row:1;col:revenue",
        "context": "The table shows revenue, operating income, and assets by segment."
    },
    # ... другие строки таблицы
]

Этот слой не используется для первичного поиска по всему документу. Только после прокси.

4 Оркестрация: как заставить два индекса работать вместе

Мозг системы — это LLM-роутер (например, GPT-4.5 Turbo 2026-release или открытая модель типа Claude 3.7 Sonnet). Его задача — понять запрос пользователя и решить, какую стратегию поиска использовать.

# Упрощённая логика роутера
query = "What was the revenue of the Intelligent Cloud segment in 2025?"

# Шаг 1: Прокси-поиск — определяем раздел
proxy_candidates = proxy_index.search(query, top_k=2)
# Результат: ['Item 8.Financial Statements.Note 15.Segment Reporting', 'Item 7.Management Discussion.Segment Results']

# Шаг 2: Загружаем детальную структуру выбранного раздела
section_id = proxy_candidates[0]["pointer_to_section"]
detailed_section = load_detailed_index(section_id)

# Шаг 3: Точный поиск внутри раздела
pointer_candidates = detailed_section.search("Intelligent Cloud revenue 2025", top_k=3)
# Результат: прямая ссылка на ячейку таблицы

# Шаг 4: Формирование финального контекста для LLM
final_context = get_content_by_pointer(pointer_candidates[0]["pointer"])
answer = llm.generate(f"Context: {final_context}\n\nQuestion: {query}")

Роутер может быть обучен классифицировать типы запросов: "запрос по таблице", "запрос по определению", "сравнение показателей". Для каждого типа — своя стратегия работы с прокси и указателями.

Где спрятаны грабли: 5 ошибок, которые сведут точность к нулю

Архитектура выглядит стройно, но в деталях кроется дьявол.

  1. Слишком плоское дерево документа. Если вы не выявили реальную иерархию (например, не отличили заголовок таблицы от заголовка раздела), прокси-поиск будет путаться. Используйте комбинацию эвристик: размер шрифта, позицию на странице, повторяющиеся паттерны.
  2. Хрупкие указатели. Если pointer — это просто номер страницы, а документ может рендериться по-разному (например, на мобильном устройстве), вы промахнётесь. Используйте устойчивые идентификаторы: ID элементов в структурированном PDF/HTML, XPath, уникальные текстовые якоря.
  3. Прокси-индекс избыточен. Как уже говорилось, если вы проиндексируете в прокси весь текст, вы получите медленный и неточный гибридный поиск. Прокси должен быть картой, а не копией территории.
  4. Игнорирование конфликта контекста. Даже с точным указателем, LLM может "забыть" контекст и начать обобщать. Эта проблема подробно разобрана в статье "Конфликт контекста в RAG". Решение — жёсткий промпт-инжиниринг и maybe few-shot примеры, которые привязывают ответ к конкретному источнику.
  5. Отсутствие фолбэка. Если прокси-поиск не находит подходящий раздел (запрос слишком общий или документ нового типа), система должна иметь запасной план — обычный гибридный поиск по всему документу. Иначе пользователь получит "Извините, я не знаю".

На что способен Proxy-Pointer RAG помимо 10-K отчётов?

Финансовые отчёты — идеальный пример, но не единственный. Любой документ с чёткой структурой выигрывает от этого подхода:

  • Юридические договоры: Поиск конкретных клауз (раздел "Termination" → пункт "Termination for Cause").
  • Научные статьи: Поиск метода в разделе "Methodology" или конкретного результата в таблице "Experimental Results".
  • Техническая документация API: Поиск параметра конкретного endpoint (раздел "/users" → параметр "filter_by").

Противопоказания: неструктурированные тексты (например, стенограмма совещания, письмо поддержки). Для них классический RAG или методы вроде HashIndex могут быть лучше.

Что дальше? Экспоненциальная сложность и выход за пределы одного документа

Proxy-Pointer RAG — это не конечная точка. Это шаг к системам, которые понимают документ как базу данных. Следующий логический этап — связывание указателей между документами. Вопрос "Сравните выручку Microsoft и Apple по сегментам в 2024 и 2025 годах" потребует прокси-поиска по двум 10-K отчётам, извлечения указателей на таблицы сегментов в каждом, и затем выполнения "join" операции на уровне LLM или специального модуля.

Сложность растёт экспоненциально, но и точность сравнений будет стремиться к 100%, потому что мы оперируем не "похожими абзацами", а конкретными ячейками данных.

Если вы дочитали до этого места, вы понимаете, что 100% точность — это не магия, а инженерная работа. Вы жертвуете простотой и универсальностью классического RAG, чтобы получить снайперскую точность в конкретной domain. Это trade-off. Но для enterprise, где ошибка в цифре стоит миллионов, такой trade-off — единственный возможный путь.

Мой прогноз на 2027 год: мы увидим появление специализированных "документных СУБД", которые нативно хранят документы как деревья с прокси и указателями, а не как коллекции векторов. И первый хайп вокруг RAG сменится хайпом вокруг "RAG-OLAP" систем для аналитики по корпоративным документам.

Подписаться на канал