TL;DR
Talk2Data — система-исследование, которая показывает как структурировать диалог с LLM при работе с табличными данными. Ключевой принцип: перед каждым ответом модель решает — генерировать код для анализа или дать текстовое объяснение. Эта развилка решений реализована через специальный промпт с примерами ("построй распределение" → код; "что означает колонка" → текст). В промпт передаётся структура данных — названия колонок, типы, примеры значений, диапазоны чисел.
Проблема: когда работаешь с данными через LLM, модель часто либо пытается объяснить словами то, что требует вычислений ("средний возраст примерно 30-35" вместо точного числа), либо наоборот — генерирует код там, где достаточно короткого ответа ("какие колонки в таблице?" → 20 строк Python вместо списка). Ещё одна боль: модель может ссылаться на колонки, которых нет в твоих данных, потому что не видит реальную схему таблицы.
Решение — явный роутинг на уровне промпта: сначала классифицируй запрос (нужен код или объяснение?), потом действуй. Плюс передавай схему данных в каждом запросе: "в таблице 8 колонок: age (числа 18-65), gender (Male/Female), score (0-100)...". Модель делает выбор осознанно и привязывается к реальным данным, а не к галлюцинациям.
Схема метода
КОНТЕКСТ (один раз при загрузке данных):
→ Извлеки схему: названия колонок, типы, примеры значений, диапазоны
→ Сохрани как metadata для всех запросов
КАЖДЫЙ ЗАПРОС:
ШАГ 1: Роутинг
→ Промпт с примерами: "нужен код или текст для этого запроса?"
→ Вывод: {"action": "code_generation"} или {"action": "chat_response"}
ШАГ 2а (если код):
→ Сгенерируй Python с привязкой к metadata
→ Выполни в безопасной среде
→ Верни результат (график/таблица/число)
ШАГ 2б (если текст):
→ Дай краткое объяснение на основе metadata и истории
→ Можешь предложить "хочешь, я посчитаю?"
ШАГ 3: Обнови историю диалога для следующего запроса
Все шаги выполняются в одном диалоге, но каждый запрос проходит через роутинг заново.
Пример применения
Задача: Ты ведёшь онлайн-магазин на Ozon, выгрузил CSV с заказами за квартал (цена, регион, категория, дата, статус). Хочешь быстро понять паттерны: где продаётся лучше, какие категории, есть ли сезонность. Но не хочешь каждый раз писать "построй график" или "посчитай среднее" — хочешь просто спрашивать, а модель сама понимает что нужно.
Промпт (первый запрос — инициализация):
У меня CSV с данными о заказах:
- order_id (уникальный номер)
- price (рубли, от 290 до 45000)
- region (Москва, МО, СПб, Екатеринбург, Казань, Новосибирск)
- category (Электроника, Одежда, Дом и сад, Спорт, Красота)
- date (2024-01-01 до 2024-03-31)
- status (Доставлен, Отменён, Возврат)
Всего 3847 строк.
Дальше я буду задавать вопросы. Для каждого:
1. Реши: нужен код (для графиков/вычислений) или текстовое объяснение (для вопросов о данных)?
2. Если код — напиши Python для pandas/matplotlib, используя df с этими колонками
3. Если объяснение — дай короткий ответ и предложи, если нужно посчитать
Примеры решений:
- "покажи распределение цен" → код (histogram)
- "что означает статус 'Возврат'?" → текст
- "средний чек по регионам" → код (groupby + bar chart)
- "какие категории есть?" → текст (список из metadata)
Первый вопрос: какой средний чек в Москве?
Результат:
Модель классифицирует запрос как code_generation (нужно вычисление), генерирует код:
df[df['region'] == 'Москва']['price'].mean()
Вернёт точное число, например: "4856 рублей — средний чек в Москве".
Следующие запросы в диалоге: - "а в СПб?" → модель помнит контекст, заменит фильтр на 'СПб' - "покажи топ-3 категории по выручке" → сгруппирует, посчитает сумму, выдаст bar chart - "что такое статус 'Отменён'?" → текстовое объяснение без вычислений
Почему это работает
Слабость LLM: Модель не знает когда нужен код, а когда — слова. Попросишь "средний чек" — может выдать приблизительную оценку вместо точного числа. Попросишь "что означает колонка" — может написать 20 строк кода для простого ответа. Ещё хуже: модель может придумать колонки, которых нет в твоих данных (галлюцинация), потому что не видит реальную схему.
Сильная сторона LLM: Модель отлично классифицирует тип задачи по примерам (few-shot learning) и хорошо работает с явными контрактами — "если X, то делай Y". Также хорошо привязывается к конкретным данным, если их показать явно.
Как метод использует это: Вместо одного универсального промпта делаем два специализированных — один для роутинга, другой для действия. В роутинге показываем примеры: "визуализация/вычисления → код", "вопрос о структуре/объяснение → текст". Это убирает двусмысленность. Плюс передаём схему данных явно в начале — модель видит реальные названия колонок, типы, примеры значений, и не может придумать несуществующие поля. Результат: точные вычисления там где нужно, короткие ответы там где достаточно, никаких галлюцинаций о структуре данных.
Рычаги управления:
- Примеры в роутинге — добавь свои типы задач ("сравнение двух метрик" → код; "расшифруй аббревиатуру" → текст), чтобы модель училась на твоих паттернах
- Детальность metadata — для больших таблиц передавай только топ-5 значений по каждой категории и диапазоны, чтобы не раздувать промпт
- История диалога — ограничь последними 3-5 обменами для длинных сессий, иначе контекст раздуется и модель начнёт путаться
- Условие выбора кода — можешь сместить баланс: "если сомневаешься — выбирай текст и предлагай код" (безопаснее) или "если сомневаешься — сразу генерируй код" (быстрее для опытных)
Шаблон промпта
У меня {формат} с данными о {тема}:
СХЕМА:
{для_каждой_колонки}
- {название} ({тип}: {диапазон_или_примеры_значений})
Всего {число} строк.
Дальше я буду задавать вопросы. Для каждого:
1. Реши: нужен КОД (для графиков/вычислений/группировок) или ТЕКСТОВОЕ ОБЪЯСНЕНИЕ (для вопросов о структуре/смысле данных)?
2. Если код — напиши Python для pandas/matplotlib, используя переменную df с этими колонками. Без комментариев, только исполняемый код.
3. Если объяснение — дай краткий ответ (1-2 предложения) и предложи, если есть смысл посчитать точно.
Примеры решений:
- "распределение {числовая_колонка}" → КОД (histogram)
- "что означает {категория_или_термин}?" → ТЕКСТ (объяснение)
- "среднее/сумма по группам" → КОД (groupby + chart/число)
- "какие значения в {колонка}?" → ТЕКСТ (список из схемы выше)
- "топ-N по {метрика}" → КОД (sort + head + chart/таблица)
- "есть ли связь между X и Y?" → ТЕКСТ ("давай построим scatter plot") или сразу КОД
Первый вопрос: {твой_запрос}
Заполнение плейсхолдеров:
- {формат} — "CSV файл", "таблица Excel", "Google Sheets"
- {тема} — "заказах интернет-магазина", "рекламных кампаниях", "транзакциях"
- {название} — реальные имена колонок из твоих данных
- {тип}: {диапазон_или_примеры} — для чисел: "рубли, от 100 до 50000", для категорий: "Москва, СПб, Новосибирск, Казань" (топ-5 значений)
- {число} — количество строк
- {твой_запрос} — твой первый вопрос к данным
🚀 Быстрый старт — вставь в ChatGPT (с Code Interpreter) или Claude:
Вот шаблон для работы с моими данными через роутинг (код/объяснение).
Адаптируй под мою задачу: [опиши свою таблицу и тип данных].
Попроси меня прислать первые 5 строк, если нужно увидеть структуру.
[вставить шаблон выше]
Модель спросит какие колонки в таблице, какие типы данных, примеры значений — потому что без схемы роутинг не сработает правильно (модель может галлюцинировать несуществующие поля). После этого она возьмёт паттерн роутинга и будет применять для каждого твоего запроса, сама решая когда генерировать код, а когда объяснять словами.
Ограничения
⚠️ Зависимость от качества метаданных: Если передашь неполную или неточную схему (забыл указать колонку, перепутал тип), модель будет генерировать неправильный код или галлюцинировать. Роутинг работает только при точном описании структуры данных.
⚠️ Роутинг не абсолютен: Маленькие модели (1.5-3B параметров) часто ошибаются в классификации — выбирают "объяснение" там где нужен код, или наоборот. Исследование показало: модели 7B+ ошибаются в 1-2% случаев, модели 1.5B — в 35% случаев. Для продакшн-использования нужны модели от 7B параметров (GPT-4, Claude Sonnet, Gemini Pro).
⚠️ Не для сложных многошаговых вычислений: Метод работает для стандартной аналитики (группировки, графики, фильтры, топы). Для сложных многоступенчатых пайплайнов (множественные джойны, ресемплинг временных рядов, custom агрегации) одного запроса может не хватить — придётся разбивать на шаги вручную или переключаться на чистое программирование.
⚠️ Требует загрузки данных: В обычном чате без Code Interpreter модель не может выполнить код — будет только симулировать вычисления (приблизительные ответы). Для работы метода нужен ChatGPT Plus/Enterprise с Code Interpreter, Claude с artifacts, или свой Python environment.
Как исследовали
Команда из ИТМО собрала прототип полноценной системы с голосовым вводом (Whisper ASR), генерацией кода (Qwen2.5-Coder), безопасным выполнением Python в sandbox и озвучкой ответов (Coqui TTS). Но исследовательский фокус был на принципе роутинга: как точно модель классифицирует тип запроса и насколько правильный код генерирует.
Проверили на 48 задачах (26 визуализаций, 13 аналитических запросов, 9 объяснений) для трёх публичных датасетов: товары Otto Group (27 МБ, 94 колонки), рейсы США 2008 года (673 МБ, 7 млн строк), успеваемость студентов (70 КБ, 8 колонок). Протестировали пять размеров модели Qwen от 1.5B до 32B параметров.
Результаты показали резкий скачок точности от размера модели: 1.5B справилась с 62.5% задач (но 17 из 48 запросов неправильно классифицировала — выбирала объяснение вместо кода), 3B — 87.5%, а 7B достигла 95.8% с всего одной ошибкой роутинга. Интересно, что 32B-параметровая модель не превзошла 7B (та же точность 95.8%), а 14B показала максимум — 97.9%, но с минимальным приростом за троекратное увеличение размера.
Ключевой инсайт: маленькие модели консервативны — боятся генерировать код и предпочитают объяснять словами ("давайте я расскажу как это посчитать" вместо самого вычисления). Средние модели агрессивны — пытаются написать код даже там, где достаточно списка категорий. Модели от 7B параметров находят баланс: точно различают "когда код, когда текст" и при этом генерируют правильный Python в 95% случаев.
Замерили скорость генерации кода: 7B модель выдаёт ответ за 1.15-1.64 секунды (чистое время модели, без учёта распознавания речи и выполнения кода). Это укладывается в требования интерактивного диалога. Latency растёт линейно с размером модели, но точность — нет, поэтому 7B оказалась sweet spot для продакшн-применения.
Дизайн показал, что разделение на два промпта (роутинг + действие) работает лучше чем один универсальный: модель меньше путается, ошибки легко отследить (видно на каком этапе сломалось — классификация или генерация), а код получается чище (без лишних комментариев и принтов).
Оригинал из исследования
Decision prompt (роутинг) — определяет нужен код или текст:
You are a conversational agent analyzing the dataset "{dataset_name}".
**Dataset Schema:**
{schema_description}
**Conversation History:**
{history}
**User Request:** "{user_query}"
**Task:** Determine if the request requires:
- "code_generation" (computation, visualization, aggregation), OR
- "chat_response" (explanation, clarification, metadata question).
**Return JSON only:**
{
"action": "code_generation" or "chat_response",
"reason": "brief explanation"
}
**Examples:**
User: "Plot age distribution" → {"action": "code_generation", "reason": "requires histogram"}
User: "What columns exist?" → {"action": "chat_response", "reason": "metadata query"}
User: "Find max sales by region" → {"action": "code_generation", "reason": "groupby + aggregation"}
Code generation prompt (если выбран код):
You are a Python code generator for tabular analysis.
**Dataset:** df (pandas DataFrame)
**Schema:** {schema}
**Task:** Generate concise Python code to answer: "{user_query}"
**Rules:**
- Use pandas, matplotlib, seaborn, plotly as needed
- Reference only columns that exist in schema
- Output must be an expression (figure object or dataframe), NOT print statements
- No comments, no explanations—just executable code
**Example:**
Query: "Scatter plot of price vs quantity"
Code:
```python
import matplotlib.pyplot as plt
plt.scatter(df['price'], df['quantity'])
plt.xlabel('Price')
plt.ylabel('Quantity')
plt.title('Price vs Quantity')
**Chat response prompt** (если выбран текст):
You are a conversational assistant for data analysis.
Dataset Schema: {schema} Conversation History: {history}
User Question: "{user_query}"
Task: Provide a brief, speakable answer (1-2 sentences). Reference the schema if needed. If computation is required, suggest switching to code.
Style: Clear, concise, no formatting (for TTS compatibility).
**Контекст:** Эти три промпта используются в оркестрации: сначала decision prompt классифицирует запрос, затем либо code generation prompt создаёт Python, либо chat response prompt формирует текстовое объяснение. Все промпты получают одинаковую схему данных (metadata) и историю диалога для консистентности.
## Адаптации и экстраполяции
💡 **Адаптация для SQL-баз вместо CSV:**
Тот же принцип роутинга, но вместо генерации Python → генерируй SQL. Передай схему таблиц (названия таблиц, колонки, типы, связи через foreign keys) и меняй промпт генерации кода:
Вместо "используй df (pandas)" напиши: "используй таблицы: {список_таблиц}. Напиши SQL запрос для PostgreSQL."
Примеры решений: - "средний чек по городам" → SELECT city, AVG(price) FROM orders GROUP BY city - "топ-10 клиентов" → SELECT customer_id, SUM(total) FROM orders GROUP BY customer_id ORDER BY SUM(total) DESC LIMIT 10
Роутинг остаётся тем же ("нужен запрос или объяснение?"), но действие — SQL вместо Python. Полезно если работаешь с Postgres/MySQL и не хочешь каждый раз выгружать CSV для анализа.
🔧 **Техника: bias роутинга → контроль риска и скорости**
В decision prompt можешь **сместить порог** в сторону безопасности или скорости:
**Для консервативного режима** (меньше кода, больше объяснений):
If uncertain, prefer "chat_response" and offer to compute if user confirms.
Эффект: модель будет чаще спрашивать "хочешь я посчитаю?" вместо того чтобы сразу генерировать код. Полезно когда данные большие, выполнение дорогое или ты хочешь контролировать каждый шаг.
**Для агрессивного режима** (больше кода, быстрее результаты):
If the question implies any computation, aggregation, or comparison—choose "code_generation" immediately.
Эффект: модель будет генерировать код даже для пограничных случаев. Полезно когда ты уверен в данных и хочешь максимум автоматизации.
🔧 **Техника: многоуровневая схема → работа с большими таблицами**
Для таблиц с **50+ колонками** передавать всю схему в каждый промпт дорого. Используй **иерархическую схему**:
СХЕМА (краткая): - 15 колонок с метриками: revenue, cost, profit, ... (числа) - 8 колонок с категориями: region, product_type, channel, ... (текст) - 5 временных: order_date, delivery_date, ... (даты) - остальные 22 колонки: технические ID и метаданные
Полная схема: {ссылка_или_детали_по_запросу}
Если пользователь спрашивает про конкретную колонку — уточни полное описание. ```
Эффект: промпт компактный, но модель знает что есть детали и может попросить, если нужно. Роутинг работает быстрее, контекст не раздувается.
Ресурсы
Talk2Data: A Multimodal Conversational Agent for Tabular Data Analysis — Mohammad Nour Al Awad, Sergey Ivanov, Olga Tikhonova, Ivan Khodnenko (ITMO University, Saint Petersburg, Russia)
Упомянутые модели и инструменты: - OpenAI Whisper (ASR) — github.com/openai/whisper - Qwen2.5-Coder (code generation) — huggingface.co/Qwen - Coqui TTS (text-to-speech) — github.com/coqui-ai/TTS - LangGraph (orchestration framework) — langchain-ai.github.io/langgraph
Датасеты для бенчмарка: - Otto Group Product Classification — Kaggle - US Flights 2008 — публичный датасет авиаперевозок - Student Performance Dataset — UCI Machine Learning Repository
