Статьи · Данные и ИИ

Зачем языковой модели синтаксическое дерево

Как структура SQL помогает ИИ понимать данные — и почему текста недостаточно

~9 мин · AST, парсинг, RAG

Языковые модели умеют читать текст. SQL — это тоже текст. Почему бы просто не дать модели текст запроса и не попросить объяснить, что он делает?

Это работает. Для одного запроса. Для объяснения. Для небольших задач.

Но когда задача — проанализировать десятки тысяч запросов, извлечь из них структуру, построить граф зависимостей и научить другую модель работать с конкретным хранилищем — простой текст оказывается недостаточным.

Что видит модель в тексте запроса

Возьмём запрос:

WITH dept_stats AS (
    SELECT d.DEPT_CODE, COUNT(DISTINCT s.STUDENT_ID) as cnt
    FROM SIS_DEPARTMENT d
    JOIN SIS_ENROLLMENT e ON d.DEPT_CODE = e.DEPT_CODE
    JOIN SIS_STUDENT s ON e.STUDENT_ID = s.STUDENT_ID
    GROUP BY d.DEPT_CODE
)
SELECT * FROM dept_stats WHERE cnt > 100

Для языковой модели это последовательность токенов: WITH, dept_stats, AS, (, SELECT, d, ., DEPT_CODE, ...

Модель достаточно умна, чтобы понять смысл. Но «понять» здесь — значит каждый раз заново восстанавливать структуру из последовательности символов. Это дорого. И каждая следующая модель, которая работает с этим текстом, делает то же самое заново.

Что такое AST и что он даёт

AST (Abstract Syntax Tree, абстрактное синтаксическое дерево) — это структурированное представление кода, где иерархия элементов явно выражена в виде дерева.

Для того же запроса AST явно говорит:

  • Это запрос типа SELECT с CTE
  • CTE называется dept_stats и является подзапросом
  • Реальные таблицы: SIS_DEPARTMENT, SIS_ENROLLMENT, SIS_STUDENT
  • JOIN-условия: d.DEPT_CODE = e.DEPT_CODE, e.STUDENT_ID = s.STUDENT_ID
  • Агрегация: COUNT DISTINCT по STUDENT_ID
  • Внешний SELECT фильтрует по результату CTE — не по реальной таблице

Эта информация не требует интерпретации — она явна в структуре дерева. Любой алгоритм, который обходит дерево, получает её мгновенно и детерминированно.

Три преимущества структуры над текстом

Первое: нормализация.

Один и тот же запрос можно написать по-разному:

-- Вариант А
SELECT s.STUDENT_ID FROM SIS_STUDENT s WHERE s.DEPT = 'Math'

-- Вариант Б
SELECT STUDENT_ID FROM SIS_STUDENT WHERE DEPT = 'Math'

-- Вариант В
select student_id from sis_student where dept = 'Math'

Текстово — три разных строки. В AST-представлении — одна и та же структура (с поправкой на регистр после нормализации). Алгоритм, работающий с AST, видит их как идентичные. Алгоритм, работающий с текстом, видит три разных запроса.

Это критично при поиске дублирующихся паттернов и при оценке покрытия схемы историческими запросами.

Второе: точное извлечение.

Из AST можно точно извлечь любой элемент: все таблицы (исключая CTE-псевдонимы), все JOIN-условия, все агрегации, все колонки в SELECT. Без риска спутать псевдоним с реальным именем, без неоднозначностей.

Из текста то же самое извлекается с ошибками. Регулярные выражения не справляются с вложенностью. Языковая модель иногда галлюцинирует или упускает детали в сложных запросах.

Третье: сравнение структур.

Два запроса могут быть похожи по смыслу, но различаться в деталях: один использует INNER JOIN, другой — подзапрос в WHERE. Семантически эквивалентно, синтаксически — нет.

С AST можно формально сравнивать структуры: считать структурное расстояние, находить общие поддеревья, группировать запросы по шаблонам. Это невозможно с текстом без предварительного его разбора.

Как AST помогает языковой модели

Исследования показывают: когда языковая модель получает на вход не текст запроса, а его структурированное представление, качество ответов улучшается на задачах, требующих понимания схемы.

Причина интуитивно понятна. Модель не тратит «внимание» на разбор синтаксиса — эта работа уже сделана. Вместо последовательности токенов она получает структуру с явными отношениями между элементами.

Для задачи поиска нужных таблиц это означает: модель видит не «WHERE d.DEPT_CODE = e.DEPT_CODE» как строку, а явное отношение «таблица SIS_DEPARTMENT соединяется с SIS_ENROLLMENT через поле DEPT_CODE». Это утверждение о структуре данных, а не синтаксическая конструкция.

Чанкинг по AST

Отдельная задача — как нарезать длинный SQL-запрос для передачи в языковую модель.

Наивный подход: разбить по числу токенов. 500 токенов на чанк — и передавать последовательно. Проблема: границы чанков режут запрос посередине подзапроса, теряется контекст JOIN, непонятно к какой таблице относится условие.

AST-чанкинг нарезает по структурным границам: каждый CTE — отдельный чанк, каждая ветка UNION — отдельный чанк, каждый подзапрос — отдельный чанк. Каждый фрагмент сохраняет контекст: какие таблицы в нём участвуют, откуда пришли данные, к чему относится условие фильтрации.

Это меняет качество векторного поиска по истории запросов: система ищет не «похожие куски текста», а «похожие структурные паттерны». Два чанка, оба про агрегацию финансовых данных по кварталам — близки в векторном пространстве, даже если написаны совершенно разными аналитиками в разное время.

AST как мост между текстом и графом

Синтаксическое дерево — промежуточный уровень между сырым текстом SQL и графом зависимостей схемы.

Текст → AST: разбор, который делает парсер. Детерминированно, без потери информации.

AST → граф: извлечение рёбер (таблица A соединяется с таблицей B через поле C). Каждый запрос добавляет рёбра в граф зависимостей. Тысячи запросов строят насыщенный граф реальных зависимостей.

Граф → ответ: поиск нужных таблиц, импакт-анализ, рекомендации по JOIN — всё это работает по графу.

Без AST как промежуточного слоя этот конвейер не работает или работает с потерями. AST — это то, что превращает «кучу текстовых запросов» в «структурированное знание о схеме».

Где AST не помогает

Честности ради: AST решает не все проблемы.

Семантика за пределами синтаксиса. AST знает, что поле называется BUDGET_AMOUNT. Он не знает, что в одной таблице это утверждённый бюджет, а в другой — запрошенная сумма. Для этого нужны данные, документация или контекст использования — не структура запроса.

Динамический SQL. Многие системы генерируют запросы на лету: строки конкатенируются в коде, WHERE-условия добавляются по логике приложения. Такой SQL никогда не попадает в историю запросов в полном виде — только фрагменты. AST можно построить только по тому, что есть.

Хранимые процедуры и макросы. Часть логики живёт не в SQL-запросах, а в хранимых процедурах, триггерах, функциях. Их разбор требует дополнительных парсеров и дополнительной работы по извлечению зависимостей.

AST — это не волшебство. Это инструмент, который превращает неструктурированный текст запросов в структурированное знание о том, как устроены данные. Без этого шага системы понимания данных работают вслепую — умно, но вслепую.

Читать также