hachiflow.com
Architektura

Magistrala jest podłożem

Rok zajęło mi nazwanie wprost tego, co drażni mnie w dwóch produktach, których używam codziennie. Jeden dopracował tryb jednoosobowy i na tym stanął. Drugi dobrze trafił z pokojem i przyspawał do niego agentów. Waggle to architektura, która wyrosła z tego rozdrażnienia, a poniższe notatki to dowody: identyfikator reguły, typ zdarzenia, ślad i błąd, który złapałem drugiego dnia.

Dwa produkty, jeden brakujący element

Grokbot wygrał doświadczenie solo i nikt mu go nie odebrał. Jedna osoba, jeden bot, jeden komputer, a pętla jest na tyle ciasna, że przestajesz zauważać oprogramowanie. Klony się z tym zgadzają. Spędziłem dzień, czytając Rakazo w commicie 616d2353, klonie Grok Bota na licencji Apache-2.0, i to dobra robota: zatwierdzenie, które zapisuje oczekujące wywołanie narzędzia i odtwarza dokładnie zatwierdzone argumenty, zamiast prosić model, by wyprowadził je jeszcze raz. Warto to ukraść.

Potem szukałem elementów, które pozwoliłyby mi zmienić jego zachowanie bez zmiany kodu. Nie było ich. Ingress jest przyspawany do celu: adres URL webhooka to POST /api/v1/bots/:botId/webhook, więc wywołujący nazywa bota i nie ma już nic do routowania. Brak artefaktu reguł, więc nie ma czego porównywać. Brak trwałego identyfikatora śladu: AdapterContext.traceId to pole logu niosące identyfikator przebiegu, a żaden przebieg nie zapisuje rodzica, więc sześcioskokowego łańcucha bot do bota nie da się odtworzyć żadnym zapytaniem. Niczego tam nie da się zoptymalizować i żaden agent nie może odejść.

Buzz to druga połowa tej irytacji, a ja sprzedaję dla niego hosting, więc mogę być uczciwie bezczelny. Buzz ma to, czego Grokbot nie potrafi wyrazić: więcej niż jednego człowieka w pokoju, z agentami jako członkami, a nie licznikowanymi dodatkami. Członek to para kluczy. Agent to para kluczy, której zdarzenie uwierzytelnienia niesie podpis właściciela, więc odznaka bota i atestacja są faktami relaya, a nie ozdobnikiem aplikacji (mechanizm jest tutaj). Wszystko wokół tego jest przyspawane: tożsamość jest bita na pulpicie w Keys::generate(), klucz prywatny jedzie do wykonawcy w ładunku wdrożenia, a ciało działa tam, gdzie zostało uruchomione. Zamknij laptopa, a Twój współpracownik znika.

Magistrala jest podłożem

Waggle to to, co zostaje, gdy odmawiasz przyspawania czegokolwiek z tego. Sześć części, każda wymienna. Ingress stempluje zaufanie i tożsamość. Log tylko do dopisywania przechowuje każde zdarzenie. Plik reguł decyduje, kto się budzi. Wykonawcy to wymienne cele. Poświadczenia leżą w depozycie z dzierżawami i proxy. Jeden identyfikator śladu spina całość.

Nic z tego nie jest abstrakcyjne. Zdarzenie ląduje na ev.<tenant>.<source>.<type>, niosąc dwa pola, które większość systemów myli: principal, kto to tam umieścił, i trust, czy to twierdzenie zostało kiedykolwiek sprawdzone. Wpis od Stripe z ważnym podpisem to partner:stripe na zaufaniu partnera. Ten sam wpis ze złym podpisem to 401 i w ogóle nie zostaje dopisany. Identyfikator wywodzi się z klucza idempotencji, który zawiera tenanta, więc to samo zdarzenie dostarczone dwukrotnie dostaje ten sam identyfikator, a log odrzuca drugą kopię.

Router czyta plik TOML. Nie prompt, nie wiersz bazy danych: plik z identyfikatorami reguł w środku, przeładowywany na gorąco, sprawdzany wobec żywego rejestru przed zapisem, odrzucany w całości, jeśli dwie reguły w jednym tenancie dzielą identyfikator. Ten plik jest artefaktem: tym, co porównujesz w pull requeście, tym, co agent może zaproponować do zmiany, i tym, co ślad nazywa, mówiąc, dlaczego coś się obudziło. Cel to też wiersz, z doręczycielem, więc zmiana miejsca, w którym wykonuje się agent, to edycja jednego wiersza.

Buzz, Slack każda powierzchnia webhook podpisany albo nie cron według zegara ingress tenant i principal tu stemplowane zaufanie plik reguł r-cos2-addressed kto budzi się i dlaczego wykonawca jedna tura na pobudkę depozyt dzierżawa i proxy powierzchnia relay i lista członków wypełniacz ev.<tenant>.<source>.<type> tylko dopisywanie hachi trace jedno id, od klawisza po wywołanie modelu
Sześć części, każda wymienna. Powierzchnia to źródło ingressu i doręczyciel, więc Buzz i Slack są wierszami, a nie przepisywaniem od nowa. Depozyt podaje wykonawcy wypełniacz i podmienia wartość dopiero na proxy, więc model nigdy nie czyta klucza. Wszystko ląduje w jednym logu, pod jednym identyfikatorem śladu.

Agenci nie rozmawiają, emitują

Reguła, której najdłużej się uczyłem ufać, jest tą, którą teraz lubię najbardziej: agenci nie prowadzą rozmowy, emitują zdarzenia, a zdarzenie zaadresowane do agenta go budzi. Nie ma osobnej magistrali wiadomości na czat i osobnej na pracę. Jest jeden log. Odpowiedź to zdarzenie z ustawionym addressed_to, a ślad zapisuje ją niezależnie od tego, czy patrzy na to człowiek.

Dwa dni po uruchomieniu prawdziwych tur złapałem to jako fałsz. 8 września o 05:07:59Z zapytałem agent:cos2, szefa sztabu na prawdziwym runtime Claude, o jego agenta marketingowego. Zrobił dobrze: wyemitował ask zaadresowany do agent:growth, zdarzenie 3YRSRD08JX549QHDHCXPZXRK7P, pod śladem 7MDV4GJYX2TWTMQH5SP23PYSJ8. Nic mu nie odpowiedziało. Wygenerowane reguły pasowały do wierszy pobudki, wiadomości konsoli i powiadomień depozytu, więc zdarzenie od partnera, którego podmiotem był inny agent, nie pasowało do żadnej z nich. Ask trafił do celu domyślnego, growth nigdy się nie obudził, przebieg cos2 zakończył się trzy sekundy później, a czat nie pokazał zupełnie nic.

Poprawka to to, co przekonało mnie, że ten kształt jest właściwy, bo nie była funkcją. Była jeszcze jedną standardową regułą generowaną dla każdego agenta, r-<name>-addressed, dopasowującą subject = "agent:<name>" i trust = "partner", umieszczoną po własnych wierszach pobudki agenta, tak by konkretna reguła wciąż wygrywała, z migracją przy starcie, która dopisuje ją raz każdemu już istniejącemu agentowi, i sprawdzeniem na ingressie, które odrzuca emisję zaadresowaną do samego siebie. Rozmowa agent do agenta, na magistrali, to pięć linii wygenerowanego TOML. Bez tego to pozycja na mapie drogowej z datą premiery.

Model nigdy nie czyta klucza

Poświadczenia to miejsce, w którym przestaje to być kwestią gustu architektonicznego. Token subskrypcji to uprawnienie w depozycie: usługa claude_code, pole oauth_token, host api.anthropic.com. Przy pobudce depozyt wystawia dzierżawę, a kontener dostaje wypełniacz __claude_code_oauth_token__, dokładnie tę zmienną, z której Claude Code odczytuje token. Jedynym wyjściem z kontenera jest proxy, które podmienia prawdziwą wartość w nagłówku żądania, tylko dla tego jednego hosta. Model widzi wypełniacz. Dysk też: dziennik przechodzącego przebiegu zawiera AGENTMAIL_API_KEY=__agentmail_api_key__, bo to właśnie dostał proces.

Każdy krok to zdarzenie i żadne nie niesie wartości: credential.requested, credential.granted, credential.leased przy pobudce, credential.used raz na każde żądanie widziane przez proxy, credential.revoked, gdy to odbierasz. Ślad audytowy to nie log, o którym ktoś pamiętał, żeby go napisać, to ten sam log, na którym jest wszystko inne. Uznanie tam, gdzie należy: kształt runnera i brokera jest autorstwa Jake'a Gaylora, z Fountain, który miał sandboksy na współpracownika, parkujące się i budzące, oraz poświadczenia inferencji tenanta, których agent nigdy nie czyta, na długo zanim ja miałem cokolwiek z tego. Nazwijmy antywzorzec wprost. Włóż klucz do środowiska, a każde polecenie powłoki uruchamiane przez model może go odczytać, i żadna ilość redakcji podciągów tego nie zamknie, bo base64 istnieje.

Jeden człowiek, a ślad jest kanałem

Więc gdzie podziewa się okno czatu? Przy jednym człowieku, nigdzie. Waggle jest interfejsem, a ślad jest kanałem: wiadomość wchodząca, reguła, która pasowała, pobudka, wywołania narzędzi modelu, jego emisje, kod wyjścia, wszystko pod jednym identyfikatorem, który możesz wydrukować. hachi trace pokazuje skok ingressu, regułę, która się odpaliła, cel, który potwierdził odbiór, i każde powiązane dziecko pod skokiem, który je spowodował. Pokazanie tego jako rozmowy to widok, nie drugi produkt.

Drugi człowiek zmienia wymaganie, nie zmieniając architektury. To, czego potrzebują dwie osoby, to wspólne miejsce z listą członków, obecnością i historią, w czym produkt czatu jest dokładnie dobry i w co magistrala właśnie nie powinna próbować się zmieniać. Powierzchnia to więc dwie zwykłe rzeczy: źródło ingressu i doręczyciel.

Dla Buzz jest to połączenie relaya na każdego powiązanego agenta, każde zaakceptowane zdarzenie dopisane jako koperta buzz.message z podpisem ponownie zweryfikowanym przy wejściu, zwykłe reguły niosące je do agenta i z powrotem answer agenta, oraz doręczyciel, który podpisuje odpowiedź jako agent. Wyceniłem to demo na jakieś cztery dni na sześciu elementach, i żaden z nich nie jest nowym pojęciem: Buzz już potrafi wywołać binarkę dostawcy. Slack to te same dwie rzeczy w innym krawacie. Buzz powinien być powierzchnią. Slack jest powierzchnią.

Agent jest Twój, miejsce pracy jest ich

Gdy wykonanie, inferencja, poświadczenia i tożsamość są osobnymi rzeczami, własność przestaje być pytaniem filozoficznym i staje się czterema polami. Własność leży na agencie. Inferencja, wykonanie i dane leżą na powiązaniu, czyli tym, co trzyma miejsce pracy: czyja subskrypcja płaci za turę, czyj wykonawca ją uruchamia i co może zachować. Identyfikator agenta to jego własny klucz publiczny, wybity przy tworzeniu, z prywatną połową jako uprawnieniem w depozycie, ujawnianym tylko wewnątrz demona i nigdy nie dzierżawionym wykonawcy.

Doświadczenie dzieli się na linii prywatności: notatki warsztatowe należą do właściciela i podróżują z nim, notatki tenanta należą do miejsca pracy i nigdy go nie opuszczają. Paszport niesie tożsamość, duszę, warsztat i odniesienia do uprawnień właściciela bez żadnych wartości w środku, i nigdy pamięć firmy. Pęk kluczy, który przenosiłby te uprawnienia do innej instancji, jest projektowany wyżej w łańcuchu jako block/buzz PR #6011. Warunki są sprawdzalne, a nie obiecane: na mini ustawiłem jedno powiązanie na inference: owner dla właściciela bez żadnego uprawnienia subskrypcji, a jego następna pobudka wyprodukowała credential.refused, mówiące wprost, że żadne uprawnienie claude_code właściciela nie obejmowało tego agenta w tym tenancie. Warunki to nie dokumentacja. To to, co czyta wystawca dzierżawy.

Liczby, małe i prawdziwe

Nic z tego nie działa na klastrze. Jeden Mac mini, docker compose. Cały scenariusz, od stworzenia szefa sztabu po pobudkę marketingu z dzierżawą, którą widzi wyłącznie jako wypełniacz, zajmuje 15 sekund od pierwszego stworzenia do PASS i cztery tury kontenera. Każdy z ośmiu sekretów w pliku środowiskowym został sprawdzony grepem zarówno w logach end-to-end, jak i w logu builda, zanim te logi opuściły maszynę. Zero trafień dla każdego. Fałszywy klucz wklejany przez przebieg nie znalazł się w żadnym logu, konfiguracji, skrzynce nadawczej ani śladzie.

Plan szacował od 39 do 54 dni roboczych dla jednego inżyniera na pięć faz, plus od 14 do 24 na kamień milowy własności. Rój agentów zbudował pięć faz i przeszły one na mini 7 września; migracja własności uruchomiła się raz przy starcie 8 września, zamieniła 13 istniejących agentów w obiekty z właścicielem i własnymi tożsamościami, i zostawiła ich 33 wygenerowane reguły nietknięte. Dwa dni na obie rzeczy. Pokrycie celu weryfikacji to 88,7 procent, a żywy zestaw testów buildera przechodzi 15 z 15. Luki, bo notatka z pola bez nich to reklama: prawdziwe wywołanie wychodzące przez proxy z podmienionym wypełniaczem jest pokryte testem wobec lokalnego upstreamu, a nie przez mini, a opisane wyżej powiązanie z Buzz to odczyt projektowy wobec źródła relaya, a nie wysłana funkcja.

Podłoże jest produktem

Oto ten sam argument bez lakieru. Podłoże jest produktem. Powierzchnie są towarem: aplikacja czatu to lista członków, relay i pole tekstowe, i w przyszłym roku będzie lepsza. Wykonawcy są towarem: kontener dziś, wynajęta moc obliczeniowa jutro, microVM po tym. Modele są towarem i celowo tanieją. Tym, co nie jest towarem, jest zapis tego, co się stało, dlaczego się stało i czego wolno było dotknąć. Ten zapis jest jedynym artefaktem, wobec którego ktokolwiek może optymalizować. Fosą jest ślad.

Więc zamiast argumentu weź test. Znajdź ostatni raz, gdy jeden z Twoich agentów zrobił coś złego, i zapytaj, jaki artefakt edytowałbyś, żeby to się nie powtórzyło. Jeśli odpowiedzią jest dłuższy prompt, masz bota. Jeśli odpowiedzią jest identyfikator reguły, który możesz porównać, i identyfikator śladu, który możesz wydrukować, masz magistralę. Zbuduj magistralę.

← Wszystkie notatki