Алексей — финансовый директор. Умный, занятой, не любящий ждать. Каждый понедельник он открывает 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)} строк) — даю статистику и случайную выборку.nn"
f"СТАТИСТИКА:n{json.dumps(summary, ensure_ascii=False, indent=2, default=str)}nn"
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-проектов.
Автор: sergei_ai


