Tovább a tartalomhoz

Eszközkontextus és promptgyorsítótár

A beépített harness minden kérésére két dolog igaz: az eszközdefiníciók a legnagyobb fix költséget jelentik benne, és szinte egyet sem fognak használni belőlük. Ez az oldal elmagyarázza, mit tesz ez ellen a Control Center, és miért nem egyszerűen az a válasz, hogy „küldjünk kevesebb eszközt”.

A Control Center körülbelül 126 eszközt regisztrál. Teljes terjedelmükben elküldve a definícióik minden kérésben nagyjából 23 000 tokent tesznek ki — olyan költség ez, amelyhez egyetlen tömörítés sem tud hozzányúlni, mert az eszközdefiníciók a kérésnek a beszélgetéstől elkülönülő részében utaznak.

A tokentöbblet a kézenfekvő panasz, és a promptgyorsítótárral ez a kisebbik tétel is. A valódi költség a kiválasztás pontossága. Az eszközhívásról szóló publikált kiértékelések egyetértenek abban, hogy a modellek megbízhatóan választják ki a megfelelő eszközt egészen addig, amíg körülbelül 30–50 eszköz van a kontextusban, azon túl viszont fokozatosan romlanak. A megnevezett ok nem a tokenek száma, hanem a szemantikai átfedés: ha egyszer egy katalógusban több hasonló leírású eszköz szerepel, a modell hasonmások közül választ, ahelyett hogy egy képességre emlékezne.

126 eszköznél egy ügynök minden egyes körben jóval túl van ezen a sávon — még azokban a körökben is, amelyekben csak egy fájlt kellett elolvasnia.

Egy futás ezért két dolgot lát:

  • egy rezidens készletet — nagyjából két tucat eszközt, amelyek minden kérésben a teljes sémájukkal együtt elküldésre kerülnek;
  • minden másról egy névindexet, „Tickets and projects” vagy „Memory and knowledge” típusú fejlécek alá csoportosítva, amely a teljes katalógusért néhány száz tokenbe kerül.

A jelenlegi katalóguson mérve ez az eszközblokkot ~23,1 ezer tokenről ~5,0 ezerre, mintegy 78%-kal kisebbre viszi, az állandóan látható készletet pedig abba a sávba teszi, ahol a kiválasztás megbízható.

A rezidens készlet azokból az eszközökből áll, amelyeket egy futás a futások többségében használ: a fájl- és shell-szókincsből (read, write, edit, search, bash, …), a felfedezőeszközökből, a feladatlistából, a memóriakeresésből, valamint mindabból, amit az aktuális mód kimeneti műveletként meg kell hívnia a kimenete előállításához. Egy mód saját kimeneti művelete mindig rezidens — egy futásnak soha nem szabad utánajárnia annak az egyetlen hívásnak, amely a szállítandóját előállítja.

Ez az a rész, amely biztonságossá teszi a konstrukciót, nem csupán olcsóvá. Egy halasztott eszköz már az első körtől hívható. Csupán a sémája marad visszatartva.

Az ügynök látja a nevet a promptjában. Ha közvetlenül meghívja ezt a nevet, a harness betölti a sémát, és ugyanabban a lépésben lefuttatja az eszközt — hiba és plusz oda-vissza út nélkül. A modellnek nem kell tudnia, hogy az eszköz valaha „nem volt betöltve”.

Ha az ügynök ismeri a feladatot, de nem tudja a nevet, a search_tools egy természetes nyelvű leírást fogad („ossz hozzá egy jegyet valakihez”), rangsorolja a futás teljes felületét, és visszaadja a találatokat a sémáikkal együtt már betöltve, így a rögtön következő kör már hívhat belőlük egyet. Ha egy keresés semmit nem talál, ezt nyíltan megmondja, és más megfogalmazást vagy szélesebb keresést javasol — szándékosan, mert az az ügynök, amely az üres keresést úgy értelmezi, hogy „ez a képesség nem létezik”, új megoldást talál ki ahelyett, hogy újra keresne.

A search_tools csak a beépített harnessben létezik. A külső MCP-klienseknek ehelyett search_tool_bm25 áll rendelkezésre, amely ugyanazt az indexet rangsorolja, de csak leírja a találatokat — ezek a kliensek ugyanis már megkapták az összes sémát a tools/list hívástól, így nincs mit betölteni.

A halasztás a mód eszközszűrője után érvényesül, soha nem helyette. Elrejt egy sémát; nem tud felfedni egy olyan eszközt, amelyet a mód megtagadott. Egy aktivált eszköz ugyanúgy átmegy a jóváhagyási kérésen és a műveleti korlátokon, ahogy egy rezidens eszköz tenné, és egy eszköz nem tud betölteni olyan nevet, amely eleve nem szerepelt a futás felületén.

Amit a külső kliensek MCP-felületként látnak, az nincs érintve. A tools/list továbbra is a teljes katalógust adja vissza. Egy korábbi kísérlet a megkurtítására csúnyán meg is törte az ügynököket: az MCP-kliensek a saját gyorsítótárazott listájukhoz ellenőrzik az eszközneveket, és mindent megtagadnak, ami nem szerepel rajta, anélkül hogy valaha is kérdeznék a szervertől, így egy „rejtett, de hívható” eszköz egyszerűen elérhetetlen volt. A halasztás a harnessben él, ahol egy eszköz futás közbeni betöltése valóban olyasmi, amire a harness képes.

A --tool-deferral=off (vagy a CC_SERVER_TOOL_DEFERRAL=off) minden beengedett eszközt újra rezidenssé tesz, pontosan azokat a kéréseket állítva elő, amelyeket a szerver a halasztás megjelenése előtt küldött. Lásd a cc_server CLI-referenciát.

Az ügynökös munkaterhelés aszimmetrikus: hatalmas promptokat küld, és kis válaszokat kap, körről körre, mindig ugyanazzal a prefixummal. A szolgáltatók gyorsítótárazzák ezt a prefixumot, és egy gyorsítótár-olvasás nagyjából a tizedébe kerül egy frissének. A gyorsítótár találati aránya ezért a harness első osztályú tulajdonsága, nem pedig optimalizálás — az Anthropicnál pedig áteresztőképességet is hoz, mert a gyorsítótárazott tokenek nem számítanak bele a percenkénti bemeneti korlátba.

A gyorsítótárazás pontos prefixumegyezésen alapul, amiből egyetlen irányadó szabály következik.

Soha ne írjuk újra, amit már elküldtünk

Szekció neve “Soha ne írjuk újra, amit már elküldtünk”

Bármely bájt, amely megváltozik, érvényteleníti a gyorsítótárat onnantól kezdve. A harness ezért csak hozzáfűz: az új üzeneteket a végére, az újonnan aktivált eszközsémákat pedig a gyorsítótárazott eszközblokk után, nem pedig beleolvasztva. Ez a sorrend az oka annak, hogy az aktiválás olcsó — a gyorsítótárazott prefixum változatlan marad, és a futás csak az imént betöltött sémákért fizet.

A ciklus emellett a régebbi körök elavult eszközkimenetét is levágja, hogy kontextust szabadítson fel. Addig vár, amíg elég felszabadítanivaló gyűlik össze ahhoz, hogy megérje az újraírás, így a levágás ritka, mély újraírás lesz, nem pedig folyamatos gyorsítótár-kihagyás.

Egy szolgáltató korlátozott számú gyorsítótár-jelölőt engedélyez, és a Control Center ezeket megfontoltan használja fel:

Jelölő Lefedi Élettartam
Utolsó rezidens eszköz Az eszközblokk 1 óra
Utolsó rendszerblokk Eszközök + rendszerprompt 1 óra
Az előző kör vége Az előzmények a legutóbbi kérésig 5 perc
Az aktuális kör vége A teljes előzmény 5 perc

Az utolsó kettő egy gördülő pár, és a pár oka nem nyilvánvaló: a gyorsítótár-keresés korlátozott számú blokkon keresztül visszafelé kutat valami után, amit egy korábbi kérés tárolt el. Egyetlen kör, amely sok eszközt futtat párhuzamosan, több blokkot bocsáthat ki ennél az ablaknál, ilyenkor az egyik zárójelölő észrevétlenül kimarad, és a kérés a teljes árat fizeti, anélkül hogy a válasz bármivel jelezné ezt. Annak a pontos helynek a megjelölése, ahol az előző kérés megállt, szerkezetivé teszi a találatot a versenyfutás helyett.

Az eszközökből és a rendszerpromptból álló prefixum azért kapja a hosszú élettartamot, mert a teljes futás alatt állandó, minden azonos alakú futás és alügynök osztozik rajta, és mert az óra akkor indul, amikor egy kérés elkezdődik — egy több percig streamelő kör már el is költötte egy rövid ablak nagy részét, mire a következő kérés összeáll.

Az alügynökök egy prefixumon osztoznak

Szekció neve “Az alügynökök egy prefixumon osztoznak”

Az azonos típusú alügynökök bájtazonos promptprefixumokat állítanak elő, ezért egyetlen gyorsítótár-bejegyzést kellene olvasniuk ahelyett, hogy mindegyik a sajátját írná. Egy bejegyzés azonban csak akkor válik olvashatóvá, amikor az őt író kérés válaszolni kezd, ezért egy egyszerre elindított fan-outnál minden gyermek megfizeti a felárat egy olyan prefixumért, amelyen mindannyian osztoznak.

A harness hagyja, hogy egy adott alak első gyermeke elinduljon, majd amint az streamelni kezd, elengedi a testvéreit. A várakozás korlátozott: egy lassú első gyermek egy gyorsítótár-találatba kerül, soha nem magába a fan-outba. Ugyanezen okból minden alügynök ugyanazt a rezidens eszközkészletet kapja, függetlenül a profiljától — profilonként változtatni széttagolná a közös prefixumot minden haszon nélkül, hiszen a profil már eldöntötte, mit hívhat a gyermek.

Mindkét fél megfigyelhető, nem pusztán feltételezett:

  • A beszélgetéshez tartozó Kontextus nézet jelentést ad a rezidens eszközökről, a halasztott indexről és arról, hogy az egyes visszatartott sémák mennyibe kerülnének — ugyanabból a kódútból felépítve, amelyet egy valódi futás használ.
  • Minden futás rögzíti, mely halasztott eszközöket töltötte be, és mi váltotta ki azt, így egy semmit nem találó keresés visszakeresési kihagyásként látható, ahelyett hogy eltűnne egy csendben valami mellékesbe kezdő ügynökben.
  • A Megfigyelhetőség Gyorsítótár-arány néven adja meg a gyorsítótár-olvasások és a feldolgozott összes prompttoken hányadosát, a gyorsítótár-írásokat is beleértve — egy futás, amely folyton újraépíti a prefixumát, alacsony értéket ér el, ahelyett hogy úgy teljesítene, mint amelyiknek soha nem is kellett gyorsítótár.