English translation is not available yet. Showing Russian content.

Как вы обрабатываете большие таблицы в RAG (500+ строк)?

Краткий тезис

Обработка больших таблиц в RAG требует специальных стратегий, поскольку таблицы не помещаются в контекстное окно LLM и плохо поддаются обычному векторному поиску. Основные подходы: (1) сжатие/суммаризация таблицы через LLM, (2) retrieval|построчный поиск (retrieval|row‑based retrieval), (3) использование структурированных форматов (Markdown, HTML) вместо text|plain text. Выбор зависит от задачи: суммаризация хороша для общих вопросов, row‑based – для точных данных, а гибридные схемы дают лучший баланс.


1. Термин: Большие таблицы в RAG

Большие таблицы (500+ строк) – это структурированные данные, где каждая строка – запись с набором полей (колонок). В RAG‑системе такие таблицы традиционно представляют как один документ, но из‑за размера их нельзя целиком передать в LLM|контекст LLM. Кроме того, стандартные эмбеддинги (текстовые) плохо кодируют числовые и категориальные соотношения внутри таблицы.

Проблема усугубляется, если запрос требует выбора конкретных строк (например, «все продажи > 1000 в январе») – поиск по эмбеддингам всего документа даёт низкую точность.

2. Основные проблемы с большими таблицами

ПроблемаОписаниеСледствие
Контекстное окно500 строк могут занимать 20–50 тыс. токенов (зависит от формата)Не помещаются даже в GPT‑4 Turbo; суммаризация неизбежна
Потеря точностиВекторные поиски по целой таблице находят её целиком, а не нужные строкиLLM получает шум, ответ может быть неверным
ФорматированиеPlain text теряет структуру колонок и связейLLM путается в числах, датах, отношениях
МасштабированиеПоиск по сотням таблиц с одинаковой схемойНужно отличать одну таблицу от другой, а внутри – строки

3. Стратегия 1: Сжатие/суммаризация через LLM

Идея: перед индексацией или на этапе retrieval передать всю таблицу LLM (например, GPT‑4) и попросить сгенерировать краткое текстовое описание – суммаризацию. Затем индексировать не саму таблицу, а этот текст.

Пример промпта:

У тебя есть таблица продаж (500 строк, колонки: Дата, Товар, Сумма, Регион). 
Составь краткое резюме: основные тенденции, средние суммы, топ-3 региона по продажам, выбросы. 
Не выдумывай числа – только из таблицы.

Плюсы:

  • Сильно уменьшает количество токенов (10–20 предложений вместо 500 строк)
  • LLM умеет выделять смысловые паттерны, которые сложно извлечь простой агрегацией

Минусы:

  • Потеря деталей – нельзя ответить на запрос «сколько продано товара X 15 марта?», если эта строка не попала в суммаризацию
  • Зависимость от качества LLM: галлюцинации, пропуск важных аномалий
  • Дорого на этапе индексации (один call|вызов LLM на таблицу)

Когда использовать:

  • Пользователь спрашивает общие тенденции («как изменились продажи за месяц?»)
  • Таблица меняется редко, можно «разово» потратить ресурс на суммаризацию

4. Стратегия 2: Row‑based retrieval (построчный поиск)

Идея: разбить таблицу на отдельные строки и индексировать каждую строку как отдельный «чанк». При этом к строке добавляется контекст – название таблицы, имена колонок, метаданные (например, дата, источник).

Пример чанка:

Таблица: Продажи 2024
Колонки: Дата, Товар, Сумма, Регион
Строка: 2024-03-15, Холодильник, 45000, Москва

Плюсы:

  • Высокая точность: запрос «продажи холодильников в марте» найдёт именно те строки, где товар = холодильник и дата ~ март
  • Возможность фильтрации по полям до поиска (hybrid search с метаданными)
  • Масштабируется на миллионы строк (каждая строка – отдельный вектор)

Минусы:

  • Потеря табличной целостности: если запрос требует агрегат («средняя сумма по всем продажам»), LLM придётся получать много строк и считать самой (неэффективно)
  • Увеличение количества чанков (500 строк = 500 чанков вместо 1)
  • Нужно поддерживать связь «строка → таблица» для контекста

Техническая реализация (Python‑псевдокод):

import pandas as pd
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings

df = pd.read_csv("sales.csv")
rows = []
for _, row in df.iterrows():
    text = f"Таблица: Продажи\nКолонки: Дата, Товар, Сумма, Регион\nСтрока: {row['Дата']}, {row['Товар']}, {row['Сумма']}, {row['Регион']}"
    rows.append(text)

embedder = OpenAIEmbeddings()
# Далее индексируем каждый 'text' как отдельный документ в векторной БД

5. Стратегия 3: Использование Markdown/HTML форматов

Идея: при индексации и передаче в контекст LLM использовать структурированные форматы (Markdown с таблицами, HTML <table>), а не простой текст. Это сохраняет визуальную структуру.

  • Markdown:
    | Дата       | Товар       | Сумма | Регион |
    |------------|-------------|-------|--------|
    | 2024-03-[[15. Какие embedding-модели вы использовали и почему\|15]] | Холодильник | 45000 | Москва |
    
  • HTML:
    <table>
      <tr><th>Дата</th><th>Товар</th>...</tr>
      <tr><td>2024-03-15</td><td>Холодильник</td><td>45000</td><td>Москва</td></tr>
    </table>
    

Плюсы:

  • LLM (особенно GPT‑4, Claude) лучше понимают Markdown/HTML, чем простой текст с разделителями (пробелы, табуляции, запятые)
  • Улучшается извлечение колонок и фильтрация (LLM может «видеть» заголовки столбцов)

Минусы:

  • Проблема с размером: Markdown/HTML «раздувают» количество токенов (много повторяющихся символов |, -)
  • Некоторые модели (например, в API OpenAI) имеют bias к HTML – могут «выдавать» разметку в ответе

Рекомендация: использовать Markdown как компромисс (меньше токенов, чем HTML, но структура читаема). Если таблица всё ещё большая – комбинировать с row‑based retrieval (каждую строку в Markdown).

6. Гибридный подход

На практике лучшие результаты даёт комбинация всех трёх стратегий:

  1. Суммаризация таблицы (глобальный контекст) – для ответов на общие вопросы.
  2. Row‑based retrieval (локальный поиск) – для точных запросов.
  3. Markdown‑формат – для обеих веток, чтобы LLM корректно интерпретировала данные.

Пример пайплайна:

  • При индексации: для каждой таблицы генерируется два типа чанков:
    • Один «суммирующий» чанк (текстовое описание).
    • N чанков по строкам (каждая в Markdown).
  • При запросе:
    • Параллельно запускается retrieval по обоим типам чанков.
    • После ранжирования отбираются top‑k чанков (например, 2 суммаризации и 5 строк).
    • Контекст собирается в порядке: сначала строки (детали), потом суммаризация (общий контекст).
    • LLM получает инструкцию: «Если вопрос требует агрегата – используй суммаризацию; если точную строку – используй данные из строк».

Оценка: метрики retrieval считаются отдельно для sum‑чанков и row‑чанков (hit rate, MRR). Итоговое качество ответа оценивается через RAGAS (faithfulness, answer relevance).

7. Инструменты и библиотеки

ИнструментРольПример использования
PandasЧтение, фильтрация, предобработка таблицdf = pd.read_csv('sales.csv'); df.info()
UnstructuredРазбор сложных таблиц (PDF, Excel) в Markdownfrom unstructured.partition.html import partition_html
LangChainИнтеграция с LLM, цепочки sum‑retrievalChain с RetrievalQA и StuffDocumentsChain
LlamaIndexГотовые парсеры таблиц, TableRetrieverQueryEnginefrom llama_index.core.indices import SQLStructStoreIndex
Pinecone / QdrantВекторные БД с поддержкой фильтрации по полям (metadata)Фильтр {"field": "table_name", "eq": "Продажи_2024"}

8. Оценка качества и метрики

Для больших таблиц стандартные метрики retrieval (hit rate, MRR) не всегда адекватны, потому что:

  • Запрос может требовать как агрегат (суммаризация), так и конкретную строку.
  • Если retrieval вернул 10 строк, а нужно только 2 – это успех, если агрегат – может быть избыточно.

Предлагаемая система оценки:

МетрикаКак считатьЕсли значение низкое
Row‑hit rateДоля запросов, где хотя бы одна нужная строка попала в top‑10Проблемы с эмбеддингами строк или метаданными
Row‑recallДоля действительно нужных строк в top‑kСлишком маленький k или плохая индексация
Sum‑hit rateДоля запросов, где суммаризация релевантна (оценка LLM‑as‑judge)Плохое качество суммаризации
Faithfulness (RAGAS)Доля утверждений в ответе, подтверждённых контекстомКонтекст содержит строки, не относящиеся к запросу
Answer relevance (RAGAS)Оценка, что ответ прямо отвечает на вопросLLM игнорирует табличные данные

9. Рекомендации по выбору стратегии

  • Если таблицы статичны (< 1 тыс. изменений в день) → делайте суммаризацию + row‑based + Markdown. Индексация дорогая, но разовая.
  • Если таблицы динамические (например, log‑файлы) → только row‑based, без суммаризации (обновлять суммаризацию каждый раз дорого). Используйте фильтры по дате.
  • Если запросы только агрегатные → можно обойтись суммаризацией (но проверьте, что она покрывает 100% нужных агрегатов).
  • Если запросы смешанные (как в 80% реальных кейсов) → гибридный подход обязателен.

Дополнительно: для таблиц с численными колонками рассматривайте табличные эмбеддинги (например, TAPAS, BART‑based модели для таблиц) – они кодируют не только текст, но и числа, что улучшает retrieval по числовым запросам.


Пет-проект для закрепления

Задача: Создать RAG‑систему для базы данных «Продажи интернет-магазина»: 3 таблицы по 1000 строк, колонки – date, product, revenue, city, manager. Реализовать гибридный подход: суммаризация каждой таблицы + row‑based retrieval.

Инструменты:

Шаги:

  1. Загрузить три CSV в Pandas.
  2. Для каждой таблицы сгенерировать через GPT‑4o краткую суммаризацию (3‑5 предложений) – сохранить как отдельные документы.
  3. Разбить все строки на чанки (каждая строка + название таблицы + колонки в Markdown).
  4. Индексировать суммаризации и строки в одной Qdrant коллекции, с метаданными type: "summary" или type: "row".
  5. Создать chain:
    • Запрос → retrieval top‑k (5 документов), из них 1 summary (если есть) + 4 строки.
    • Контекст: строки + summary.
    • Prompt: «Если вопрос требует общих тенденций – смотри summary; если точные данные – смотри строки. Ответь на русском.»
  6. Протестировать на 10 вопросах разного типа.

Ожидаемый результат:

  • Система корректно отвечает на «Какова средняя выручка за март?» (используя summary).
  • Отвечает на «Сколько заработал менеджер Иванов за последний квартал?» (вытягивая строки).
  • Векторный поиск по строкам находит именно строки с Ивановым, а не всю таблицу.

Связь с другими вопросами

ВопросТема
3Как выбирать стратегию chunking для структурированных данных
5Оценка качества retrieval (hit rate, MRR)
17Обработка структурированных документов (CSV, JSON) в RAG
20Мультимодальный RAG: картинки, таблицы, графики
55Как индексировать числовые данные и искать по диапазонам
114(предыдущий вопрос части 7 – например, «Как обрабатывать PDF с таблицами»)

Навигация