Межі та lifecycle агентів
На минулому вебінарі ви найняли першого спеціаліста - read-only reviewer. Сьогодні наймаємо другого - tester, і йому потрібне право писати файли. Ось тут і починається частина роботи з агентами: як видати мінімум прав і не отримати співавтора production-коду, чим обмежити знання ролі, як ізолювати її роботу від вашої і як приймати результат не на віру, а за контрактом.
Сьогодні розберемо:
- tools і least privilege - права під результат ролі, а не "про всяк випадок";
- scoped skills, MCP і пам'ять - контур знань агента;
- context isolation і worktree isolation - що саме ви захищаєте;
- output contract - як приймати, повертати і відхиляти результат;
- оцінку агента, failure modes і життєвий цикл до чесного deprecated.
Наскрізний приклад - .claude/agents/tester.md: зберемо його, обмежимо, ізолюємо, приймемо результат і доведемо від шумної v0.1 до командної v0.3.
Інструкція без замка
Ви полагодили баг у refund flow і просите tester-агента написати regression test. В інструкції чесно написано: "production-код не змінюй". А в tools видано все підряд: Read, Glob і Grep для читання й пошуку, Bash для команд, Edit і Write - повне редагування проєкту.
Агент вирішує "допомогти" швидше: править production-код, тести зелені, проблема формально зникла. Замість тестера у вас ініціативний співавтор, якого ніхто не просив. Інструкція без технічної межі - це табличка "не заходити" на дверях без замка.
Різниця, яку варто запам'ятати з цього слайда:
- роль відповідає на питання "навіщо агент існує" - її ви описали словами;
- tools відповідають "що він має право робити руками" - це вже техніка, а не прохання.
Залишите тільки перше - отримаєте зайвий diff і знайоме відчуття "я просив перевірити тести, а не переставити меблі в усій квартирі". Tools - межа відповідальності ролі, а не декоративне поле frontmatter.
Права сесії - стеля
Агент не живе у вакуумі: він працює всередині permission-контексту вашої сесії. Deny-правила і protected paths, які ви налаштовували на старті курсу, діють і на нього. І сам собі дозволити він нічого не може: коли фоновому агенту потрібна спірна дія, permission prompt приходить до вас у сесію.
На схемі важливий напрямок: поле tools працює лише в один бік - звужує. Розширити агента за межі сесії воно не може.
- не вказали tools - агент успадковує всі інструменти сесії (це ви пам'ятаєте з минулого вебінару);
- вказали - все інше відрізано: allow-list, а не побажання;
- зворотний спосіб -
disallowedTools: "все, крім" - зручно, коли простіше назвати заборонене, ніж перелічувати дозволене.
Мінімум прав на живих ролях
Робоче питання при видачі прав - не "що вміє Claude", а "який мінімум дій потрібен цій ролі заради її результату". Тому збирайте від результату: спочатку "що агент має повернути", потім мінімальні дії під це, потім неприємне питання - "що станеться, якщо він застосує інструмент неідеально?".
| Роль | Результат | Tools | Чого не видаємо |
|---|---|---|---|
reviewer | findings по diff з evidence | Read, Glob, Grep, Bash | Edit і Write |
tester | тести і результати прогонів | Read, Glob, Grep, Bash, Edit, Write | редагування production-файлів |
documenter | оновлені docs | Read, Glob, Grep, Edit, Write | Bash "про запас" |
debugger | root cause з evidence | Read, Glob, Grep, Bash | зазвичай нічого не пише |
Тонке місце - tester: Edit йому справді потрібен, але не як "право на все", а заради одного класу файлів. Межу каталогів дасть інструкція - зберемо її на наступному слайді. А швидкий тест будь-якого конфігу один: чи зможе агент випадково покращити те, що ви не просили покращувати? Так - набір усе ще широкий.
Збираємо 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 | що можна руками | tools | Read, Bash, вузький Edit |
| skills | за якою процедурою працює | skills | test-strategy |
| MCP | які зовнішні дані читає | mcpServers | зазвичай нічого |
| memory | що пам'ятає між запусками | memory | нотатки про структуру тестів |
З чотирьох шарів новий тут лише один. MCP - спосіб підключати до Claude зовнішні інструменти й дані: наприклад, щоб агент читав документацію бібліотек або завдання з трекера. Решту шарів ви вже бачили в ділі.
Least privilege застосовується до всіх чотирьох шарів одразу, не лише до tools. Skill, MCP і memory теж видаються за least privilege: не потрібно ролі - не підключаємо. Роль не потребує пам'яті - її відсутність не "недоналаштований агент", а дисципліна. Перевірка проста: приберу цей skill, MCP чи пам'ять - роль перестане працювати? Якщо відповідь "ні, просто йому спокійніше" - це зайвий контекст.
Дві ізоляції, два болі
Права звузили, знання обмежили - а ідеально налаштований агент усе ще може зіпсувати вам роботу. Причому двома різними способами.
Спосіб перший ви знаєте з минулого вебінару: завалити основну сесію проміжним читанням - п'ятнадцять файлів заради одного факту. Спосіб другий новіший: агент лаконічний і дисциплінований, але пише в той самий робочий каталог, де ви правите руками. Diff розпух, незрозуміло, де чиї правки, а git status дивиться на вас з осудом.
| Context isolation | Worktree 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.baseRef: "head", щоб worktree стартував від вашого локального HEAD..claude/worktrees/ у .gitignore, щоб вміст worktree не світився як untracked-файли в основному checkout.Яку ізоляцію обирати
Вибір зводиться до двох питань: агент багато читатиме без правок? будуть правки, особливо експериментальні чи паралельні?
| Ситуація | Головний біль | Що обрати |
|---|---|---|
| багато читання, правок немає | сесія засмітиться | context isolation - звичайний subagent |
| агент пробуватиме правки | зіпсує ваш diff | worktree isolation |
| спершу аналіз, потім експеримент | під загрозою і контекст, і файли | обидві, по черзі |
| завдання на один-два файли | ізоляція дорожча за користь | іноді не потрібна взагалі |
На нашому refund-багу зв'язка виглядає так: розвідку віддаємо Explore або read-only варіанту ролі - він проходить по коду і тестах і повертає зведення "regression-покриття на partial refund немає"; це context isolation. Write-tester вмикаємо другим кроком, коли зрозуміло, який тест потрібен: з isolation: worktree він додає тест і ганяє прогони у своїй копії - основний checkout чистий.
"Перевір це" - мутний результат
Рука сама тягнеться до короткого "перевір diff". У відповідь приходить: "Код загалом виглядає нормально, можливо, варто додати пару тестів". Агент не дурний - ви не задали йому форму результату. Він не знає, чи повертати severity, чи вказувати file:line, чи відділяти гіпотези від фактів і що робити з diff на пів модуля.
Погано:
Перевір цей diff.
Краще:
Перевір поточний diff по refund flow. Код не редагуй.
Фокус: regressions, пропущені тести, сумісність API.
Поверни findings за severity з file:line і evidence.
Diff більший за 500 рядків - зупинись і попроси розбити.
У другому варіанті зафіксовано п'ять речей:
- scope - refund flow, а не весь проєкт;
- межі ролі - код не редагувати;
- фокус уваги - regressions, тести, API;
- формат результату - severity, file:line, evidence;
- умова зупинки - великий diff, не геройствувати.
Форма потрібна не стільки агенту, скільки вам: саме вам потім вирішувати - брати результат у роботу чи повертати на доопрацювання.
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.
Приймання: прийняти, повернути, відхилити
Найдоросліша частина делегування починається не в момент запуску агента, а в момент повернення результату. Тут видно, у вас інженерний процес чи ще один чат із самовпевненим співрозмовником. Рішень лише три:
- прийняти - evidence є, scope дотримано, межі ролі не порушені;
- повернути на уточнення - ідея корисна, але немає доказів або маркування гіпотез;
- відхилити - неочікувані edits, порушені межі ролі або стоп-умови.
Було (повернути на уточнення):
- Можливо, є проблеми з 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-файл, - це не "уточнити". Це відхилити: порушена межа ролі.
Сенс схеми простий: ви приймаєте не розумне враження, а структурований артефакт, який можна перевірити за полями.
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 і застаріває. Супроводжують його як код, а не як налаштування "створив і забув".
Найбільш недооцінений етап на схемі - маленькі кейси. Не тестуйте нового агента одразу на живому завданні: візьміть 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/, зафіксувати залежності й перевірити запуск. У здачі мають лежати:
.claude/agents/file2md.md;- три скрипти в
.claude/scripts/; requirements.txtз точними версіями;- README з двома інтерфейсами, output contract і таблицею стиску;
- 4 демо-прогони в
log/.
Практика: деталі реалізації
Усередині агента три формати, але одна ідея: по одному вузькому скрипту на формат і єдиний машинний контракт для всіх. pdf читає pdfplumber, html - beautifulsoup4 + markdownify, excel - openpyxl.
У скриптів спільний інтерфейс: <input> <output> [--pages n,n] плюс службовий --info. Агент приймає людські параметри й перетворює їх на точний виклик скрипта.
| Тип | Параметр | Поведінка |
|---|---|---|
| обов'язковий | шлях до вхідного файлу | файлу немає - стоп |
| опціональний | type = auto | pdf | html | excel | auto визначає за розширенням; можна задати явно |
| опціональний | 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, якщо зовсім коротко:
- агент і три скрипти в git, requirements.txt з точними версіями;
- type=auto працює, невідомий формат - чесна відмова;
- pages словами доведений логом обох викликів - службового і робочого;
- межі каталогів в інструкції; 4 демо-прогони в log/.