Глава 18: Что мы узнали
Пять архитектурных ставок
Claude Code — не единственная агентная система. И не первая. Но он сделал пять архитектурных ставок, которые отличают его от ландшафта agent frameworks, и после почти двух тысяч файлов и семнадцати глав эти ставки заслуживают разбора.
Ставка 1: Цикл-генератор вместо callback’ов
Большинство agent frameworks дают вам pipeline: определите инструменты, зарегистрируйте обработчики, позвольте фреймворку всё оркестрировать. Разработчик пишет callback’и. Фреймворк решает, когда их вызывать.
Claude Code делает наоборот. Функция query() — это async generator: разработчик владеет циклом. Модель стримит ответ, генератор выдаёт tool calls, вызывающий код исполняет их, добавляет результаты, и генератор повторяет цикл. Есть одна функция, один поток данных, одно место, через которое проходит каждое взаимодействие. 10 терминальных состояний и 7 состояний продолжения в типе возврата генератора кодируют каждый возможный исход. Цикл и есть система.
Ставка была в том, что одна функция-генератор, даже выросшая до 1 700 строк, окажется понятнее, чем распределённый граф callback’ов. После изучения исходников эта ставка сыграла. Когда вы хотите понять, почему сессия закончилась, вы смотрите на одну функцию. Когда вы хотите добавить новое терминальное состояние, вы добавляете один вариант в одно discriminated union. Type system заставляет обрабатывать всё исчерпывающе. Callback-архитектура размазала бы эту логику по десяткам файлов, а взаимодействия между callback’ами были бы неявными, а не видимыми в потоке управления.
Ставка 2: Файловая память вместо баз данных
Глава 11 подробно обосновала это, но архитектурное значение выходит за пределы памяти. Решение использовать обычные Markdown-файлы вместо SQLite, векторной базы данных или облачного сервиса — это ставка на прозрачность вместо возможностей. База данных дала бы более богатые запросы, более быстрый поиск и транзакционные гарантии. Файлы не дают ничего из этого. Зато файлы дают доверие.
Пользователь, который открывает ~/.claude/projects/myapp/memory/MEMORY.md в vim и видит в точности, что агент о нём помнит, находится в принципиально иных отношениях с системой, чем пользователь, который должен спросить агента «что ты обо мне помнишь?» и надеяться, что ответ полный. Файловый дизайн делает состояние знаний агента внешне наблюдаемым, а не только самодекларируемым. Это важнее, чем производительность запросов. Система recall на базе LLM компенсирует простоту хранения умным извлечением — side query на Sonnet, выбирающий пять релевантных воспоминаний из manifest, точнее embedding similarity и не требует никакой инфраструктуры.
Ставка 3: Самоописывающиеся инструменты вместо центрального оркестратора
Agent frameworks обычно предоставляют реестр инструментов: вы описываете инструменты в центральной конфигурации, и фреймворк показывает их модели. Инструменты Claude Code описывают себя сами. Каждый объект Tool несёт собственные имя, описание, input schema, вклад в prompt, флаг безопасности параллельного исполнения и логику выполнения. Задача tool system — не описывать инструменты модели, а дать инструментам возможность описать себя.
Эта ставка окупается в расширяемости. Инструменты MCP (глава 15) становятся полноправными гражданами, реализуя тот же интерфейс. Инструмент с MCP-сервера и встроенный инструмент неотличимы для модели. Системе не нужен отдельный слой «MCP tool adapter» — обёртка создаёт стандартный объект Tool, а дальше существующий pipeline инструментов обрабатывает его: проверку разрешений, параллельное выполнение, бюджетирование результатов, перехват hooks.
Ставка 4: Fork agents ради шаринга кэша
В главе 9 был рассмотрен механизм fork: sub-agent, который начинает с полной беседы родителя в своём окне контекста, разделяя prompt cache родителя. Это не просто удобная оптимизация — это архитектурная ставка на то, что модель разделяемого кэша стоит сложности управления жизненным циклом fork.
Альтернатива — породить нового агента с summary беседы — проще, но дороже. Каждый новый агент платит полную цену за обработку контекста с нуля. Forked agent получает кэшированный префикс родителя бесплатно (скидка 90% на input tokens), что делает выгодным порождение агентов для маленьких задач: извлечение памяти, code review, verification passes. Фоновый агент извлечения памяти (глава 11) запускается после каждого хода query loop, и его стоимость маргинальна именно потому, что он делит кэш родителя. Без шаринга кэша через fork такой агент был бы непозволительно дорогим.
Ставка 5: Hooks вместо плагинов
Большинство систем расширяемости используют plugins — код, который регистрирует возможности и работает внутри процесса хоста. Claude Code использует hooks — внешние процессы, которые запускаются в точках жизненного цикла и общаются через exit codes и JSON на stdin/stdout.
Ставка в том, что изоляция процесса стоит своих накладных расходов. Плагин может уронить хост. Hook роняет только свой процесс. Плагин может утечь памятью в heap хоста. Память hook’а умирает вместе с его процессом. Плагину нужен API surface, который нужно версионировать и поддерживать. Hook’у нужны stdin, stdout и exit code — протокол, который стабилен с 1971 года.
Накладные расходы реальны: запуск процесса на каждый вызов hook’а стоит миллисекунды, которых in-process callback не стоил бы. Fast path -70% для внутренних callback’ов (глава 12) показывает, что система понимает, что эта цена важна. Но для внешних hooks — пользовательских скриптов, team linters, enterprise policy servers — гарантия изоляции делает систему безопаснее для расширения. Enterprise может развернуть enforcement политики через hooks, не опасаясь, что некорректный script hook’а уронит сессии его разработчиков.
Что переносится, а что нет
Не каждый паттерн Claude Code обобщается. Некоторые — следствие масштаба, ресурсов или специфических ограничений, которые другие строители агентов могут не разделять.
Паттерны, которые переносятся на любого агента
Паттерн цикла-генератора. Любой агент, которому нужно стримить ответы, обрабатывать tool calls и управлять несколькими терминальными состояниями, выигрывает от явного цикла вместо сокрытия его за callback’ами. Discriminated union в типе возврата — кодирование того, почему цикл остановился, — это паттерн, который устраняет целый класс отладочных сессий в духе «почему агент остановился?».
Файловая память с recall через LLM. Конкретные детали реализации принадлежат Claude Code, но принцип — простое хранилище плюс умное извлечение — применим к любому агенту, которому нужно сохранять знания между сессиями. Таксономия из четырёх типов (user, feedback, project, reference) и тест на выводимость («можно ли это заново вывести из текущего состояния проекта?») — это переиспользуемые эвристики дизайна.
Асимметричные каналы чтения/записи для удалённого исполнения. Когда чтение — это высокочастотные потоки, а запись — низкочастотные RPC, разносить их правильно независимо от конкретного транспортного протокола.
Bitmap-предфильтры для поиска. Любой агент, ищущий по большому индексу файлов, выигрывает от 26-битного letter bitmap как предфильтра. Четыре байта на запись, одно целочисленное сравнение на кандидата — соотношение затрат и выгоды выдающееся.
Стабильность prompt cache как архитектурный вопрос. Если ваш агент использует API с prompt caching, строить prompt так, чтобы стабильное содержимое шло сначала, а изменчивое — последним, это не оптимизация — это архитектурное решение, определяющее вашу структуру затрат.
Паттерны, специфичные для масштаба Claude Code
Forked terminal renderer. Claude Code форкнул Ink и переосмыслил rendering pipeline с packed typed arrays, pool-based interning и cell-level diffing, потому что ему нужен был streaming в терминале на 60 fps. Большинство агентов рендерят в веб-интерфейс или простой лог. Такие инженерные вложения оправданы только когда терминальный рендеринг — ваш основной UI и поток данных высокочастотный.
50+ контрольных точек профилирования запуска. Это имеет смысл, когда у вас сотни тысяч пользователей и сэмплирование 0,5% даёт статистически значимые данные. Для меньшего агента достаточно более простой системы измерения времени.
Восемь типов MCP-транспорта. Claude Code поддерживает stdio, SSE, HTTP, WebSocket, SDK, две IDE-вариации и прокси Claude.ai, потому что ему нужно интегрироваться со всеми топологиями развёртывания. Большинству агентов нужны stdio и HTTP.
Модель безопасности hooks snapshot. Заморозка конфигурации hooks при запуске и отказ от её неявного перечитывания — это защита от конкретной угрозы: вредоносный код в репозитории меняет hooks после того, как пользователь принял trust dialog. Это важно, когда агент работает в произвольных репозиториях с недоверенными конфигурациями .claude/. Агент, который работает только в доверенных средах, может использовать более простое управление hooks.
Цена сложности
Почти две тысячи файлов. Что это даёт и чего стоит?
Количество файлов — обманчивый показатель сложности. Большая часть — это тестовая инфраструктура, определения типов, конфигурационные схемы и forked Ink renderer. Фактическая поведенческая сложность сосредоточена в небольшом числе плотных файлов: query.ts (1 700 строк, агентный цикл), hooks.ts (4 900 строк, система перехвата жизненного цикла), REPL.tsx (5 000 строк, интерактивный оркестратор) и функции построения prompt в системе памяти.
Сложность приходит из трёх источников, каждый со своим характером:
Разнообразие протоколов. Поддержка пяти терминальных клавиатурных протоколов, восьми MCP-типов транспорта, четырёх топологий удалённого исполнения и семи scopes конфигурации по своей природе сложна. Каждый дополнительный протокол — это линейное добавление в кодовую базу, а не экспоненциальное, но сумма получается большой. Эта сложность случайна в брооксиновском смысле: она идёт от среды (фрагментация терминалов, эволюция MCP-транспорта, топологии удалённого развёртывания), а не от решаемой проблемы.
Оптимизация производительности. Pool-based rendering, bitmap search pre-filters, sticky cache latches и speculative tool execution — всё это добавляет сложность в обмен на измеримые выигрыши производительности. Эта сложность оправдана измерениями: каждой оптимизации предшествовали данные профилирования, которые указывали на bottleneck. Риск в том, что оптимизации накапливаются и взаимодействуют так, что hot paths становится труднее менять.
Поведенческая настройка. Prompt-инструкции системы памяти, предупреждения о stale, протокол проверки, инструкция-антипаттерн «ignore memory» — это не сложность кода. Это сложность prompt’ов, и у них другая цена сопровождения. Когда поведение модели меняется между версиями, prompt-инструкции, тщательно доведённые через evals, могут потребовать новой настройки. Eval-инфраструктура (упоминаемая по всей кодовой базе через номера кейсов и eval scores) — это защита от регрессий, но она требует постоянных вложений.
Бремя сопровождения этой системы значительное. Новому инженеру, читающему кодовую базу, нужно понимать не только кодовые пути, но и результаты eval’ов, которые мотивировали конкретные формулировки prompt’ов, production incidents, которые мотивировали конкретные security checks, и профили производительности, которые мотивировали конкретные оптимизации. Комментарии в коде подробны — многие включают номера eval-кейсов и измерения до/после — но подробные комментарии в почти двух тысячах файлов сами по себе становятся нагрузкой на чтение.
Куда движутся agentic systems
Из паттернов в Claude Code видны четыре тренда, и они показывают, куда идёт отрасль.
MCP как универсальный протокол
Глава 15 описала Claude Code как одного из самых полных MCP-клиентов. Важен не сам Claude Code — важно, что MCP вообще существует. Стандартизированный протокол для discovery и invocation инструментов означает, что инструменты, построенные для одного агента, работают с любым агентом. Эффект для экосистемы очевиден: MCP-сервер для Postgres, однажды созданный, обслуживает любого агента, который говорит по MCP. Инвестиции разработчика в интеграцию инструментов становятся переносимыми.
Вывод для авторов агентов: если вы проектируете собственный протокол инструментов, вы, вероятно, ошибаетесь. MCP достаточно хорош, он становится лучше, и преимущества стандартного протокола накапливаются со временем. Делайте MCP-клиент, вносите вклад в спецификацию и дайте протоколу развиваться через обратную связь сообщества.
Многоагентная координация
Система sub-agent’ов Claude Code (глава 8), координация задач (глава 10) и механизм fork (глава 9) — это ранние реализации multi-agent паттернов. Они решают конкретные задачи — шаринг кэша, параллельное исследование, структурированную верификацию — но одновременно показывают фундаментальную проблему: накладные расходы на координацию.
Каждое сообщение между агентами потребляет токены. Каждый fork делит кэш, но добавляет ветку беседы, которую родитель должен будет в итоге согласовать. Машина состояний Task system (queued, running, completed, failed, cancelled) — это механизм координации, который добавляет сложность, не добавляя возможностей. По мере того как агенты становятся мощнее, давление сместится от «как нам координировать несколько агентов?» к «как сделать одного агента достаточно способным, чтобы координация была не нужна?»
Текущие данные говорят, что оба подхода будут сосуществовать. Простые задачи будут использовать одиночных агентов. Сложные задачи будут использовать скоординированные multi-agent системы. Инженерная задача — сделать накладные расходы на координацию достаточно низкими, чтобы точка пересечения благоприятствовала multi-agent именно для по-настоящему параллельной работы, а не просто для сложных задач.
Постоянная память
Система памяти Claude Code — это версия 1 постоянной памяти агента. Файловый дизайн, таксономия из четырёх типов, recall на базе LLM, система stale и режим KAIROS для долгих сессий — всё это решения первого поколения для проблемы, которая будет существенно эволюционировать.
Будущие системы памяти, вероятно, добавят структурированное извлечение (сейчас система забирает целые файлы; в будущем она может извлекать конкретные факты), перенос знаний между проектами (предпочтения пользователя, применимые везде, и конвенции проекта, которые не переносятся), а также совместную память (team memory в главе 11 — это первый шаг, но синхронизация, разрешение конфликтов и контроль доступа там минимальны).
Открытый вопрос — масштабируется ли файловый подход. При 200 записях памяти на проект он работает. При 2 000 записях памяти на проект manifest side query от Sonnet становится слишком большим, консолидация — слишком дорогой, а индекс выходит за свои лимиты. Архитектурная ставка на файлы вместо баз данных пройдёт своё самое жёсткое испытание по мере роста использования.
Автономная работа
Режим KAIROS, фоновый агент извлечения памяти, auto-dream consolidation, speculative tool execution — всё это шаги к автономной работе. Агент делает полезную работу без прямого запроса: он помнит то, что вы забыли сказать ему запомнить, он консолидирует собственные знания, пока вы спите, он начинает выполнять следующий инструмент до завершения текущего ответа.
Траектория ясна. Будущие агенты будут менее реактивными и более проактивными. Они будут замечать паттерны, которые пользователь не описал, предлагать исправления, которых пользователь не просил, и поддерживать собственные знания без явных команд /remember. Система памяти Claude Code с фоновым safety net для извлечения и эвристиками what to save, продуманными через prompt engineering, — это прототип этого будущего.
Ограничение — доверие. Автономная работа требует, чтобы пользователь доверял агенту в отсутствие присмотра. Файловая память, наблюдаемая система hooks, предупреждения о stale, диалоги разрешений — всё это существует потому, что доверие нужно заслужить, а не предполагать. Путь к более автономным агентам проходит через более прозрачных агентов.
Заключение
Семнадцать глав. Шесть базовых абстракций. Генераторный цикл в центре, инструменты, расходящиеся наружу, память, уходящая назад во времени, hooks, охраняющие периметр, движок рендеринга, превращающий всё это в символы на экране, и MCP, соединяющий систему с миром за пределами codebase.
Самый глубокий паттерн в Claude Code — не какой-то один приём. Это повторяющееся решение выталкивать сложность на границы. Система рендеринга выталкивает сложность в pools и diff — внутри пайплайна всё сводится к целочисленным сравнениям. Система ввода выталкивает сложность в tokenizer и resolver сочетаний клавиш — внутри обработчиков всё сводится к typed actions. Система памяти выталкивает сложность в протокол записи и селектор recall — внутри беседы всё сводится к контексту. Цикл агента выталкивает сложность в терминальные состояния и систему инструментов — внутри цикла это просто: stream, collect, execute, append, repeat.
Каждая граница поглощает хаос и экспортирует порядок. Сырые байты становятся ParsedKey. Markdown-файлы становятся recalled memories. MCP JSON-RPC становится объектами Tool. Exit codes hooks становятся решениями о разрешении. По одну сторону каждой границы мир хаотичен — пять клавиатурных протоколов, хрупкие OAuth-серверы, устаревшие воспоминания, недоверенные hooks репозитория. По другую сторону мир типизирован, ограничен и обрабатывается исчерпывающе.
Если вы строите агентную систему, вот переносимый урок. Не конкретные приёмы — вам могут не понадобиться pool-based rendering, KAIROS mode или восемь MCP-транспортов. А принцип: определите границы, поглотите там сложность и держите всё между ними чистым. Границы — это место, где инженерия трудна. Внутренность — это место, где инженерия приятна. Проектируйте приятные внутренности и вкладывайте бюджет сложности в края.
Исходный код открыт. Краб держит карту в клешне. Идите читать его.