Agent harness: co właściwie zamienia lokalny model w agenta kodującego

Autor: Jakub Rusinowski · Ostatnia aktualizacja 2026-08-04 · Wartości sprzętowe wyliczone naszym silnikiem VRAM

Harness to oprogramowanie wokół modelu, które czyni z niego agenta: składa prompt, wywołuje narzędzia, wraca z wynikami, kompaktuje kontekst, egzekwuje uprawnienia i utrzymuje stan między turami. Model dostarcza osądu; harness całą resztę. Na lokalnym sprzęcie to harness jest połową o większej dźwigni — te same wagi 27B zachowują się pod dobrym harnessem jak inny model, a ulepszenia harnessu nie kosztują ani gigabajta VRAM-u.

Spytaj, co sprawia, że Claude Code, Cline albo Aider są dobre, a oczekiwaną odpowiedzią jest „model”. Puść te same wagi przez goły czat i przekonasz się, że jest inaczej. Benchmarki mówią to samo z drugiej strony: identyczne otwarte wagi punktują drastycznie różnie w zależności od rusztowania agenta, pod którym są oceniane — dlatego wynik SWE-bench na karcie modelu zawsze wskazuje harness, w którym go uzyskano.\n\nDla lokalnych konfiguracji to najużyteczniejszy fakt w całej dziedzinie. Nie pobierzesz lepszego modelu do karty 16 GB, ale *możesz* dać modelowi, którego już masz, lepsze narzędzia, ciaśniejszy kontekst i czystsze sprzężenie zwrotne — a to większa różnica niż jeden stopień kwantyzacji w którąkolwiek stronę. Inżynieria harnessu to ta część jakości agenta, której nie ogranicza twój VRAM.

Harness, rusztowanie, model: nazwijmy rzeczy po imieniu

Te trzy pojęcia bywają używane zamiennie, a nie znaczą tego samego. Podział, który ustalił się w 2026:

TerminCo obejmujeKiedy działa
ModelWagi. Produkują następny token i nic poza tymW każdej turze
Rusztowanie (scaffold)Wszystko, co powstaje *przed* pierwszym promptem: prompt systemowy, definicje narzędzi, pliki z regułami, indeksowanie repozytorium, podpięcie serwerów MCPPrzy konfiguracji, na starcie sesji
HarnessOrkiestracja w czasie działania, już *po* tym: parsowanie wyjścia modelu, wywoływanie narzędzi, wstrzykiwanie wyników, kompaktowanie kontekstu, egzekwowanie uprawnień, decyzja o kontynuacjiNa każdym kroku każdej tury
PętlaCykl sterujący, który prowadzi harness — zrób, obserwuj, zweryfikuj, powtórzGłówna funkcja harnessu

W swobodnej mowie „harness” obejmuje też rusztowanie i to w porządku — ale przy diagnozowaniu warto ten podział trzymać. *Agent użył złego pliku* to zwykle problem rusztowania (kiepskie składanie kontekstu, brakujące reguły). *Agent wysypał się na zepsutym wywołaniu narzędzia albo zapomniał instrukcji w trzydziestej turze* to problem harnessu (parsowanie, kompaktowanie). Mają różne rozwiązania.

Sześć zadań i sposób, w jaki każde psuje się przy lokalnych modelach

Każdy harness — pisany ręcznie czy wzięty z półki — robi te sześć rzeczy. Każda ma charakterystyczną awarię typową dla lokalnych modeli, którą warto rozpoznawać na pierwszy rzut oka.\n\n1 · Składanie kontekstu. Buduje prompt: prompt systemowy, plik z regułami, schematy narzędzi, pliki uznane za istotne, dotychczasową rozmowę. *Awaria lokalna:* złożony prompt po cichu przekracza serwowane okno kontekstu i jego początek — razem z twoimi instrukcjami — zostaje odcięty. Stąd bierze się klasyczne „zignorował moje reguły”, a lekarstwem jest ustawienie kontekstu po stronie serwera, nie lepszy prompt. Opisuje to context engineering.\n\n2 · Parsowanie wyjścia. Zamienia odpowiedź modelu w ustrukturyzowane działanie. *Awaria lokalna:* model owija JSON prozą, wymyśla nazwę argumentu albo emituje dwa wywołania tam, gdzie schemat dopuszcza jedno. Dekodowanie ograniczone gramatyką likwiduje większość tej klasy od ręki — zobacz tool calling.\n\n3 · Wywoływanie narzędzi. Faktycznie wykonuje działanie — odczyt pliku, nałożenie edycji, uruchomienie polecenia w powłoce, zapytanie do serwera MCP — i formatuje wynik z powrotem do kontekstu. *Awaria lokalna:* rozdęty *wynik* narzędzia. ls -R albo pełny log testów to dziesiątki tysięcy tokenów; dobry harness przycina je i streszcza, zanim wynik w ogóle dotrze do modelu z oknem 32K.\n\n4 · Egzekwowanie uprawnień. Decyduje, co może wykonać się bez nadzoru, a co wymaga człowieka. *Awaria lokalna:* zmęczenie zatwierdzaniem — po dwudziestym pytaniu każdy klika „tak” bez czytania, co funkcjonalnie równa się brakowi bramki. Napraw to strukturalnie, listą dozwolonych operacji plus sandboxem, a nie kolejnymi pytaniami (sandbox).\n\n5 · Zarządzanie kontekstem. Decyduje, co zachować, skompaktować albo wypchnąć, gdy okno się zapełnia. Harnessy produkcyjne robią to etapami — najpierw przycinają nieświeże wyjścia narzędzi, potem streszczają starsze tury, a zestaw roboczy odbudowują dopiero wtedy, gdy muszą. *Awaria lokalna:* nic się nie kompaktuje, okno się zapełnia, a jakość spada z klifu w połowie zadania. Lokalne modele degradują się wcześniej w oknie niż te z czołówki, więc progi kompaktowania dobrane pod kontekst 200K w API są złe dla twoich 32K.\n\n6 · Trwałość stanu. Utrzymuje sesję między restartami: co się zmieniło, co jest zatwierdzone, jakie jest zadanie. *Awaria lokalna:* zwykle harness radzi sobie z tym dobrze — ryzyko jest odwrotne: wznowienie pracy w zatrutym, nieświeżym kontekście, który lepiej byłoby zacząć od nowa.

Inżynieria harnessu dla tych, którzy agentów używają, a nie budują

Większość czytelników nigdy nie napisze harnessu. I tak będziesz uprawiać jego inżynierię, bo każde pokrętło, jakie wystawia agent kodujący, jest pokrętłem harnessu. Kolejność według zwrotu z włożonej pracy przy lokalnych modelach:\n\n1. Ustaw serwowane okno kontekstu jawnie. Zmiana o najwyższej wartości i ta, którą najczęściej się pomija. Domyślny kontekst Ollamy po cichu ucina prompty agentowe. Utwórz wariant z num_ctx ustawionym na tyle, ile obroni twój VRAM (16K przy 16 GB, 32K przy 24 GB) i wybieraj *ten*. Cała reszta z tej listy jest nic niewarta, jeśli prompt harnessu jest przecinany na pół.\n2. Napisz plik z regułami. AGENTS.md w katalogu głównym repozytorium — polecenie builda, polecenie testów, układ projektu, konwencje, czego nigdy nie ruszać. Małe modele zależą od niego nieporównanie bardziej niż duże, bo nie wydedukują konwencji z kilku plików. Zobacz AGENTS.md dla lokalnych agentów.\n3. Utnij powierzchnię narzędziową. Każda definicja narzędzia to tokeny w każdym żądaniu, a każde dodatkowe narzędzie to kolejna szansa, że słabszy model wybierze źle. Dziesięć serwerów MCP potrafi zjeść jedną trzecią dużego okna kontekstu, zanim cokolwiek napiszesz — lokalnie to zabójcze. Trzymaj narzędzia, których zadanie wymaga. (MCP przy lokalnych agentach.)\n4. Daj mu weryfikator i pozwól sprawdzać samego siebie. Skonfiguruj polecenie testowe, które agent uruchamia po edycjach. To zamienia harness poziomu 1 w pętlę poziomu 2 — zobacz loop engineering.\n5. Zakreśl zestaw roboczy. Dodawaj pliki świadomie; nie pozwól agentowi wciągnąć w kontekst całego monorepo. /add w Aiderze, wzmianki o plikach w Cline i wykluczenia w stylu .gitignore istnieją właśnie po to.\n6. Zaczynaj od nowa, często. Nowa sesja nic nie kosztuje i bije szarpanie się ze zdegradowaną. Traktuj „zacznij od nowa z ciaśniej zakreślonym zadaniem” jako pierwszy krok diagnostyczny, nie ostatnią deskę ratunku.

Zbuduj minimalny harness w jakichś 60 linijkach

Warto to zrobić raz, nawet jeśli potem wrócisz do Cline'a — dzięki temu każda opcja konfiguracyjna prawdziwego harnessu staje się czytelna. Ten rozmawia z dowolnym lokalnym serwerem zgodnym z OpenAI, wystawia dwa narzędzia i egzekwuje trywialną regułę uprawnień.

# harness.py — najmniejsza rzecz, którą uczciwie można nazwać harnessem agenta
import json, subprocess, pathlib, urllib.request

ENDPOINT, MODEL = "http://localhost:11434/v1/chat/completions", "qwen3-coder:8b"
ROOT = pathlib.Path("src").resolve()          # granica uprawnień
MAX_TOOL_CHARS = 6000                         # przycinanie wyniku narzędzia
MAX_TURNS = 12

TOOLS = [
  {"type": "function", "function": {"name": "read_file",
    "description": "Read a UTF-8 text file inside src/.",
    "parameters": {"type": "object", "required": ["path"],
      "properties": {"path": {"type": "string"}}}}},
  {"type": "function", "function": {"name": "run_tests",
    "description": "Run the project test suite. Returns pass/fail and output.",
    "parameters": {"type": "object", "properties": {}}}},
]

def dispatch(name, args):
    if name == "read_file":                                   # 4 · bramka uprawnień
        p = (ROOT / args["path"]).resolve()
        if ROOT not in p.parents and p != ROOT:
            return "DENIED: path outside src/"
        return p.read_text()[:MAX_TOOL_CHARS]                 # 3 · przycięcie wyniku
    if name == "run_tests":
        r = subprocess.run(["pytest", "-q"], capture_output=True, text=True)
        return f"exit={r.returncode}\n" + (r.stdout + r.stderr)[-MAX_TOOL_CHARS:]
    return f"unknown tool {name}"

def chat(messages):                                            # 1 · składanie kontekstu
    body = json.dumps({"model": MODEL, "messages": messages,
                       "tools": TOOLS, "temperature": 0.1}).encode()
    req = urllib.request.Request(ENDPOINT, body, {"Content-Type": "application/json"})
    with urllib.request.urlopen(req) as r:
        return json.load(r)["choices"][0]["message"]

msgs = [{"role": "system", "content": "You fix bugs. Use tools. Stop when tests pass."},
        {"role": "user", "content": "The suite is red. Diagnose and report the cause."}]

for turn in range(MAX_TURNS):
    m = chat(msgs)
    msgs.append(m)
    calls = m.get("tool_calls") or []                           # 2 · parsowanie wyjścia
    if not calls:
        print(m.get("content", "")); break
    for c in calls:                                            # 3 · wywołanie narzędzia
        out = dispatch(c["function"]["name"], json.loads(c["function"]["arguments"] or "{}"))
        msgs.append({"role": "tool", "tool_call_id": c["id"], "content": out})
    if len(json.dumps(msgs)) > 60_000:                          # 5 · prymitywne kompaktowanie
        msgs = msgs[:2] + [{"role": "user", "content": "…earlier steps elided…"}] + msgs[-6:]

To zadania od 1 do 5 w miniaturze; do zadania 6 brakuje jednego json.dump. To, co dokłada harness produkcyjny, nie jest koncepcyjne — to sto starannych decyzji o tym, *które* narzędzie, *ile* przycinać, *kiedy* kompaktować i *co* streszczać. W tych decyzjach siedzi cała jakość.

Dobór harnessu pod lokalny model

Wybierasz tu harness w dużo większym stopniu niż interfejs. Lokalnie liczy się: jak duży ma prompt systemowy, ile narzędzi definiuje domyślnie, czy da się go wycelować w endpoint zgodny z OpenAI i czy sensownie kompaktuje kontekst.\n\n| Harness | Kształt | Lokalny endpoint | Waga harnessu | Najlepszy, gdy |\n|---|---|---|---|---|\n| Aider | Terminal, zrośnięty z gitem | Ollama / dowolny zgodny z OpenAI | Lekki — mały prompt, mapa repozytorium, formaty edycji zamiast schematów narzędzi | Małe modele. Najlżejszy poważny harness i najbardziej wyrozumiały dla okna 16K |\n| Cline | Agent w VS Code | Ollama / LM Studio | Ciężki — duży prompt systemowy, tryby planowania i działania, bogate narzędzia | Od 24 GB wzwyż, gdzie stać cię na ten dodatkowy narzut promptu |\n| Continue | Asystent w VS Code / JetBrains | Ollama / llama.cpp / vLLM | Lekki w trybie asysty, cięższy w trybie agenta | Autouzupełnianie plus czat, z agentem jako trybem drugoplanowym |\n| OpenCode / Qwen Code | Agent terminalowy, otwartoźródłowy | Dowolny dostawca zgodny z OpenAI | Średni, konfigurowalny | Praca przede wszystkim w terminalu i podmiana modelu pod zadanie (agenty terminalowe) |\n\nHeurystyka: im mniejszy model, tym lżejszy powinien być harness. Duży prompt systemowy plus tuzin schematów narzędzi potrafi zjeść ćwierć okna 16K, zanim zadanie zostanie w ogóle postawione — a model 8B ma mniej tokenów uwagi do zmarnowania. Projekt Aidera (zwarta mapa repozytorium i edycja oparta na diffach zamiast szerokiej powierzchni narzędziowej) nie jest staroświecki; to projekt, który przeżywa na skromnym sprzęcie. Pełne porównanie w macierzy narzędzi.

Jak rozpoznać, czy problemem jest harness, czy model

Krótka diagnostyka, po kolei. Każdy krok izoluje jedną warstwę.\n\n1. Czy goły model w ogóle odpowiada na to pytanie? Wklej zadanie i istotny plik do zwykłego czatu z tym samym modelem. Dobra odpowiedź → z modelem jest w porządku, to harness go głodzi albo dezorientuje. Zła odpowiedź → ograniczeniem jest próg modelu i żaden harness go nie uratuje.\n2. Czy prompt dociera w całości? Zaloguj żądanie harnessu albo sprawdź aktywny rozmiar kontekstu przez ollama ps czy w logach serwera. Jeśli złożony prompt go przekracza, wszystko inne jest szumem, dopóki tego nie naprawisz.\n3. Czy wywołania narzędzi się parsują? Policz ponowienia po zepsutych wywołaniach. Więcej niż sporadyczne oznacza problem z ograniczeniem dekodowania, nie z inteligencją.\n4. Czy jakość spada z klifu w konkretnym momencie? To zarządzanie kontekstem. Kompaktuj wcześniej albo częściej restartuj sesje.\n5. Czy robi właściwą rzecz, tylko w złym miejscu? To rusztowanie: pracy wymaga plik z regułami i zestaw roboczy, nie model.\n\nDopiero po tych pięciu krokach masz prawo uznać, że potrzebujesz większego modelu — a wtedy będziesz dokładnie wiedzieć którego progu, bo będziesz wiedzieć, której warstwie zabrakło miejsca.

Dlaczego praca nad harnessem procentuje, a wymiana modelu nie

Lepsza karta poprawia wszystko o stałą wartość, raz, za stałą cenę. Lepszy harness poprawia wszystko w sposób kumulatywny, na stałe, za cenę jednego popołudnia — a do tego przenosi się dalej, kiedy *już* wymienisz sprzęt, i jeszcze raz, gdy w kolejnym kwartale wyjdzie lepszy otwarty model.\n\nTa asymetria jest argumentem za traktowaniem harnessu jako głównego wytworu lokalnej konfiguracji do kodowania: plik z regułami, lista dozwolonych narzędzi, polecenie weryfikujące, ustawienia kontekstu, sandbox. Te pliki są twoje. Wagi są wymienne.

Najczęstsze pytania

Czym jest agent harness?
To warstwa oprogramowania wokół modelu językowego, która zamienia go w agenta: składa prompt, parsuje wyjście modelu na wywołania narzędzi, wykonuje te narzędzia, wraca z wynikami, kompaktuje kontekst, gdy okno się zapełnia, egzekwuje uprawnienia i utrzymuje stan sesji. Model wybiera następne działanie; harness robi wszystko, co potrzebne, żeby ten wybór miał jakikolwiek skutek.
Czym harness różni się od rusztowania?
Rusztowanie to wszystko, co budujesz przed pierwszym promptem — prompt systemowy, definicje narzędzi, pliki z regułami, indeksowanie repozytorium, podpięcie MCP. Harness to środowisko uruchomieniowe działające potem: wywołuje narzędzia, zarządza kontekstem, pilnuje bezpieczeństwa, decyduje o kontynuacji. W codziennym użyciu te słowa się nakładają; podział zaczyna się liczyć przy diagnozowaniu, bo błędy „zły plik” to zwykle problemy rusztowania, a błędy „zapomniał instrukcji” — problemy harnessu.
Czy harness liczy się bardziej niż model?
Poniżej czołówki zwykle tak. Te same otwarte wagi punktują bardzo różnie pod różnymi rusztowaniami agenta — dlatego karty modeli podają, jakiego harnessu użyto do wyników w benchmarku. Lokalnie jest to jeszcze ostrzejsze: nie dołożysz możliwości wagom, które mieszczą się w twoim VRAM-ie, ale możesz dać im lepsze narzędzia, ciaśniejszy kontekst i weryfikator — a to zwykle większa poprawa.
Który harness najlepiej sprawdza się z małym lokalnym modelem?
Najlżejszy. Poniżej 16 GB zwykłą odpowiedzią jest Aider: zwarta mapa repozytorium i formaty edycji oparte na diffach zamiast szerokiego schematu narzędzi, więc dużo mniej okna idzie na start, zanim zadanie się zacznie. Cline i inne agenty z ciężkim promptem nabierają sensu od 24 GB wzwyż, gdzie ich prompt systemowy plus definicje narzędzi przestają wypychać właściwą pracę.
Czy budować własny harness?
Zbuduj raz mały, żeby zrozumieć warstwy — sześćdziesiąt linijek daje ci wywoływanie narzędzi, granicę uprawnień, przycinanie i prymitywne kompaktowanie. A potem do prawdziwej pracy używaj gotowego. To, co dokładają Aider, Cline i OpenCode, to nie wyrafinowanie koncepcyjne, tylko sto dostrojonych decyzji o przycinaniu, kompaktowaniu i formatach edycji — a odbudowa tego to projekt, nie popołudnie.
Dlaczego mój lokalny agent po jakimś czasie ignoruje instrukcje?
Prawie zawsze przez zarządzanie kontekstem. Albo złożony prompt przekracza serwowane okno kontekstu i jego początek — tam, gdzie siedzą instrukcje — zostaje po cichu odcięty, albo okno zapełniło się nieświeżymi wyjściami narzędzi i model patrzy na niewłaściwe tokeny. Napraw najpierw serwowany kontekst (num_ctx albo odpowiednik), a potem kompaktuj agresywniej lub częściej zaczynaj nowe sesje.

Co dalej