Межі та lifecycle агентів

На минулому вебінарі ви найняли першого спеціаліста - read-only reviewer. Сьогодні наймаємо другого - tester, і йому потрібне право писати файли. Ось тут і починається частина роботи з агентами: як видати мінімум прав і не отримати співавтора production-коду, чим обмежити знання ролі, як ізолювати її роботу від вашої і як приймати результат не на віру, а за контрактом.

Сьогодні розберемо:

Наскрізний приклад - .claude/agents/tester.md: зберемо його, обмежимо, ізолюємо, приймемо результат і доведемо від шумної v0.1 до командної v0.3.

Сьогодні пройдемо: рівень 12. Інструменти, permissions, context isolation і lifecycle агентів.

Інструкція без замка

Ви полагодили баг у refund flow і просите tester-агента написати regression test. В інструкції чесно написано: "production-код не змінюй". А в tools видано все підряд: Read, Glob і Grep для читання й пошуку, Bash для команд, Edit і Write - повне редагування проєкту.

Агент вирішує "допомогти" швидше: править production-код, тести зелені, проблема формально зникла. Замість тестера у вас ініціативний співавтор, якого ніхто не просив. Інструкція без технічної межі - це табличка "не заходити" на дверях без замка.

Різниця, яку варто запам'ятати з цього слайда:

Залишите тільки перше - отримаєте зайвий diff і знайоме відчуття "я просив перевірити тести, а не переставити меблі в усій квартирі". Tools - межа відповідальності ролі, а не декоративне поле frontmatter.


Права сесії - стеля

Агент не живе у вакуумі: він працює всередині permission-контексту вашої сесії. Deny-правила і protected paths, які ви налаштовували на старті курсу, діють і на нього. І сам собі дозволити він нічого не може: коли фоновому агенту потрібна спірна дія, permission prompt приходить до вас у сесію.

flowchart LR A["Права вашої сесії"] --> B["tools агента: звуження під роль"] B --> C["Що агент реально може"] D["deny-правила і protected paths"] -. діють на всіх .-> C

На схемі важливий напрямок: поле tools працює лише в один бік - звужує. Розширити агента за межі сесії воно не може.

В агента є й інші поля тюнінгу - permissionMode, maxTurns, effort. Це для спеціальних випадків: першому tester вони не потрібні.

Мінімум прав на живих ролях

Робоче питання при видачі прав - не "що вміє Claude", а "який мінімум дій потрібен цій ролі заради її результату". Тому збирайте від результату: спочатку "що агент має повернути", потім мінімальні дії під це, потім неприємне питання - "що станеться, якщо він застосує інструмент неідеально?".

РольРезультатToolsЧого не видаємо
reviewerfindings по diff з evidenceRead, Glob, Grep, BashEdit і Write
testerтести і результати прогонівRead, Glob, Grep, Bash, Edit, Writeредагування production-файлів
documenterоновлені docsRead, Glob, Grep, Edit, WriteBash "про запас"
debuggerroot cause з evidenceRead, Glob, Grep, Bashзазвичай нічого не пише

Тонке місце - tester: Edit йому справді потрібен, але не як "право на все", а заради одного класу файлів. Межу каталогів дасть інструкція - зберемо її на наступному слайді. А швидкий тест будь-якого конфігу один: чи зможе агент випадково покращити те, що ви не просили покращувати? Так - набір усе ще широкий.

Не проєктуйте права тижнями. Видайте мінімум, проженіть на реальному завданні, подивіться diff - і розширюйте лише там, де реально вперлися.

Збираємо agents/tester.md

Перетворюємо tester "з усім підряд" з першої сцени на вузьку роль. Файл той самий за форматом, що reviewer.md з минулого вебінару, - інша начинка:

---
name: tester
description: Пише і запускає тести для поточного diff.
  Production-код не змінює.
tools: Read, Glob, Grep, Bash, Edit, Write
model: inherit
---

## Роль
Tester: проєктуєш і пишеш тести, ганяєш прогони.

## Межі
Редагуй лише файли в tests/ і src/test/.
Потрібен фікс production-коду, щоб тест пройшов, -
зупинись і поверни пояснення людині.

## Формат результату
Які тести додано, які команди запущено,
що не вдалося перевірити.

Edit і Write тут не "про всяк випадок", а заради одного результату - тестових файлів. Збирали б reviewer - їх не було б зовсім. Поверх tools іде текстове звуження: каталоги плюс стоп-умова "потрібен фікс production-коду - зупинись". Тільки чесно: це контракт поведінки, а не технічний замок - tools не вміють обмежувати запис одним каталогом. Тому після прогону дивимось changed files: production-файл у списку - результат не приймаємо, навіть якщо тести зелені.

Порівняйте з reviewer.md: обидва дивляться на той самий diff, але в одного write-прав немає взагалі, у другого вони зв'язані межами каталогів. Це least privilege у живому вигляді, а не гасло на стіні. Формат результату поки короткий - до повного контракту доростимо в другій половині вебінару.


Не лише руки: шари capability

Після налаштування tools легко вирішити, що роботу зроблено: інструменти видно одразу, і вони дають приємне відчуття контролю. Але tools відповідають лише на питання "що агент може руками" - і мовчать про те, за якою процедурою він думає, куди дивиться назовні і що пам'ятає між запусками.

ШарГоловне питанняПоле frontmatterПриклад для tester
toolsщо можна рукамиtoolsRead, Bash, вузький Edit
skillsза якою процедурою працюєskillstest-strategy
MCPякі зовнішні дані читаєmcpServersзазвичай нічого
memoryщо пам'ятає між запускамиmemoryнотатки про структуру тестів

З чотирьох шарів новий тут лише один. MCP - спосіб підключати до Claude зовнішні інструменти й дані: наприклад, щоб агент читав документацію бібліотек або завдання з трекера. Решту шарів ви вже бачили в ділі.

Least privilege застосовується до всіх чотирьох шарів одразу, не лише до tools. Skill, MCP і memory теж видаються за least privilege: не потрібно ролі - не підключаємо. Роль не потребує пам'яті - її відсутність не "недоналаштований агент", а дисципліна. Перевірка проста: приберу цей skill, MCP чи пам'ять - роль перестане працювати? Якщо відповідь "ні, просто йому спокійніше" - це зайвий контекст.


Дві ізоляції, два болі

Права звузили, знання обмежили - а ідеально налаштований агент усе ще може зіпсувати вам роботу. Причому двома різними способами.

Спосіб перший ви знаєте з минулого вебінару: завалити основну сесію проміжним читанням - п'ятнадцять файлів заради одного факту. Спосіб другий новіший: агент лаконічний і дисциплінований, але пише в той самий робочий каталог, де ви правите руками. Diff розпух, незрозуміло, де чиї правки, а git status дивиться на вас з осудом.

Context isolationWorktree isolation
Що захищаєясність основної сесіїфайли і Git state
Біль без неїконтекст засмічений дослідженнямdiff змішаний, правки конфліктують
Підходить дляширокого read-only аналізуризикованих або паралельних правок
Не вирішуєконфлікти зміншум у контексті

Звідси головний висновок блоку: ізоляція - не тумблер "зробити безпечніше", а вибір, який саме біль ви лікуєте. Поки не назвете захищуване своїм ім'ям, ізоляція вмикається навмання.


Worktree: окрема копія репозиторію

Worktree - окрема робоча директорія зі своєю гілкою при тій самій історії репозиторію. Правки в ній не чіпають ваш основний checkout: експеримент живе у своїй теці і у своїй гілці.

claude --worktree refund-lab
# Claude Code створює .claude/worktrees/refund-lab/
# на новій гілці worktree-refund-lab і стартує сесію в ній

Експеримент невдалий - видалили worktree, і основний checkout у стані "нічого не сталося". Вдалий - порівняли diff і перенесли потрібне. Дешево в обидва боки.

Для агента те саме робиться одним рядком frontmatter: isolation: worktree - агент отримує тимчасову копію репозиторію, і вона сама видаляється, якщо правок не було. Рівно наш випадок: tester пише тестові файли - нехай пише їх у своїй копії, а не поруч із вашими ручними правками.

---
name: tester
tools: Read, Glob, Grep, Bash, Edit, Write
isolation: worktree
---
Worktree - свіжий checkout: незакомічені правки туди не потраплять, а гілкується він за замовчуванням від default-гілки репозиторію, не від вашої. Агенту потрібен ваш свіжий фікс - спершу закомітьте його, а в settings задайте worktree.baseRef: "head", щоб worktree стартував від вашого локального HEAD.
Додайте .claude/worktrees/ у .gitignore, щоб вміст worktree не світився як untracked-файли в основному checkout.

Яку ізоляцію обирати

Вибір зводиться до двох питань: агент багато читатиме без правок? будуть правки, особливо експериментальні чи паралельні?

СитуаціяГоловний більЩо обрати
багато читання, правок немаєсесія засмітитьсяcontext isolation - звичайний subagent
агент пробуватиме правкизіпсує ваш diffworktree isolation
спершу аналіз, потім експериментпід загрозою і контекст, і файлиобидві, по черзі
завдання на один-два файлиізоляція дорожча за користьіноді не потрібна взагалі

На нашому refund-багу зв'язка виглядає так: розвідку віддаємо Explore або read-only варіанту ролі - він проходить по коду і тестах і повертає зведення "regression-покриття на partial refund немає"; це context isolation. Write-tester вмикаємо другим кроком, коли зрозуміло, який тест потрібен: з isolation: worktree він додає тест і ганяє прогони у своїй копії - основний checkout чистий.

Ізоляція - не ознака просунутості. Для правки в одному файлі окрема копія репозиторію - чистий overhead; найпрофесійніший вибір іноді - не вмикати її.

"Перевір це" - мутний результат

Рука сама тягнеться до короткого "перевір diff". У відповідь приходить: "Код загалом виглядає нормально, можливо, варто додати пару тестів". Агент не дурний - ви не задали йому форму результату. Він не знає, чи повертати severity, чи вказувати file:line, чи відділяти гіпотези від фактів і що робити з diff на пів модуля.

Погано:
Перевір цей diff.

Краще:
Перевір поточний diff по refund flow. Код не редагуй.
Фокус: regressions, пропущені тести, сумісність API.
Поверни findings за severity з file:line і evidence.
Diff більший за 500 рядків - зупинись і попроси розбити.

У другому варіанті зафіксовано п'ять речей:

Форма потрібна не стільки агенту, скільки вам: саме вам потім вирішувати - брати результат у роботу чи повертати на доопрацювання.


Output contract

Output contract - та сама домовленість про формат відповіді, що й у разового subagent, але для постійної ролі вона працює на приймання: що роль має повернути, де докази й коли вона має зупинитися. Handoff note передає стан завдання далі; контракт робить зворотне - приймає результат ролі назад.

СекціяЩо ви за нею перевіряєте
summaryчи зрозумів агент завдання і scope
findingsчи є severity і прив'язка file:line
команди й exit codesщо реально запускалося, а не "ніби запускав"
uncertaintyгіпотези позначені, а не видані впевненим тоном
changed filesчи не порушені межі ролі: у read-only ролі тут none
next stepодна зрозуміла дія далі

Зверніть увагу на баланс: file:line і exit code перевіряються майже механічно, а severity і рекомендації потребують вашого судження. Хороший контракт тримає і те, і друге - тільки "м'які" поля дають упевненого коментатора, тільки "жорсткі" - сухий протокол без сенсу.

Контракт живе у файлі агента - це той самий розділ "Формат результату" в tester.md. Запит у сесії не вигадує формат заново, а лише заземляє його в конкретний diff.

Сигнал підвищення: втретє диктуєте в запиті ті самі секції відповіді - формат доріс до контракту. Зафіксуйте його у файлі агента, і далі він працюватиме без нагадувань у кожній сесії.

Приймання: прийняти, повернути, відхилити

Найдоросліша частина делегування починається не в момент запуску агента, а в момент повернення результату. Тут видно, у вас інженерний процес чи ще один чат із самовпевненим співрозмовником. Рішень лише три:

Було (повернути на уточнення):
- Можливо, є проблеми з refund flow.
- Варто ще подивитися тести.

Після (прийняти):
- major - orders/refund/refundPolicy.ts:87
  evidence: умова пропускає amount == 0, тесту немає
  recommendation: regression test на zero-amount case
- команди: npm test -- refundPolicy -> exit code 0
- changed files: none

Слабкий результат не оплакуємо і не викидаємо: можливо, агент щось помітив - просимо переоформити за контрактом. А от reviewer, у якого в changed files раптом production-файл, - це не "уточнити". Це відхилити: порушена межа ролі.

flowchart TD A[Агент повернув результат] --> B{"Є evidence і file:line?"} B -->|ні| C[Повернути на уточнення] B -->|так| D{"Scope і межі ролі дотримані?"} D -->|ні| E[Відхилити] D -->|так| F[Прийняти в роботу]

Сенс схеми простий: ви приймаєте не розумне враження, а структурований артефакт, який можна перевірити за полями.


Failure modes: ламається передбачувано

Коли агент дивно поводиться, хочеться сказати "AI сьогодні не в гуморі". Звучить по-людськи, користі нуль. В агента не настрій, а failure mode - повторюваний тип збою, який можна розпізнати й полагодити.

ЗбійЯк виглядаєЧим лікується
hallucinated findingsупевнені твердження без evidenceжорсткіші вимоги до evidence в контракті
over-permissioned agentправить зайве "щоб тести пройшли"звузити tools, прибрати write-права
stale instructionsагент живе за старими правилами проєктуоновити файл, підняти версію
memory pollutionтягне старі гіпотези в нові завданняпочистити або звузити memory
lost contextвідповідає за шаблоном, але повз завданняуточнити description і вхідний контекст

Зверніть увагу на закономірність: кожне лікування - правка конфігурації, а не вмовляння агента "бути уважнішим". І правило на випадок дивної поведінки: не додавайте магію - спростіть стек. Що складніша ієрархія capabilities, то важче знайти джерело збою.


Lifecycle: від чернетки до довіри

Агент - інженерний артефакт: живе в репозиторії, змінюється, проходить review і застаріває. Супроводжують його як код, а не як налаштування "створив і забув".

flowchart TD A[Створили agent] --> B[Прогнали на маленьких кейсах] B --> C[Розібрали збої, звузили tools і контракт] C --> D[Підняли версію, поділилися з командою] D --> E[Зібрали feedback] E --> F{"Допомагає більше, ніж шумить?"} F -->|так| C F -->|ні| G[Позначили deprecated і видалили]

Найбільш недооцінений етап на схемі - маленькі кейси. Не тестуйте нового агента одразу на живому завданні: візьміть 3-5 коротких кейсів із заздалегідь зрозумілим очікуванням - "тільки test-файли, не можна чіпати refundPolicy.ts". Так ви одразу бачите порушення меж, а не гадаєте "мабуть, загалом непогано".

Наш tester пройшов цей цикл на ваших очах:

ВерсіяПоведінкаЩо змінили
v0.1пише тести, але іноді править productionприбрали широкий write, посилили контракт
v0.2не лізе в production, але findings без evidenceзробили evidence обов'язковим
v0.3передбачуваний і кориснийможна ділитися з командою

У циклу є фінальна стадія, яку не люблять романтики автоматизації: агент шумить більше, ніж допомагає, - чесно позначити deprecated і видалити. Якщо агент економить менше часу, ніж забирає на розбір output, його треба спростити або прибрати. Іноді найкраща версія агента - та, яку вчасно спростили або прибрали. А правки інструкції йдуть звичайною review-гілкою, як будь-який код: критерії команди лежать в одному файлі й змінюються на виду у всіх.


Практика: завдання і мета

Тепер ваш хід - і агент нового типу. md2html з минулого рівня робив роботу сам; file2md будує інструмент і оркеструє його. Тут важливо спершу зрозуміти, що ви здаєте і навіщо, а вже потім іти в деталі реалізації.

Завдання - зібрати .claude/agents/file2md.md: агента, який дістає markdown з pdf, html і excel через python-скрипти. Мета практики - відпрацювати зв'язку "агент думає, скрипт працює": LLM розуміє людський запит, а python робить важку роботу з файлами.

Скрипти ви не пишете руками. Ви просите Claude Code створити їх, покласти в .claude/scripts/, зафіксувати залежності й перевірити запуск. У здачі мають лежати:


Практика: деталі реалізації

Усередині агента три формати, але одна ідея: по одному вузькому скрипту на формат і єдиний машинний контракт для всіх. pdf читає pdfplumber, html - beautifulsoup4 + markdownify, excel - openpyxl.

У скриптів спільний інтерфейс: <input> <output> [--pages n,n] плюс службовий --info. Агент приймає людські параметри й перетворює їх на точний виклик скрипта.

ТипПараметрПоведінка
обов'язковийшлях до вхідного файлуфайлу немає - стоп
опціональнийtype = auto | pdf | html | excelauto визначає за розширенням; можна задати явно
опціональнийpages словами: "першу, середню, останню"агент перетворює слова на числа; для html чесна відмова - у нього немає сторінок
опціональнийoutputкуди класти .md; дефолт - поруч із джерелом

Least privilege тут гостріший, ніж у md2html: агенту потрібні Bash для запуску python і Write з Edit для створення скриптів - це небезпечніше за read-only.

Межа в інструкції обов'язкова: скрипти тільки в .claude/scripts/, не чіпати нічого за межами input/, output/ і scripts/; вихід за межу - стоп.

Практика: як довести, що працює

Головна думка проєкту: "параметри агента" і "параметри скрипта" - два різні інтерфейси. Людина говорить агенту живою мовою будь-що; скрипт розуміє тільки --pages 1,10,20. Агент - перекладач між ними.

Ви: дістань першу, середню й останню сторінки report.pdf

Агент: python .claude/scripts/pdf2md.py report.pdf --info
       -> 20 сторінок
Агент: середня = 10, остання = 20
       python .claude/scripts/pdf2md.py report.pdf report.md --pages 1,10,20
       -> exit code 0, report.md готовий

Чому ділити саме так: скрипт тупий, вузький і надійний - його легко написати через Claude Code і легко перевірити. Уся гнучкість - в агенті. Засунете "розуміння слів" у скрипт - він розпухне; змусите людину говорити числами - навіщо тоді агент.

Output contract з першої половини вебінару - у дії: після запуску агент повертає команду, exit code, що витягнув і чого не зміг. Скрипт упав - агент не бреше "готово", а показує лог. А в README потрапляє замір стиску: pdf на 1.8 МБ перетворюється на 12 КБ текстової суті - інструмент відкинув службове й оформлення, а не "md ефективніший у 150 разів".

Definition of Done, якщо зовсім коротко:

Головна пастка - агент вгадує "середню" без --info. У few-shot прикладах інструкції явно: спочатку --info, потім рахуй, потім --pages.
Вийшло, якщо за запитом "першу, середню й останню" в логу видно обидва виклики - службовий --info і робочий --pages. Це і є "агент думає, скрипт працює".