feat: initial commit
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
# ADR-0001: APM jako łańcuch dostaw kontekstu agentowego
|
||||
|
||||
- Status: przyjęty
|
||||
- Data: 2026-08-28
|
||||
|
||||
## Kontekst
|
||||
|
||||
Prompty, skille i guardraile dla agentów mają wszystkie cechy zależności: są współdzielone
|
||||
między repozytoriami, zmieniają się w czasie, mają właścicieli i wpływają na wynik.
|
||||
Trzymane jako pliki w repozytorium silnika stają się nierozróżnialne od kodu: nie da się ich
|
||||
niezależnie wersjonować, nie da się powiedzieć "ten przebieg użył wiedzy migracyjnej w wersji 1.2.0".
|
||||
|
||||
## Decyzja
|
||||
|
||||
Kontekst agentowy dostarczamy pakietami APM (Microsoft Agent Package Manager):
|
||||
`apm.yml` deklaruje zależności, `apm install` rozwiązuje je do `apm_modules/`,
|
||||
`apm.lock.yaml` pinuje commity i hashe, `apm-policy.yml` ogranicza dopuszczalne źródła
|
||||
i prymitywy, `apm audit` wykrywa dryf.
|
||||
|
||||
Silnik (Python + agno) czyta prymitywy z `.apm/` oraz `apm_modules/` i kompiluje je
|
||||
do obiektów agno. Prymitywy lokalne mają pierwszeństwo przed zainstalowanymi.
|
||||
|
||||
## Konsekwencje
|
||||
|
||||
**Pozytywne**
|
||||
|
||||
- Zespół właściciela SDK dostarcza wiedzę migracyjną jako pakiet, bez dostępu do silnika.
|
||||
- Hash lockfile'a w manifeście przebiegu odpowiada na pytanie "co dokładnie wiedział model".
|
||||
- Polityka instalacyjna jest egzekwowana w CI, a nie w regulaminie.
|
||||
|
||||
**Negatywne**
|
||||
|
||||
- Dodatkowa zależność narzędziowa (APM CLI) w obrazie runnera.
|
||||
- Potrzebny wewnętrzny rejestr pakietów (w praktyce: repozytoria GitLaba) i dyscyplina tagowania.
|
||||
- Ryzyko rozjazdu między wersją silnika a formatem prymitywów - łagodzone testem
|
||||
`test_apm_context.py`, który waliduje kontekst przy każdym MR.
|
||||
|
||||
## Rozważane alternatywy
|
||||
|
||||
- **Prompty w repozytorium silnika.** Najprostsze, ale uniemożliwia niezależne wersjonowanie
|
||||
wiedzy dziedzinowej i rozmywa własność.
|
||||
- **Baza wektorowa z dokumentacją migracji.** Nieodtwarzalne: dwa przebiegi na tym samym commicie
|
||||
mogą dostać inny kontekst. Odpada w środowisku wymagającym audytu.
|
||||
- **Własny format pakietu.** Koszt utrzymania bez zysku; APM daje lockfile, politykę i skanowanie
|
||||
ukrytego Unicode z pudełka, opierając się na otwartych standardach (AGENTS.md, Agent Skills, MCP).
|
||||
@@ -0,0 +1,39 @@
|
||||
# ADR-0002: Deterministycznie tyle, ile się da; model tylko na resztę
|
||||
|
||||
- Status: przyjęty
|
||||
- Data: 2026-08-28
|
||||
|
||||
## Kontekst
|
||||
|
||||
Migracja SDK to w większości mechaniczne podmiany. Powierzenie ich modelowi kosztuje tokeny,
|
||||
wydłuża przebieg i wprowadza wariancję tam, gdzie wariancja jest czystą stratą -
|
||||
nikt nie chce, żeby dwa uruchomienia dawały inny diff dla tej samej zmiany nazwy metody.
|
||||
|
||||
## Decyzja
|
||||
|
||||
Dzielimy pracę wzdłuż linii "czy da się to zrobić kodem":
|
||||
|
||||
1. Wykrycie ekosystemu, plików zależności i komendy testowej - adapter (`adapters/`).
|
||||
2. Podbicie deklaracji wersji - adapter.
|
||||
3. Migracja objęta regułami - silnik reguł czytający `*.codemod.yaml` z pakietu APM.
|
||||
4. Reszta - agent `coder` z LLM.
|
||||
5. Ocena wyniku - agent `reviewer` **oraz** niezależna kontrola mechaniczna.
|
||||
|
||||
Reguły codemod są częścią pakietu APM (leżą obok notatki migracyjnej), więc podlegają
|
||||
temu samemu przeglądowi i wersjonowaniu co wiedza dla modelu.
|
||||
|
||||
## Konsekwencje
|
||||
|
||||
**Pozytywne**
|
||||
|
||||
- Powstaje tryb `--offline`: ta sama topologia bez modelu. Smoke test całego pipeline'u
|
||||
na każdym MR, bez GPU i bez internetu.
|
||||
- Punkt odniesienia: widać, ile przypadków model faktycznie dołożył ponad reguły.
|
||||
- Mniejszy koszt i krótszy przebieg dla typowych migracji.
|
||||
|
||||
**Negatywne**
|
||||
|
||||
- Dwie ścieżki do utrzymania (`RuleBrain`, `LlmBrain`) i wspólny kontrakt między nimi.
|
||||
- Reguły regexowe mają znane ograniczenia (zmiany strukturalne, wieloliniowe konteksty).
|
||||
Świadomie nie budujemy własnego silnika AST - od tego są narzędzia dziedzinowe
|
||||
(OpenRewrite, jscodeshift), które można podpiąć jako kolejny adapter.
|
||||
@@ -0,0 +1,38 @@
|
||||
# ADR-0003: Runtime w efemerycznym jobie GitLab CI, nie w usłudze
|
||||
|
||||
- Status: przyjęty
|
||||
- Data: 2026-08-28
|
||||
|
||||
## Kontekst
|
||||
|
||||
agno pozwala wystawić agentów jako usługę (AgentOS/FastAPI) z bazą sesji, pamięcią i UI.
|
||||
Kuszące, ale dla zadania "zmodyfikuj kod i otwórz MR" oznacza nową usługę produkcyjną:
|
||||
własne SLA, uwierzytelnianie, przechowywanie stanu, przegląd bezpieczeństwa i dostęp
|
||||
do repozytoriów z długożyjącego procesu.
|
||||
|
||||
## Decyzja
|
||||
|
||||
Runtime to efemeryczny job GitLab CI. Jedno uruchomienie = jeden job = jeden katalog roboczy
|
||||
= jeden komplet artefaktów. Stan przebiegu żyje w artefaktach (`run.json`, `trace.jsonl`,
|
||||
`changes.patch`), nie w bazie. Uprawnienia to uprawnienia joba (token projektowy),
|
||||
a nie konta usługowego z dostępem do wszystkiego.
|
||||
|
||||
Wyzwalanie: issue z labelką (webhook → trigger token), zdarzenie na MR, harmonogram
|
||||
oraz ręczny formularz w "Run pipeline" (zmienne z `description` i `options`).
|
||||
|
||||
## Konsekwencje
|
||||
|
||||
**Pozytywne**
|
||||
|
||||
- Zero nowej usługi do utrzymania i przeglądu bezpieczeństwa.
|
||||
- Naturalna izolacja: brak stanu współdzielonego między przebiegami.
|
||||
- Bramka manualna w CI daje właściciela podpisu pod zmianą.
|
||||
- Limity zasobów i czasu przychodzą z platformy CI.
|
||||
|
||||
**Negatywne**
|
||||
|
||||
- Brak pamięci między przebiegami - powtórka zaczyna od zera (świadomy kompromis:
|
||||
pamięć między przebiegami w narzędziu modyfikującym kod to więcej ryzyka niż korzyści).
|
||||
- Zimny start: instalacja kontekstu APM w każdym przebiegu (łagodzone cache'em i artefaktami).
|
||||
- Brak interaktywnego trybu "zapytaj człowieka w trakcie" - stąd wzorzec `requires_human`:
|
||||
agent nie pyta, tylko oznacza i idzie dalej.
|
||||
@@ -0,0 +1,36 @@
|
||||
# ADR-0004: Guardraile w dwóch warstwach - prompt nie jest zabezpieczeniem
|
||||
|
||||
- Status: przyjęty
|
||||
- Data: 2026-08-28
|
||||
|
||||
## Kontekst
|
||||
|
||||
Instrukcje w promptcie ("nie modyfikuj .gitlab-ci.yml") działają w większości przypadków,
|
||||
ale są miękkie: zależą od modelu, długości kontekstu i sformułowania zadania.
|
||||
W repozytorium bankowym pytanie audytora nie brzmi "czy model zwykle tego nie robi",
|
||||
tylko "co się stanie, jeśli spróbuje".
|
||||
|
||||
## Decyzja
|
||||
|
||||
Każdy istotny guardrail istnieje dwukrotnie:
|
||||
|
||||
- jako instrukcja APM dla modelu (`security.instructions.md`) - żeby agent w ogóle nie próbował,
|
||||
- jako mechanizm w kodzie (`WorkspaceTools`, `Settings.deny_globs`, budżety, brak powłoki) -
|
||||
żeby próba zakończyła się błędem narzędzia zapisanym w śladzie audytowym.
|
||||
|
||||
Ta sama zasada dotyczy oceny wyniku: werdykt agenta `reviewer` jest uzupełniany niezależną
|
||||
kontrolą mechaniczną (pliki objęte zakazem, zmiany w testach, budżet zakresu, status weryfikacji).
|
||||
|
||||
## Konsekwencje
|
||||
|
||||
**Pozytywne**
|
||||
|
||||
- Naruszenie guardraila jest zdarzeniem obserwowalnym, a nie niewidoczną zmianą w diffie.
|
||||
- Da się odpowiedzieć na pytanie "co system uniemożliwia", a nie tylko "o co prosi".
|
||||
- Testy zabezpieczeń (`test_workspace_tools.py`) są testami jednostkowymi, nie ćwiczeniem z promptowania.
|
||||
|
||||
**Negatywne**
|
||||
|
||||
- Duplikacja reguł w dwóch miejscach - trzeba je świadomie utrzymywać razem.
|
||||
- Zbyt ciasne budżety potrafią zablokować poprawną, ale szeroką zmianę; wartości są
|
||||
konfigurowalne przez zmienne środowiskowe i powinny być dostrajane per klasa repozytoriów.
|
||||
@@ -0,0 +1,73 @@
|
||||
# ADR-0005: Backend LLM wybierany per platforma, atrapa jako test double
|
||||
|
||||
- Status: przyjęty
|
||||
- Data: 2026-08-29
|
||||
|
||||
## Kontekst
|
||||
|
||||
Docelowo pipeline korzysta z vLLM wystawionego na OpenShifcie. Do pracy nad samym
|
||||
pipeline'em potrzebny jest jednak model uruchamialny lokalnie - i tu kończy się
|
||||
jednorodność: **nie istnieje jedna komenda uruchamiająca vLLM na Linuksie i na macOS**.
|
||||
|
||||
- Linux z GPU NVIDIA: kontener `vllm/vllm-openai`, ścieżka najbliższa produkcji.
|
||||
- macOS na Apple Silicon: Docker nie ma dostępu do Metala, więc kontener odpada.
|
||||
Zostaje natywny plugin społecznościowy `vllm-project/vllm-metal` (backend MLX),
|
||||
instalowany skryptem do własnego venva, wymagający arm64 Pythona 3.12 i Xcode CLT.
|
||||
- Maszyna bez GPU i bez Apple Silicon: żaden z powyższych nie ma sensu.
|
||||
|
||||
Osobny problem: testy. Tryb `--offline` sprawdza `RuleBrain`, ale nie dotyka tego,
|
||||
co jest w tym projekcie najbardziej kruche - budowy agentów z definicji APM,
|
||||
wywoływania narzędzi przez model i parsowania strukturalnych wyjść. Uzależnienie
|
||||
tego testu od GPU oznaczałoby, że nie uruchomi go nikt poza jedną maszyną.
|
||||
|
||||
## Decyzja
|
||||
|
||||
**Kontrakt jest jeden: endpoint zgodny z API OpenAI pod `CODEMOD_LLM_BASE_URL`.**
|
||||
Backendy różnią się wyłącznie tym, jak ten endpoint powstaje. Konfiguracja mieszka
|
||||
w `llm/models.yml` (jedno źródło prawdy: adresy, modele per rola, argumenty serwera),
|
||||
a `task llm:up` wykrywa platformę i uruchamia właściwy wariant:
|
||||
|
||||
| Backend | Kiedy | Jak |
|
||||
|---|---|---|
|
||||
| `vllm-gpu` | Linux + NVIDIA + Docker | `docker compose --profile vllm-gpu` |
|
||||
| `vllm-metal` | macOS Apple Silicon | natywny plugin MLX, `vllm serve` |
|
||||
| `ollama` | awaryjnie, obie platformy | natywnie (Metal) lub kontener |
|
||||
| `mock` | testy, CI | `llm/mock_server.py`, stdlib, bez pobierania |
|
||||
|
||||
Atrapa (`mock`) to **test double, nie symulator modelu**: odpowiada z góry ustalonymi
|
||||
wywołaniami narzędzi i strukturami zestrojonymi z fixture'em `acme-app`, a routing
|
||||
odpowiedzi opiera na nazwie agenta wstrzykiwanej przez agno do komunikatu systemowego.
|
||||
Dzięki niej `tests/test_llm_path.py` przepuszcza pełny przebieg przez `LlmBrain`
|
||||
na każdym MR, bez GPU i bez sieci.
|
||||
|
||||
## Konsekwencje
|
||||
|
||||
**Pozytywne**
|
||||
|
||||
- Projekt startuje od zera na Linuksie i na macOS jedną komendą; różnice platformowe
|
||||
są schowane za `task llm:up`, a nie rozsypane po README.
|
||||
- Ścieżka agentowa jest testowana automatycznie, nie tylko ręcznie na czyjejś maszynie.
|
||||
- Podmiana modelu lub backendu to zmiana w `llm/models.yml`, nie w kodzie.
|
||||
- Wszystkie backendy wymuszają `--enable-auto-tool-choice` z parserem `hermes`;
|
||||
bez tego vLLM zwraca opis wywołania w treści zamiast `tool_calls` i agent `coder`
|
||||
nie tknąłby żadnego pliku.
|
||||
|
||||
**Negatywne**
|
||||
|
||||
- Cztery ścieżki uruchomieniowe do utrzymania. Ograniczamy koszt tym, że wiedza
|
||||
o nich jest w jednym pliku YAML plus jednym skrypcie, a nie w zadaniach.
|
||||
- `vllm-metal` jest pluginem społecznościowym - może się rozjechać z upstreamem vLLM.
|
||||
Dlatego Ollama zostaje jako wariant awaryjny na macOS.
|
||||
- Atrapa jest zestrojona z fixture'em: zmiana fixture'u wymaga aktualizacji scenariusza.
|
||||
To świadomy koszt - alternatywą jest brak testu tej ścieżki.
|
||||
- Model 4B wystarcza do sprawdzenia hydrauliki, ale nie do realnych migracji.
|
||||
Do pracy trzeba większego - stąd tabela rekomendacji per rola w `llm/models.yml`.
|
||||
|
||||
## Rozważane alternatywy
|
||||
|
||||
- **Tylko vLLM, macOS niech używa zdalnego endpointu.** Odpada: uniemożliwia pracę
|
||||
offline i uzależnia każdego developera od środowiska współdzielonego.
|
||||
- **Tylko Ollama.** Prostsze, ale rozjeżdża się z produkcją (inne API tool-callingu,
|
||||
inne kwantyzacje), więc testy przestałyby cokolwiek mówić o zachowaniu na vLLM.
|
||||
- **Brak atrapy, testy tylko na realnym modelu.** Niedeterministyczne i niedostępne
|
||||
w CI; test, który czasem przechodzi, jest gorszy niż brak testu.
|
||||
Reference in New Issue
Block a user