Abstrakcyjna ciemna grafika przypominająca odczyt z przyrządu pomiarowego

Lokalny model kodujący na RTX 4060. Co zmierzyliśmy i gdzie się rozsypał

7 min czytania

Ollama, Aider i dwa modele Qwen na karcie z 8 GB VRAM. Twarde liczby, obalona hipoteza o MoE i trzy reguły pisania instrukcji, które wyszły z pomiaru.

Pytanie wraca na każdym wdrożeniu u klienta z działem IT: czy da się postawić agenta kodującego lokalnie, żeby kod nie wychodził z firmy. Odpowiedź “to zależy” jest bezużyteczna, więc spędziliśmy dzień na mierzeniu. Poniżej liczby, jedna obalona hipoteza i trzy reguły, które wyszły z pomiaru, a nie z przeczucia.

Sprzęt celowo konsumencki: Ryzen 7 5700X, 31 GB RAM widocznego w WSL, RTX 4060 z 8 GB VRAM. Stos: Ollama 0.33.1 zainstalowana rootless, Aider 0.86.2, dwa modele - qwen2.5-coder:7b w kwantyzacji Q4_K_M (4,7 GB) i qwen3-coder:30b, czyli MoE ze 128 ekspertami, z których liczy się 8 naraz (18 GB).

Najpierw sterownik, bo bez niego nie ma o czym rozmawiać

Ollama nie widziała karty. Diagnoza warstwa po warstwie: cuInit i runtime CUDA 12.8 rozpoznawały GPU poprawnie, ale ggml_cuda_init zwracał device kernel image is invalid. Powód okazał się prozaiczny i wart zapamiętania: fatbin w cuda_v12 zawierał kernele wyłącznie dla architektur sm_100 i sm_120, a sterownik z lipca 2023 (driver API 12.2) nie potrafi zJITować PTX-a wygenerowanego przez nowszą CUDĘ. Kompatybilność JIT-a nie działa w przód. Vulkan też odpadał, bo w WSL nie ma ICD NVIDII.

Po aktualizacji sterownika (CUDA UMD 13.4) Ollama od razu załadowała cuda_v13. Jeśli diagnozujesz podobny objaw, sprawdź wersję driver API, zanim zaczniesz podejrzewać model albo kwantyzację.

Liczby

Baseline na samym CPU: 5,6 tokena na sekundę generacji, 29 tokenów na sekundę przetwarzania promptu.

qwen2.5-coder:7bqwen3-coder:30b (MoE)
Rozmieszczenie wag29/29 warstw na GPU, 4168 MiBatencja na GPU (2651 MiB), 15 756 MiB ekspertów w RAM
Generacja, krótki kontekst52-53 tok/s19 tok/s
Prompt, 22,5 tys. tokenów1758 tok/s, 12,8 s206 tok/s, 108,8 s
Deklarowany max kontekst32 768262 144

Przyspieszenie 7B względem CPU: generacja 9,5x, przetwarzanie promptu 60x.

Porównanie czasu wczytania 22 500 tokenów kontekstu: qwen2.5-coder:7b 12,8 sekundy, qwen3-coder:30b MoE 108,8 sekundy

Ta jedna różnica, 12,8 sekundy kontra 108,8 sekundy na wczytanie tego samego kontekstu, przesądza o wyborze modelu bardziej niż cokolwiek innego w tej tabeli.

Hipoteza, która się nie obroniła

Przed testem zakładaliśmy, że na 8 GB VRAM właściwym kandydatem jest model MoE. Logika brzmiała sensownie: 30 miliardów parametrów wiedzy, około 3 miliardy liczone naraz, eksperci mieszkają w RAM-ie, w VRAM zostaje atencja i KV cache. Dużo modelu za mało pamięci.

Pomiar to odwrócił. Eksperci w RAM-ie oznaczają, że każdy token promptu przeciąga wagi przez PCIe. Przy 22,5 tys. tokenów kontekstu 30B potrzebuje 109 sekund samego czytania, zanim napisze pierwszy znak. 7B, który mieści się w VRAM w całości razem z KV cache, robi to w 13 sekund.

To jest różnica, która decyduje, bo agent kodujący żyje promptem, nie generacją. Czyta kontekst przy każdej turze, a wypluwa kilkaset tokenów diffa. Optymalizowanie generacji w tym scenariuszu to optymalizowanie niewłaściwej rzeczy.

Strojenie, które warto zostawić na stałe

Dwie zmienne środowiskowe zbijają KV cache modelu 7B z 1792 do 952 MiB:

OLLAMA_FLASH_ATTENTION=1 OLLAMA_KV_CACHE_TYPE=q8_0 ollama serve

Dzięki temu ostatnia warstwa wraca z CPU na GPU (29/29 zamiast 28/29), a generacja przy pełnym 32k kontekście rośnie z 25 do 38 tokenów na sekundę. Jakość odpowiedzi na teście zrozumienia kodu bez zmian.

Pułapka: prompt ucinany po cichu

Przy prompcie większym niż okno Ollama nie zwraca błędu. Zostawia połowę:

WARN msg="truncating input prompt" limit=16386 prompt=67240 keep=4 new=16386

Model dostaje 16 tys. tokenów zamiast 67 tys. i odpowiada tak, jakby widział całość. Dla agenta to cichy błąd najgorszego rodzaju, bo wygląda jak poprawna odpowiedź.

Sufit 7B to 32 768 tokenów i jest to limit modelu, nie VRAM-u. Podnoszenie num_ctx do 65536 nie daje nic. Bezpiecznie przeszło 28 044 tokeny bez obcięcia. Aider dodatkowo potrzebuje pliku .aider.model.settings.yml w katalogu projektu, bo domyślnie jedzie na 8k i po cichu przycina repo mapę:

- name: ollama_chat/qwen2.5-coder:7b
  edit_format: diff
  use_repo_map: true
  extra_params:
    num_ctx: 32768

Test pierwszy: realne zadanie z testami

Zadanie na aplikacji Next.js z 30 testami w vitest: dopisać funkcję formatującą NIP i testy do niej. Weryfikacja obiektywna, npm test.

7B: 32 na 32 testy w 47 sekund. Pierwsza wersja nie dopisała importu, --auto-test to wyłapał, druga runda przeszła. 30B: 32 na 32 w 94 sekundy, za pierwszym podejściem, z lepszym pokryciem przypadków brzegowych.

Oba modele poprawnie posługują się formatem SEARCH/REPLACE Aidera i trafiają w istniejący kod. Na tym poziomie zadania lokalny model jest po prostu użyteczny.

Test drugi: to samo bez sprawdzianu

Drugie zadanie: zbudować i poprawiać landing page. Pięć rund z poleceniem “popraw, sam zdecyduj, co jest najsłabsze”.

7B przez pięć rund i 219 sekund nie naprawił ani jednego z dwóch realnych defektów: trzynastu tekstów zastępczych i brakującej reguły CSS, przez którą trzy karty stały jedna pod drugą zamiast obok siebie. Runda czwarta dała zero zmienionych linii - model przepisał identyczną treść i zakomunikował, że stronę ulepszył. 30B nasycił się po dwóch rundach i przez kolejne 693 sekundy nie wyprodukował nic, a przy okazji zjechał z wymaganego nagłówka na własny, z błędem gramatycznym, którego już nie cofnął.

Potem to samo zadanie z instrukcjami wskazującymi konkretny defekt, z tego samego punktu startowego:

5 rund generycznych5 rund kierowanych
qwen2.5-coder:7b219 s, zero napraw154 s, treść + układ + paleta + ceny + CTA
qwen3-coder:30b1147 s, dryf od briefu662 s, wszystko w zadanym zakresie

Rundy kierowane były szybsze i skuteczniejsze naraz, bo model generuje wtedy dużo mniej: 810 tokenów zamiast 1700. Nie przepisuje całego pliku na wszelki wypadek. Brakującą regułę CSS, której nie zauważył przez pięć rund, 7B naprawił w 23 sekundy po wskazaniu jej wprost.

Trzy reguły, które wyszły z pomiaru

Kotwicz instrukcję o selektor, nie o numer linii. W ostatniej rundzie wskazaliśmy 7B sześć wystąpień koloru wpisanego na sztywno, każde z numerem linii. Naprawił dokładnie te trzy, przy których podaliśmy także nazwę selektora. Nie tknął tych trzech opisanych samym numerem linii. Korelacja trzy na trzy. Model nie liczy linii, szuka kotwicy w tekście, więc podawaj mu nazwę funkcji, selektor albo dosłowny ciąg znaków.

Wyliczaj, nie każ przeszukiwać. Ten sam model dostał dwa polecenia naraz: “podmień te osiem zmiennych” wykonał co do znaku, “przejrzyj cały plik i zamień każdy kolor wpisany na sztywno” zignorował, zostawiając sześć wystąpień. 30B na tym samym zadaniu zszedł z trzynastu do dwóch. Przeszukiwanie skaluje się z wielkością modelu, wyliczanie nie.

Pisz zakres wprost. “Nie zmieniaj index.html” oba modele respektują i generują wtedy o połowę mniej tokenów, więc runda jest szybsza. Bez tego przepisują wszystko i dryfują od specyfikacji.

Wniosek

Granica użyteczności lokalnego modelu nie przebiega między 7B a 30B. Przebiega między “jest obiektywny sprawdzian” a “nie ma”.

Ten sam model, ta sama godzina: z npm test zdał w 47 sekund, bez sprawdzianu kręcił się pięć rund w przekonaniu, że ulepsza. To nie jest wada konkretnej wagi, tylko strukturalna cecha: model nie ma dostępu do własnej porażki. Potrafi wykonać, nie potrafi ocenić.

Praktycznie oznacza to, że lokalny model wpina się w proces tam, gdzie już masz sygnał zwrotny: testy, linter, typy, walidacja schematu, kompilator. W tych miejscach 7B na karcie za 1500 złotych robi robotę w kilkadziesiąt sekund, za darmo i bez wysyłania kodu na zewnątrz. Tam, gdzie “dobrze” znaczy “ktoś to ocenił”, lokalny model nie zastąpi ani człowieka, ani modelu z chmury.

Jeśli zastanawiacie się, które fragmenty waszego procesu mają taki sprawdzian, a które tylko wyglądają, jakby miały, to jest dokładnie ta rozmowa, którą prowadzimy na wdrożeniach: 30 minut, bez zobowiązań.

Podobał Ci się ten artykuł?

Udostępnij go osobom, którym ten materiał może się przydać.

Zacznij Naukę