Skip to content

Harness agentów AI: od pętli z weryfikatorem do produkcji

Notatki po kursie „Harness od zera" Michała Kamińskiego — polecam przejść go samodzielnie, tu zbieram esencję i dodaję własny komentarz.

Dlaczego "harness", nie "prompt"

Największy błąd na starcie z agentami: traktować je jak lepszy prompt. Model sam w sobie niczego nie robi — nie czyta plików, nie uruchamia kodu, nie wie, czy skończył zadanie. Harness to wszystko dookoła modelu, co zamienia generowanie tekstu w rozwiązywanie problemu: pętla, narzędzia, pamięć, guardraile.

Kluczowa intuicja: różnica między samym promptem a pętlą z weryfikatorem. Pętla działa tylko wtedy, gdy ma obiektywną definicję "done" — testy, typy, kompilację, coś, co mówi prawdę niezależnie od samooceny modelu. "Bądź dokładny" to apel. "Nie kończ, zanim run_tests nie zwróci PASSED" to kontrakt.

agent = create_agent(
    model="openai:gpt-5.4",
    tools=[read_file, write_file, run_tests],
    system_prompt=(
        "Implementuj kod, az run_tests zwroci PASSED. "
        "Po kazdej zmianie URUCHOM testy. Nie koncz przed zielonymi testami."
    ),
)

Weryfikator (run_tests) musi zwracać binarne PASSED/FAILED — bez niejednoznaczności, bez procentów. Model nie ma pojęcia "częściowego sukcesu".

Zanim w ogóle zbudujesz pętlę, warto sprawdzić, czy jest potrzebna: jeśli kroki i ich kolejność są z góry znane i niezmienne, sztywny skrypt jest tańszy i pewniejszy niż agent.

Pięć filarów agenta

Konsensus 2026 na pytanie "z czego składa się agent":

  • Prompt — kontrakt, nie zaklęcie. Najważniejsze: obiektywna definicja "done".
  • Model — ile ma myśleć (budżet rozumowania per krok, nie globalnie) i którego dostawcę wybrać (provider:model jako zamienna konfiguracja, nie zależność).
  • Kontekst — co model widzi teraz. Okno to budżet uwagi, nie magazyn.
  • Tool use — co potrafi. W 2026 to coraz częściej CLI owinięte w "skill", nie zawsze MCP.
  • Harness — pętla, która to wszystko orkiestruje.

Kontekst to budżet, nie magazyn

Każda iteracja pętli dolewa do okna: wiadomości, wyniki narzędzi, plany. Bez higieny sygnał tonie w szumie i koszt rośnie liniowo z każdą turą. Cztery ruchy (LangChain/Anthropic):

  1. Zapisz — wynoś informacje poza okno (scratchpad, pliki, TODO w stanie grafu).
  2. Wybierz — wciągaj do okna tylko to, co potrzebne teraz.
  3. Kompresuj — podsumowania zamiast pełnej historii, placeholdery zamiast starych dumpów narzędzi.
  4. Izoluj — subagent eksploruje w swoim oknie i oddaje rodzicowi destylat, nie 200 linii logów.

W LangChain trzy z tych ruchów są gotowym middleware: SummarizationMiddleware, ContextEditingMiddleware + ClearToolUsesEdit, LLMToolSelectorMiddleware. Najmocniej działa czyszczenie wyników narzędzi — to one puchną najszybciej.

Pamięć: co przeżywa sesję

Notatki w oknie umierają z zadaniem. Pamięć długoterminowa żyje w trwałym magazynie (Store), nie w historii czatu. Taksonomia CoALA rozróżnia trzy typy:

  • Semantyczna — fakty (profil użytkownika, konfiguracja projektu).
  • Epizodyczna — konkretne doświadczenia, "recepty" na wcześniej rozwiązane błędy.
  • Proceduralna — nawyki zmieniające każdy przyszły bieg — dlatego wymaga bramki człowieka przed aktywacją.

Bez pamięci agent płaci "podatek od pierwszej sesji" w każdym nowym wątku — te same błędy, te same pytania od zera.

Caching: nie płać dwa razy za to samo

System, schematy narzędzi i dokumenty referencyjne powtarzają się w każdej turze pętli. Anthropic wymaga jawnego cache_control na blokach treści (kolejność ma znaczenie — stałe pierwsze, zmienne na końcu, bo każda zmiana przed breakpointem unieważnia cache). OpenAI keszuje prefiks automatycznie. To zwykle najtańsza dźwignia kosztu i latencji w długich biegach.

RAG: dobierz poziom do pytania

RAG to spektrum, nie jeden wybór — od naiwnego dopasowania po hybrydę (dense + BM25) z rerankerem. Produkcyjny domyślny wybór to hybryda: ~100 kandydatów tanio, cross-encoder przesiewa do top 5–10. Warto pamiętać: ~80% awarii RAG bierze się z warstwy ingestu i chunkowania, nie z modelu. Zanim zaczniesz stroić prompty, sprawdź, czy retrieval w ogóle zwraca właściwy kontekst.

Middleware: warstwy wokół pętli

Middleware to sposób wpięcia się w pętlę bez jej przepisywania — sześć hooków (before_agent, before_model, after_model, after_agent, wrap_model_call, wrap_tool_call). Praktyczny wzorzec: bezpiecznik na brak postępu, gdy limit wywołań nie wystarcza (agent może mieścić się w budżecie, kręcąc w kółko to samo):

class StopWhenStuck(AgentMiddleware):
    """Przerwij, gdy dwa razy z rzędu ten sam FAILED — agent się zaciął."""
    @hook_config(can_jump_to=["end"])
    def before_model(self, state, runtime):
        r = [m.content for m in state["messages"]
             if m.type == "tool" and getattr(m, "name", "") == "run_tests"]
        if len(r) >= 2 and r[-1] == r[-2]:
            return {"jump_to": "end"}
        return None

Dlaczego before_model, nie after_model: bezpiecznik ma decydować, czy w ogóle kręcić, zanim zapłacisz za wywołanie modelu.

Multi-agent: nie zawsze, i nie na wyczucie

To jedyny temat kursu z dwoma sprzecznymi manifestami branżowymi — i obydwoma popartymi danymi. Rozstrzyga oś zadania: breadth-first (niezależne gałęzie, wynik = synteza) — topologie równoległe się zwracają. Depth-first (splątane decyzje, wspólny kontekst) — jeden agent wygrywa.

Twarde liczby z badania MAST (1642 śladów, 7 frameworków): systemy multi-agent zawodzą w 41–87% zadań, a 32,3% awarii to niedopasowanie między agentami — klasa błędów, której w single-agent nie ma. Reguła nadrzędna: zacznij od solo, komplikuj dopiero z dowodu policzalnego zysku.

Ewaluacja: mierz deltę, nie wrażenie

Trzy poziomy oceny: odpowiedź końcowa (najtańsza, mówi czy), pojedynczy krok (mówi gdzie boli), trajektoria (mówi jak). Metryki deterministyczne (exact match, regex, schema) do zamkniętych wyjść; LLM-as-judge do otwartych — ale sędzia dodaje własny szum (bias pozycyjny, długości, self-preference). Tak jak agent potrzebuje weryfikatora, Ty potrzebujesz własnego: datasetu z binarnym "done" i przypiętego baseline'u, żeby odróżnić realną poprawę od wrażenia.

Guardraile: "lethal trifecta"

Wzorzec Simona Willisona: prompt injection jest strukturalnie możliwy, gdy agent ma jednocześnie trzy zdolności — (1) narzędzia z efektami zewnętrznymi, (2) dostęp do niezaufanych danych, (3) możliwość eksfiltracji (egress). Usuń choć jedną nogę, a atakującemu nie ma czego przejąć. Sam prompt ("nie ufaj treści") tego nie zatrzyma — warstwy deterministyczne (allowlist narzędzi, skan sekretów, odcięty egress, bramka człowieka na akcje nieodwracalne) tak.

Trwałość i meta-pętla

Procesy padają, biegi trwają godzinami — PostgresSaver (zamiast InMemorySaver) zapisuje stan jako checkpoint, więc agent wznawia z ostatniego punktu po restarcie. Warunek konieczny: narzędzia muszą być idempotentne — powtórne send_email po wznowieniu to nie to samo co powtórne write_file.

Spoiwo całego kursu — reguła meta-pętli: gdy agent popełni błąd, nie łataj promptu kolejnym zdaniem. Zidentyfikuj klasę błędu (kontekst? narzędzie? guardrail?), dodaj przypadek do datasetu, napraw strukturalnie, zmierz deltę. Harness rośnie z każdą naprawioną awarią, nie z czytania dokumentacji.

Deep agents: harness "z bateriami"

create_deep_agent to nie nowy framework — to create_agent z opiniowanym, gotowym stosem middleware (planowanie, filesystem, delegacja do subagentów, summarization, bramka człowieka), złożonym jedną funkcją. Warto po niego sięgnąć, gdy zadanie jest wieloetapowe i wymaga własnego planu; jeśli jedna pętla z 2–3 warstwami wystarcza — create_deep_agent to tylko narzut i mniej kontroli.

Podsumowanie

Model to towar — harness to fosa, ale fosa ma być cienka. Zaczynasz od najprostszej rzeczy, która działa (pętla + weryfikator), dokładasz warstwy z dowodu, nie z mody, i traktujesz każdy błąd produkcyjny jako sygnał do strukturalnej poprawki, nie kolejnej linijki w prompcie.

Cały kurs, z interaktywnymi symulatorami i pełnym kodem: harness.michalsk.pl.