Алексей — финансовый директор. Умный, занятой, не любящий ждать. Каждый понедельник он открывает Excel с продажами за прошлую неделю и задаёт вопросы.Но Excel —Алексей — финансовый директор. Умный, занятой, не любящий ждать. Каждый понедельник он открывает Excel с продажами за прошлую неделю и задаёт вопросы.Но Excel —

Одна функция, которая заменила аналитика

2026/03/05 20:06
6м. чтение
Для обратной связи или замечаний по поводу данного контента, свяжитесь с нами по адресу crypto.news@mexc.com

Алексей — финансовый директор. Умный, занятой, не любящий ждать. Каждый понедельник он открывает Excel с продажами за прошлую неделю и задаёт вопросы.

Но Excel — не собеседник. Алексей идёт к аналитику.

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

Я посчитал: среднее время от вопроса Алексея до ответа было 2 часа 17 минут. Сейчас — 4 минуты 30 секунд. Алексей пишет вопрос в чат, получает ответ с цифрами и объяснением.

Расскажу, как это работает. Без BI-систем, без баз данных, без аналитика в цепочке — просто Python и Claude API.

Что за задача

Типичный файл финансового отдела: CSV или Excel с колонками — дата, категория, менеджер, сумма, регион. 3000–50 000 строк. Обновляется раз в день или раз в неделю.

Вопросы бывают двух видов.

Простые: «Какой итог за февраль по Москве?»

Сложные: «Почему в феврале минус 15% к январю — это сезон или конкретные менеджеры провалились?»

На простые Excel справляется. На сложные — нет: нужно копать, сравнивать, делать вывод. Вот здесь и нужен аналитик. Или — Claude.

Архитектура за 5 минут

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%). Остальные менеджеры держатся. Это не сезон — это точечная проблема. Рекомендую: уточнить у Сидорова по сделкам в Москве в феврале.

Алексей сразу идёт к Сидорову. Не ждёт аналитика, не строит сводную сам.

Где грабли

1. Галлюцинации в цифрах

Первую неделю я принимал все ответы за правду. На второй — заметил расхождение.

В одном анализе Claude написал сумму 1 234 500 ₽. В таблице стояло 1 243 500 ₽. Разница 9 000 — копейки в процентах, но неприятно. Добавил в промпт: «Каждую цифру сверяй с данными. Если не уверен — пиши "требует проверки".»

Стало лучше. Но не идеально — на больших выборках Claude иногда считает агрегаты с погрешностью ±1–2%. Для оперативного анализа ок, для бухгалтерии — нет.

2. Контекстное окно

Таблица на 50 000 строк в контекст не лезет. Поэтому prepare_data_for_context — она режет большие файлы до статистики + случайная выборка.

Минус очевидный: на случайной выборке можно пропустить аномалию. Если у вас 100 строк и одна из них — ключевой выброс, а она не попала в выборку — Claude её не увидит.

Решение на практике: для файлов > 5 000 строк я сначала прошу «дай общую статистику», потом «посмотри конкретно за февраль» — уже меньше данных, влезает целиком.

3. Форматы из 1С

Это отдельная боль. Выгрузки из 1С и многих российских систем выглядят так:

1 234 567,89 ← пробел как разделитель тысяч, запятая как десятичная

pandas читает это как строку. fix_column_types как раз это и фиксит — убирает пробелы, меняет запятую на точку.

Но бывают сюрпризы: в одной выгрузке даты шли как «Январь 2026», в другой — «01.2026», в третьей — числом 202601. Первые два pd.to_datetime ест, третий — нет.

Пришлось добавить отдельный обработчик для числовых дат.

4. Название колонок

Никто в мире не называет колонки одинаково. В одном файле «Сумма продаж», в другом «Выручка», в третьем «Revenue_RUB». Claude справляется с интерпретацией — это его сильная сторона. Но иногда путается, если колонок много и названия перекрываются.

Помогает: в начале диалога один раз спросить «Какие колонки есть в таблице и что они значат?» — получить описание, потом задавать вопросы.

Ограничения — честно

Не подходит для:

  • Таблиц > 50 000 строк без предобработки

  • Точной бухгалтерии (нужна проверка цифр)

  • Сложных join'ов между несколькими файлами (теоретически можно, но громоздко)

  • Real-time данных — придётся перечитывать файл каждый раз

Подходит для:

  • Оперативного анализа: «что происходит прямо сейчас»

  • Вопросов «почему» — когда нужен вывод, а не просто цифры

  • Нерегулярных запросов — тех, под которые не строят дашборды

  • Людей, которые не умеют в Excel pivot tables

Что дальше

Алексей теперь спрашивает сам. Аналитик занимается более сложными задачами.

Следующий шаг — веб-интерфейс: загрузи файл, задай вопрос, получи ответ. Без Python, без терминала. Это уже другая история.


А у вас есть такая задача — перевести данные из «немых» таблиц в диалог? Как решаете вопрос "переводчика" между данными и нужным человеком?


Сергей Цветков. AI-автоматизация бизнес-процессов. 15 лет в IT, 30+ AI-проектов.

Источник

Отказ от ответственности: Статьи, размещенные на этом веб-сайте, взяты из общедоступных источников и предоставляются исключительно в информационных целях. Они не обязательно отражают точку зрения MEXC. Все права принадлежат первоисточникам. Если вы считаете, что какой-либо контент нарушает права третьих лиц, пожалуйста, обратитесь по адресу crypto.news@mexc.com для его удаления. MEXC не дает никаких гарантий в отношении точности, полноты или своевременности контента и не несет ответственности за любые действия, предпринятые на основе предоставленной информации. Контент не является финансовой, юридической или иной профессиональной консультацией и не должен рассматриваться как рекомендация или одобрение со стороны MEXC.