Programovanie s AI agentmi: zadanie, testy a kontrola
Ako agentovi napísať zadanie s kritériami prijatia, prečo pracovať po malých krokoch, pokus s mutačným testovaním (koľko chýb odhalia testy šťastnej cesty, okrajové prípady a vlastnostné testy), kto má písať testy a na čo si dať pozor.
Séria Základy AI Diel 51 z 52
- Mapa umelej inteligencie: čo je čo a ako to do seba zapadá
- Stručná história umelej inteligencie: od Turinga po jazykové modely
- Dáta, model, tréning, inferencia: základné pojmy na jednom príklade
- Ako čítať matematický zápis: symboly, indexy, sumy a kvantifikátory
- Množiny a relácie: stavebnica, z ktorej sa skladajú definície v AI
- Funkcie a zobrazenia: prečo je každý model v AI funkcia
- Logika a dôkazy: pravidlá, podľa ktorých sa v AI uvažuje
- Grafy a stromy: štruktúra, na ktorej stojí hľadanie aj neurónové siete
- Lineárna algebra pre strojové učenie: vektory, matice a prečo AI hlavne násobí
- Derivácie, gradient a reťazové pravidlo: ako model zistí, ktorým smerom sa učiť
- Gradientný zostup: ako sa model učí
- Čo je pravdepodobnosť: od hodu kockou ku Kolmogorovovým axiómam
- Podmienená pravdepodobnosť a Bayesova veta: ako zmeniť odhad, keď sa dozviete niečo nové
- Náhodná premenná a distribučná funkcia: jedna funkcia, ktorá o rozdelení povie všetko
- Funkcia hustoty a pravdepodobnostná funkcia: prečo má spojitá premenná v bode pravdepodobnosť nula
- Stredná hodnota, rozptyl a kovariancia: kde rozdelenie leží, aké je široké a čo s čím súvisí
- Prehľad pravdepodobnostných rozdelení: Bernoulliho, binomické, Poissonovo, rovnomerné, normálne a exponenciálne
- Zákon veľkých čísel a centrálna limitná veta: prečo sa priemery správajú tak predvídateľne
- Štatistika a odhady parametrov: maximálna vierohodnosť, MAP a intervaly spoľahlivosti
- Teória informácie: entropia, krížová entropia a KL divergencia — odkiaľ sa berú stratové funkcie
- Prehľadávanie stavového priestoru: BFS, DFS, UCS a A* s animáciami
- Hry a protivník: minimax, alfa-beta orezávanie a Monte Carlo tree search
- Splňovanie obmedzujúcich podmienok: backtracking, propagácia obmedzení a AC-3 na sudoku a mape krajov
- Logika a znalostné systémy: dopredné a spätné reťazenie, rezolúcia a expertné systémy
- Plánovanie: STRIPS, svet kociek, heuristiky z relaxácie a plánovanie v AI agentoch
- Lokálne hľadanie a simulované žíhanie: obchodný cestujúci cez krajské mestá
- Genetické algoritmy: populácia, kríženie, mutácia a výber na batohu na výlet
- Evolučné stratégie a genetické programovanie: od pravidla jednej pätiny po Keplerov zákon
- Optimalizácia mravčou kolóniou: ako feromónová stopa nájde krátku cestu
- Optimalizácia rojom častíc: zotrvačnosť, vlastná skúsenosť a skúsenosť roja
- Neuroevolúcia: evolúcia váh aj tvaru neurónových sietí a algoritmus NEAT
- Fuzzy množiny a funkcie príslušnosti: ako zapísať „trochu teplo“
- Fuzzy logika a pravdepodobnosť: prečo „trochu teplo“ neznamená „pravdepodobne teplo“
- Fuzzy logika: t-normy, implikácie a pravidlá AK–TAK
- Fuzzy riadenie: Mamdani a Sugeno od ventilátora po kyvadlo na vozíku
- Fuzzy zhlukovanie: algoritmus fuzzy c-means a body medzi zhlukmi
- Neuro-fuzzy systémy (ANFIS): fuzzy pravidlá, ktoré sa učia z dát
- Markovove reťazce: prechodová matica, stacionárne rozdelenie a PageRank
- Skryté Markovove modely: Viterbiho algoritmus a nepoctivé kasíno
- Bayesovské siete: prečo je tráva mokrá a čo z toho vyplýva pre postrekovač
- Kalmanov filter: ako z nepresných meraní odhadnúť, kde auto je a ako rýchlo ide
- Druhy strojového učenia: s učiteľom, bez učiteľa, s čiastočným učiteľom a ďalšie
- Prompty, kontext a RAG: ako modelu dať správne informácie
- Variačné autoenkodéry (VAE): nové obrázky zo spojitého latentného priestoru
- Generatívne adversariálne siete (GAN): falšovateľ proti detektívovi
- Difúzne modely: ako sa z čistého šumu stane obrázok
- Markovove rozhodovacie procesy: stavy, akcie, odmeny a Bellmanove rovnice
- Q-learning a DQN: učenie hodnoty akcií od tabuľky po neurónovú sieť
- Policy gradient a PPO: učenie priamo stratégie
- Ako funguje AI agent: jazykový model v slučke s nástrojmi
- Programovanie s AI agentmi: zadanie, testy a kontrola
- Vyhodnocovanie a bezpečnosť AI systémov: ako merať a ako strážiť
Celá osnova série Hotovo: 52 z 83 tém
- Čo patrí do zadania pre agenta, aby vedel, kedy je hotovo.
- Koľko chýb odhalia rôzne dôkladné testy a prečo na okrajových prípadoch záleží.
- Aké chyby agenti robia a ako ich pri kontrole nájsť.
Čo sa hodí vedieť vopred: Ako funguje AI agent: jazykový model v slučke s nástrojmi, Ako pristupovať k vývoju s AI agentmi
V minulom diele sme rozobrali, ako agent funguje vo vnútri. Tento diel je praktický: ako mu zadávať prácu, ako ju kontrolovať a ako nestratiť prehľad o tom, čo sa v projekte deje. Väčšinu rád možno zhrnúť jednou vetou — agentovi musíte dať spôsob, ako sám spozná, že je hotovo, a vy musíte mať spôsob, ako to overiť. Na pokuse ukážeme, nakoľko na tom záleží.
Nadväzuje na diely Ako funguje AI agent a Ako pristupovať k vývoju s AI agentmi, kde je viac o úlohe človeka a o tom, prečo nechať agenta najprv navrhnúť riešenie.
1. Zadanie: čo, kde a ako spoznať hotovo
Definícia — kritériá prijatia
Nech je daná úloha a R je jej výsledok (stav projektu po odovzdaní práce). Kritérium prijatia je podmienka K o výsledku, ktorá spĺňa:
- týka sa len toho, čo sa dá na výsledku pozorovať alebo spustiť — konkrétne správanie („pre prázdny košík vráti 0 Kč“), testy, ktoré musia prejsť, príkaz, ktorý musí dobehnúť bez chyby;
- je overiteľná: existuje vopred známy postup (test, príkaz, kontrola podľa zoznamu), ktorý pre každý výsledok R jednoznačne rozhodne, či K(R) platí, alebo neplatí.
Ak má úloha kritériá prijatia K1, …, Km (m ≥ 1), je
úloha hotová ⇔ K1(R) ∧ K2(R) ∧ … ∧ Km(R).
Úloha je teda hotová práve vtedy, keď výsledok splní všetky kritériá. Na rozdiel od opisu cieľa („nech je to prehľadné“) sa pri kritériu dá jednoznačne rozhodnúť, či platí.
„Uprac si izbu“ je cieľ. „Na zemi nič neleží, posteľ je ustlaná a kôš je vynesený“ sú kritériá prijatia. Až s nimi sa dá povedať, či je upratané — a dieťa (alebo agent) vie, kedy môže prestať.
Definícia — špecifikácia ako predpoklad a záväzok
Presne sa kritériá prijatia dajú zapísať Hoarovou trojicou (C. A. R. Hoare, 1969; dnešný zápis so zloženými zátvorkami). Nech
- Σ je množina stavov programu — stav σ ∈ Σ priraďuje hodnotu každej premennej;
- C je program; zápis ⟨C, σ⟩ ⇓ σ′ znamená, že C spustený v stave σ dobehne a skončí v stave σ′;
- P a Q sú podmienky na stavoch, t. j. pre každé σ ∈ Σ je P(σ) aj Q(σ) pravda, alebo nepravda.
P sa nazýva predpoklad (čo smie program o vstupe očakávať), Q záväzok (čo musí o výsledku platiť). Hoarova trojica {P} C {Q} platí práve vtedy, keď
∀ σ, σ′ ∈ Σ : ( P(σ) ∧ ⟨C, σ⟩ ⇓ σ′ ) ⇒ Q(σ′).
Slovami: kedykoľvek pred spustením C platí P a C dobehne, platí potom Q. Program C je (čiastočne) správny voči špecifikácii (P, Q) práve vtedy, keď trojica platí; úplne správny práve vtedy, keď navyše z každého stavu σ s P(σ) dobehne. O stavoch, kde P neplatí, trojica nič nehovorí.
{ castka ≥ 0 } s := sazba(castka) { s = 0 pre castka < 1 000; s = 0,05 pre 1 000 ≤ castka < 5 000; s = 0,1 inak }
- castka ≥ 0
- predpoklad: za zápornú sumu funkcia neručí — je tu, aby bolo jasné, čo testovať nemusíme a čo musí ustrážiť volajúci
- s := sazba(castka)
- program, o ktorom hovoríme; špecifikácia nehovorí nič o tom, ako je napísaný, takže ju agent môže splniť ľubovoľne
- s ∈ {0; 0,05; 0,1}
- záväzok: presne tri pásma a ich hranice (≤ alebo <) — práve tu sa najčastejšie chybuje, preto patria do kritérií prijatia hodnoty 999, 1 000, 4 999 a 5 000
Dobré zadanie pre agenta má štyri časti. Nie preto, že by to bol obrad, ale preto, že každá zodpovedá jednej veci, ktorú agent inak musí hádať:
Cieľ: Na stránke košíka zobraz zľavu podľa sumy (od 1 000 Kč 5 %, od 5 000 Kč 10 %).
Kontext: Výpočet ceny je v src/kosik.js, šablóna v pohledy/kosik.ejs.
Podobne sa počíta doprava vo funkcii doprava() — drž sa rovnakého štýlu.
Obmedzenia: Nemeň formát dát v databáze. Žiadne nové knižnice.
Hotovo, keď:
- funkcia sazba(castka) vracia 0 / 0,05 / 0,1 a testy v test/kosik.test.js prejdú,
- pridáš testy pre hranice 999, 1 000, 4 999 a 5 000 Kč,
- npm test dobehne bez chyby a ukážeš mi rozdiel (git diff).
Kontext šetrí agentovi hľadanie (a vám tokeny — pozri cena kontextu). Obmedzenia bránia tomu, aby problém „vyriešil“ inak, než chcete. A kritériá prijatia mu dávajú spätnú väzbu, bez ktorej sa dlhé úlohy rozpadajú.
2. Malé kroky, časté kontroly
V diele o agentoch sme vypočítali, že úloha z dvadsiatich krokov, z ktorých každý sa podarí v 90 %, bez kontroly vyjde len v 12 % prípadov. Rovnaké pravidlo platí pre vás: čím väčší kus práce agent odovzdá naraz, tým horšie sa kontroluje a tým drahšia je oprava. Praktické dôsledky:
- Jedna zmena = jedna úloha. Veľkú vec rozdeľte na kroky, ktoré sa dajú skontrolovať samostatne (najprv výpočet, potom zobrazenie, potom úprava dát).
- Systém na správu verzií ako záchranná sieť. Pred každou úlohou čistý stav v gite, po nej commit. Keď sa niečo pokazí, vrátite sa jedným príkazom.
- Čítajte rozdiel, nie zhrnutie. Agentovo „hotovo, všetko funguje“ je tvrdenie. Rozdiel v kóde (git diff) a výstup testov sú dôkazy.
Púspech = pn
- p
- pravdepodobnosť, že sa jeden krok podarí; je tu, pretože každý krok je ďalšia príležitosť na chybu
- n
- počet krokov medzi dvoma kontrolami; v exponente je preto, že sa pravdepodobnosti nezávislých krokov násobia — a s každým ďalším krokom bez kontroly šanca klesá geometricky, nie lineárne
- Púspech
- šanca, že celý úsek medzi kontrolami dopadne dobre a nič sa nemusí prerábať
Príklad — koľko práce ušetrí kontrola po piatich krokoch
Farby podľa vzorca vyššie. Bez kontroly: Púspech = 0,920 = 0,1216, teda 12 %. S kontrolou po každých piatich krokoch: 0,95 = 0,5905 pre každý zo štyroch úsekov.
Predpokladajme, že kontrola chybu vždy spozná a pokazený úsek sa urobí znova, pričom pokusy sú nezávislé. Počet pokusov do prvého úspechu má potom geometrické rozdelenie: k-tý pokus je prvý úspešný s pravdepodobnosťou (1 − Púspech)k−1 Púspech a stredný počet pokusov je ∑k=1∞ k (1 − Púspech)k−1 Púspech = 1/Púspech. Celá úloha bez kontroly teda stojí v priemere 20/0,1216 ≈ 165 krokov, s kontrolou po piatich 4 · 5/0,5905 ≈ 34 krokov a s kontrolou po každom kroku 20/0,9 ≈ 22 krokov. Malé kroky nezvyšujú šancu, že sa jeden krok podarí — zmenšujú, koľko práce sa pri chybe zahodí.
3. Testy ako poistka: pokus
Nakoľko testy pomôžu? Vezmeme päť malých funkcií, aké agent bežne píše — priemer, priestupný rok, skrátenie textu, binárne vyhľadávanie a zľavu podľa sumy — a urobíme z nich zámerne pokazené verzie.
Definícia — mutačné testovanie
Nech f je program (originál), X množina jeho prípustných vstupov a T = {t1, …, tk} sada testov; každý test spustí program na zvolených vstupoch a buď prejde, alebo zlyhá (za zlyhanie sa počíta aj pád programu a zacyklenie).
- Mutačný operátor je pravidlo pre jednu drobnú syntaktickú zmenu kódu:
<namiesto<=,- 1namiesto+ 1,||namiesto&&, iná konštanta. - Mutant f′ je program, ktorý vznikne z f jediným použitím jedného mutačného operátora na jedno miesto kódu. M označme množinu všetkých takto vyrobených mutantov.
- Mutant f′ je ekvivalentný práve vtedy, keď ∀ x ∈ X : f′(x) = f(x), t. j. na žiadnom vstupe sa od originálu nelíši. E ⊆ M je množina ekvivalentných mutantov.
- Sada T mutanta f′ odhalí práve vtedy, keď ∃ t ∈ T : t na f′ zlyhá. O ⊆ M ∖ E je množina odhalených neekvivalentných mutantov.
Mutačné skóre sady T je podiel odhalených mutantov medzi neekvivalentnými (pre |M| > |E|):
MS(T) = |O|/|M| − |E| ∈ [0, 1].
Mutačné skóre teda meria, akú veľkú časť umelých chýb testy chytia. Ekvivalentné mutanty sa nepočítajú, pretože ich žiadny test odhaliť nemôže. Ekvivalencia sa vo všeobecnosti nedá algoritmicky rozhodnúť; v našom pokuse ju odhadujeme tak, že mutanta porovnáme s originálom na 2 000 náhodných vstupoch.
Skúška požiarneho hlásiča: schválne zapálite malú sviečku a pozeráte sa, či zapípa. Tu schválne vložíte do kódu malú chybu — presne takú, akú robia ľudia aj agenti — a pozeráte sa, či to testy spoznajú.
Zo zdrojového kódu piatich funkcií vzniklo automaticky 32 mutantov. 3 z nich sa na
2 000 náhodných vstupoch nikdy nelíšilo od originálu (napríklad a[s] <= x namiesto a[s] < x
v binárnom vyhľadávaní, kde rovnosť už ošetril predchádzajúci riadok), takže zostáva 29 skutočných chýb. Proti nim
štyri úrovne testov, každá obsahuje aj tie predchádzajúce:
- Šťastná cesta — jeden alebo dva typické príklady na funkciu. Takéto testy vznikajú, keď sa povie len „pridaj testy“.
- Okrajové prípady — prázdne pole, hranice pásiem (999, 1 000, 4 999, 5 000 Kč), roky 1900, 2000 a 2100, text presne na hranici dĺžky.
- Vlastnosti — 200 náhodných vstupov a pravidlá, ktoré musia platiť vždy: výsledok skrátenia má najviac max znakov a je začiatkom textu, nájdený index naozaj ukazuje na hľadané číslo, priemer sedí s inak zapísaným výpočtom.
Testy šťastnej cesty chytia len 15 z 29 chýb (52 %). Prejdú aj s funkciou, ktorá pre prázdne pole vracia 1, so zľavou, ktorá začína až od 1 001 Kč, alebo s binárnym vyhľadávaním, ktoré v poli s jedným prvkom nič nenájde. Pridanie okrajových prípadov zvýši podiel na 90 %, vlastnostné testy na 97 %.
| funkcia | mutantov | šťastná cesta | + okraje | + vlastnosti |
|---|---|---|---|---|
prumer | 5 | 4 | 5 | 5 |
jePrestupny | 8 | 5 | 5 | 7 |
zkrat | 3 | 2 | 3 | 3 |
najdi | 6 | 3 | 6 | 6 |
sazba | 7 | 1 | 7 | 7 |
MS = |O|/|M| − |E|
- MS
- mutačné skóre — podiel skutočných chýb, ktoré sada testov chytí — výška stĺpca v grafe
- |O|
- počet odhalených mutantov (aspoň jeden test na nich zlyhal, alebo spadli či sa zacyklili); to, čo testy dokážu
- |M|
- všetky vyrobené mutanty — zásobník chýb, proti ktorému testy meriame
- |E|
- ekvivalentné mutanty; odčítajú sa, pretože sa od originálu nelíšia, a žiadny test ich preto odhaliť nemôže — inak by ani dokonalé testy nedosiahli 100 %
U nás |M| = 32 a |E| = 3, takže menovateľ je 32 − 3 = 29 (farby podľa vzorca vyššie). Šťastná cesta: MS = 15/32 − 3 = 0,517; s okrajmi 26/32 − 3 = 0,897; s vlastnosťami 28/32 − 3 = 0,966. Postup, ktorým čísla vznikli:
pre každú funkciu f:
originál = prelož(kód f)
pre každý operátor (z → na) a každý výskyt z v kóde:
mutant = prelož(kód, kde je tento výskyt z nahradený na)
keď sa mutant na 2 000 náhodných vstupoch nikdy nelíši od originálu:
ekvivalentný — nepočíta sa, ďalší
pre každú úroveň testov u:
odhalený[u] = niektorý test úrovne u na mutantovi zlyhá,
spadne, alebo sa zacyklí (poistka po 10 000 prechodoch)
MS(u) = počet odhalených na úrovni u / počet neekvivalentných
Najzaujímavejší je priestupný rok. Okrajovými prípadmi prejdú mutanty, ktoré za priestupný považujú každý párny rok,
alebo ktoré vyradia roky deliteľné desiatimi — teda aj rok 2020. Testy s rokmi 1900, 2000 a 2100 to nespoznajú, pretože
na tie náhodou odpovedajú správne. Vlastnostný test s náhodnými rokmi obe chyby nájde. Jediný mutant, ktorý prežil
všetko, je (rok % 4 === 0 && rok % 100 !== 0) || rok % 200 === 0:
líši sa len pri rokoch deliteľných 200, ale nie 400 (200, 600, 1000…), a medzi 200 náhodnými rokmi žiadny taký nebol.
Ani náhodné testy nie sú všemocné.
Pprežije = ( 1 − q )n
- q
- podiel vstupov, na ktorých sa mutant líši od originálu; 1 − q je šanca, že ho jeden náhodný vstup nespozná
- n
- počet náhodných vstupov; vstupy sa losujú nezávisle, takže sa šance „nespoznal“ násobia
- Pprežije
- pravdepodobnosť, že mutanta minie všetkých n vstupov — chyba prejde, hoci vlastnosť je napísaná správne
Príklad — prečo prežil mutant s rokom deliteľným 200
Vlastnostný test losuje roky 0 až 2 399. Mutant rok % 200 sa od originálu líši len v 6 z nich (200, 600, 1000, 1400, 1800, 2200), takže q = 6/2400 = 0,0025 a pri n = 200 prežije s pravdepodobnosťou Pprežije = (1 − 0,0025)200 = 0,606 (farby podľa vzorca vyššie) — zhruba v šiestich pokusoch z desiatich. To, že prežil, teda nebola smola, ale očakávaný výsledok.
Mutant rok % 2 sa líši pri 600 rokoch (q = 0,25): prežije s pravdepodobnosťou Pprežije = 0,75200 ≈ 10-25, teda prakticky nikdy. Mutant rok % 10 sa líši pri 96 rokoch (q = 0,04) a prežije s pravdepodobnosťou Pprežije = 0,96200 = 0,00028.
Koľko vstupov by bolo treba, aby sa mutant s rokom 200 chytil s pravdepodobnosťou 95 %? Požadujeme (1 − q)n ≤ 0,05. Po zlogaritmovaní n ln(1 − q) ≤ ln 0,05, a keďže ln(1 − q) < 0, pri delení sa nerovnosť obráti: n ≥ ln 0,05/ln(1 − q) = −2,996/−0,00250 ≈ 1 196,8, teda 1 197 vstupov. Lacnejšie je doplniť okrajový prípad rok 200 alebo 600 — kto pozná pravidlo kalendára, vie, kde hľadať.
4. Kto má písať testy
Testy v pokuse sme písali podľa zadania (čo má funkcia robiť), nie podľa kódu. To je podstatné. Keď agent napíše kód a hneď k nemu testy, hrozí, že testy len zopakujú, čo kód robí — vrátane jeho chýb. Prejdú, a nič nedokazujú. Pomáha:
- Testy (alebo aspoň ich zoznam) vopred. Nechajte agenta najprv napísať testy podľa zadania, prečítajte si ich, a až potom kód. Kritériá prijatia zo zadania sú presne ten zoznam.
- Okrajové prípady vymenovať. Prázdny vstup, jedna položka, hranice pásiem, záporné čísla, diakritika. Agent ich často doplní sám, ale len keď ich chcete.
- Vlastnosti tam, kde sa dá. „Zoradené pole je stále zoradené“, „zakódovať a dekódovať = pôvodný text“, „nový výpočet dáva to isté čo starý“. Knižnice ako fast-check (JavaScript) alebo Hypothesis (Python) generujú náhodné vstupy samy.
Definícia — vlastnostné testovanie (property-based testing)
Nech f : X → Y je testovaná funkcia. Vlastnosť je podmienka φ : X × Y → {pravda, nepravda} na dvojici vstup–výstup; funkcia ju spĺňa práve vtedy, keď
∀ x ∈ X : φ(x, f(x)).
Vlastnostný test je tento algoritmus.
- Vstup: funkcia f, vlastnosť φ, počet pokusov N ∈ ℕ, N ≥ 1, generátor náhodných prvkov X a zjednodušovacia funkcia, ktorá ku každému x ∈ X vráti konečnú množinu kandidátov zjednoduš(x) ⊆ X, z ktorých každý má ostro menšiu veľkosť než x (veľkosť je prirodzené číslo — dĺžka textu, absolútna hodnota celého čísla, dĺžka poľa).
- Výstup: protipríklad x ∈ X s ¬φ(x, f(x)), alebo správa „prešlo N vstupov“.
- Pre i = 1, …, N: vygeneruj náhodné x ∈ X; ak platí ¬φ(x, f(x)), pokračuj krokom 2.
- Zmenšovanie: kým existuje y ∈ zjednoduš(x) s ¬φ(y, f(y)), polož x := y. Veľkosť je prirodzené číslo a v každom prechode klesne, takže zmenšovanie skončí. Vráť protipríklad x a skonči.
- Ak v kroku 1 žiadny vstup vlastnosť neporušil, vráť „prešlo N vstupov“.
Test teda hľadá protipríklad k výroku „pre všetky x“; keď ho nenájde, vlastnosť tým dokázaná nie je. Myšlienku spopularizovala knižnica QuickCheck (Koen Claessen a John Hughes, 2000).
Nepýtate sa „koľko je 7 + 5“, ale „platí, že a + b je vždy to isté ako b + a?“ — a necháte počítač vyskúšať tisíc náhodných dvojíc. Nájde prípady, na ktoré by ste nepomysleli.
Vlastnosť je výrok s kvantifikátorom „pre všetky“. Pre funkciu zkrat z pokusu ju test zapisuje takto:
∀ t, m ≥ 1: |v| ≤ m ∧ ( |t| ≤ m ⇒ v = t ) ∧ ( |t| > m ⇒ v = t[0, |v| − 1) + „…“ ), kde v = zkrat(t, m)
- ∀ t, m ≥ 1
- pre každý text a každú dĺžku — preto sa dá overovať na náhodných vstupoch a nepotrebujeme poznať správny výsledok vopred
- |v| ≤ m
- hlavný sľub funkcie: výsledok sa zmestí; chytí mutanta, ktorý zabudne započítať „…“ (
slice(0, max)) - |t| ≤ m ⇒ v = t
- krátky text sa nemení; bez toho by prešla funkcia, ktorá skracuje vždy
- v = t[0, |v| − 1) + „…“
- skrátený výsledok je začiatok pôvodného textu a končí výpustkou; bez toho by prešla funkcia, ktorá vracia napríklad prázdny text
Nástroj na vlastnostné testovanie potom robí zhruba toto:
pre i = 1 … N: # N býva 100 až 1 000
x = vygeneruj náhodný vstup
keď vlastnosť(x, f(x)) neplatí:
# zmenšovanie: hľadaj jednoduchší vstup, na ktorom to stále zlyhá
kým existuje y = zjednoduš(x), na ktorom vlastnosť neplatí:
x = y # kratší text, menšie číslo, kratšie pole
ohlás protipríklad x
ohlás „prešlo N vstupov“ # dôkaz to nie je, len dobrá správa
Posledný riadok je podstatný. Testy, ani náhodné, nedokazujú správnosť — len hľadajú protipríklady. Edsger Dijkstra to v roku 1969 povedal tak, že testovanie dokáže ukázať prítomnosť chýb, nikdy ich neprítomnosť. Pravdepodobnosť, že zriedkavá chyba prekĺzne, sme si vypočítali vyššie.
5. Na čo si dať pozor
Agenti robia iné chyby ako ľudia. Najčastejšie, ktoré sa oplatí pri kontrole hľadať:
- „Hotovo“ bez overenia. Agent oznámi úspech bez toho, aby testy spustil, alebo ich spustil len čiastočne. Chcite výstup testov.
- Úprava testov namiesto kódu. Keď test zlyhá, najjednoduchšia „oprava“ je zmeniť očakávanú hodnotu alebo test zmazať. V rozdiele hľadajte zmeny v testoch.
- Umlčané chyby. Prázdny
catch, predvolená hodnota namiesto chyby, natvrdo zapísaný výsledok pre testovací vstup. - Práca navyše. Prepísaný súbor, ktorý s úlohou nesúvisí, nová knižnica, „vylepšený“ formát dát. Obmedzenia v zadaní a malé kroky to držia v medziach.
- Tajomstvá a nevratné kroky. Heslá v kóde, mazanie súborov, zápis do produkčnej databázy, odoslanie do vzdialeného repozitára. Tieto akcie nechajte schvaľovať.
6. Nestratiť prehľad
Agent si medzi sedeniami nič nepamätá. Čo má vedieť zakaždým — ako sa projekt spúšťa a testuje, aké sú konvencie, čomu sa vyhnúť — patrí do súboru s pokynmi v koreni projektu, ktorý si agent prečíta na začiatku. Píšte tam to, na čo ste museli agenta upozorniť viac než raz. A to, čo viete len vy (prečo je niečo urobené takto, čo sa skúšalo a nefungovalo), patrí do commitov a dokumentácie — pre agenta aj pre seba o pol roka.
Posledná rada je najjednoduchšia: zostaňte pri tom. Nie nevyhnutne pri počítači — agent môže pracovať sám a vy sa pozriete, až bude potrebovať rozhodnutie. Preto vznikol coden: zadanie, schvaľovanie krokov a kontrolu výsledku máte v telefóne, zatiaľ čo agent pracuje na vašom počítači.
7. Zhrnutie
- Dobré zadanie: cieľ, kontext (súbory, vzory), obmedzenia a overiteľné kritériá prijatia.
- Malé kroky, commit po každom, a kontrola rozdielu v kóde a výstupu testov, nie zhrnutia.
- V pokuse odhalili testy šťastnej cesty 52 % chýb, s okrajovými prípadmi 90 %, s vlastnosťami 97 %.
- Testy píšte (alebo schvaľujte) podľa zadania a ideálne pred kódom, inak len zopakujú chyby implementácie.
- Strážte úpravy testov, umlčané chyby, prácu navyše a nevratné kroky.
Chcete si to vyskúšať? Požiadajte AI agenta, nech na váš projekt pustí nástroj na mutačné testovanie (Stryker pre JavaScript, mutmut pre Python) a ukáže vám mutanty, ktoré prežili. Každý z nich je test, ktorý chýba.
Máte na to iný názor? Napíšte na support@coden-app.com.