English translation is not available yet. Showing Russian content.

Как вы делаете retrieval для структурированных данных (SQL, Knowledge Graph)?

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

Retrieval для структурированных данных принципиально отличается от векторного поиска по тексту: вместо эмбеддингов и косинусной близости используется перевод естественно-языкового запроса в формальный язык запросов (SQL для реляционных баз, SPARQL/Cypher для графов знаний). Ключевая задача — точное понимание схемы данных и генерация корректного запроса, результаты которого затем подаются LLM как контекст. Такой подход позволяет отвечать на точные фактологические вопросы, агрегации и запросы по связям, недоступные при поиске по чанкам.


1. Термин: Структурированные данные в контексте RAG

Структурированные данные — это данные, организованные по строгой схеме: таблицы с колонками и типами (SQL) или графы с узлами, рёбрами и свойствами (graph|Knowledge Graph). В отличие от неструктурированного текста, они требуют точного соответствия между запросом и схемой. Ошибка в имени таблицы или синтаксисе запроса делает результат бесполезным.

Зачем нужен отдельный подход

  • поиск|Векторный поиск по сериализованным таблицам (например, «превратить строку таблицы в текст») теряет структуру и связи.
  • LLM не может напрямую «выполнить» SQL — нужен промежуточный шаг генерации запроса.
  • Результаты структурированного retrieval — это не чанки, а строки, ячейки или пути графа, которые затем форматируются для LLM.

2. Text-to-SQL: общий пайплайн

Text-to-SQL — задача преобразования естественного языка в SQL-запрос. Пайплайн:

  1. Пользовательский запрос (NL): «Сколько заказов сделал клиент с ID 42 в марте 2024?»
  2. Формирование промпта: LLM получает схему БД (таблицы, колонки, типы, внешние ключи) + запрос + инструкцию.
  3. Генерация SQL: LLM выводит, например, SELECT COUNT(*) FROM orders WHERE customer_id = 42 AND order_date BETWEEN '2024-03-01' AND '2024-03-31'.
  4. Выполнение SQL на реальной БД (с проверкой безопасности).
  5. Форматирование результата: строки таблицы превращаются в текст или Markdown-таблицу.
  6. Передача контекста LLM для финального ответа.

Пример промпта (упрощённо):

Ты — SQL-генератор. Схема БД:
Таблица orders: id (INT), customer_id (INT), order_date (DATE), total (FLOAT)
Таблица customers: id (INT), name (VARCHAR)

Запрос пользователя: "Покажи общую сумму заказов для клиента 'Иван' за 2024 год."
Сгенерируй только SQL-запрос.

LLM ответит: SELECT SUM(o.total) FROM orders o JOIN customers c ON o.customer_id = c.id WHERE c.name = 'Иван' AND YEAR(o.order_date) = 2024.


3. Проблемы Text-to-SQL и их решения

ПроблемаОписаниеРешение
Неверная генерация SQLLLM путает имена таблиц/колонок, синтаксисFew-shot примеры, fine-tuning на датасетах (Spider, WikiSQL), проверка через парсер
SQL-инъекцииЗлонамеренный запрос в NL может сгенерировать опасный SQLИспользовать параметризованные запросы (не конкатенацию), ограничить права БД (только SELECT)
Сложные запросыМного JOIN, подзапросов, оконных функцийРазбивать на подзадачи (агентный подход), использовать chain-of-thought
Ошибки выполненияБД возвращает ошибку (несуществующая колонка, деление на ноль)Повторная генерация с сообщением об ошибке в промпте
Неоднозначность«клиенты из Москвы» — что значит «из Москвы»? (город в адресе или регион?)Уточняющий диалог с пользователем или жёсткое правило в промпте

Fine-tuning на специализированных датасетах (например, Spider, Bird) повышает точность генерации SQL. Self-consistency (генерация нескольких SQL, выполнение, выбор по большинству) снижает риск единичной ошибки.


4. Retrieval из Knowledge Graph (KG)

Knowledge Graph — это графовая структура, где узлы — сущности (люди, компании, фильмы), рёбра — отношения (работает_в, снялся_в), а свойства — атрибуты (возраст, дата). Для извлечения данных используются языки запросов:

Пайплайн аналогичен Text-to-SQL:

  1. NL запрос → LLM генерирует SPARQL/Cypher.
  2. Выполнение запроса к графовой БД.
  3. Результат (список узлов, путей, агрегаций) → контекст для LLM.

Пример для Neo4j (Cypher):

Запрос: «Какие фильмы снял режиссёр Кристофер Нолан?»

Промпт содержит схему графа: узлы Person (name), Movie (title), отношение DIRECTED. LLM генерирует:

MATCH (p:Person {name: 'Christopher Nolan'})-[:DIRECTED]->(m:Movie)
RETURN m.title

Результат: ["Inception", "Interstellar", "Tenet", ...] → LLM отвечает списком.


5. Проблемы и решения для KG

ПроблемаОписаниеРешение
Сложность онтологииСхема графа может быть большой (сотни типов узлов и отношений)Предоставлять LLM только релевантную часть схемы (через ретривер по метаданным)
Неоднозначность сущностей«Нолан» — может быть Кристофер Нолан или Джонатан НоланИспользовать entity linking (сопоставление с URI), уточняющий вопрос
ПроизводительностьСложные графовые запросы могут выполняться долгоИндексировать узлы по имени, ограничивать глубину обхода, кэшировать частые запросы
Альтернатива — GraphRAGВместо генерации запросов — эмбеддинги графа и поиск по нимИспользовать графовые нейросети (GNN) для создания эмбеддингов подграфов, затем векторный поиск

GraphRAG (например, Microsoft GraphRAG) — гибридный подход: граф разбивается на сообщества, для каждого строится текстовое описание, которое индексируется векторно. Это упрощает retrieval, но теряет точность формальных запросов.


6. Сравнение подходов: SQL vs Knowledge Graph

ХарактеристикаSQL (реляционные БД)Knowledge Graph
Модель данныхТаблицы, строки, колонкиУзлы, рёбра, свойства
Язык запросовSQL (ANSI, диалекты)SPARQL, Cypher, Gremlin
Типичные вопросыАгрегации, фильтры, JOIN по ключамПути, связи, транзитивные зависимости
Сложность генерацииСредняя (схема фиксирована)Высокая (онтология сложнее)
ПроизводительностьВысокая (индексы, оптимизаторы)Зависит от размера графа, глубины обхода
Пример использования«Средний чек по магазинам за прошлый месяц»«Какие лекарства взаимодействуют с аспирином?»

7. Интеграция с RAG: как использовать результаты

Результаты SQL/KG запроса — это не готовый ответ, а структурированный контекст. LLM должна его интерпретировать и сформулировать ответ на естественном языке.

Пример для SQL:

Контекст (результат SQL):
| customer_name | total_orders |
|---------------|--------------|
| Иван          | [[15. Какие embedding-модели вы использовали и почему\|15]]           |
| Мария         | [[22. Какие методы fine-tuning вы знаете и какой используете чаще всего\|22]]           |

Вопрос: "Сколько заказов сделал Иван?"
Ответ: Иван сделал 15 заказов.

Для KG:

Контекст (пути графа):
- Кристофер Нолан -> режиссёр -> "Inception"
- Кристофер Нолан -> режиссёр -> "Interstellar"

Вопрос: "Какие фильмы снял Нолан?"
Ответ: Кристофер Нолан снял фильмы "Inception" и "Interstellar".

Форматирование критично: таблицы Markdown, списки, JSON — в зависимости от задачи.


8. Инструменты и фреймворки

Пример на LangChain (SQL):

from langchain_community.utilities import SQLDatabase
from langchain.chains import create_sql_query_chain
from langchain_openai import ChatOpenAI

db = SQLDatabase.from_uri("sqlite:///chinook.db")
llm = ChatOpenAI(model="gpt-4", temperature=0)
chain = create_sql_query_chain(llm, db)

question = "Сколько треков в альбоме 'Let There Be Rock'?"
result = chain.invoke({"question": question})
print(result)  # SQL-запрос
# Затем выполнить и получить ответ

9. Оценка качества retrieval для структурированных данных

Традиционные метрики (Hit Rate, MRR) здесь не работают, потому что результат — не набор документов, а точный ответ на запрос.

Метрики для Text-to-SQL:

  • Execution Accuracy — доля запросов, для которых результат выполнения SQL совпадает с эталонным (золотой стандарт).
  • Exact Set Match — совпадение множества строк (порядок не важен).
  • Valid Efficiency Score — учитывает и корректность, и производительность.

Метрики для KG:

  • F1 по узлам/рёбрам — насколько найденный подграф совпадает с эталонным.
  • Path Accuracy — точность восстановления пути в графе.

Оценка требует размеченного датасета (NL-запрос → правильный SQL/SPARQL → ожидаемый результат). Популярные бенчмарки: Spider (SQL), LC-QuAD 2.0 (SPARQL).


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

Задача: Создать агента, который отвечает на вопросы по SQLite-базе данных (например, Chinook — музыкальный магазин) и по небольшому графу знаний (например, FilmKG — фильмы, актёры, режиссёры).

Инструменты: Python, LangChain, SQLite, Neo4j (или in-memory граф через NetworkX + SPARQLWrapper для имитации).

Шаги:

  1. Загрузить Chinook (SQLite) и создать граф FilmKG (можно в Neo4j Desktop или AuraDB).
  2. Написать функцию, которая по NL-запросу генерирует SQL через LangChain create_sql_query_chain.
  3. Реализовать выполнение SQL с обработкой ошибок (повторная генерация при неудаче).
  4. Для графа — аналогично через GraphCypherQAChain.
  5. Объединить в одного агента с маршрутизацией: если вопрос про таблицы → SQL, если про связи → KG.
  6. Протестировать на 10 вопросах разного типа.

Ожидаемый результат: Агент, который на естественном языке возвращает точные данные из обеих БД, например:

  • «Сколько треков в жанре Rock?» → SQL → «125 треков».
  • «Какие фильмы снял Стивен Спилберг?» → Cypher → «Список: "Список Шиндлера", "Парк Юрского периода"...».

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

ВопросТема
376Проектирование агентных RAG-систем
378Гибридный поиск (векторный + ключевой)
379Маршрутизация запросов в RAG
380Мультимодальный retrieval
381Кэширование результатов retrieval
382Оценка качества RAG-системы

Навигация