Files
agentic-codemod-pipeline/docs/architecture.md
T
2026-08-29 13:17:59 +02:00

176 lines
9.9 KiB
Markdown

# Architektura sieci agentowej modyfikującej kod
## 1. Problem
Zmiany typu "podnieś wersję SDK w 60 repozytoriach" są mechanicznie proste, ale kosztowne:
każde repozytorium ma inny układ, inne miejsca użycia biblioteki i inny poziom pokrycia testami.
Klasyczna automatyzacja (skrypt + sed) załatwia 70% przypadków i zostawia najgorsze 30%.
Agent z LLM załatwia pozostałe 30%, ale wprowadza trzy nowe problemy: niepowtarzalność,
brak audytowalności i nieograniczony zakres zmian.
Ten projekt jest odpowiedzią na pytanie: **jak wpuścić agenta do repozytorium tak,
żeby dało się to pokazać audytorowi.**
## 2. Trzy warstwy i jedna zasada
```
┌───────────────────────────────────────────────────────────────────┐
│ KONTEKST — APM (Microsoft Agent Package Manager) │
│ apm.yml + .apm/{instructions,skills,prompts,agents,context} │
│ Wersjonowany, pinowany lockfile'em, audytowany polityką. │
└──────────────────────────────┬────────────────────────────────────┘
│ apm install → apm_modules/
┌──────────────────────────────▼────────────────────────────────────┐
│ RUNTIME — agno Workflow │
│ Kompilacja prymitywów APM → Agent/Skills/Steps. │
│ Deterministyczne kroki + sandboxowane narzędzia. │
└──────────────────────────────┬────────────────────────────────────┘
│ agentic-codemod run
┌──────────────────────────────▼────────────────────────────────────┐
│ EGZEKUCJA — GitLab CI │
│ Efemeryczny job, bramka manualna, artefakty audytowe, MR. │
└───────────────────────────────────────────────────────────────────┘
```
Zasada nadrzędna: **kontekst jest artefaktem, nie kodem**. Zmiana zachowania sieci agentowej
(nowa reguła migracji, ostrzejszy guardrail, inny model dla roli) to podbicie wersji pakietu APM,
a nie merge request do Pythona. Dzięki temu zespół właściciela SDK może dostarczyć wiedzę
migracyjną, nie mając dostępu do silnika pipeline'u.
## 3. Przepływ
```mermaid
flowchart TD
T[Trigger: issue z labelką / MR / harmonogram / formularz] --> C[apm install + apm audit]
C --> I[intake: deterministyczny wsad zadania]
I --> R[recon: fakty o repozytorium]
R --> P[plan: ChangePlan z kryteriami akceptacji]
P --> G{jest co wdrażać?}
G -- nie --> M[manifest + komentarz w issue]
G -- tak --> B[bump wersji: deterministyczny]
B --> L[pętla: implement → verify]
L -- czerwono, iteracja < limit --> L
L -- zielono --> V[review: recenzja diffa]
V --> S[scribe: opis merge requesta]
S --> H{bramka manualna w CI}
H -- zatwierdzone --> MR[branch + commit + push + MR]
H -- nie --> A[artefakty do wglądu, zero zmian zdalnych]
```
### Role w sieci
| Agent | Model | Narzędzia | Wyjście |
|---|---|---|---|
| `scout` | planner | odczyt, wyszukiwanie | `RepoProfile` |
| `planner` | planner | odczyt, wyszukiwanie | `ChangePlan` |
| `coder` | coder | odczyt, edycja, weryfikacja | zmieniony kod |
| `reviewer` | reviewer | odczyt, diff | `ReviewVerdict` |
| `scribe` | scribe | diff | `MergeRequestDraft` |
Definicje ról leżą w `.apm/agents/*.agent.md`. Kod nie zna nazw agentów - czyta je z kontekstu.
## 4. Co jest deterministyczne, a co należy do modelu
To jest najważniejsza decyzja projektowa. Model dostaje wyłącznie tę część pracy,
której nie da się zrobić inaczej.
| Krok | Wykonawca | Dlaczego |
|---|---|---|
| Wykrycie ekosystemu, plików zależności, komendy testowej | kod (`adapters/`) | jednoznaczne, sprawdzalne |
| Podbicie deklaracji wersji | kod (`adapters/`) | zero powodów, by ryzykować halucynację w pliku zależności |
| Migracja objęta regułami codemod | kod (`workflow/codemod.py`) | reguły z pakietu APM, w pełni powtarzalne |
| Nietypowe użycia API, kontekst biznesowy | model (`coder`) | tu klasyczna automatyzacja się kończy |
| Ocena zakresu i ryzyka zmiany | model (`reviewer`) + kontrola mechaniczna | dwa niezależne spojrzenia |
| Uruchomienie testów | kod (`tools/verification.py`) | agent nie dostaje powłoki |
| Branch, commit, push, MR | kod (`workflow/runner.py`) | jednoznaczny autor i format historii |
Konsekwencja: pipeline ma **dwa tryby o identycznej topologii**. Tryb `--offline` używa
wyłącznie reguł (`RuleBrain`), tryb domyślny dokłada agentów (`LlmBrain`). Tryb offline jest
bramką jakości na każdym MR - sprawdza cały przepływ bez kosztu GPU i bez dostępu do modelu.
## 5. Model bezpieczeństwa
Guardraile istnieją w dwóch warstwach, bo prompt nie jest zabezpieczeniem.
**Warstwa promptowa** (`.apm/instructions/security.instructions.md`) - dla modelu:
zakaz zmian w plikach pipeline'u, zakaz sekretów, zakaz rozszerzania zakresu,
zakaz usuwania testów, zakaz nowych zależności.
**Warstwa egzekucji** (`tools/workspace.py`, `config.py`) - dla audytora:
- każda ścieżka rozwiązywana względem korzenia repozytorium; wyjście przez `..` i dowiązania niemożliwe,
- lista `DEFAULT_DENY_GLOBS` blokuje zapis do `.gitlab-ci.yml`, `.git/`, `Dockerfile*`, `*.pem`, `.env*`, manifestów APM,
- budżety: liczba zmienionych plików, rozmiar pliku, liczba wywołań narzędzi, limit czasu weryfikacji,
- agent nie ma powłoki - jedyna operacja wykonawcza to `run_verification` z komendą ustaloną przez adapter,
- środowisko weryfikacji jest czyszczone ze zmiennych zawierających `TOKEN`, `SECRET`, `PASSWORD`, `API_KEY`,
- każde wywołanie narzędzia trafia do `trace.jsonl` z redakcją sekretów.
**Warstwa dostawy kontekstu** (`apm-policy.yml`): dozwolone źródła pakietów, zakaz prymitywu
`hooks` (wykonuje kod na runnerze), wymagane piny po tagu, wymagany lockfile, `apm audit`
wykrywający ręczną edycję zainstalowanego kontekstu.
## 6. Artefakty przebiegu
Każde uruchomienie zostawia w `--run-dir` komplet dowodów:
| Plik | Zawartość |
|---|---|
| `run.json` | manifest: zadanie, profil, plan, weryfikacje, werdykt, modele, hash lockfile'a APM |
| `plan.json` | plan zmian z kryteriami akceptacji i pozycjami `requires_human` |
| `profile.json` | ustalone fakty o repozytorium |
| `review.json` | werdykt recenzenta z findingami |
| `merge_request.md` | tytuł i opis MR |
| `changes.patch` | pełny diff |
| `trace.jsonl` | każde wywołanie narzędzia z czasem i wynikiem |
To jest odpowiedź na pytanie audytora "na jakiej podstawie ta zmiana weszła do repozytorium".
## 7. Backend LLM
Silnik zna wyłącznie jeden kontrakt: **endpoint zgodny z API OpenAI pod `CODEMOD_LLM_BASE_URL`**.
Cała wiedza o dostawcy jest w `llm.py` (fabryka modeli) i `llm/models.yml` (adresy, modele per rola).
Docelowo jest to vLLM na OpenShifcie. Lokalnie nie ma jednej ścieżki dla obu systemów -
Docker na macOS nie ma dostępu do Metala - więc `task llm:up` wykrywa platformę:
| Backend | Kiedy | Mechanizm |
|---|---|---|
| `vllm-gpu` | Linux + NVIDIA + Docker | `vllm/vllm-openai` przez docker compose |
| `vllm-metal` | macOS Apple Silicon | plugin `vllm-project/vllm-metal` (MLX), natywny `vllm serve` |
| `ollama` | awaryjnie, obie platformy | `/v1` zgodne z OpenAI, natywnie lub w kontenerze |
| `mock` | testy i CI | `llm/mock_server.py` - stdlib, zero pobierania |
Każdy backend serwujący realny model wymusza `--enable-auto-tool-choice` z parserem `hermes`.
Bez tego vLLM zwraca opis wywołania w treści odpowiedzi zamiast `tool_calls`, agent `coder`
nie tknąłby żadnego pliku, a przebieg kończyłby się "sukcesem" bez jednej zmiany - awaria cicha,
czyli najgorszy rodzaj.
### Atrapa jako test double
`mock` nie jest symulatorem modelu. Odpowiada z góry ustalonymi wywołaniami narzędzi
i strukturami zestrojonymi z fixture'em `acme-app`, a rozpoznaje pytającego po nazwie agenta,
którą agno wstrzykuje do komunikatu systemowego. Dzięki temu `tests/test_llm_path.py`
przepuszcza pełny przebieg przez `LlmBrain` - budowę agentów z definicji APM, tool-calling,
parsowanie strukturalnych wyjść i pętlę weryfikacji - na każdym MR, bez GPU i bez sieci.
Podział odpowiedzialności testów jest więc taki:
| Co jest sprawdzane | Czym |
|---|---|
| reguły, adaptery, sandbox, workflow | tryb `--offline` (`RuleBrain`) |
| agenci, narzędzia, schematy, pętla | tryb `llm` na atrapie (`LlmBrain` + `mock`) |
| jakość migracji na nietypowym kodzie | realny model, ręcznie |
## 8. Granice rozwiązania
- **Bez agenta-nadzorcy.** Topologia jest jawnym przepływem (agno `Workflow`), a nie
swobodną delegacją między agentami. W zamian za mniejszą elastyczność dostajemy
przewidywalny koszt, powtarzalny ślad i możliwość wstawienia bramki między krokami.
- **Bez AgentOS.** Runtime to efemeryczny job CI. Nie utrzymujemy usługi z pamięcią sesji -
stan przebiegu żyje w artefaktach.
- **Bez dostępu do sieci z poziomu agenta.** Cała wiedza migracyjna przychodzi w pakiecie APM.
Jeśli notatki migracyjnej nie ma, pipeline zatrzymuje się i zgłasza `requires_human`,
zamiast migrować "z pamięci modelu".
- **Nie każde repozytorium się nadaje.** Bez działającej komendy testowej weryfikacja nic nie znaczy,
a bez weryfikacji ten pipeline nie ma prawa niczego publikować.