Алексей — финансовый директор. Умный, занятой, не любящий ждать. Каждый понедельник он открывает Excel с продажами за прошлую неделю и задаёт вопросы.
Но Excel — не собеседник. Алексей идёт к аналитику.
Аналитик строит сводную, ищет причины, пишет письмо. Иногда это занимает полдня. Иногда — до вторника.
Я посчитал: среднее время от вопроса Алексея до ответа было 2 часа 17 минут. Сейчас — 4 минуты 30 секунд. Алексей пишет вопрос в чат, получает ответ с цифрами и объяснением.
Расскажу, как это работает. Без BI-систем, без баз данных, без аналитика в цепочке — просто Python и Claude API.
Типичный файл финансового отдела: CSV или Excel с колонками — дата, категория, менеджер, сумма, регион. 3000–50 000 строк. Обновляется раз в день или раз в неделю.
Вопросы бывают двух видов.
Простые: «Какой итог за февраль по Москве?»
Сложные: «Почему в феврале минус 15% к январю — это сезон или конкретные менеджеры провалились?»
На простые Excel справляется. На сложные — нет: нужно копать, сравнивать, делать вывод. Вот здесь и нужен аналитик. Или — Claude.
Excel/CSV → pandas → Claude API → текстовый ответ с цифрами
Без векторных баз, без SQL, без инфраструктуры. Файл читается, данные готовятся для контекста, задаётся вопрос — получается ответ.
Полный код — ~75 строк.
import anthropic import pandas as pd import json from pathlib import Path def get_table_summary(df: pd.DataFrame) -> dict: """Статистика по таблице — используется для больших файлов.""" summary = { "shape": {"rows": df.shape[0], "cols": df.shape[1]}, "columns": list(df.columns), "dtypes": df.dtypes.astype(str).to_dict(), "null_counts": df.isnull().sum().to_dict(), "numeric_stats": {}, } for col in df.select_dtypes(include="number").columns: summary["numeric_stats"][col] = { "min": float(df[col].min()), "max": float(df[col].max()), "mean": round(float(df[col].mean()), 2), "sum": float(df[col].sum()), } return summary def prepare_data_for_context(df: pd.DataFrame, max_rows: int = 200) -> str: """ Маленькие таблицы — целиком. Большие — статистика + случайная выборка 50 строк. """ if len(df) <= max_rows: return df.to_csv(index=False) summary = get_table_summary(df) sample = df.sample(min(50, len(df)), random_state=42) return ( f"ТАБЛИЦА БОЛЬШАЯ ({len(df)} строк) — даю статистику и случайную выборку.\n\n" f"СТАТИСТИКА:\n{json.dumps(summary, ensure_ascii=False, indent=2, default=str)}\n\n" f"СЛУЧАЙНАЯ ВЫБОРКА (50 строк из {len(df)}):\n{sample.to_csv(index=False)}" ) def fix_column_types(df: pd.DataFrame) -> pd.DataFrame: """ Фикс типов для выгрузок из 1С и других систем: пробелы как разделители тысяч, запятые вместо точек в числах. """ for col in df.columns: col_lower = col.lower() if any(x in col_lower for x in ["дата", "date", "период", "месяц"]): try: df[col] = pd.to_datetime(df[col], dayfirst=True) except Exception: pass elif df[col].dtype == object: cleaned = ( df[col] .astype(str) .str.replace(r"\s", "", regex=True) .str.replace(",", ".") ) try: df[col] = pd.to_numeric(cleaned) except (ValueError, TypeError): pass return df def ask_about_data(file_path: str, question: str) -> str: path = Path(file_path) if path.suffix.lower() == ".csv": df = pd.read_csv(path) else: df = pd.read_excel(path) df = fix_column_types(df) data_context = prepare_data_for_context(df) prompt = f"""Ты аналитик данных. Перед тобой данные из таблицы. ДАННЫЕ: {data_context} ВОПРОС: {question} Правила: - Отвечай конкретно, с цифрами из данных. - Если данных не хватает для точного ответа — скажи об этом прямо. - Не придумывай числа, которых нет в таблице. - Если нужна разбивка — дай её.""" client = anthropic.Anthropic() message = client.messages.create( model="claude-opus-4-6", max_tokens=2048, messages=[{"role": "user", "content": prompt}] ) return message.content[0].text if __name__ == "__main__": answer = ask_about_data( "sales_february_2026.xlsx", "Почему в феврале падение 15% к январю? Это сезон или конкретные менеджеры?" ) print(answer)
Файл: выгрузка продаж за январь–февраль 2026, 4 200 строк, 7 менеджеров, 3 региона.
Вопрос Алексея: «Почему в феврале минус 15% к январю — это сезон или менеджеры?»
Ответ за 4 секунды:
Падение в феврале — 14,8% к январю (январь: 8 340 000 ₽, февраль: 7 104 000 ₽). Разбивка по менеджерам: — Иванова: −3% (в норме, сезонный паттерн) — Петров: −8% (небольшое снижение) — Сидоров: −41% (критическое падение: 1 820 000 → 1 070 000 ₽) — Козлова: −2% (стабильно) — Остальные: ±5% (в пределах нормы) Вывод: 78% общего падения объясняется Сидоровым — у него провал именно в Московском регионе (−52%). Остальные менеджеры держатся. Это не сезон — это точечная проблема. Рекомендую: уточнить у Сидорова по сделкам в Москве в феврале.
Алексей сразу идёт к Сидорову. Не ждёт аналитика, не строит сводную сам.
Первую неделю я принимал все ответы за правду. На второй — заметил расхождение.
В одном анализе Claude написал сумму 1 234 500 ₽. В таблице стояло 1 243 500 ₽. Разница 9 000 — копейки в процентах, но неприятно. Добавил в промпт: «Каждую цифру сверяй с данными. Если не уверен — пиши "требует проверки".»
Стало лучше. Но не идеально — на больших выборках Claude иногда считает агрегаты с погрешностью ±1–2%. Для оперативного анализа ок, для бухгалтерии — нет.
Таблица на 50 000 строк в контекст не лезет. Поэтому prepare_data_for_context — она режет большие файлы до статистики + случайная выборка.
Минус очевидный: на случайной выборке можно пропустить аномалию. Если у вас 100 строк и одна из них — ключевой выброс, а она не попала в выборку — Claude её не увидит.
Решение на практике: для файлов > 5 000 строк я сначала прошу «дай общую статистику», потом «посмотри конкретно за февраль» — уже меньше данных, влезает целиком.
Это отдельная боль. Выгрузки из 1С и многих российских систем выглядят так:
1 234 567,89 ← пробел как разделитель тысяч, запятая как десятичная
pandas читает это как строку. fix_column_types как раз это и фиксит — убирает пробелы, меняет запятую на точку.
Но бывают сюрпризы: в одной выгрузке даты шли как «Январь 2026», в другой — «01.2026», в третьей — числом 202601. Первые два pd.to_datetime ест, третий — нет.
Пришлось добавить отдельный обработчик для числовых дат.
Никто в мире не называет колонки одинаково. В одном файле «Сумма продаж», в другом «Выручка», в третьем «Revenue_RUB». Claude справляется с интерпретацией — это его сильная сторона. Но иногда путается, если колонок много и названия перекрываются.
Помогает: в начале диалога один раз спросить «Какие колонки есть в таблице и что они значат?» — получить описание, потом задавать вопросы.
Не подходит для:
Таблиц > 50 000 строк без предобработки
Точной бухгалтерии (нужна проверка цифр)
Сложных join'ов между несколькими файлами (теоретически можно, но громоздко)
Real-time данных — придётся перечитывать файл каждый раз
Подходит для:
Оперативного анализа: «что происходит прямо сейчас»
Вопросов «почему» — когда нужен вывод, а не просто цифры
Нерегулярных запросов — тех, под которые не строят дашборды
Людей, которые не умеют в Excel pivot tables
Алексей теперь спрашивает сам. Аналитик занимается более сложными задачами.
Следующий шаг — веб-интерфейс: загрузи файл, задай вопрос, получи ответ. Без Python, без терминала. Это уже другая история.
А у вас есть такая задача — перевести данные из «немых» таблиц в диалог? Как решаете вопрос "переводчика" между данными и нужным человеком?
Сергей Цветков. AI-автоматизация бизнес-процессов. 15 лет в IT, 30+ AI-проектов.
Источник


