Preskočiť na obsah
coden

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.

10 minút Matěj Žalmánek 2026-10-16

Séria Základy AI Diel 51 z 52
  1. Mapa umelej inteligencie: čo je čo a ako to do seba zapadá
  2. Stručná história umelej inteligencie: od Turinga po jazykové modely
  3. Dáta, model, tréning, inferencia: základné pojmy na jednom príklade
  4. Ako čítať matematický zápis: symboly, indexy, sumy a kvantifikátory
  5. Množiny a relácie: stavebnica, z ktorej sa skladajú definície v AI
  6. Funkcie a zobrazenia: prečo je každý model v AI funkcia
  7. Logika a dôkazy: pravidlá, podľa ktorých sa v AI uvažuje
  8. Grafy a stromy: štruktúra, na ktorej stojí hľadanie aj neurónové siete
  9. Lineárna algebra pre strojové učenie: vektory, matice a prečo AI hlavne násobí
  10. Derivácie, gradient a reťazové pravidlo: ako model zistí, ktorým smerom sa učiť
  11. Gradientný zostup: ako sa model učí
  12. Čo je pravdepodobnosť: od hodu kockou ku Kolmogorovovým axiómam
  13. Podmienená pravdepodobnosť a Bayesova veta: ako zmeniť odhad, keď sa dozviete niečo nové
  14. Náhodná premenná a distribučná funkcia: jedna funkcia, ktorá o rozdelení povie všetko
  15. Funkcia hustoty a pravdepodobnostná funkcia: prečo má spojitá premenná v bode pravdepodobnosť nula
  16. Stredná hodnota, rozptyl a kovariancia: kde rozdelenie leží, aké je široké a čo s čím súvisí
  17. Prehľad pravdepodobnostných rozdelení: Bernoulliho, binomické, Poissonovo, rovnomerné, normálne a exponenciálne
  18. Zákon veľkých čísel a centrálna limitná veta: prečo sa priemery správajú tak predvídateľne
  19. Štatistika a odhady parametrov: maximálna vierohodnosť, MAP a intervaly spoľahlivosti
  20. Teória informácie: entropia, krížová entropia a KL divergencia — odkiaľ sa berú stratové funkcie
  21. Prehľadávanie stavového priestoru: BFS, DFS, UCS a A* s animáciami
  22. Hry a protivník: minimax, alfa-beta orezávanie a Monte Carlo tree search
  23. Splňovanie obmedzujúcich podmienok: backtracking, propagácia obmedzení a AC-3 na sudoku a mape krajov
  24. Logika a znalostné systémy: dopredné a spätné reťazenie, rezolúcia a expertné systémy
  25. Plánovanie: STRIPS, svet kociek, heuristiky z relaxácie a plánovanie v AI agentoch
  26. Lokálne hľadanie a simulované žíhanie: obchodný cestujúci cez krajské mestá
  27. Genetické algoritmy: populácia, kríženie, mutácia a výber na batohu na výlet
  28. Evolučné stratégie a genetické programovanie: od pravidla jednej pätiny po Keplerov zákon
  29. Optimalizácia mravčou kolóniou: ako feromónová stopa nájde krátku cestu
  30. Optimalizácia rojom častíc: zotrvačnosť, vlastná skúsenosť a skúsenosť roja
  31. Neuroevolúcia: evolúcia váh aj tvaru neurónových sietí a algoritmus NEAT
  32. Fuzzy množiny a funkcie príslušnosti: ako zapísať „trochu teplo“
  33. Fuzzy logika a pravdepodobnosť: prečo „trochu teplo“ neznamená „pravdepodobne teplo“
  34. Fuzzy logika: t-normy, implikácie a pravidlá AK–TAK
  35. Fuzzy riadenie: Mamdani a Sugeno od ventilátora po kyvadlo na vozíku
  36. Fuzzy zhlukovanie: algoritmus fuzzy c-means a body medzi zhlukmi
  37. Neuro-fuzzy systémy (ANFIS): fuzzy pravidlá, ktoré sa učia z dát
  38. Markovove reťazce: prechodová matica, stacionárne rozdelenie a PageRank
  39. Skryté Markovove modely: Viterbiho algoritmus a nepoctivé kasíno
  40. Bayesovské siete: prečo je tráva mokrá a čo z toho vyplýva pre postrekovač
  41. Kalmanov filter: ako z nepresných meraní odhadnúť, kde auto je a ako rýchlo ide
  42. Druhy strojového učenia: s učiteľom, bez učiteľa, s čiastočným učiteľom a ďalšie
  43. Prompty, kontext a RAG: ako modelu dať správne informácie
  44. Variačné autoenkodéry (VAE): nové obrázky zo spojitého latentného priestoru
  45. Generatívne adversariálne siete (GAN): falšovateľ proti detektívovi
  46. Difúzne modely: ako sa z čistého šumu stane obrázok
  47. Markovove rozhodovacie procesy: stavy, akcie, odmeny a Bellmanove rovnice
  48. Q-learning a DQN: učenie hodnoty akcií od tabuľky po neurónovú sieť
  49. Policy gradient a PPO: učenie priamo stratégie
  50. Ako funguje AI agent: jazykový model v slučke s nástrojmi
  51. Programovanie s AI agentmi: zadanie, testy a kontrola
  52. Vyhodnocovanie a bezpečnosť AI systémov: ako merať a ako strážiť

Celá osnova série Hotovo: 52 z 83 tém

Čo si odnesiete
  • Č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:

  1. 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;
  2. 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í.

Laicky povedané

„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

  1. Σ je množina stavov programu — stav σ ∈ Σ priraďuje hodnotu každej premennej;
  2. C je program; zápis ⟨C, σ⟩ ⇓ σ′ znamená, že C spustený v stave σ dobehne a skončí v stave σ′;
  3. 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
Riadok „Hotovo, keď“ v zadaní nižšie je ten istý záväzok, zapísaný tak, aby ho bolo možné overiť testom.

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ť
Zjednodušene: kroky zlyhávajú nezávisle a chyba sa spozná až pri kontrole.
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).

  1. Mutačný operátor je pravidlo pre jednu drobnú syntaktickú zmenu kódu: < namiesto <=, - 1 namiesto + 1, || namiesto &&, iná konštanta.
  2. 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.
  3. 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.
  4. 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.

Laicky povedané

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.
0 %25 %50 %75 %100 % 0 z 29 žiadne 15 z 29 šťastná cesta 26 z 29 + okraje 28 z 29 + vlastnosti úroveň testov (kumulatívna, každá obsahuje predchádzajúce) odhalené chyby (% z 29)
Koľko z 29 chýb testy odhalia

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 %.

funkciamutantovšťastná cesta+ okraje+ vlastnosti
prumer5455
jePrestupny8557
zkrat3233
najdi6366
sazba7177

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
Náhodné testy spoľahlivo chytia chyby, ktoré sa prejavujú často; zriedkavé chyby len s veľkým počtom vstupov.
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“.
  1. Pre i = 1, …, N: vygeneruj náhodné x ∈ X; ak platí ¬φ(x, f(x)), pokračuj krokom 2.
  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.
  3. 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).

Laicky povedané

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
t je ľubovoľný text, m ∈ ℕ, |t| je počet znakov textu t a t[0, k) je prvých k znakov textu t. Vlastnosť nehovorí, koľko presne znakov zostane — len čo musí platiť; tým je všeobecnejšia než jeden konkrétny príklad.

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.

Agent na vašom počítači, ovládaný z telefónu