Создание data-аналитика на базе LLM: генерация SQL-запросов, pandas-скриптов, визуализация графиков. Автоматический анализ CSV-файлов и поиск инсайтов.
Устанавливаем pandas, matplotlib, openai и sqlite3. Загружаем тестовый CSV (продажи интернет-магазина) и создаём SQLite-базу для демонстрации SQL-возможностей агента. Показываем авто-определение схемы таблицы.
# Установка pip install pandas matplotlib openai sqlite3-tools import pandas as pd import sqlite3, json, os from openai import OpenAI client = OpenAI(api_key=os.environ["OPENAI_API_KEY"]) # Загружаем CSV (продажи магазина) df = pd.read_csv("sales_2025.csv") print(f"Загружено: {len(df)} строк, {len(df.columns)} колонок") print(f"Колонки: {list(df.columns)}") print(f"Типы:\n{df.dtypes}") # Создаём SQLite базу для SQL-запросов conn = sqlite3.connect(":memory:") df.to_sql("sales", conn, index=False) # Авто-схема для LLM schema = pd.io.sql.get_schema(df, "sales") print(f"\nСхема:\n{schema}")
Ключевой компонент — LLM определяет тип запроса (SQL, pandas или визуализация) и генерирует исполняемый код. Системный промпт содержит схему данных и примеры. Используем gpt-4o для точной генерации кода.
SYSTEM_PROMPT = """Ты — AI Data Analyst. Твоя задача — анализировать данные. На вход подаётся вопрос на русском и схема данных. Верни JSON с полями: - type: "sql" | "pandas" | "viz" - code: исполняемый код Python (для sql — строка с запросом) - explanation: краткое описание что делает код (на русском) Правила: 1. Для SQL: переменная conn уже есть (sqlite3.Connection) 2. Для pandas: переменная df уже есть (pd.DataFrame) 3. Для viz: используй matplotlib, сохраняй в result.png 4. ВСЕГДА присваивай результат в переменную result 5. Никаких input(), только обработка данных """ def generate_analysis_code(question: str, schema_info: str) -> dict: """LLM генерирует код анализа по вопросу.""" response = client.chat.completions.create( model="gpt-4o", response_format={"type": "json_object"}, messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": f"Схема:\n{schema_info}\n\nВопрос: {question}" } ] ) return json.loads(response.choices[0].message.content) # Пример: пользователь спрашивает про топ-5 товаров result = generate_analysis_code( "Покажи топ-5 товаров по выручке за 2025 год", schema ) print(json.dumps(result, indent=2, ensure_ascii=False))
Код, сгенерированный LLM, нельзя выполнять бесконтрольно. Создаём sandbox-исполнитель с ограниченным globals, таймаутом и чёрным списком опасных вызовов. Результат захватывается через stdout и переменную result.
import sys, io, traceback from contextlib import redirect_stdout, redirect_stderr # Чёрный список опасных вызовов FORBIDDEN = {"__import__", "eval", "exec", "compile", "open", "subprocess", "os.system", "shutil"} def safe_execute(code: str, context: dict) -> dict: """Выполняет код в sandbox-окружении.""" # Проверка на запрещённые вызовы for bad in FORBIDDEN: if bad in code: return {"error": f"Запрещённый вызов: {bad}"} namespace = {**context, "__builtins__": {}} stdout = io.StringIO() try: with redirect_stdout(stdout), redirect_stderr(stdout): exec(code, namespace) result = namespace.get("result", None) # Конвертируем DataFrame в dict if isinstance(result, pd.DataFrame): result = json.loads(result.to_json(orient="records")) elif isinstance(result, pd.Series): result = json.loads(result.to_json()) return { "success": True, "result": result, "stdout": stdout.getvalue()[:2000] } except Exception as e: return { "success": False, "error": f"{type(e).__name__}: {e}", "traceback": traceback.format_exc()[:500] }
Объединяем все компоненты в единого агента. Пользователь задаёт вопрос на русском, LLM генерирует код, sandbox исполняет, результат форматируется в читаемый ответ. При ошибке код отправляется на доработку.
def analyze(question: str, max_retries: int = 2) -> str: """Главный метод AI Data Analyst.""" context = { "df": df, "conn": conn, "pd": pd, "json": json } for attempt in range(max_retries): plan = generate_analysis_code(question, schema) print(f"\n🔍 Тип: {plan['type']}") print(f"📝 Пояснение: {plan['explanation']}") # Для SQL-запросов оборачиваем в pd.read_sql code = plan["code"] if plan["type"] == "sql" and "pd.read_sql" not in code: code = f"result = pd.read_sql_query({code}, conn)" # Исполняем exec_result = safe_execute(code, context) if exec_result["success"]: # Формируем ответ через LLM return format_response(question, exec_result, plan) else: print(f"⚠️ Ошибка: {exec_result['error']}") # Дорабатываем с сообщением об ошибке question += f"\n(Ошибка: {exec_result['error']}, исправь код)" return "❌ Не удалось выполнить анализ после нескольких попыток" def format_response(q: str, exec_r: dict, plan: dict) -> str: """LLM форматирует результат в читаемый ответ.""" data_str = json.dumps(exec_r["result"], ensure_ascii=False) response = client.chat.completions.create( model="gpt-4o", messages=[{ "role": "system", "content": "Преврати JSON-результат в читаемый ответ на русском. Добавь числовые итоги и выводы." }, { "role": "user", "content": f"Вопрос: {q}\nРезультат: {data_str}" }] ) return response.choices[0].message.content # Тест агента answer = analyze("Какова средняя выручка по месяцам? Покажи тренд.") print(f"\n📊 Результат:\n{answer}")
Когда пользователь просит график, LLM генерирует matplotlib-код. Агент исполняет его в sandbox и сохраняет результат как PNG. Картинку можно отобразить в UI или сохранить для отчёта. Добавляем поддержку русских шрифтов.
import matplotlib matplotlib.use("Agg") # безголовый режим import matplotlib.pyplot as plt # Настройка русских шрифтов plt.rcParams['font.family'] = 'DejaVu Sans' def analyze_with_viz(question: str): """Версия с поддержкой графиков.""" context = { "df": df, "conn": conn, "pd": pd, "plt": plt, "json": json } plan = generate_analysis_code(question, schema) if plan["type"] == "viz": # Добавляем сохранение графика code = plan["code"] if "savefig" not in code: code += "\nplt.savefig('result.png', dpi=150, bbox_inches='tight')" exec_result = safe_execute(code, context) print(f"📊 График сохранён: result.png") return exec_result return analyze(question) # Пример: круговая диаграмма продаж по категориям analyze_with_viz( "Построй круговую диаграмму распределения продаж по категориям товаров" )
AI Data Analyst превращает вопросы на естественном языке в полноценный анализ данных с SQL, pandas и визуализацией. Агент понимает контекст ваших данных, самостоятельно выбирает правильный инструмент и выполняет вычисления в изолированном sandbox. Решение легко встраивается в Jupyter-ноутбуки, дашборды и Slack-ботов. Для production-использования добавьте кэширование частых запросов и подключение к PostgreSQL/ClickHouse.