Lyhyt versio
- Tekoälykoodausagentit ovat hyviä lisäämään koodia ja huonoja huomaamaan, että koodi on jo olemassa. Jätettynä omilleen ne antavat repositoriolle toisen apufunktion jokaista ensimmäistä kohden, ja jokaisen lukijan on tarkistettava molemmat luottaakseen kumpaankaan.
- Korjaus on kaksi kirjattua tapaa, jotta agentti noudattaisi niitä. Syyn suunta (grain): etsi lähin olemassa oleva esimerkki samanlaisesta asiasta ja peilaa sen muoto, sijainti ja nimet. Poninhäntä (ponytail): kiipeä tikapuita kysymyksestä “tarvitseeko tämän olla olemassa” kohtaan “minimi, joka toimii” ja pysähdy ensimmäiselle askelmalle, joka kestää.
- Molemmat ovat nyt julkinen taito, MIT-lisenssillä: grain-skill GitHubissa . Se sisältää kirjoitussäännöt, katselmointikierroksen kiinteillä tunnisteilla ja sen lopetuslauseen muodon, joka saa agentin nimeämään, mitä se peilasi.
- Päärepositoriossamme elokuusta lähtien: 386 issuea ja pull requestia sisältävät suunnittelukommentin, joka nimeää peilattavan esikuvan, 1 333 commitin aikana noin 18 000 tiedoston puuhun. 35 kohdassa koodia on
ponytail:-kommentti, joka nimeää tarkoituksellisen yksinkertaistuksen ja sen päivityspolun. - Epämukava havainto: agentti kutsui taitoa taitona nolla kertaa. Säännöt toimivat, koska laitoin ne myös projektin CLAUDE.md-tiedostoon, työntekijäagenttien määritelmiin ja katselmointilistaan. Laita sääntö sinne, missä agentti jo lukee.
Kirjoitin tästä kerran aiemmin, enkä pitänyt tuloksesta, koska se kuvasi periaatteen eikä mekanismia. Tämä on versio, jossa on mekanismi.
Vika, jota varten tämä on
Kolme esimerkkiä backendistämme, kaikki todellisia, kaikki agentin kirjoittamista pull requesteista, jotka läpäisivät testinsä.
Uusi REST-reitti, joka haki sovelluspalvelun suoraan tehtaalta. Jokainen sisarreitti repositoriossa kulkee reitistä kontrolleriin, palveluun ja repositorioon, jokainen hyppy injektoituna. Tämä luhistutti ketjun reitistä palveluun. Se toimi. Se tarkoitti myös, että se ainoa paikka, jossa kyseisen ominaisuuden pyyntöjen validointi ja vastausten muotoilu elävät, ei ollut olemassa, ja seuraavalla agentilla, joka kosketti ominaisuutta, oli kaksi muotoa valittavanaan.
Ylisuuri palvelu jaettiin tiedostoihin _payloads.py, _protocol.py ja _tokens.py, joista jokainen on moduuli pelkkiä async def -funktioita, ja palvelu kutsuu funktiota protocol.discover(...) importin kautta. Tiedostopituuden tarkistus meni vihreäksi. Juuri se, mitä riippuvuuksien injektointi on olemassa estääkseen, palasi suoraan: kumppani, joka valitaan importin yhteydessä, jota ei voi korvata patchäämättä moduulia, ja joka puuttuu konstruktorista, joka dokumentoi, mistä palvelu riippuu.
Toinen normalisoija. Sellainen oli jo olemassa. Agentti ei löytänyt sitä, kirjoitti kahdeksan riviä lisää, jotka tekivät lähes saman asian, ja nyt kaksi funktiota, joilla on erilainen reunatapausten käyttäytyminen, väittävät molemmat kanonisoivansa saman kentän.
Mikään näistä ei ole bugi siinä mielessä, että testi nappaisi sen. Jokainen on agentti, joka valitsee sen koodin, jota se pystyi generoimaan, olemassa olleen koodin sijaan. Tee niin muutaman sadan pull requestin yli, ja repositorio lakkaa olemasta muotoa.
Syyn suunta
Uuden koodin pitäisi olla erottamaton ympäröivästä koodista. Taito muuttaa tuon lauseen viideksi askeleeksi, jotka agentti ottaa ennen kirjoittamista.
Etsi esikuva ensin. Päätepisteelle, palvelulle, repositoriolle, kyselyhookille, store-siivulle tai komponentille paikanna lähin olemassa oleva ja kopioi sen muoto, tiedostosijainti ja nimeäminen. Älä keksi rakennetta, kun malli on olemassa. Repositoriossamme branding-siivu on backendin nimetty esikuva, koska se on pieni, siisti ja kattaa kaikki kerrokset, ja taidon taulukko kertoo jokaiselle artefaktityypille, mikä tiedosto avataan ja mitä peilataan.
Kunnioita kerroksia. Esityskerros kutsuu sovelluskerrosta, joka kutsuu domainia, ja infrastruktuuri toteuttaa domainin. Uusi import näiden rajojen yli epäonnistuu arkkitehtuuritarkistuksessa. Lue kerrosdokumentti, älä johda sitä uudelleen.
Paikallinen syyn suunta vastaan repositorion syyn suunta. Mukaudu sisariisi. Mutta jos sisaret itse poikkeavat kanonisesta ketjusta, seuraa kanonista ketjua ja merkitse poikkeama sen sijaan, että kopioit ajautuman. “Se vastaa tämän tiedoston viittä muuta reittiä” ei riitä, jos kaikki kuusi ohittavat kerroksen, jota kanoninen siivu käyttää. Vertasit liian paikallisesti.
Käytä uudelleen kanonista apufunktiota. Jos apufunktio, tyyppi tai malli on jo repositoriossa, käytä sitä. Kaksi normalisoijaa pakottaa jokaisen lukijan vertaamaan molemmat.
Nimillä on merkitys. Käytä uudelleen sanastoa, joka on jo koodissa. Uusi nimi olemassa olevalle käsitteelle on bugi, joka odottaa lukijaa.
Lopun testi on se, jonka annan katselmoijille: jos pystyt pelkän muodon perusteella sanomaan, mitkä rivit agentti kirjoitti, se taisteli syytä vastaan.
Poninhäntä
Laiska tarkoittaa tehokasta, ei huolimatonta. Paras koodi on koodi, jota ei koskaan kirjoitettu. Ymmärrä ongelma ensin kokonaan, jokainen tiedosto, johon muutos koskee, todellinen päästä päähän -kulku, ja kiipeä sitten näitä tikapuita ja pysähdy ensimmäiselle askelmalle, joka kestää.
Tikapuiden mukana kulkee kaksi sääntöä.
Bugikorjaus on juurisyy, ei oire. Tiketti nimeää oireen. Greppaa jokainen kutsuja sen funktion osalta, jota aiot koskettaa. Yksi vartija jaetussa funktiossa on pienempi diffi kuin vartija jokaisessa kutsujassa, ja vain tiketin nimeämän polun paikkaaminen jättää sisarkutsujat rikki.
Pidä diffi minimaalisena. Diffi on se, mitä katselmointi lukee. Ei ohi kulkiessa tehtyjä refaktorointeja, ei uudelleennimeämisiä, ei uudelleenmuotoilua, ei “kun tässä nyt olen” -parannuksia, ei tiedostojen koskettamista, joita tehtävä ei vaatinut. Jokainen niistä hautaa alleen todellisen muutoksen. Jos diffi on suurempi kuin ongelma, karsi sitä. Huomaamasi parannukset ovat jatkoissue, eivät ylimääräisiä committeja tässä haarassa.
Ja rajat, koska “laiska” on sana, joka todennäköisimmin ymmärretään väärin. Älä koskaan yksinkertaista pois syötteen validointia luottamusrajalla, virheenkäsittelyä, joka estää tietojen menetyksen, tietoturvaa, saavutettavuuden perusasioita tai mitään, mitä käyttäjä pyysi. Älä koskaan ole laiska ymmärtämisessä: tikapuut lyhentävät ratkaisua, eivät koskaan lukemista. Pieni diffi väärässä paikassa on toinen bugi. Ei-triviaali logiikka jättää jälkeensä yhden ajettavan tarkistuksen, pienimmän asian, joka hajoaa, jos logiikka hajoaa.
Kun agentti ottaa askelman tarkoituksella ja tuntee katon, se sanoo sen koodissa:
# ponytail: global lock, per-account locks if throughput matters
Niitä on repositoriossamme nyt 35. Jokainen on päätös, joka tulevan lukijan olisi muuten rakennettava uudelleen, ja merkki siitä, mistä katsoa, kun katto tulee vastaan.
Miten agentti saadaan todella tekemään se
Dokumentissa oleva periaate ei ole käyttäytymistä. Kolme mekanismia teki näistä kahdesta käyttäytymistä, ja asettaisin ne tähän järjestykseen.
Toimeksianto kantaa esikuvan. Arkkitehtikuviossa päämalli kirjoittaa toimeksiannon työntekijälle, ja toimeksiannon Design-kenttä nimeää lähimmän olemassa olevan mallin, jota peilata, ja minimaalisen juurisyymuutoksen. Työntekijän ei tarvitse löytää syyn suuntaa. Se annetaan sille. Repositoriossamme juuri sitä ovat ne 491 suunnittelukommenttia 386 issuessa ja pull requestissa: suunnitteluvaihe, joka nimeää peilattavan asian ennen kuin kukaan kirjoittaa.
Agentti päättää kertomuksensa siihen, mitä se peilasi. Jokainen toteuttajan raportti päättyy yhteen riviin, mirrored: <path>, tai nimeää uuden mallin ja syyn siihen, miksi yksikään sisar ei sopinut. Tämä on halpaa ja se on paras yksittäinen katselmoinnin apuväline, jonka olen lisännyt. mirrored:-rivi, joka osoittaa vääränlaiseen tiedostoon, kertoo, että diffi on väärä ennen kuin avaat sen. Puuttuva rivi kertoo, ettei agentti katsonut.
Katselmointi valvoo samaa rimaa kiinteässä järjestyksessä. Katselmointitaitomme sisältää kaksi kierrosta kiinteillä tunnisteilla, ja tunnisteet tekevät havainnoista vertailukelpoisia katselmoijien ja agenttien kesken.
Rakenne ensin. Poikkeaako tämä lähimmästä esikuvasta, kopioiko olemassa olevan apufunktion, ylittääkö kerroksen, onko väärässä paikassa, kurkottaako toisen moduulin sisäisiin osiin, käyttääkö epäjohdonmukaisia nimiä, ottaako omistukseen jotain, joka sen pitäisi injektoida, vai rikkooko kanonisen kutsuketjun? Sitten karsinnat. Mitä voitaisiin poistaa, korvata standardikirjastolla, korvata natiivilla ominaisuudella, pudottaa spekulatiivisena tai kutistaa?
Järjestys on sääntö, ei mieltymys. Johdonmukaisuus voittaa karsinnat. Karsinta, joka jättää koodin poikkeamaan sisaristaan, ei ole voitto, koska lukijan on nyt opittava kaksi muotoa säästääkseen kahdeksan riviä. Aja rakenne ensin, niin karsintakierros koskee vain koodia, joka jo vastaa mallia.
Mitä repositoriossa tapahtui, mukaan lukien osa, joka ei toiminut
Elokuun 1. päivästä lähtien päärepositoriomme on ottanut vastaan 1 333 committia, joista useimmat on kirjoitettu agentilla katselmoinnin alaisena. 386 tuon ajanjakson issueta ja pull requestista sisältää suunnittelukommentin, joka nimeää peilattavan esikuvan, yhteensä 491 tällaista kommenttia. Kanoninen ketju on uuden päätepisteen oletusmuoto, ja ne kaksi perintöreittiä, jotka ohittavat kontrollerin, on lueteltu dokumentissa poistuvana poikkeamana, ei toisena tyylinä.
Ne 35 ponytail:-kommenttia ovat pienempi määrä kuin odotin, ja mielestäni se on oikein. Useimmat yksinkertaistukset eivät ole kommentin arvoisia. Ne, jotka ovat, ovat niitä, joilla on katto.
Nyt se osa, jota en mieluiten raportoisi. Julkaisin syyn suunnan ja poninhännän Claude Code -taitona: kuvaus taitojen hakemistossa, runko, joka latautuu pyynnöstä, ja rivi CLAUDE.md:ssä, jossa sanotaan, että se pitää kutsua ennen koodin kirjoittamista. Viiden viikon litteraateissa agentti kutsui sitä nimeltä nolla kertaa. mirrored:-loppurivi esiintyi seitsemässä vastauksessa.
Säännöt tulivat silti perille. Ne tulivat perille, koska sama teksti oli myös kolmessa paikassa, joita agentti lukee valitsematta: projektin CLAUDE.md, joka on kontekstissa ensimmäisestä vuorosta lähtien; työntekijäagenttien määritelmät, joissa sanotaan “peilaa lähimmän olemassa olevan esimerkin muoto, sijainti ja nimeäminen” ja “tavoittele laiskinta ratkaisua, joka todella toimii” sääntöinä, ei harkittavana taitona; ja katselmointilista, jonka tunnisteet hylkäävät pull requestin, joka jättää ne huomiotta. Taidon rungosta tuli kanoninen viite, johon nuo kolme osoittavat. Laukaisijana se ei tehnyt mitään.
Oppi, jonka välittäisin kenelle tahansa, joka paketoi käytäntöjä agentille, on tämä. Taito on hyvä paikka säännön pitkälle versiolle. Se on huono paikka itse säännölle, koska agentin on päätettävä ladata se, eikä se useimmiten tee niin. Laita yhden kappaleen versio tiedostoon, joka on aina kontekstissa, laita imperatiivinen versio sen agentin määritelmään, joka kirjoittaa koodin, ja laita tarkistus katselmointiin, joka portittaa mergen. Osoita sitten kaikki kolme taitoon yksityiskohtia varten.
Julkinen versio osoitteessa github.com/LivinTribunal/grain-skill on rakennettu juuri niin: lyhyt SKILL.md, jossa ovat säännöt, jotka agentti voi pitää mielessään, täysi dokumentti tikapuineen ja vastamalleineen, katselmointitunnisteet ja README, joka kertoo, mikä kappale menee CLAUDE.md-tiedostoosi ja mikä toteuttajasi määritelmään. Korvaa esimerkki omalla kanonisella esikuvallasi, ja se on siinä.

