CLAUDE.md: улучшение поведения Claude Code по Karpathy
Один файл CLAUDE.md, основанный на наблюдениях Андрея Карпати, помогает исправить типичные ошибки LLM‑кода и повысить предсказуемость работы Claude Code.
Основные проблемы LLM‑кода в Claude Code
Основные проблемы LLM‑кода в Claude Code
Типичные ошибки при генерации кода
Во-первых, LLM‑модели часто игнорируют явные типы и квалификацию импортов, из‑за чего получаем код, который не проходит проверку линтером или статического анализатора.
python# Пример плохого фрагмента, генерируемого Claude Code
def process_file(path):
with open(path) as f:
data = f.read()
return data.upper()
- Проблемы:
- отсутствие указания кодировки → зависит от локали системы;
- отсутствие проверки существования файла → возникает
FileNotFoundErrorв рантайме; - отсутствие аннотаций → ухудшает автодополнение и статический анализ.
Как исправить – добавить типизацию, использовать pathlib и явно указывать кодировку:
pythonimport pathlib
def process_file(path: str) -> str:
p = pathlib.Path(path)
if not p.is_file():
raise FileNotFoundError(f"{p} does not exist")
text = p.read_text(encoding="utf-8")
return text.upper()
Справедливый пример из практического опыта Andrej Karpathy, 2023 observations on LLM code generation показывает, что модели часто генерируют hard‑coded пути вместо параметризованных аргументов, что делает код непереносимым.
Во-вторых, неpinning зависимостей – модель может выдать код, использующий последнюю версию библиотеки, а ваш проект остаётся на старой версии, что приводит к несовместимости. Рекомендуется фиксировать версии в requirements.txt или poetry.lock и проверять их перед запуском:
bash# проверка совместимости
pip install -r requirements.txt --upgrade-strategy only-if-needed
Наконец, отсутствие обработки исключений и неявные side‑effects (например, использование os.system вместо subprocess.run) часто приводят к неожиданным сбоям. В продакшене такие pattern‑ы следует заменять на безопасные аналоги:
pythonimport subprocess
def run_command(cmd: list[str]) -> subprocess.CompletedProcess:
return subprocess.run(cmd, capture_output=True, text=True, check=True)
Непредсказуемое поведение при рефакторинге
С практической точки зрения, рефакторинг кода, сгенерированного Claude Code, часто приводящий к разрыву цепочки вызовов. Модель видит лишь локальный контекст и не гаранрует, что все места использования функции будут обновлены. Типичный сценарий:
- Claude Code генерирует функцию
fetch_data(url: str) -> dict. - Вы refactor'ите её, добавляя параметр
timeout: int = 30. - Часть кода, написанная ранее вручную, по‑прежнему вызывает
fetch_data(url)без нового аргумента →TypeError.
python# До рефакторинга
result = fetch_data("https://example.com")
# После изменения сигнатуры
result = fetch_data("https://example.com", timeout=30) # TypeError
Чтобы избежать таких проблем, всегда проверяйте diff перед применением и запускайте локальные тесты:
bashgit diff # убедиться, что изменения охватывают все вызовы
pytest -q # быстро проверить, не сломались ли тесты
Кроме того, изменения в конфигурационных файлах (Dockerfile, docker‑compose.yml) могут привести к неожиданным ошибкам сборки. Например, смена базового образа python:3.9-slim → python:3.11-slim без обновления зависимостей часто ломает работу кода, требующего старых пакетов.
dockerfile# Dockerfile до изменения FROM python:3.9-slim # После изменения без проверки совместимости FROM python:3.11-slim
Рекомендация: использовать docker build --pull и проверять вывод docker build в CI, а также фиксировать точные версии базовых образов в requirements.txt.
В итоге, чтобы повысить предсказуемость работы Claude Code, следует:
- фиксировать версии зависимостей и проверять их перед генерацией кода;
- добавлять типизацию и явную обработку ошибок в каждый сгенерированный модуль;
- проводить рефакторинг только после проверки
git diffи запуска тестов; - при изменении конфигурационных файлов убеждаться в обратной совместимости.
Следуя этим практикам, вы уменьшаете количество «случайных» падений и делаете поведение Claude Code более предсказуемым.
Принципы улучшения поведения
Принципы улучшения поведения
Когда мы работаем с Claude Code, мы, по сути, управляем агентом, который обладает огромным объёмом знаний, но часто страдает от избыточной «вежливости» или попыток угадать намерения пользователя. Андрей Карпати подчеркивал, что эффективность работы с LLM напрямую зависит от того, насколько жестко мы ограничиваем пространство её маневра.
Файл CLAUDE.md служит своего рода «конституцией» для модели в рамках конкретного проекта. Если мы не зададим правила игры, агент начнет предлагать решения, которые подходят для абстрактного учебного примера, но ломают архитектуру вашего реального проекта.
Ясные инструкции и контекст
Главная проблема при работе с ИИ-агентами — это размытость контекста. Если вы просите «исправить ошибку в API», модель может начать переписывать весь контроллер, не учитывая принятые в проекте паттерны. Чтобы добиться предсказуемости, в CLAUDE.md нужно прописать не просто «как писать код», а «как работать с текущей структурой».
С практической точки зрения, я рекомендую разделить инструкции на три уровня: технологический стек, архитектурные правила и процесс работы с командами.
Пример блока инструкций для проекта на FastAPI и PostgreSQL:
markdown## Технологический стек и стандарты
- Framework: FastAPI (используем только асинхронные эндпоинты).
- Database: PostgreSQL через SQLAlchemy 2.0 (обязательно использование `select()` вместо legacy-стиля).
- Validation: Pydantic v2.
## Архитектурные правила
- Все бизнес-логику выносим в слой `services/`, контроллеры в `api/` должны быть тонкими.
- Ошибки обрабатываем через кастомные исключения, которые маппятся в `app/core/exceptions.py`.
- Не используй глобальные переменные для хранения состояния.
Такой подход избавляет от ситуации, когда Claude предлагает использовать синхронный драйвер базы данных там, где вся остальная система работает на asyncio.
Контроль вывода и форматирование
Вторая критическая область — это то, как именно модель выдаёт результат. Без чётких указаний Claude Code может начать генерировать избыточные пояснения или, что хуже, предлагать изменения в файлах, которые не соответствуют вашему стилю линтинга.
Чтобы минимизировать количество правок, которые приходится вносить вручную после агента, в CLAUDE.md стоит зафиксировать требования к формату вывода и проверке кода. Если в проекте настроен ruff или eslint, агент должен знать об этом и использовать их как финальный фильтр.
Добавьте в конфигурационный файл следующие правила:
markdown## Правила вывода и проверки
- Перед подтверждением изменений всегда запускай `make lint` для проверки стиля.
- Если изменение затрагивает схему БД, обязательно обнови файл `migrations/`.
- Не пиши длинные вступления и заключения. Ответ должен содержать либо краткое описание изменений, либо сразу код.
- При создании новых файлов всегда проверяй наличие импортов в соответствии с `isort`.
Если вы работаете в терминале, вы можете явно указать агенту, какие команды он должен использовать для верификации своих действий. Например:
«После внесения изменений в auth_service.py выполни:
bashpytest tests/test_auth.py
Если тесты упали, не предлагай решение, пока не проанализируешь вывод journald или логи приложения».
Такая дисциплина превращает Claude из «умного чат-бота» в полноценного участника процесса разработки, чьи действия можно предсказать и контролировать.
Практические приёмы
Создание и использование CLAUDE.md
Когда вы работаете с Claude Code или другими агентными моделями через терминал, главная проблема — это «дрейф» контекста. Модель начинает гадать, какой стиль написания кода вы предпочитаете, какие библиотеки используете и как принято именовать переменные в вашем проекте. Без чётких инструкций агент превращается в стажёра, который вроде бы знает синтаксис, но постоянно нарушает ваши локальные стандарты.
Решение, предложенное Андреем Карпати, заключается в использовании файла CLAUDE.md. Это своего рода «бортовой журнал» или манифест проекта, который модель считывает перед началом работы. Если в обычном проекте у нас есть .gitignore для исключения мусора, то CLAUDE.md нужен для фиксации правил игры.
С практической точки зрения, этот файл должен быть лаконичным. Не нужно писать туда учебник по Python. Нам нужны только конкретные инструкции, которые повышают предсказуемость. Я рекомендую структурировать его по трём направлениям: стиль кода, команды сборки и правила тестирования.
Вот пример того, как может выглядеть такой файл для типичного проекта на FastAPI:
markdown# CLAUDE.md — Project Guidelines
## Code Style
- Use type hints for all function signatures.
- Prefer asynchronous def for all route handlers.
- Follow PEP 8, but use double quotes for strings.
- Naming: use snake_case for variables and functions, PascalCase for classes.
## Build and Run Commands
- Install dependencies: `pip install -r requirements.txt`
- Run development server: `uvicorn main:app --reload`
- Linting: `flake8 .`
## Testing Patterns
- Use pytest for all new features.
- Test files must be named `test_*.py`.
- Run all tests: `pytest tests/`
- Run a specific test: `pytest tests/test_module.py#TestClass.test_method`
С точки зрения системного инженера, такой подход напоминает конфигурационные файлы в systemd. Вы один раз описываете желаемое состояние системы, и всё остальное должно ему соответствовать. Когда Claude Code видит этот файл, он перестаёт тратить токены на «размышления» о том, как лучше написать тест, и сразу переходит к делу.
Автоматизация проверок и тестов
Одной из типичных ошибок LLM-агентов является написание кода, который «выглядит правильно», но не проходит ваши локальные проверки. Модель может создать файл, который синтаксически верен, но нарушает архитектуру вашего проекта или использует устаревшие методы.
Чтобы минимизировать этот риск, я советую внедрить в CLAUDE.md не просто список команд, а строгий алгоритм проверки. Если вы используете Claude Code для автоматизации рутинных задач, заставьте его проверять свою работу перед тем, как он скажет «готово».
Во-первых, добавьте в инструкции обязательный этап линтинга. Если в проекте настроен ruff или flake8, модель должна запускать их после каждого изменения. Во-вторых, тесты должны быть не просто «есть в проекте», а должны запускаться при каждом запросе на изменение логики.
Если вы работаете в среде, где развёртывание происходит через Docker, добавьте команды для проверки состояния контейнеров. Например, если модель правит конфиг Nginx, она должна уметь проверить его валидность:
bash# Команда для проверки синтаксиса конфига внутри контейнера
docker exec nginx-container nginx -t
В CLAUDE.md это будет выглядеть так:
markdown## Validation Workflow
1. After any config change, run: `docker exec <container_name> <service> -t`
2. Always run `pytest` after modifying any logic in `/app`.
3. If tests fail, analyze the logs in `journalctl -u <service>` before retrying.
Такой подход делает работу агента предсказуемой. Вместо того чтобы получать от модели сообщение «Я обновил код, проверьте сами», вы получаете результат, который уже прошел базовый цикл проверки. Это экономит время и снижает вероятность того, что вы случайно зальёте в репозиторий сломанный конфиг.
Подробнее о концепции использования контекстных файлов для управления поведением моделей можно почитать в оригинальных заметках Андрея Карпати.
Мониторинг и поддержка
Логирование ошибок
Когда мы работаем с Claude Code или любыми другими AI-агентами, главная проблема — это «галлюцинации» в контексте выполнения команд. Агент может запустить скрипт, тот завершится с ошибкой, но LLM интерпретирует пустой вывод терминала как успех. Чтобы этого избежать, в CLAUDE.md нужно прописать жесткое требование: любой результат выполнения команды должен быть верифицирован через логи.
С практической точки зрения, я всегда советую добавлять в инструкции для модели правило: «Если команда изменила состояние системы, проверь результат через логи или статусную команду».
Если вы используете системные службы, Claude должен уметь работать с journald. Вместо того чтобы просто надеяться, что сервис поднялся, агент должен проверить его состояние. Например, вот как стоит описать ожидаемое поведение в секции инструкций:
bash# Вместо простого запуска сервиса, требуйте проверку логов на наличие ошибок
# Пример команды для проверки последних 20 строк лога после перезапуска
journalctl -u nginx.service -n 20 --no-pager
Если же вы разворачиваете приложения в Docker, Claude должен уметь смотреть логи контейнера, а не только статус процесса в хостовой системе. В CLAUDE.md стоит зафиксировать паттерн проверки:
bash# Проверка логов конкретного контейнера на наличие критических ошибок
docker logs --tail 50 <container_name> | grep -Ei "error|critical|fatal"
Это повышает предсказуемость. Мы не просто просим модель «исправить ошибку», мы заставляем её сначала собрать доказательную базу из логов.
Настройка алертов
Мониторинг в домашней лаборатории часто заканчивается на том, что мы раз в день заходим в Grafana. Но если Claude Code вносит изменения в конфигурацию вашего сервера — например, правит правила в iptables или меняет конфиг Nginx — вы должны узнать об этом мгновенно, а не когда сервис перестанет отвечать.
В контексте работы с AI-агентами, я рекомендую внедрить принцип «инфраструктурного контроля». Если агент выполняет деплой или изменение конфигурации, это должно триггерить событие.
Если у вас уже развернута связка Prometheus + Alertmanager, вы можете настроить алерты на изменения в файлах конфигурации через node_exporter или специализированные проверки. Но для быстрой отладки работы Claude Code проще всего использовать системные уведомления или простые вебхуки.
Например, можно добавить в инструкции для модели команду, которая отправляет уведомление в ваш Telegram-бот после завершения критической операции. Это не «решение на коленке», а способ держать руку на пульсе, когда вы доверяете выполнение команд автоматизированному агенту.
Пример простого скрипта для отправки алерта, который можно прописать в CLAUDE.md как обязательный шаг после изменения системных настроек:
bash# Отправка уведомления через curl (замените токен и ID на свои)
curl -s -X POST https://api.telegram.org/bot<TOKEN>/sendMessage \
-d chat_id=<CHAT_ID> \
-d text="[Claude Code] Изменена конфигурация Nginx на сервере Винни-Пух"
С точки зрения системного инженера, предсказуемость системы обеспечивается не отсутствием ошибок, а скоростью их обнаружения. Инструкции в CLAUDE.md должны превращать Claude из «черного ящика», который просто пишет код, в управляемый инструмент, оставляющий за собой понятный след в логах и системе оповещений.
Часто задаваемые вопросы
Зачем нужен файл CLAUDE.md, если я и так пишу подробные промпты?
Промпты в чате обычно носят временный характер и привязаны к конкретной сессии общения. Файл CLAUDE.md работает как постоянная конфигурация проекта, которая автоматически подгружается в контекст Claude Code при каждом обращении. С практической точки зрения это позволяет один раз описать стандарты кодирования, правила типизации и структуру проекта, обеспечивая предсказуемость поведения модели во всех будущих задачах.
Поможет ли CLAUDE.md исправить ошибки в уже существующем коде?
Этот файл в первую очередь задаёт правила для генерации нового кода и внесения правок. Если вы попросите Claude Code отрефакторить старый модуль, модель будет использовать инструкции из CLAUDE.md как эталон. Если в файле прописано обязательное использование pathlib вместо os.path, Claude применит эти правила при обновлении старых функций.
Нужно ли прописывать в CLAUDE.md все правила оформления кода?
Не стоит превращать этот файл в избыточную копию вашего линтера или документации к языку. Достаточно указать те специфические моменты, которые LLM склонна игнорировать: например, обязательное использование аннотаций типов, запрет на hard-coded пути или требования к обработке исключений. Если правила слишком раздуты, они могут начать «размывать» внимание модели, что снизит качество выполнения сложных задач.
Как правильно внедрить инструкции по зависимостям в CLAUDE.md?
В файле стоит зафиксировать требование всегда проверять наличие библиотек в текущем окружении перед предложением нового кода. Рекомендуется прописать инструкцию, чтобы модель при использовании сторонних модулей сверялась с вашим requirements.txt или poetry.lock. Это поможет избежать ситуации, когда Claude предлагает синтаксис из новой версии библиотеки, которая ещё не установлена в вашем проекте.