Запіс цыклав, якія відпрацоўвае штучны інтэлект-агент у мобільным дапрыемку.
Запісваць ціклы, якія віконуюцься агентамі AI у мобільным даплі: чатыры пытанні, на якія кожны цікл павінен адпаведаць, шасць вкладаных ціклоў ад натыкання да розрэзу, а таксама перакананні, што нічога не ловіцца.
Скажыце агенту толькі працаваць над тым, каб тэст західу ў сістэму прыйшоў як успешны, і ён сам, у той жа момент, стварыць цікл. Зазвычай выйшае адна з двух рэчаў: ён прабуець знову, пакуль не будзе адзінаго успеху, нават калі тэст пераканальваецца меншым колікасцю дадзеных, чым раней. Алеў жо ён можа прачытаць свой сабэй, не знайсці ніякіх адказаў на проблемы і паведаміць, што заданне завершана. У обох случаях гэта ёсць цікламі, і ў кожным з яных модэль выбірае спосаб стварэння ціклу на адной з падстав, якія ён прачытаў.
Інжынерыя ціклаў адмахваець такі выбор модэлем. Чалавек загадзя, у пісамай форме, вяршыць рашэнне пра тое, што будзе выканана, што ён прачытае для прыняцья рашэння, што завершыць цікл і калі ёму трэба зупыніцца і папросіць дапамогі у чалавека. Чырвоныя прыклады такога падходу выкарыстоўваюцца ў мобільных дапытках.
Аранжаванне
Практычным прыкладам являецца дапрыг для голасных паведамленняў для Android і iOS, створаны ў значныяй меры за дапамогою Claude Code. Кожная сесія атрымлеўць самастоятны git checkout, які называецца worktree. У ўсём гэтым агент стварае дапрыг, інсталюе яго на эмулятор, керуе UI за дапамогою Maestro — працоўніка тэстаў на YAML, які чытае дрэво доступнасці экрана (контролы, пазначаныя і выклікваныя ОС) і фіксуе свою работу. Нічога ў цыклах не залежыць ад гэтага конкрэтнага дапрыгу. Усё, што ўможлівяе ўпрацаву, — цэх агента, які можа дзеяць і пасля таго спаглядаць.
Harness — цэ хутар, файлы, прыстроі і правілы, якія вярнююць, што бачыць і чаму дзеяе агент. Цыклы стосуюцца іншага пытання, а самэт — таго, як агент ведае ся з гэтым harness з часам.
Чатыры пытанні
Разглядзім самы маленькі цыкл: нажміце на кантакт, прачытайце дрэво доступнасці і пераканайцеся, чы не з’явілася апэктаваная пазначка. Калі ні, чакайце і прачытайце знову; пасля дзесяці секундэў прызначыце аббыцце. Незалежна ад габаротаў цыклу ў налаштаваннях агента, нават да падзеі „Чы гэтае функцыя ўжо выканана?“, ён павінен даць адпаведзенні на чатыро запытанні:
- Што выканана?
- Што ён чытае, каб прыняць рашэнне?
- Што яго завершае?
- Калі ён зупініцца і папросіць чалавека?
Без усіх чатырох адпаведзенняў немае цыклу — толькі скрыпт, які выкананае раз і спадзяецца. Дача адпаведзенняў на гэтыя запытанні для кожнага цыклу, які выкананае агент, — гэта вся сутнасць гэтай дысцыпліны. Рэшта — толькі прыклады.
Цыкл 3, аутэнзацыя, паказвае чатыро адпаведзенні, пазначаныя на рэальным помачніку. Кожны тэст, залежны ад стану, пачынаецца з яго:
- launchApp:
stopApp: true # 1. what runs: a cold launch, every time
- retry:
maxRetries: 3 # 3. what ends it: Home on screen, or three tries
commands:
- runFlow:
when:
visible:
text: "Continue" # 2. what it reads: the Login screen's button
file: login.yaml # -> sign in only if it's showing
- runFlow: assert-home.yaml # -> then prove Home is on screen
# 4. when it asks a human: never. A fourth failure is red, and red is the answer.
Фізычныя прычыны адказваюць за павторную спробу. Адразу пасля холаднага запуску экран паведамлення закрывае як апшынку, так і галоўную сторону прыблізна на тры секунды, таму па першай атракцыі можна нічога не пабачыць. Ліміт — тры спробы. Простая схема «павторны запуск пакуль не з’явіцца галоўная сторона» ніколі не завершыцца пад час несправнага запуску, тады як тры спробы дазволяюць пераканацца з медленным запускам і ўсё ж даюць можлівасць пазначыць справжню неудачу як адмову.
Шасць вярткаў, якія ўзаемна занурэны
Шасць вярткаў працуюць ад найменшага да найбольшага, прычаму кожная знаходзіцца ўнутры наступной. Вяртка 1 выконваецца дзесяткі разоў у кожным тэсце, кожны тэст працуе ў рамках запуску на два прыстроі, кожны запуск — у рамках пераканаў коміту, а кожны коміт — у рамках часткі работы.
Спачатку трэба стварыць найвнутрэшняю вяртку, адколькі всі зовнішнія залежаць ад яе. Вярткі 1, 3 і 6 — цэў вяртка натыку, апшынка і прызначэнне прыстрою. Тры з іншых вярткаў патрабуюць адзначэння.
Цікл 2 — гэта обмежаная практыка перэпробавання. Калі кэшы порожні, першая практыка складання коду ў новай структуры праекта можа спрычыніць збой у компіляторэ Kotlin. Файл памяці агента мае простая правіла: перапробаваць раз, перш чым западзецца на тое, што адбуваецца з змянамі. Гэты ліміт мае важлівэ значэнне. Безлімітна практыка перэпробавання пакуль не будзе рэзультата можа замаскаваць справжнія проблемы, але адна спроба ператварае вядомыя непасткі на нішто і робіць другую неудачу значным фактам.
Цікл 4 — гэта хук pre-commit. У гэтым рэпазітарыі няма сістэмы CI, таму гук ёсць едынны цікл, які сам актываецца пасля кожной змяны. Ён завершаецца незусім: калі відсутній сканер секрэтных дадзеных gitleaks, коміт адхоўваецца. Ён таксама запускае тэсты самых гэтых вората пры ўвядзенні змян, таму што вората, якія не можа знайсці своіе вхідныя даны, проста выдаюць значэнне OK. У складзенай форме гук выглядае так:
#!/bin/sh
set -u
if ! command -v gitleaks >/dev/null 2>&1; then
echo "pre-commit: gitleaks not found — refusing to commit without a secret scan." >&2
exit 1
fi
gitleaks git --pre-commit --staged --redact --no-banner || exit 1
Другая частына выглядае так:
# The gates' own tests run first: a discovery bug produces no answer,
# after which every gate iterates an empty list and prints OK.
python3 ./scripts/test_gates.py >/dev/null 2>&1 || exit 1for gate in check_string_parity audit_hardcoded_strings; do
[ -x "./scripts/$gate.sh" ] && { "./scripts/$gate.sh" || exit 1; }
done
Цікл 5 — гэта зовнішній цікл, які обходзіць кожную елемента работы. Фрагмент — гэта невялы частка функцыі, якую можна адправіць, і ён праходзіць праз чатыры кроку. Крок «Прапанаце» запісвае запіс прыменні з інформацыяй пра тое, што зменяецца, чаму і крытэрыяя прынятнасці. Крок «Застосаванне» выконвае гэтыя змены. Крок «Параболка» пераканальваецца, чы рэзультат пасуе гэтаму запісу. Крок «Архіўаванне» сінхронізуе спэкыфікацыі і заканчвае працу над фрагментам. Крок «Параболка» — гэта самастоятэльны крок з адзінам выходам, а не проста заўважэнне ад рэалізавальніка пра тое, што тэсты працуюць, такім чынам код не пісаецца толькі адной особай, якая за ўсё ручаецца.
Чалавечыя контрольныя пункты выклікаюцца ў пісанай табелі, якая паказвае, какія рашэнні агент прыменяе сам і якія вымагаюць спачатку запытання. Табелю керуе адна фраза: спачатку запытайцеся, чы перадзворот змены не будзе коштаць больш, чым праця ў тры дні. Агент, які прыхоўчуе крок, павінен зупініцца, паспяшыць адказ і атрымаць згоду.
Памяць — гэта тое, што перадае інфармацыю между цікламі і па разныя сесіі: група короткіх зьведзень, які Claude Code зноў чытае ў кожны раз, калі пачынаецца сесія. У гэтым праекце ёсць 52 такіх зьведзення, кожна з яых — правілле, падобнае да таго з ціклу 2. Без іх новая сесія павтарае паказаны ранейшы дыягназы з нуля.
Перакананні, якія не можуць зазнаць невялікосты
Усі гэтыя ціклы завершаюцца, калі якое-небудзь перакананне паведамляе "завершанае". Перакананне, якое паведамляе про завершанне, не знайшоўшы нічога, тыха абрывае цікл. Гэта трапілася пяць разоў у гэтым рэпазітарыі.
Вората, якія не маглі зламацца. Вората локалізацыі — гэта скрыпт, які блакуе адправку змяненняў, калі англійскі тэкст закодаваны ў інтэрфейсе без выкарыстання каталогу стрэнгаў. У ям быў фільтр пад назвай filter_animation_labels, які меў на мету адхіляцыю толькі тых элементаў, якія выглядзелі як абозначэнні анімацый. Фільтр пераглядаў window ~ tagbuilder у awk, але параметр tagbuilder ніколі не пасылаўся да awk. Незаданая змянная awk ёсць порожняя стрэлка, і window ~ "" падходзіць для всего, таму фільтр адхіляў усе элементы, якія толькі знаходзіў. Што да воратаў, то яны ніколі не былі актывацаваны жаднага разу.
Фільтр знайшоў 49 стацыянарных рэчэй у 14 файлах у функцыі надзеi дапрынаму, сярод яых англійскі contentDescription для кантакта SOS, які знаходзіцца праз адны невыкарыстоўваны пераклад. Скрыншоты, знятыя на самам прыстрое, паказалі всі рэальныя недагадкі локалізацыі ў гэтым роўні, а прыемнік нічога не выявіў. Выконваючы ролю умовы завершэння ціклу 4, ён пераглядаў кожны коміт прыблізна два дні і нічога не знайшоў.
Прыемнік, які нікуды не прывёў. Тэхнічныя спецыфікацыі выклікалі паўтарную невярнасьць будовы, але нічога гэта не контролавало, так як не існуець сістэмы CI. Першая версія хука таксама вікорыстоўвала exec gitleaks, якая заменяе шэл, і тым самым не дазволяла выконваць ніякія наступныя каманды.
Хук, які працюе з усім. Шаблон if: дозволяе обмежыты хукі Claude Code певнімі командамі. Хук, прызначэній для gh pr create, вікорыстовував лішэ Bash(gh pr create *), што ёсць занадто вузькім підходом. Шаблоні, які віходзяць за межы простоі назві команды, не функціонуюць пры зустрычы з $(), $VAR або херэдокамі. Рышэнням було вікорыстання if: Bash(gh *), дэ сам скрыпт фільтруе команду, отрыману з stdin.
Набір, які нічого не перэвірав. На працягу трох тыжнів тэсты для iOS завершуваліся ужо пасля першого натыскання, і нічого не вказувало на помілку, оскількі жодні працэсі не запускаліся. Крок, прызначэній для перэвіркі кнопкі SOS, повідомляв про COMPLETED, хоча нічого не віконував, тож тэст SOS пройшов однаково як з справнім, так і з пошкоджэнім кодом.
Прамерк, які пасуюць не даўжыню. Тры зеленыя рэзультаты былі застосаваны да APK, установленага ў іншай сесіі. Прабавальнік спецыфікацый затвердзіў 182 з 182 спецыфікацый, хоць адна з іх опісвала экран, які быў выдалены ўжо калькі месцаў раней. PATCH падаў код 200 з бэкенду, які не прымеў міграцыю, і тыха адклёкаў гэты поле.
Перш чым прамерк заслужыць довяр’е, неабходна вернуць точны дыфект, які ён створаны для выяўлення, і пераканацца, што ён показвае цвет «чырвоны», пасля чаго адключыць гэты дыфект і пераканацца, што рэзультат стае «зеленым», усё гэта ў той самай сесіі, дзе быў напісан прамерк. Скрыпт аўдыту дакументаў тут следав гэтаму прынцыпу: кожны раз, калі рэзультат стаў чырвоным, ён зменяў статус, дадаў недзейнае запэўненне або адключаў частку дадатаку. У прымітках да яго праекта гэты прынцып выказаны ў адной лініі — прамерк, які не можа паспяліць, ёсць толькі дэкоратыўны. Ён таксама не зберагае базовыя файлы, адтолькі ўтаімленне 292 вядомых знаходак, каб пасягнуць на рэзультат «зелены», спрыяе адчуванню павнай каўеражы.
З чаго пачаць
Спачатку запішыце чатыры адказу пры якомун кодзе, як каментар на пачатку флу, скрыпту чыста правіла. Тады недастыя адказы будуць відразу відны, а не ў рамках выканання, якое праходзіць без жадных паведамленняў.
Далей выкорыстоўваецца самы маленькі цыкл — цыкл tap, які мае два трэбаванні. Контролы павінны мець абазаванні: contentDescription чыста testTag для Android, accessibilityLabel для iOS. Кроме таго, агенту патрабуецца стаўкавае правіла для кожнай вяселі з экранам. У гэтым случае правіла знаходзяцца ў файле інструкцый рэпазітарыю, які Claude Code завантажае на пачатку кожнай сесыі, і ўсё зводзіцца да гэтага:
Driving the emulator:
- Before every tap: `adb shell uiautomator dump`, and find the label you intend to tap.
- Tap the centre of that node's bounds. Never reuse a coordinate from an earlier dump.
- After the tap: dump again and confirm the expected label appeared. Poll up to 10 s,
then stop and report red — do not guess a second tap.
- If the app won't launch: `pm dump <pkg> | grep enabled=` before suspecting the build.
Этыя чатыры рэчы ўтвараюць цікл, які выкананы агентам. Усё, што выкананае, — цэх dump, tap, dump. Агент чытае дрэво. Ён завершаецца, калі з’яўляецца метка або пасля 10 секунд. Агент ніколі не запытае чалавека, таму што адпаведны ўтварытак — цэх ружовы.
У канцы, перш чым паверыць у пераконтроўку, якая заканчвае цікл, спецыяльна яго нарабейце. Перазначыце метку і паглядзіце, чы не стане цікл ружовым. Калі ён застаецца зеленым, значыта нічога не чытаецца.
Усё неабходнае для наладкі складаецца з шасцях элементаў:
- Цікл tap, запісаны як правіла вышэй у файле інструкцыйяў агента. Усё інша вырачваецца з таго, што агент можа дзейсніць дзеяння, а пасля — паглядзець.
- Хук pre-commit, які не выкаананы, які зберагаецца ў рэпазітарыі і падкладаецца ў
.git/hooksза дапамогою короткага скрыпта наладкі, так што кожная перзявлянне выкананае з версіяй, якая збераглена. Без сканера немае можлівасці зберагчы змяны.
У суме гэта дае структуру працы, якая дазволяе ствараць аблікакцыю для заходу, экран, які можа прачытаць агент, прыстрой для ўрабаткі заданняў, а таксама цыклы, якія завершаюцца пасля пераканання, што прабавы зазналі нявезення.