Як писати агентів Claude Code, які тобі не брешуть
Два правила для надійних пайплайнів з агентами Claude Code: один агент — одна спеціалізація, і shell-команди замість промптів усюди, де відповідь має бути кількісною.
Ти попросив Claude Code зверстати цей дизайн і перевірити що він збігається з макетом у Figma. Він повернувся: Готово. Усі секції відповідають, відступи правильні, кольори ті. Відкриваєш сторінку — половина відступів не та, hover-стану взагалі немає, кнопки на відтінок не той. Модель не збрехала зі зла — вона передбачила, що ти хочеш почути перевірено, і згенерувала саме цю послідовність токенів. Кроку перевірки не було. Його й не могло бути — перевірка вимагає порівняння з ground truth, а один агент в одному контексті не вміє вийти за межі власної відповіді і себе ж перевірити.
Два правила перетворили мої хаотичні робочі процеси з галюцинаціями на надійні пайплайни: один агент — одна спеціалізація і усе що можна виконати як shell-команду, мусить виконуватись як shell-команда. Це не теорія. Це те що я роблю щодня з Claude Code, і саме ці патерни реально зрушують справу.
Чому генералістські агенти брешуть
LLM — це передбачувач наступного токена. Коли в промпті дві ролі — зроби X і перевір X — модель закінчує першу роль, а потім передбачає, як виглядав би вивід другої, не виконуючи її насправді. Самоперевірка структурно слабка: той самий контекст, та сама модель, ті самі сліпі плями. Пройдено перевірки корелює з пройдено побудови — вони фейляться разом.
Модель не знає, що бреше. З її точки зору, я все ретельно перевірив — це когерентне продовження я написав код. Це та сама причина, чому ти впевнений? не ловить галюцинації: модель упевнена і на другому проході. Впевненість не корелює з правильністю — вона корелює з тим, наскільки правдоподібно звучить наступне речення.
Рішення — не кращий промпт. Будь обережний, перевір ще раз, не галюцинуй — ці інструкції не роблять нічого. Рішення структурне: спеціалізуй агента так, щоб він фізично не міг прикидатися, і пропускай кількісну роботу через shell, щоб відповідь приходила з реального стану, а не з ймовірності токенів.
Правило 1: один агент — одна спеціалізація
Розділи роботу на окремих агентів з окремими контекстами. У кожного — одна відповідальність і тісний набір інструментів. Увесь процес стає естафетою, а не одним агентом, що бігає по колу:
- Builder-агент: бере spec, пише код. Це його єдина задача. У нього
Read,Edit,Write,Bash. - Reviewer-агент: бере spec плюс diff, перевіряє по критеріях. Чистий контекст. Не знає як код було написано — тільки що вийшло. У нього
Bash,Read,Grep,Glob— жодних інструментів запису. - Analytics-агент: відповідає на питання по даних, конструюючи і запускаючи запити. Тільки
Bash. Не може дійти до відповіді без запуску реальної команди. - Orchestrator: основна сесія, яка викликає кожного агента по черзі і ніколи не просить одного робити роботу іншого.
Конкретний приклад: верстка UI плюс візуальна перевірка по макету в Figma. Builder пише компоненти і комітить diff. Далі orchestrator викликає Reviewer-а з URL макету, diff-ом і чіткими acceptance criteria. Reviewer запускає Playwright, знімає скріншоти, дифить їх з референсом і повертає PASS або FAIL з реальними шляхами скріншотів і pixel-diff. Builder взагалі не наближається до кроку перевірки — і саме тому перевірка справжня.
Анти-патерн — це мега-агент: один промпт типу зверстай цей UI і переконайся що він збігається з макетом. Гарантую — він напише, що все збігається. Не збігається. Наратив я перевірив — це просто найімовірніша послідовність токенів після я зверстав.
Правило 2: shell замість промпта, завжди
Усе кількісне, усе що зачіпає реальний стан, усе де відповідь може бути неправильною так, що виглядає правильно — пускай через sh. Задача агента — сконструювати і запустити команду, потім прочитати її вивід. Агент не є джерелом істини. Вивід shell є.
- Підрахунок:
wc -l logs.txt— правда. Приблизно 47 рядків логу від моделі — галюцинація. - Аналітика:
psql -c "SELECT count(*) FROM events WHERE created_at > now() - interval '30 days'". Не оціни обʼєм. - Тести:
pnpm test --reporter=json | jq '.numFailedTests'. Не розкажи що зафейлилось. - Git-стан:
git rev-list --count main..HEAD,git diff --stat. Не порахуй коміти чи опиши зміни.
Коли це засвоїш, починаєш помічати кожне місце, де агент щойно зібрався вигадати число. Виглядає що тут близько 200 записів... — ні. Запусти SELECT count(*). Більшість тестів проходить... — ні. Запусти тести, спарси JSON. Модель чудова в конструюванні команди. Вона ненадійна в тому, щоб бути командою.
Конкретні режими фейлу, які я ловив
Це не гіпотетичні приклади. Кожен з них коштував мені реального часу до того, як я змінив патерн:
- Примарна перевірка. Агент сказав я перевірив усі 14 секцій по макету. Не відкривав макет. Не знімав скріншот. Перевірка була галюцинованим кроком у наративі.
- Упевнені неправильні числа. Просив monthly active users з аналітичних даних. Отримав число, що промахнулось у ~3 рази. Модель інтерполювала з зразкових рядків замість запустити реальний запит.
- Вигадані зміни файлів. Агент сказав я оновив
config/feature-flags.json. Не оновлював. Він тільки збирався.git diffбув порожній. - Фейкові прогони тестів. Усі тести проходять. Жоден тест не запускався. Агент навіть не викликав test runner — він передбачив, як виглядав би вивід раннера.
Усі чотири розв'язуються тими ж двома правилами: розділи агента, виштовхуй у shell. У Reviewer-а немає Write, тому він не може фейково редагувати файли. У Analytics-агента тільки Bash, тому він не може повернути число, яке не прийшло з запиту. Структурна неможливість виграє у добрих намірів завжди.
Як це структурувати в Claude Code
Claude Code підтримує sub-agents, що описуються у .claude/agents/*.md. Кожен файл агента декларує імʼя, опис, дозволений набір інструментів і системний промпт. Orchestrator (твоя основна сесія) викликає їх через інструмент Agent. Ось тип визначення, який я використовую для reviewer-а — короткий, вузький, фізично нездатний писати код:
---
name: reviewer
description: Reviews a diff against acceptance criteria. Cannot edit code.
tools: Bash, Read, Grep, Glob
---
You are a strict code reviewer. You receive:
- A diff (already produced by the builder)
- Acceptance criteria
Your job:
1. Run the build, the tests, the linter — through Bash.
2. Read the changed files directly.
3. Compare the actual behavior to the criteria.
4. Report PASS or FAIL with concrete evidence (command output, file excerpts).
You must NOT:
- Trust the builder's summary
- Assume anything was verified just because it was claimed
- Mark something PASS without running the actual checkЗверни увагу на набір інструментів: Bash, Read, Grep, Glob. Жодного Write, жодного Edit, жодного Agent. Reviewer може запускати команди, читати файли, шукати патерни — і нічого більше. Якщо він спробує видати галюцинований diff за перевірений, форма його викликів інструментів робить це очевидним: реальних перевірок не було. Можна заавдитити виклики і побачити рівно те, що інспектувалось.
Патерн оркестрації: основна сесія викликає Builder-а → чекає → сама запускає git diff, щоб зафіксувати реальну зміну → викликає Reviewer-а зі spec-ом і diff-ом → читає вердикт. Основна сесія ніколи не просить одного агента робити обидва. Обмеження інструментів сильніші за інструкції в промпті: не фейкай перевірку — це побажання. Відсутність Write — це факт.
Анти-патерни, які треба викинути
Те, що бачу в промптах і що не робить нічого — або, гірше, дає хибне відчуття безпеки:
- Будь обережний і перевір роботу ще раз. Не генерує жодної додаткової поведінки. Модель уже видає те, що виглядає як обережна робота.
- Переконайся що ти дійсно перевірив. Слово дійсно не додає семантики, на яку модель може зреагувати. Вона дійсно заявить що перевірила.
- Не галюцинуй. Мем у prompt engineering. Галюцинація — не перемикач, який модель може вимкнути.
- Довіра моделі на маленьких числах. Саме на маленьких числах вона бреше найвпевненіше. Порогу чесності не існує.
- Додавання правил у промпт, щоб змусити чесність. Структурні рішення (розділити + shell) виграють у твіків промпта завжди. Якщо правило треба enforce-ити — закодуй його в доступі до інструментів, а не в англійській.
Якщо твоя стратегія ловити галюцинації — це підбирати більш емфатичні формулювання, то в тебе не стратегія. У тебе надія.
Ментальна модель
Агент — не колега. Це функція: prompt → tokens. Функція чудова в писанні коду і погана в інтроспекції того, чи вона зробила правильну річ. Сприймай її твердження про власну роботу як гіпотезу. Diff, exit-код, скріншот, row count — ось докази. Підсумок наприкінці ходу — найбрехливіша поверхня в усій системі.
Спеціалізація — твоя страховка від наративного дрейфу. Shell — твоє єдине джерело істини. Builder пише. Reviewer перевіряє. Bash вирішує.
Висновок
Якщо запамʼятати одне: не дозволяй одному агенту і виробляти, і судити власний вивід; і не дозволяй жодному агенту відповідати на кількісне питання без запуску команди. Усе інше — наслідок цих двох правил. Конфігуруй доступ до інструментів агресивно, аудитуй виклики інструментів замість підсумків — і поверхня галюцинацій скорочується з усюди до кількох конкретних місць, де ти вже знаєш дивитись.