🔍 Simon Willison
Создатель Django, исследователь LLM и автор блога simonwillison.net. Подход Willison строится на одном принципе: CC — мощный инструмент который врёт с уверенным видом. Верификация каждого вывода — не паранойя, а профессионализм.
"Never Trust, Always Verify" — CC может написать отличный код и при этом незаметно изменить логику, сослаться на несуществующий метод или "вспомнить" факт которого нет в данных. Каждая строка кода, каждый вывод из данных, каждое утверждение — требует проверки человеком прежде чем попасть в production.
Willison активно использует LLM для анализа данных и документов в своих исследовательских проектах. Но он установил жёсткое правило: каждый факт из вывода CC должен сопровождаться конкретной ссылкой на источник в исходных данных.
Это не недоверие к LLM — это понимание архитектуры: модель интерполирует и обобщает. При анализе CSV или JSON файлов она может "сгладить" выброс, округлить цифру или создать правдоподобный но несуществующий паттерн.
Промпт-шаблон для безопасного анализа данных:
import anthropic
client = anthropic.Anthropic()
def analyze_with_citations(data: str, question: str) -> str:
"""
Анализ данных с обязательными цитатами источников.
Willison-паттерн: каждое утверждение = ссылка на строку данных.
"""
prompt = f"""Вот данные для анализа:
{data}
Вопрос: {question}
ВАЖНЫЕ ПРАВИЛА:
1. Для каждого факта или вывода — укажи точную строку/запись из данных
2. Используй формат: "Факт X (строка N: [цитата из данных])"
3. Если данных недостаточно для вывода — прямо скажи об этом
4. НЕ ДОДУМЫВАЙ факты которых нет в предоставленных данных
5. Разделяй: "данные показывают" vs "можно предположить"
Ответ:"""
message = client.messages.create(
model="claude-opus-4-8",
max_tokens=2048,
messages=[{"role": "user", "content": prompt}]
)
return message.content[0].text
# Пример использования
with open("sales_data.csv", "r") as f:
data = f.read()
result = analyze_with_citations(
data,
"Какой месяц был самым прибыльным и почему?"
)
print(result)
# Вывод будет содержать: "Март (строка 47: 'март,145000,+23%')"
# а не просто "Март был лучшим месяцем"
CC обучен на огромном корпусе текстов и умеет генерировать правдоподобные выводы. При анализе данных эта "правдоподобность" работает против тебя: модель может сгенерировать вывод который звучит логично но не следует из твоих конкретных данных. Требование цитат делает это очевидным.
Simon Willison создал Datasette — инструмент который превращает любую SQLite базу данных в REST API и веб-интерфейс. CC идеально вписывается в этот workflow: пишет SQL-запросы, Python скрипты для ETL, но каждый шаг верифицируется через реальный результат запроса.
Паттерн работы с данными через CC:
import sqlite3
import anthropic
def cc_sql_pipeline(db_path: str, research_question: str) -> dict:
"""
Willison-паттерн: CC пишет SQL, Python верифицирует,
только потом CC интерпретирует.
"""
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# Шаг 1: получить схему для CC
cursor.execute("SELECT sql FROM sqlite_master WHERE type='table'")
schema = "\n".join(row[0] for row in cursor.fetchall() if row[0])
client = anthropic.Anthropic()
# Шаг 2: CC пишет SQL запрос (только запрос, без интерпретации)
sql_response = client.messages.create(
model="claude-opus-4-8",
max_tokens=512,
messages=[{
"role": "user",
"content": f"""Схема БД:
{schema}
Напиши ТОЛЬКО SQL запрос (без объяснений) для ответа на:
"{research_question}"
Требования: только SELECT, без subqueries сложнее 2 уровней."""
}]
)
sql_query = sql_response.content[0].text.strip()
# Убрать markdown если CC обернул в ```sql
if sql_query.startswith("```"):
sql_query = sql_query.split("\n", 1)[1].rsplit("```", 1)[0].strip()
# Шаг 3: выполнить запрос и получить РЕАЛЬНЫЕ данные
try:
cursor.execute(sql_query)
results = cursor.fetchmany(100) # Лимит для безопасности
columns = [d[0] for d in cursor.description]
except Exception as e:
return {"error": str(e), "sql": sql_query}
# Шаг 4: CC интерпретирует верифицированные данные
results_text = "\n".join(str(dict(zip(columns, row))) for row in results[:20])
interpretation = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
messages=[{
"role": "user",
"content": f"""Реальные результаты SQL запроса:
{results_text}
Вопрос исследования: {research_question}
Интерпретируй ТОЛЬКО то что видно в данных выше.
Каждый вывод — с цитатой из результатов."""
}]
)
conn.close()
return {
"sql": sql_query,
"row_count": len(results),
"sample": results[:5],
"interpretation": interpretation.content[0].text
}
Willison ввёл в своей практике правило: каждый нетривиальный фрагмент кода написанного CC должен иметь человеческий комментарий, объясняющий почему CC принял именно это решение. Это принципиально отличается от обычных комментариев к коду.
Через 6 месяцев ты не вспомнишь что именно ты просил CC сделать. Аннотация сохраняет эту информацию прямо в коде:
# [CC-DECISION 2026-03-15] Willison-аннотация:
# Задача была: разобрать CSV с непоследовательными датами (3 разных формата).
# CC выбрал dateutil.parser вместо strptime потому что в промпте
# было упомянуто "форматы неизвестны заранее". Если форматы стабилизируются —
# заменить на явный strptime для скорости.
from dateutil import parser as dateutil_parser
def parse_flexible_date(date_str: str):
"""Парсинг даты с неизвестным форматом."""
return dateutil_parser.parse(date_str)
# [CC-DECISION 2026-03-15] Willison-аннотация:
# Пагинация через cursor вместо offset/limit — CC предложил это
# когда я описал проблему пропуска строк при параллельных вставках.
# offset/limit давал дублирование при изменении данных между запросами.
# Cursor-based pagination решает это. Источник: промпт содержал
# "данные могут изменяться во время экспорта".
def paginate_results(cursor_id: str = None, limit: int = 100):
query = "SELECT * FROM events WHERE id > ? ORDER BY id LIMIT ?"
params = (cursor_id or 0, limit)
return db.execute(query, params).fetchall()
Обычный комментарий # cursor-based pagination для стабильности не объясняет: почему не offset? Какое требование это вызвало? Безопасно ли изменить? Аннотация Willison сохраняет контекст промпта прямо рядом с кодом — это документация решения, а не документация реализации.
Если тебе требуется больше 5 минут чтобы понять что именно сделал CC и почему — это не значит что задача сложная. Это значит что промпт был неточным или контекст был недостаточным.
Willison использует это как диагностический инструмент: непонятный код от CC — сигнал переформулировать задачу, не сигнал принять код на веру.
# Ситуация: CC написал что-то и ты не понимаешь зачем
# ПЛОХО: принять код потому что "наверное оно умнее знает"
# ХОРОШО: применить Five Minute Rule
# Промпт 1: объяснение для junior
"Объясни этот код как будто я Junior developer который
никогда не видел этот паттерн. Почему именно такой подход?
Какие альтернативы существуют и почему ты выбрал этот?"
# Промпт 2: упрощение
"Этот код решает задачу но я не понимаю зачем нужны
эти 3 уровня абстракции. Можешь переписать проще,
даже если будет чуть менее 'правильно'?"
# Промпт 3: явные ограничения
"Перепиши это решение с явными комментариями для каждого
нетривиального шага. Если используешь паттерн — назови его."
# Промпт 4: trade-offs
"Ты использовал [паттерн X]. Объясни:
1. Что это даёт в этом контексте
2. Что это усложняет
3. При каких условиях стоило бы сделать иначе"
Если ты попросил объяснить и после объяснения всё равно не понял — возможно задача действительно требует изучения домена. Но если объяснение понятно а код непонятен — попроси переписать так чтобы код сам объяснял себя через именование и структуру.
Willison придерживается строгого правила: тесты для кода написанного CC должен писать человек. Не CC. Это противоречит популярному паттерну "попроси CC написать и код и тесты" — и вот почему.
Рекомендуемый стек для верификации:
import pytest
from hypothesis import given, strategies as st
# Код написан CC: функция нормализации email
# Тест написан ЧЕЛОВЕКОМ на основе бизнес-требований
from app.utils import normalize_email # CC написал эту функцию
class TestNormalizeEmail:
"""
Тесты написаны разработчиком — не CC.
Каждый тест отражает БИЗНЕС-требование, а не реализацию.
"""
# Property-based test: hypothesis генерирует сотни случаев
@given(st.emails())
def test_result_is_always_lowercase(self, email):
"""Email всегда должен быть в нижнем регистре."""
result = normalize_email(email)
assert result == result.lower()
@given(st.emails())
def test_result_has_no_leading_trailing_spaces(self, email):
"""Email никогда не должен иметь пробелы по краям."""
result = normalize_email(f" {email} ")
assert result == result.strip()
def test_gmail_dots_are_removed(self):
"""Gmail игнорирует точки в local-части — наш нормализатор тоже."""
assert normalize_email("si.mon.w@gmail.com") == "simonw@gmail.com"
def test_gmail_plus_addressing_stripped(self):
"""Gmail plus-адреса нормализуются к базовому."""
assert normalize_email("simon+test@gmail.com") == "simon@gmail.com"
def test_preserves_non_gmail_plus(self):
"""Для не-Gmail доменов plus оставляем как есть."""
result = normalize_email("user+tag@company.com")
assert "+" in result # company.com может использовать plus
# Намеренно СЛОМАТЬ реализацию и убедиться что тест падает:
# normalize_email = lambda x: x # раскомментировать для проверки
# pytest должен выдать FAILED — иначе тест бесполезен
Принцип "намеренно сломай": после написания теста временно сломай реализацию и проверь что тест падает. Зелёный тест при сломанной реализации = тест проверяет не то что надо.
Библиотека hypothesis генерирует сотни случайных входных данных и находит граничные случаи которые ты не придумал бы сам. Именно для верификации CC-кода это особенно ценно: CC мог пропустить edge case который hypothesis обнаружит за секунды.
Willison ведёт simonwillison.net с 2002 года и сделал его инструментом верификации: когда он публично описывает что сделал LLM в его проектах — сообщество находит ошибки. Публичность создаёт давление точности.
Для командной разработки этот принцип трансформируется в внутренние decision logs:
# docs/cc-decisions/2026-05-10-payment-refactor.md
## Задача
Рефакторинг PaymentService для поддержки Stripe + YooKassa
## Что попросили CC сделать
"Рефакторни PaymentService чтобы добавить второй провайдер платежей.
Используй Strategy pattern. Интерфейс должен не меняться для клиентов."
## Что CC предложил
Strategy pattern с PaymentProviderInterface.
Конкретные реализации: StripeProvider, YooKassaProvider.
PaymentService принимает провайдер через DI.
## Что мы изменили после ревью
- CC использовал abstract class вместо interface — изменили на interface
потому что у нас есть mock для тестов и abstract class с ним плохо дружит
- CC не добавил retry logic — добавили вручную
- Имена методов изменили с execute() на charge() — более явная семантика
## Что НЕ проверяли (риск)
- Обработка webhooks от YooKassa не тестировалась на реальном sandbox
- Refund flow написан CC без покрытия тестами — TODO
## Кто делал ревью
@senior-dev — архитектура
@backend-dev — тесты
CC не участвовал в ревью своего же кода
Ресурсы Simon Willison
| Ресурс | Тема | Почему читать |
|---|---|---|
| simonwillison.net/tags/llm | LLM в реальных проектах | Честные разборы с ошибками и выводами, не хайп |
| datasette.io | SQLite → API | Инструмент от автора, воплощение data-pipeline паттерна |
| PyCon US talks (YouTube) | LLM для разработчиков | Практические демонстрации без маркетинга |
| llm.datasette.io | CLI для LLM | Его собственный инструмент для работы с LLM из терминала |
#Code with Claude 2026 — live blog
Ключевые анонсы
- 📈 API вырос в 17× за год — партнёрство со SpaceX (Colossus datacenter)
- ⏱️ Удвоение лимита времени Claude Code для Pro, Max и Enterprise
- 🔢 Rate limits ×2 для Opus, повышение API-лимитов
- 🤖 Multi-agent orchestration — флоты агентов для сложных задач
- 💤 Dreaming — фоновый анализ сессий, обновление памяти агентов
- 🎯 Outcomes — задание критериев успеха, итеративное достижение
- 📱 Remote Agents — управление сессией с телефона
- ⏰ Routines — асинхронные автоматизации, готовые PR в фоне
Наблюдения Саймона
Advisor Strategy
Новый паттерн: топовая модель (Opus 4.8 или Fable 5) в роли «советчика» для более быстрых. Качество frontier-модели при кратном снижении стоимости. Claude Opus планирует, Sonnet исполняет — разработчик наблюдает за результатом.
Антипаттерны из сообщества (реальный опыт)
- Агрессивные auto-memory writes → 315 дублей файлов за 2 месяца
- Parallel-agent fanout для простых задач — overhead > выгода
- Hook-driven auto-commits → коммиты сломанного кода
- Parallel-agent fanout для простых задач
- 6 плагинов: code-reviewer, brainstorming, debugging, claude-md-improver
- 3 хука: SessionStart, PreToolUse bash-safety, PostToolUse format
- Claude Code для 3+ файлов, Copilot для single-file completion
- CLAUDE.md < 500 токенов — принципиально важно