init: pi_coder_v2 — Single-Server (Option B)

Beide Rollen (Coder + Judge) auf einem llama.cpp-Server (Port 8001).
switchModel wechselt zwischen qwen3.5-coder und qwen3.5-judge via
llama-cpp-single-Provider ohne Server-Neustart. start-single.sh startet
den gemeinsamen Server, GPU wählbar per GPU_DEVICE-Env-Variable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-06-15 01:04:55 +02:00
commit 3cf9ef928b
10 changed files with 3361 additions and 0 deletions

948
BEDIENUNGSANLEITUNG.md Normal file
View file

@ -0,0 +1,948 @@
# Bedienungsanleitung: pi_coder
pi_coder ist ein Werkzeug, das zwei lokale KI-Modelle als **Coder** und **Judge** einsetzt,
um Software automatisch zu schreiben, zu prüfen und zu verbessern — alles gesteuert über
einfache Slash-Kommandos in der pi-Agent-Oberfläche.
---
## Inhaltsverzeichnis
1. [Konzept: Coder und Judge](#1-konzept-coder-und-judge)
2. [Vorbereitung](#2-vorbereitung)
3. [Server starten und stoppen](#3-server-starten-und-stoppen)
4. [Neues Projekt anlegen](#4-neues-projekt-anlegen)
5. [Manueller Workflow: /coder → /judge → /fix → /shipit](#5-manueller-workflow)
6. [Automatischer Workflow: /optimize](#6-automatischer-workflow-optimize) (inkl. [Interactive-Modus](#interactive-modus))
7. [Kleine Änderungen: /patch und /quick_check](#7-kleine-änderungen-patch-und-quick_check)
8. [Dokumentation generieren: /update_doku](#8-dokumentation-generieren-update_doku)
9. [Versionsverwaltung: /version](#9-versionsverwaltung-version)
10. [TASK.md verstehen und nutzen](#10-taskmd-verstehen-und-nutzen)
11. [Typische Anwendungsfälle](#11-typische-anwendungsfälle)
12. [Fehlermeldungen und Lösungen](#12-fehlermeldungen-und-lösungen)
---
## 1. Konzept: Coder und Judge
pi_coder verwendet zwei Rollen:
**Coder** (Port 8001): Schreibt und repariert Code. Liest die Aufgabe aus `TASK.md`,
implementiert sie, führt Tests aus und erstellt Git-Commits.
**Judge** (Port 8002): Überprüft den Code mit dem Blick eines skeptischen Senior-Entwicklers.
Prüft Korrektheit, Robustheit, Randfälle, Sicherheit und Produktionsreife. Gibt ein Urteil:
- `PASS` — Code ist in Ordnung
- `PASS WITH CONCERNS` — grundsätzlich akzeptabel, aber mit Anmerkungen
- `FAIL` — enthält Blocker, die behoben werden müssen
Der Grundgedanke: Coder und Judge haben keine „Höflichkeitsschranke" zueinander —
der Judge kritisiert direkt und konkret, der Coder repariert ohne Widerspruch.
---
## 2. Vorbereitung
### Server starten
```bash
cd ~/pi_coder
./start-servers.sh
```
Ausgabe bei Erfolg:
```
[*] Starte beide Server parallel ...
[✓] Coder (:8001) bereit
[✓] Judge (:8002) bereit
```
Dauer: bis zu 5 Minuten (Modell wird in GPU-VRAM geladen; neuere llama.cpp-Versionen
prüfen beim Start die VRAM-Verfügbarkeit, was zusätzliche Zeit kostet).
### Status prüfen
```bash
./status.sh
```
```
=== LLaMA-Server Status ===
qwen36-27b-coder (Port 8001): Container=RUNNING HTTP=OK
qwen36-27b-judge (Port 8002): Container=RUNNING HTTP=OK
```
### pi agent öffnen
pi agent im Projektverzeichnis starten — das ist das Verzeichnis, in dem dein Code liegt,
**nicht** `~/pi_coder`:
```bash
cd ~/MeinProjekt
pi
```
---
## 3. Server starten und stoppen
### GPU-Zuordnung (3-GPU-Setup)
Bei einem System mit drei GPUs sind die Rollen fest in den Startskripten verankert —
**keine manuelle Konfiguration nötig:**
| GPU | Modell | Rolle |
|-----|--------|-------|
| GPU 0 (NVIDIA T600) | — | Display / Monitor — wird von den Skripten nicht angetastet |
| GPU 1 (RTX 3090, 24 GB) | Qwen3.6-27B | **Coder** (Port 8001) |
| GPU 2 (RTX 3090, 24 GB) | Qwen3.6-27B | **Judge** (Port 8002) |
Jeder Server bekommt seine eigene GPU exklusiv — so gibt es keinen VRAM-Konflikt,
auch wenn beide gleichzeitig laufen.
**Wichtig:** Andere GPU-Prozesse (z. B. TTS-Dienste, Ollama) auf GPU 1 oder GPU 2 können
VRAM belegen und dazu führen, dass ein Server nicht startet. Im Zweifel prüfen:
```bash
nvidia-smi --query-gpu=index,name,memory.used,memory.free --format=csv
```
### Beide starten (empfohlen)
```bash
cd ~/pi_coder
./start-servers.sh
```
Erwartete Ausgabe:
```
[*] Starte beide Server parallel ...
[✓] Coder (:8001) bereit
[✓] Judge (:8002) bereit
```
Dauer: bis zu 5 Minuten. Danach sind beide Modelle im VRAM und sofort antwortbereit.
### Einzelnen Server neu starten
Z.B. wenn nur der Judge-Server abgestürzt ist:
```bash
./start-judge.sh # Port 8002, GPU 2
# oder:
./start-coder.sh # Port 8001, GPU 1
```
### Beide stoppen
```bash
./stop-servers.sh
```
### Status prüfen
```bash
./status.sh
```
### Alternativer Modellpfad
Falls die GGUF-Datei an einem anderen Ort liegt:
```bash
HF_HOME=/mnt/daten/huggingface ./start-servers.sh
```
---
## 4. Neues Projekt anlegen
### Kommando
```
/new_project <pfad>
```
### Beispiel
```
/new_project ~/Python_Programs/mein_tool
```
Was passiert:
- Verzeichnis `~/Python_Programs/mein_tool` wird angelegt
- `git init` wird ausgeführt
- `.gitignore` wird mit Standardeinträgen angelegt und committed
**Wichtig:** pi agent wechselt **nicht automatisch** in das neue Verzeichnis —
die Session bleibt im aktuellen Verzeichnis. Nach dem Anlegen:
```bash
cd ~/Python_Programs/mein_tool
pi
```
Dann kannst du `/coder` oder `/optimize` mit dem neuen Projekt verwenden.
---
## 5. Manueller Workflow
Der manuelle Workflow gibt dir volle Kontrolle über jeden Schritt.
### Schritt 1: /coder — Aufgabe übergeben
```
/coder <auftrag>
```
Der Coder:
1. Legt `TASK.md` im aktuellen Verzeichnis an (oder hängt an bestehende an)
2. Liest `TASK.md` und implementiert den Auftrag
3. Führt Tests oder Build-Checks aus
4. Erstellt einen Git-Commit
**Beispiel:**
```
/coder Schreibe ein Python-Kommandozeilenprogramm 'textcount'. Es soll eine Textdatei als Argument nehmen und folgendes ausgeben: Anzahl Zeichen, Wörter, Zeilen und die 5 häufigsten Wörter (ohne Stoppwörter).
```
Typische Ausgabe des Coders:
```
Implementierung abgeschlossen.
- src/textcount.py erstellt (Hauptprogramm)
- tests/test_textcount.py erstellt (Unit-Tests)
- requirements.txt angelegt (keine externen Abhängigkeiten)
- Alle 8 Tests bestanden
- Commit: feat: implement textcount CLI tool
Risiken: Stoppwortliste nur Deutsch/Englisch, keine Konfigurations-Option.
```
### Schritt 2: /judge — Code überprüfen lassen
```
/judge
```
Optionaler Fokus:
```
/judge Besonderes Augenmerk auf Fehlerbehandlung und Edge Cases
```
Der Judge:
1. Liest `TASK.md` und prüft ob alle Anforderungen umgesetzt sind
2. Analysiert `git show HEAD`
3. Führt Tests aus
4. Gibt ein strukturiertes Urteil aus
**Beispiel-Ausgabe PASS:**
```
Urteil: PASS WITH CONCERNS
Blocker: keine
Major:
- Stoppwortliste ist hardcoded; große Projekte erwarten --stopwords-file Option
Minor:
- Keine --version Flag
- Fehlermeldung bei nicht-existenter Datei gibt keinen Exit-Code 1 zurück
Fehlende Tests:
- Test für leere Datei fehlt
- Test für Datei mit nur Leerzeichen fehlt
Produktionsrisiken:
- Bei sehr großen Dateien (>1 GB) wird alles in den RAM geladen
Konkrete Fix-Aufträge:
1. exit(1) bei FileNotFoundError
2. Test für leere Eingabedatei
```
**Beispiel-Ausgabe FAIL:**
```
Urteil: FAIL
Blocker:
- textcount.py importiert 'collections.Counter' aber das ist nicht installiert
(Counter ist stdlib, aber der Import-Fehler tritt bei Python < 3.9 auf)
- ./textcount.py existiert nicht — tests/test_textcount.py schlägt komplett fehl
Major: ...
```
### Schritt 3: /fix — Kritik beheben
```
/fix
```
Optionaler Hinweis:
```
/fix Den Major-Punkt mit der Stoppwortliste kannst du weglassen, das ist kein Produktionsprojekt
```
Der Coder arbeitet die Judge-Kritik ab (Blocker zuerst, dann Major, dann Minor)
und erstellt einen neuen Commit.
### Schritt 4: /shipit — Finale Freigabe
```
/shipit
```
Der Judge gibt ein finales Urteil:
- `SHIP` — bereit für Produktion
- `NO-SHIP` — noch Probleme offen
**Beispiel:**
```
Urteil: SHIP
Letzte Blocker: keine
Restrisiken:
- Kein Streaming für sehr große Dateien (dokumentiert in README)
Empfohlene Sofortmaßnahmen: keine
```
---
## 6. Automatischer Workflow: /optimize
`/optimize` führt den gesamten Coder→Judge→Fix-Zyklus automatisch durch.
### Syntax
```
/optimize <auftrag> [--rounds N] [--with-doku] [--continue] [--interactive]
[--no-tests] [--approve-concerns] [--test-cmd "cmd"] [--test-timeout N]
```
- `--rounds N` — maximale Anzahl Runden (Standard: 2)
- `--with-doku` — nach SHIP automatisch `/update_doku` ausführen
- `--continue` — überspringt die Implementierungsphase und startet direkt mit dem
Judge→Fix-Zyklus ab dem aktuellen Code-Stand. Nützlich wenn man bereits manuell
`/coder`, `/judge` und `/fix` durchgeführt hat und den Rest automatisieren möchte.
Im `--continue`-Modus werden Coder- und Judge-Server gleichzeitig geprüft.
- `--interactive` — pausiert nach erstem PASS für einen menschlichen Checkpoint.
Details: siehe [Interactive-Modus](#interactive-modus) weiter unten.
- `--no-tests` — überspringt die automatische Test-Erkennung. Sinnvoll wenn keine
Test-Suite vorhanden ist oder Tests über externe Infrastruktur laufen.
- `--approve-concerns` — behandelt „PASS WITH CONCERNS" wie „PASS": kein ShipIt-Call,
direktes SHIP. Für Projekte, bei denen du dem Judge-Urteil vertraust.
- `--test-cmd "befehl"` — überschreibt die automatische Test-Erkennung mit einem
eigenen Befehl (z.B. `--test-cmd "pytest tests/ -x"`).
- `--test-timeout N` — maximale Laufzeit pro Test-Befehl in Sekunden (Standard: 120).
### Beispiel: einfacher Auftrag
```
/optimize Schreibe ein Rust-Programm 'genpw' das sichere Passwörter generiert. Optionen: --length N (Standard 16), --count N (Standard 1), --no-symbols, --no-numbers.
```
Was im Hintergrund passiert:
```
Phase 1: Coder implementiert...
Phase 2: Runde 1/2: Quick-Check (kompakter Erstcheck)...
→ Urteil: FAIL (2 Blocker)
Phase 3: Runde 1/2: Coder fixt...
Phase 4: Runde 2/2: Judge — TASK.md + letzter Commit + Tests...
→ Urteil: PASS WITH CONCERNS
✓ PASS WITH CONCERNS nach Runde 2
Finale ShipIt-Prüfung... (nur bei PASS WITH CONCERNS, nicht bei klarem PASS)
→ SHIP
[Dialog: Version → v0.1.0 (empfohlen)]
```
**Runde 1 = Quick-Check:** Kompakter Prompt ohne TASK.md-Analyse — erkennt offensichtliche
Fehler schnell. Erst ab Runde 2 (oder bei `--continue`) kommt der vollständige Judge-Prompt.
Bei klarem `PASS` entfällt die ShipIt-Runde — es wird direkt SHIP ausgelöst.
Mit `--approve-concerns` gilt das auch für `PASS WITH CONCERNS`.
Während des Ablaufs zeigt die Statuszeile die aktuelle Phase — mit laufendem Timer,
der beweist, dass die LLM tatsächlich arbeitet (und nicht hängt):
```
◉ Coder implementiert: Login-Flow mit JWT [01:23]
◉○ Runde 1/2: Quick-Check [00:47]
●◉ Runde 2/2: Coder fixt — fehlendes Null-Check [01:05]
●● ✓ PASS nach Runde 2/2 — ShipIt…
🚀 SHIP produktionsreif
```
Steht der Timer still, hängt der Prozess — dann `/cancel` verwenden.
### Beispiel: mehr Runden
```
/optimize Implementiere einen vollständigen REST-API-Client für die GitHub API in Python mit Rate-Limiting, Retry-Logic und Caching --rounds 5
```
### Beispiel: mit automatischer Dokumentation
```
/optimize Schreibe ein Go-Tool 'logfilter' das Logdateien nach Regex-Muster filtert --with-doku
```
Nach SHIP werden automatisch ausgeführt:
1. Code-Kommentare einfügen
2. README.md schreiben
3. BEDIENUNGSANLEITUNG.md schreiben
### Vom manuellen Workflow in den automatischen wechseln
Du hast bereits `/coder`, `/judge` und `/fix` manuell durchgeführt und möchtest
den Rest automatisch ablaufen lassen:
```
/optimize --continue
```
```
/optimize --continue --rounds 5
```
```
/optimize --continue --with-doku
```
Die Implementierungsphase wird übersprungen — der Judge prüft sofort den aktuellen
Stand und der Fix-Zyklus läuft automatisch bis PASS oder max. N Runden.
### Loop-Erkennung
Wenn zweimal hintereinander genau dieselben Blocker auftreten, bricht `/optimize` ab:
```
⚠ Derselbe Blocker tritt erneut auf Schleife abgebrochen. Bitte manuell prüfen.
```
In diesem Fall: `/judge` manuell ausführen, Blocker lesen, mit `/fix` manuell eingreifen.
### Max. Runden ohne PASS
```
⚠ 2 Runden durchlaufen ohne PASS. Bitte manuell prüfen.
```
Dann: `/judge` und `/fix` manuell für gezielte Eingriffe.
Mit `--rounds N` kann die Grenze hochgesetzt werden, z.B. `--rounds 5` für komplexe Aufgaben.
### Interactive-Modus
Mit `--interactive` pausiert `/optimize` nach dem ersten PASS und wartet auf menschliches
Feedback — bevor das abschließende SHIP ausgelöst wird.
```
/optimize Implementiere Feature X --interactive
```
Typischer Ablauf:
```
Phase 1: Coder implementiert...
Phase 2: Judge prüft...
→ Urteil: PASS
⏸ PASS erreicht. Weitere Features? /continue "Zusatzauftrag" — oder /continue zum Shippern.
```
Jetzt hast du drei Optionen:
**Option A: Direkt shippern**
```
/continue
```
→ ShipIt wird gestartet, Version-Dialog erscheint.
**Option B: Zusatzauftrag hinzufügen**
```
/continue "Füge außerdem eine --verbose Option hinzu"
```
→ Coder implementiert den Zusatz, dann läuft der Judge-Loop erneut an.
→ Nach erneutem PASS erscheint der Checkpoint wieder — du kannst beliebig viele
Iterationen anhängen, bevor du mit `/continue` zum SHIP gehst.
**Option C: Abbrechen**
```
/cancel
```
→ Loop wird abgebrochen, kein SHIP.
**Timeout:** Wenn du 30 Minuten lang nichts eingibst, bricht `/optimize` automatisch ab.
**Wann ist `--interactive` sinnvoll?**
- Wenn der Auftrag aus mehreren voneinander abhängigen Features besteht
- Wenn du nach jeder fertigen Stufe entscheiden möchtest, ob du weitermachst
- Wenn du sicherstellen willst, dass nichts unbeabsichtigt committed wird
---
## 7. Kleine Änderungen: /patch und /quick_check
Für minimale Korrekturen — kein voller Review-Zyklus, keine TASK.md-Änderungen.
### /patch — kleine Änderung umsetzen
```
/patch <beschreibung der änderung>
```
Der Coder ändert **ausschließlich** das Beschriebene, prüft ob es noch kompiliert/startet
und erstellt einen Commit.
**Beispiele:**
```
/patch Mindestpasswortlänge von 4 auf 8 Zeichen erhöhen
```
```
/patch Fehlermeldung bei ungültigem Argument von stderr auf stdout umleiten
```
```
/patch Versionsnummer in Cargo.toml von 0.1.0 auf 0.2.0 erhöhen
```
```
/patch Die Funktion parse_args() soll bei fehlendem --input-Argument eine sinnvolle Hilfsnachricht ausgeben statt zu paniken
```
### /quick_check — Änderung schnell prüfen lassen
```
/quick_check [was geprüft werden soll]
```
Der Judge schaut sich `git show HEAD` an und gibt nur `OK` oder `PROBLEM` zurück.
**Beispiele:**
```
/quick_check
```
```
/quick_check Prüfe ob die Mindestlängen-Änderung korrekt umgesetzt ist und keine Randfälle fehlen
```
**Typische Ausgaben:**
```
Urteil: OK
Die Änderung in src/main.rs Zeile 47 ist korrekt. Mindestlänge wird jetzt
sowohl bei --length als auch im Standardfall geprüft.
```
```
Urteil: PROBLEM
src/lib.rs Zeile 23: Der neue Mindestwert von 8 wird nur bei --length geprüft,
nicht beim Standardwert (16). Wenn jemand --length 6 übergibt, schlägt die
Validierung korrekt fehl, aber der Standardfall ist nicht abgedeckt.
Fix: Validierung in die Funktion generate_password() verschieben statt in parse_args().
```
### Typischer /patch + /quick_check Workflow
```
/patch Timeout bei HTTP-Requests von 30 auf 10 Sekunden setzen
```
*(Coder ändert, committet)*
```
/quick_check Prüfe ob der Timeout auch bei Retry-Versuchen korrekt gilt
```
*(Judge gibt OK oder zeigt konkretes Problem)*
---
## 8. Dokumentation generieren: /update_doku
Nach Abschluss der Entwicklung (nach `/shipit` oder `/optimize`) erstellt `/update_doku`
drei Dinge automatisch:
1. **Code-Kommentare** — erklärt das WARUM in den Quelldateien (Deutsch)
2. **README.md** — Entwicklerperspektive: Installation, Build, Verwendung
3. **BEDIENUNGSANLEITUNG.md** — Endnutzerperspektive: einfach, ohne Jargon
```
/update_doku
```
### Inkrementelles Update
`/update_doku` merkt sich via Git-Tags welche Dateien seit dem letzten Lauf geändert wurden.
Nur geänderte Quelldateien werden neu kommentiert — unveränderte bleiben unangetastet.
```
Code-Kommentare: keine Änderungen seit letztem Lauf übersprungen.
README.md: 2 Datei(en) geändert wird geprüft
BEDIENUNGSANLEITUNG.md: 2 Datei(en) geändert wird geprüft
```
### Zusammen mit /optimize
```
/optimize Implementiere Feature X --with-doku
```
Führt nach SHIP automatisch `/update_doku` aus.
---
## 9. Versionsverwaltung: /version
pi_coder verwaltet Versionsnummern im SemVer-Format (`vMAJOR.MINOR.PATCH`) automatisch —
basierend auf den Commit-Messages des generierten Codes.
### Wie Commit-Messages die Version bestimmen
Der Coder verwendet standardmäßig das Conventional-Commits-Format:
| Commit-Prefix | Beispiel | Bump-Typ |
|---|---|---|
| `feat!:` oder `BREAKING CHANGE` | `feat!: API komplett überarbeitet` | major (v1.0.0 → v2.0.0) |
| `feat:` | `feat: CSV-Export hinzugefügt` | minor (v1.0.0 → v1.1.0) |
| `fix:`, `chore:`, andere | `fix: Crash bei leerer Datei` | patch (v1.0.0 → v1.0.1) |
### Automatisch nach SHIP
Nach einem erfolgreichen SHIP-Verdikt in `/optimize` oder `/shipit` erscheint automatisch
ein Dialog:
```
┌─ Version ──────────────────────────────────────────────┐
│ Aktuelle Version: v1.2.3. Commits seit letztem Tag: │
│ minor-Bump erkannt. │
│ │
│ patch → v1.2.4 │
│ minor → v1.3.0 (empfohlen) │
│ major → v2.0.0 │
│ Überspringen │
└─────────────────────────────────────────────────────────┘
```
Du kannst den empfohlenen Wert bestätigen oder manuell einen anderen wählen.
### Manuell aufrufen
```
/version
```
Nützlich wenn du den Tag nachträglich setzen möchtest oder nach manuellen Commits.
### Was passiert nach der Auswahl
1. Die Versionsnummer wird in die Projekt-Manifest-Datei geschrieben (falls vorhanden):
- `package.json``npm version --no-git-tag-version X.Y.Z`
- `Cargo.toml``version = "X.Y.Z"` in `[package]`
- `pyproject.toml``version = "X.Y.Z"` in `[project]`
- `VERSION` → Dateiinhalt `vX.Y.Z`
2. Commit: `chore: bump version to vX.Y.Z`
3. Git-Tag: `vX.Y.Z` wird gesetzt
Wenn keine der genannten Dateien vorhanden ist, wird nur der Git-Tag gesetzt.
### Erstes Mal — kein Tag vorhanden
```
┌─ Version ──────────────────────────────────────────────┐
│ Noch kein Versions-Tag vorhanden. │
│ │
│ patch → v0.0.1 │
│ minor → v0.1.0 (empfohlen) │
│ major → v1.0.0 │
│ Überspringen │
└─────────────────────────────────────────────────────────┘
```
Empfehlung: `v0.1.0` für ein frisches, funktionierendes Projekt; `v1.0.0` wenn es
sofort produktionsreif ist.
---
## 10. TASK.md verstehen und nutzen
`TASK.md` ist die persistente Aufgabenbeschreibung im Projektverzeichnis. Sie wird von
allen Kommandos als Referenz gelesen.
### Erstellt von /coder und /optimize
Beim ersten `/coder`-Aufruf:
```markdown
# Aufgabe
Schreibe ein Python-Kommandozeilenprogramm 'textcount'...
## Erstellt
2026-05-19T14:30:00.000Z
## Status
- [ ] Implementierung
- [x] Review bestanden (PASS)
- [ ] Produktionsreif (SHIP)
```
### Zusatzauftrag hinzufügen
Wenn du später `/coder` mit einer neuen Aufgabe aufrufst, wird TASK.md erweitert statt überschrieben:
```
/coder Füge zusätzlich eine --csv-Option hinzu, die das Ergebnis als CSV ausgibt
```
```markdown
# Aufgabe
[...ursprüngliche Aufgabe...]
---
## Zusatzauftrag
2026-05-19T15:45:00.000Z
Füge zusätzlich eine --csv-Option hinzu...
## Status
- [ ] Implementierung
- [ ] Review bestanden (PASS)
- [ ] Produktionsreif (SHIP)
```
### Status-Checkboxen
Die Checkboxen werden automatisch abgehakt:
- `[x] Implementierung` — nach erfolgreichem `/coder` oder `Phase 1` von `/optimize`
- `[x] Review bestanden (PASS)` — nach PASS durch `/judge` oder in `/optimize`
- `[x] Produktionsreif (SHIP)` — nach SHIP durch `/shipit` oder `/update_doku`
---
## 11. Typische Anwendungsfälle
### Neues Rust-Programm von Null
```bash
# 1. Verzeichnis anlegen
/new_project ~/Rust_Programs/mein_tool
# 2. Terminal: in Verzeichnis wechseln und pi neu starten
# cd ~/Rust_Programs/mein_tool && pi
# 3. In pi: vollautomatisch implementieren + dokumentieren
/optimize Schreibe ein Rust-CLI-Tool 'csvfilter' das CSV-Dateien zeilenweise filtert. Optionen: --column NAME, --value WERT, --regex. Ausgabe auf stdout. --with-doku
```
### Bestehendes Projekt verbessern
```bash
# In pi, im Projektverzeichnis:
/coder Refaktoriere die Datenbankschicht: ersetze das raw-SQL durch sqlx mit typsicheren Queries. Alle Tests müssen danach noch laufen.
/judge
/fix
/shipit
```
### Schnelle Bugfixes
```bash
/patch Die Funktion split_csv() schlägt bei Feldern mit eingebetteten Kommas fehl (RFC 4180 nicht implementiert)
/quick_check
```
### Repo ohne Test-Suite oder mit externer CI
```bash
# Test-Erkennung überspringen — Judge bewertet nur den Code
/optimize "Implementiere Feature X" --no-tests
# Externe Test-Suite explizit angeben
/optimize "Implementiere Feature X" --test-cmd "make integration-test"
```
### Schneller Loop ohne ShipIt-Runde
```bash
# Für Projekte wo "PASS WITH CONCERNS" ausreicht:
/optimize "Kleines Refactoring" --approve-concerns
# Kombination: kein Test, kein ShipIt bei Concerns, 1 Runde
/optimize "Typo-Fix in Fehlermeldungen" --rounds 1 --no-tests --approve-concerns
```
### Versionsnummer nach der Entwicklung setzen
```bash
# Nach SHIP: Dialog erscheint automatisch
/optimize Neues Feature X --rounds 2
# → SHIP → Dialog → "minor → v1.1.0" wählen → Tag gesetzt
# Oder manuell:
/version
```
### Kommentarlosen Legacy-Code dokumentieren
```bash
# Nur Kommentare und Dokumentation, kein Code ändern:
/update_doku
```
### Schrittweise mit manuellem Review
```bash
/coder Implementiere OAuth2-Login mit GitHub
# → Code lesen, verstehen
/judge Besonderes Augenmerk auf Token-Speicherung und CSRF-Schutz
# → Judge-Bericht lesen
/fix Ignoriere den Minor-Punkt mit der Logging-Verbosität, das ist Absicht
/shipit
```
### Experiment: mehrere Runden explizit
```bash
/optimize Schreibe einen vollständigen Markdown-Parser mit AST in Python --rounds 5
```
### Schrittweise Features hinzufügen mit --interactive
```bash
# Erst Grundgerüst implementieren und PASS abwarten:
/optimize Schreibe ein CLI-Tool 'filewatch' das Dateiänderungen überwacht --interactive
# Nach PASS erscheint: ⏸ PASS warte auf /continue…
# Option A: Genug, direkt shippern:
/continue
# Option B: Weiteres Feature anhängen:
/continue "Füge außerdem einen --filter GLOB-Parameter hinzu"
# → Coder implementiert, Judge prüft erneut, PASS → Checkpoint wieder aktiv
# Nochmal erweitern:
/continue "Füge --output-log DATEI hinzu um Änderungen zu protokollieren"
# Fertig → shippern:
/continue
```
---
## 12. Fehlermeldungen und Lösungen
### "Modell-Datei nicht gefunden"
```
[!] Modell-Datei nicht gefunden: /home/.../models/qwen3/Qwen3.6-27B-Uncensored-...gguf
```
**Ursache:** Die GGUF-Datei liegt nicht am erwarteten Ort.
**Lösung:**
```bash
# Pfad prüfen:
ls $HF_HOME/models/qwen3/
# Oder mit explizitem Pfad starten:
HF_HOME=/korrekter/pfad ./start-servers.sh
```
### Server startet nicht / HTTP nicht erreichbar
```
[!] HTTP-Server wurde nicht rechtzeitig erreichbar.
```
**Ursachen und Lösungen:**
1. Zu wenig VRAM — Container bricht beim Laden ab:
```bash
docker logs qwen36-27b-coder | tail -50
# Suche nach: "CUDA out of memory" oder "failed to allocate"
```
→ Kontext reduzieren: `-c 32768` statt `-c 131072`
2. GPU nicht verfügbar:
```bash
nvidia-smi # alle drei GPUs sichtbar?
# GPU 1 (Coder) testen:
docker run --gpus '"device=1"' --rm nvidia/cuda:12.0-base nvidia-smi
# GPU 2 (Judge) testen:
docker run --gpus '"device=2"' --rm nvidia/cuda:12.0-base nvidia-smi
```
3. Port bereits belegt:
```bash
ss -tlnp | grep 800[12]
docker ps -a # alter Container noch vorhanden?
./stop-servers.sh
./start-servers.sh
```
### "Agent is already processing a prompt"
**Ursache:** Ein Kommando wurde aufgerufen während pi agent noch auf eine Antwort wartet.
**Lösung:** Warten bis die aktuelle Antwort fertig ist, dann das Kommando wiederholen.
Bei `/optimize` passiert das automatisch — der interne Mechanismus wartet auf `idle`.
### "edits[n] ... oldText must match exactly"
**Ursache:** Der interne pi-agent-Edit-Mechanismus hat beim Anwenden mehrerer Änderungen
an derselben Datei versagt.
**Was pi_coder dagegen tut:** Ein `tool_call`-Hook in der Extension sortiert
Mehrfach-Edits automatisch von hinten nach vorne (Bottom-up-Reordering), sodass
frühere Edits spätere Positionen nicht verschieben. Zusätzlich steht das `apply_patch`-Tool
bereit, das GNU `patch -p1` mit Fuzzy-Matching nutzt.
**Falls es trotzdem auftritt:** Das Modell manuell anweisen:
```
Lies die Datei neu ein und wende die Änderungen als unified diff mit apply_patch an.
```
### "N Runden ohne PASS" / Loop-Erkennung schlägt an
```
⚠ Derselbe Blocker tritt erneut auf Schleife abgebrochen.
```
**Ursache:** Der Coder kann einen bestimmten Blocker nicht beheben — z.B. weil die
Aufgabe einen Widerspruch enthält oder ein externes System fehlt.
**Lösung:** Manuell eingreifen:
```
/judge ← Judge-Bericht lesen
```
Dann den Blocker analysieren und entweder:
- `/fix Ignoriere Blocker X, das ist nicht Teil dieser Aufgabe`
- Den Code selbst anpassen und dann `/fix` aufrufen
- Die Aufgabe in TASK.md präzisieren
- Bei komplexen Aufgaben mit mehr Runden wiederholen: `/optimize --continue --rounds 5`
### Server läuft, aber pi wechselt nicht das Modell
**Ursache:** `models.json` wurde nach einer Änderung nicht neu deployt.
**Lösung:**
```bash
cd ~/pi_coder
./install_servers_and_pi_coder_extension.sh
# Dann /reload in pi agent
```
### "Neues Projekt" wechselt nicht das Verzeichnis
Das ist gewollt — pi-Sessions sind an ihr Startverzeichnis gebunden.
Nach `/new_project <pfad>` im Terminal:
```bash
cd <pfad>
pi
```

330
README.md Normal file
View file

@ -0,0 +1,330 @@
# pi_coder — Automatisierter Coder/Judge-Workflow für pi agent
Dieses Repository enthält die Konfiguration und Skripte für einen automatisierten
Coding-Workflow mit zwei lokalen LLaMA-Modellen: ein Coder-Modell und ein Judge-Modell,
gesteuert über [pi agent](https://github.com/earendil-works/pi).
---
## Überblick
```
Nutzer gibt Auftrag
/coder → qwen3.5-coder (:8001) → Implementierung + git commit
/judge → qwen3.5-judge (:8002) → Review: PASS / FAIL + Blocker
FAIL? ▼
/fix → qwen3.5-coder (:8001) → Fixes + git commit
PASS? ▼
/shipit → qwen3.5-judge (:8002) → Finale Freigabe: SHIP / NO-SHIP
(nur bei "PASS WITH CONCERNS" — klares PASS → direkt SHIP)
/optimize = Coder→Judge→Fix-Schleife automatisch (bis PASS oder max. N Runden)
--interactive: pausiert nach PASS für menschlichen Checkpoint + optionale Zusatzaufträge
```
Beide Modelle laufen als **separate llama.cpp-Docker-Container** und sprechen eine
OpenAI-kompatible API (`/v1/chat/completions`). pi agent wechselt automatisch zwischen
den Endpunkten wenn du ein `/judge`-, `/fix`- oder `/coder`-Kommando aufrufst.
---
## Modelle
| Rolle | Modell | Port | Container | Alias | GPU |
|--------|---------------------------------------------------|------|------------------|----------------|----------|
| Coder | Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ4_XS | 8001 | qwen36-27b-coder | qwen3.5-coder | device=1 |
| Judge | Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ4_XS | 8002 | qwen36-27b-judge | qwen3.5-judge | device=2 |
Beide Container verwenden dasselbe GGUF-Datei, aber mit unterschiedlichen
Serverparametern (Kontext, Temperatur, Parallelität).
---
## Voraussetzungen
- Docker mit NVIDIA-GPU-Support:
```bash
# NVIDIA Container Toolkit installieren (falls nicht vorhanden)
distribution=$(. /etc/os-release; echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list \
| sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
sudo systemctl restart docker
```
- NVIDIA-GPUs: empfohlen 2 × RTX 3090 (je 24 GB VRAM) für Coder und Judge parallel.
Bei 3 GPUs: GPU 0 für Display reservieren, GPU 1 → Coder, GPU 2 → Judge.
- GGUF-Modell vorhanden unter:
`$HF_HOME/models/qwen3/Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ4_XS.gguf`
- Standard-Pfad: `HF_HOME=/home/dschlueter/nvme2n1p7_home/huggingface`
- Überschreibbar: `HF_HOME=/anderer/pfad ./start-servers.sh`
- [pi agent](https://github.com/earendil-works/pi) installiert (`~/.pi/`)
---
## Installation
```bash
# 1. Repository klonen
git clone https://kitux.de/forgejo/dschlueter/pi_coder.git ~/pi_coder
cd ~/pi_coder
# 2. Extension und Modell-Config nach ~/.pi/agent/ deployen
./install_servers_and_pi_coder_extension.sh
# 3. pi agent neu laden (in der pi-Oberfläche)
# /reload
# 4. Server starten
./start-servers.sh
```
Nach späteren Änderungen an `pi-coder-judge-extension.ts` oder `models.json`:
```bash
./install_servers_and_pi_coder_extension.sh # kopiert nach ~/.pi/agent/
# dann /reload in pi agent
```
---
## Server starten / stoppen / status
```bash
# Beide Server parallel starten (empfohlen — dauert 13 Minuten)
./start-servers.sh
# Einzeln starten (z.B. nur einen neu starten)
./start-coder.sh # Port 8001
./start-judge.sh # Port 8002
# Beide stoppen
./stop-servers.sh
# Status beider Server prüfen
./status.sh
```
`start-servers.sh` startet beide Container gleichzeitig und wartet bis beide
HTTP-ready sind (max. 5 Minuten — die neue llama.cpp-Version prüft beim Start, ob
Modell + KV-Cache in den VRAM passen, was zusätzliche Zeit kostet). Logs werden
getrennt gesammelt und nur bei Fehler ausgegeben.
Wenn Server bereits laufen und du `start-servers.sh` (oder ein Einzelskript)
aufrufst, werden die laufenden Container zuerst per `docker rm -f` gestoppt
und dann neu gestartet — ein laufender Inference-Request wird dabei abgebrochen.
---
## llama.cpp-Serverparameter im Detail
### Gemeinsame Parameter
| Parameter | Wert | Bedeutung |
|---|---|---|
| `--jinja` | — | Verwendet das im GGUF eingebettete Jinja-Chat-Template (Qwen-Format). Notwendig für korrekte `<\|im_start\|>`-Tokens. |
| `--reasoning on` | — | Aktiviert das interne Thinking-Budget des Modells (Qwen3-spezifisch). Ersetzt das frühere `--chat-template-kwargs '{"enable_thinking":true}'`. |
| `--no-context-shift` | — | Kontextfenster wird **nicht** verschoben wenn es voll ist — stattdessen Fehler. Verhindert stille Datenverluste. |
| `--repeat-penalty 1.05` | — | Leichte Penalty für Wiederholungen. Wert > 1.0 unterdrückt Loops. |
| `--top-k 20` | — | Nur die 20 wahrscheinlichsten nächsten Tokens werden berücksichtigt. |
| `--min-p 0.01` | — | Tokens mit Wahrscheinlichkeit < 1 % des wahrscheinlichsten Tokens werden ausgeschlossen. |
| `-ngl 999` | — | Alle Layer auf die GPU laden (999 = „alle"). Bei zu wenig VRAM reduzieren. |
| `-fa on` | — | Flash Attention — schnellere Attention-Berechnung, weniger VRAM für den Attention-Pass. |
| `--kv-unified` | — | Einheitlicher KV-Cache über alle Schichten. Effizienter bei langen Kontexten. |
| `--cache-type-k q4_0` | — | KV-Cache Keys in 4-Bit quantisiert. Spart ~75 % VRAM gegenüber fp16 — nötig für 256K Kontext auf einer 24-GB-GPU. |
| `--cache-type-v q4_0` | — | KV-Cache Values ebenfalls 4-Bit quantisiert. |
| `--cont-batching` | — | Continuous Batching: neue Anfragen werden in laufende Batches eingefügt — höherer Durchsatz bei mehreren parallelen Anfragen. |
| `--main-gpu 0` | — | GPU-Index (0 = erste des Containers) für Nicht-Tensor-Operationen. |
| `--gpus '"device=1"'` (Coder) | — | Docker: nur GPU 1 dem Coder-Container zuweisen. |
| `--gpus '"device=2"'` (Judge) | — | Docker: nur GPU 2 dem Judge-Container zuweisen. |
### Coder-Server (Port 8001) — optimiert für Coding-Aufgaben
| Parameter | Wert | Erklärung / Wirkung |
|---|---|---|
| `-c 262144` | 256K Tokens | Sehr großes Kontextfenster: gesamte Codebasis + langer Gesprächsverlauf passt rein. **Sehr hoher VRAM-Bedarf.** Reduziere auf `65536` wenn VRAM knapp. |
| `-n 16384` | 16K Tokens | Maximale Ausgabelänge pro Anfrage. Für Kommentieraufgaben (`/update_doku`) nötig. |
| `--temp 0.2` | — | Niedrige Temperatur: deterministisch, konsistenter Code. Erhöhe auf `0.40.6` für kreativere Lösungsansätze. |
| `--top-p 0.95` | — | Nucleus Sampling: 95 % der Wahrscheinlichkeitsmasse. Passend zu temp 0.2. |
| `--batch-size 1024` | — | Prompt-Verarbeitungs-Batch. Größer = schnelleres Einlesen langer Dateien. |
| `--ubatch-size 512` | — | Micro-Batch für GPU-Kernel. Muss ≤ batch-size sein. |
| `--parallel 2` | — | 2 gleichzeitige Request-Slots. Nützlich wenn pi agent schnell Folgeanfragen schickt. |
### Judge-Server (Port 8002) — optimiert für Reviews
| Parameter | Wert | Erklärung / Wirkung |
|---|---|---|
| `-c 262144` | 256K Tokens | Großes Kontextfenster: nötig bei langen /optimize-Runden, wo der Gesprächsverlauf stark anwächst. |
| `-n 16384` | 16K Tokens | Lange Reviews und Begründungen passen vollständig in die Ausgabe. |
| `--temp 0.1` | — | Sehr niedrige Temperatur: maximale Konsistenz und Reproduzierbarkeit der Urteile. |
| `--top-p 0.9` | — | Etwas enger als beim Coder — weniger Variation im Urteil gewünscht. |
| `--batch-size 512` | — | Kleiner als beim Coder — Judge bekommt selten sehr lange Prompts. |
| `--ubatch-size 256` | — | Entsprechend kleiner. |
| `--parallel 1` | — | Judge-Aufgaben sind immer sequenziell im Workflow, daher 1 Slot ausreichend. |
---
## GPU-Konfiguration und VRAM
### Aktuelles Setup (1 GPU pro Server)
Jeder Server bekommt eine dedizierte GPU — dadurch kein VRAM-Konflikt:
```
GPU 0 NVIDIA T600 → Display / Monitor (nicht für KI genutzt)
GPU 1 RTX 3090 (24 GB) → qwen36-27b-coder (Port 8001)
GPU 2 RTX 3090 (24 GB) → qwen36-27b-judge (Port 8002)
```
Die Skripte verwenden `--gpus '"device=1"'` (Coder) bzw. `'"device=2"'` (Judge).
Innerhalb des Containers ist dies stets `CUDA:0`, daher `--main-gpu 0` ohne `--tensor-split`.
### VRAM-Abschätzung für das 27B IQ4_XS-Modell (eine GPU)
| Komponente | Größe (ca.) |
|---|---|
| Modell-Gewichte (IQ4_XS, 27B) | ~14 GB |
| KV-Cache bei 262 144 Tokens (q4_0) | ~810 GB |
| KV-Cache bei 131 072 Tokens (q4_0) | ~45 GB |
| KV-Cache bei 65 536 Tokens (q4_0) | ~23 GB |
| **Summe (262k Kontext)** | **~2224 GB** |
Bei einer **24-GB-GPU** ist 262k Kontext knapp kalkuliert. Wichtig: keine anderen Prozesse
dürfen nennenswert VRAM belegen (z. B. TTS-Dienste, Ollama). Prüfen mit:
```bash
nvidia-smi --query-gpu=index,memory.used,memory.free --format=csv
```
Falls der VRAM nicht reicht: Kontext in den Startskripten auf `131072` oder `65536` reduzieren.
### Anpassung für andere GPU-Konfigurationen
```bash
# 2 GPUs (beide für KI, keine Display-GPU):
--gpus '"device=0"' # Coder
--gpus '"device=1"' # Judge
# 1 GPU (beide Server auf gleicher GPU — nicht empfohlen, VRAM-Konflikt):
--gpus '"device=0"' # beide Server
--tensor-split 0.5,0.5 # Modell hälftig aufteilen
-c 65536 # Kontext stark reduzieren
# 2 GPUs für einen Server (wenn sehr großer Kontext nötig):
--gpus '"device=0,1"' \
--tensor-split 0.5,0.5 \
--main-gpu 0 \
-c 262144
```
Bei einer **16-GB-GPU** ist die Modellgröße allein schon grenzwertig.
Entweder ein kleineres Modell verwenden oder die Quantisierung erhöhen (IQ3_XS, Q4_K_M).
---
## Parameter-Tuning-Guide
### Temperatur (`--temp`)
| Wert | Eignung |
|---|---|
| `0.00.1` | Maximale Reproduzierbarkeit. Gut für Judge/Review. |
| `0.10.3` | Guter Kompromiss für Coding. **Empfohlen für Coder.** |
| `0.40.6` | Kreativere Lösungen, mehr Varianz. Sinnvoll für Prototyping. |
| `0.71.0` | Kreativschreiben, Brainstorming. Für Coding meist zu viel Rauschen. |
### Kontextgröße (`-c`)
Je größer der Kontext, desto mehr VRAM braucht der KV-Cache.
Faustregel: KV-Cache ≈ `context_size × layers × head_dim × 2 × bytes_per_element`.
Bei q8_0 (1 Byte/Element) und Qwen3-27B (28 Schichten, 128 Head-Dim, 32 Heads):
KV-Cache ≈ `context_size × 28 × 128 × 32 × 2 × 1 Byte ≈ context_size × 0,23 MB`
| Kontext | KV-Cache (q4_0) | Empfehlung |
|---|---|---|
| 32 768 | ~1,9 GB | 1 × 16-GB-GPU |
| 65 536 | ~3,7 GB | 1 × 24-GB-GPU |
| 131 072 | ~7,5 GB | 2 × 16-GB-GPU |
| 262 144 | ~810 GB | 1 × 24-GB-GPU (q4_0) — **aktuell gesetzt** |
### KV-Cache-Quantisierung
| `--cache-type-k/v` | VRAM | Qualität |
|---|---|---|
| `f16` | 100 % (Basis) | Referenz |
| `q8_0` | ~50 % | Kaum merklich schlechter |
| `q4_0` | ~25 % | Merklicher Qualitätsverlust bei langen Kontexten — aber nötig für 256K Kontext auf 2× 24 GB. **Aktuell gesetzt.** |
### Parallelität (`--parallel`)
Mehr parallele Slots erhöhen den Durchsatz bei gleichzeitigen Anfragen, aber jeder Slot
reserviert Speicher im KV-Cache. Im pi-coder-Workflow sind echte Parallelaufrufe selten,
daher ist `--parallel 1` für den Judge ausreichend. Coder `--parallel 2` bietet Puffer
wenn pi agent Folgeanfragen schnell hintereinander schickt.
---
## Dateien
| Datei | Zweck |
|---|---|
| `pi-coder-judge-extension.ts` | pi agent Extension (Kommandos, Tools, Hooks) |
| `models.json` | Provider- und Modell-Konfiguration für pi agent |
| `test-utils.ts` | Unit-Tests für Hilfsfunktionen der Extension |
| `run-tests.sh` | Unit-Tests ausführen (TypeScript-Stripping + node) |
| `start-servers.sh` | Beide Server parallel starten (empfohlen) |
| `start-coder.sh` | Nur Coder-Container starten (Port 8001) |
| `start-judge.sh` | Nur Judge-Container starten (Port 8002) |
| `stop-servers.sh` | Beide Container stoppen |
| `status.sh` | Laufstatus beider Server anzeigen |
| `install_servers_and_pi_coder_extension.sh` | Extension + models.json nach `~/.pi/agent/` kopieren |
| `examples/` | Demo-Projekte (Python, Rust, Go, C, Bash, HTML/JS) für Live-Demos |
| `examples/restore-all.sh` | Examples nach Demo-Lauf in Ausgangszustand zurücksetzen |
---
## pi-Kommandos (Kurzübersicht)
| Kommando | Modell | Beschreibung |
|---|---|---|
| `/coder <auftrag>` | Coder | TASK.md anlegen, Implementierung starten |
| `/judge [fokus]` | Judge | Code-Review gegen TASK.md + letzten Commit |
| `/fix [hinweis]` | Coder | Judge-Kritik beheben, committen |
| `/shipit` | Judge | Finale Freigabeprüfung |
| `/optimize <auftrag> [--rounds N] [--with-doku] [--continue] [--interactive]` | beide | Vollautomatische Schleife bis PASS (Standard: 2 Runden, Runde 1: Quick-Judge) |
| `/optimize ... [--no-tests] [--approve-concerns] [--test-cmd "cmd"] [--test-timeout N]` | beide | Test-Erkennung überspringen / PASS WITH CONCERNS direkt shippern |
| `/patch <änderung>` | Coder | Gezielte Minimaländerung ohne Review |
| `/quick_check [was]` | Judge | Schnelle Prüfung der letzten Änderung |
| `/version` | — | Versionsnummer erhöhen (SemVer + Git-Tag) |
| `/update_doku` | Coder | Code kommentieren + README + Bedienungsanleitung |
| `/plan <auftrag>` | Coder | Implementierungsplan in PLAN.md (kein Code) |
| `/continue` | Coder | Unterbrochenen Prozess fortsetzen |
| `/cancel` | — | Laufenden Loop nach aktuellem Schritt abbrechen |
| `/new_project <pfad>` | — | Neues Projektverzeichnis + git init |
Ausführliche Beschreibung aller Kommandos mit Beispielen: siehe **BEDIENUNGSANLEITUNG.md**.
---
## Live-Aktivitätsstatus
Während der Ausführung zeigt pi_coder in der Statuszeile, was gerade passiert —
inklusive eines laufenden `[MM:SS]`-Timers während jeder LLM-Inference-Phase:
| Situation | Anzeige |
|---|---|
| Coder implementiert | `◉ Coder implementiert: Login-Flow mit JWT [01:23]` |
| edit-Tool aktiv | `Editiere src/main.py…` |
| git commit | `Git-Commit…` |
| Judge (Quick-Check, Runde 1) | `◉○ Runde 1/2: Quick-Check [00:47]` |
| Judge (voller Review, Runde 2) | `●◉ Runde 2/2: Judge — TASK.md + letzter Commit + Tests [02:15]` |
| Tests laufen | `●○ Runde 1/2: Tests laufen (pytest, max. 120s)…` |
| Fix-Phase | `●◉ Runde 2/2: Coder fixt — fehlendes Null-Check [01:05]` |
| ShipIt | `●●◉ ShipIt — finale Freigabe [00:33]` |
| Interactive-Pause (--interactive) | `⏸ PASS warte auf /continue…` |
Der Timer beweist, dass die LLM tatsächlich arbeitet. Steht er still, hängt der Prozess.

150
SINGLE_SERVER_PLAN.md Normal file
View file

@ -0,0 +1,150 @@
# Plan: pi_coder_v2 — Single-Server (Option B)
## Kontext
pi_coder verwendet zwei llama.cpp-Server für Coder (GPU 1) und Judge (GPU 2),
die aber dasselbe GGUF-Modell ausführen. Die Rollentrennung entsteht ausschließlich
durch System-Prompts. Wenn nur eine GPU verfügbar ist, kann ein einziger Server
beide Rollen übernehmen — Rollenwechsel per Prompt statt per Server-Neustart.
**pi_coder bleibt unverändert.** Die Umsetzung erfolgt im neuen Verzeichnis `~/pi_coder_v2`.
---
## Schritt 1: Verzeichnis anlegen und Dateien kopieren
```bash
mkdir ~/pi_coder_v2
cp ~/pi_coder/pi-coder-judge-extension.ts ~/pi_coder_v2/
cp ~/pi_coder/models.json ~/pi_coder_v2/
cp ~/pi_coder/start-coder.sh ~/pi_coder_v2/
cp ~/pi_coder/start-judge.sh ~/pi_coder_v2/
cp ~/pi_coder/start-servers.sh ~/pi_coder_v2/
cp ~/pi_coder/stop-servers.sh ~/pi_coder_v2/
cp ~/pi_coder/status.sh ~/pi_coder_v2/
cp ~/pi_coder/install_servers_and_pi_coder_extension.sh ~/pi_coder_v2/
cp ~/pi_coder/README.md ~/pi_coder_v2/
cp ~/pi_coder/BEDIENUNGSANLEITUNG.md ~/pi_coder_v2/
cp ~/pi_coder/CLAUDE.md ~/pi_coder_v2/
# Plan-Datei ebenfalls kopieren
cp <dieser-plan> ~/pi_coder_v2/SINGLE_SERVER_PLAN.md
# git init
cd ~/pi_coder_v2 && git init && git add -A && git commit -m "init: kopiert aus pi_coder"
```
Nicht kopieren: `run-tests.sh`, `test-utils.ts`, `examples/` (nicht relevant für Kernfunktion).
---
## Schritt 2: `start-single.sh` anlegen
Basiert auf `start-coder.sh` mit folgenden Änderungen:
| Parameter | Alt (coder) | Neu (single) |
|---|---|---|
| `CONTAINER_NAME` | `qwen36-27b-coder` | `qwen36-27b-single` |
| `HOST_PORT` | 8001 | 8001 |
| `MODEL_ALIAS` | `qwen3.5-coder` | `qwen3.5-single` |
| `--temp` | 0.6 | 0.65 (Kompromiss) |
| `--gpus` | `"device=1"` | `"device=1"` (oder konfigurierbar) |
Der Server registriert sich unter dem Alias `qwen3.5-single`.
---
## Schritt 3: `models.json` anpassen
Neuen Provider `llama-cpp-single` hinzufügen mit **beiden** Modell-IDs auf Port 8001:
```json
"llama-cpp-single": {
"baseUrl": "http://127.0.0.1:8001/v1",
"api": "openai-completions",
"apiKey": "none",
"compat": {
"supportsDeveloperRole": false,
"supportsReasoningEffort": false,
"maxTokensField": "max_tokens",
"thinkingFormat": "qwen-chat-template"
},
"models": [
{
"id": "qwen3.5-coder",
"name": "Qwen3.6 27B Single-Server Coder (llama.cpp :8001)",
"reasoning": true,
"input": ["text"],
"contextWindow": 262144,
"maxTokens": 16384,
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
},
{
"id": "qwen3.5-judge",
"name": "Qwen3.6 27B Single-Server Judge (llama.cpp :8001)",
"reasoning": true,
"input": ["text"],
"contextWindow": 262144,
"maxTokens": 16384,
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
}
]
}
```
Die bestehenden Provider `llama-cpp-coder` und `llama-cpp-judge` bleiben erhalten
(für Kompatibilität), werden aber nicht aktiv genutzt.
---
## Schritt 4: `pi-coder-judge-extension.ts` — switchModel-Aufrufe
Alle Aufrufe von `switchModel(pi, ctx, "llama-cpp-coder", "qwen3.5-coder")`
und `switchModel(pi, ctx, "llama-cpp-judge", "qwen3.5-judge")` werden auf
`"llama-cpp-single"` umgestellt. Der Modell-ID-Parameter (`"qwen3.5-coder"` /
`"qwen3.5-judge"`) bleibt unverändert — so bleiben die Registry-Wechsel erhalten,
sind aber sofort (kein Server-Neustart).
Betroffene Stellen (ca. 8):
- `/coder` handler
- `/judge` handler
- `/fix` handler
- `/shipit` handler
- `/optimize` loop (Coder-Kickoff, Judge-Phase, Fix-Phase, ShipIt-Phase)
- `/patch` handler
- `/quick_check` handler
- `/plan` handler
---
## Schritt 5: `install_servers_and_pi_coder_extension.sh` anpassen
Pfade auf `pi_coder_v2` anpassen (falls nötig — Skript referenziert vermutlich `$REPO`
als relatives Verzeichnis, was automatisch korrekt ist).
---
## Schritt 6: `waitUntilModelReady` im `--continue`-Modus
Im `--continue`-Modus prüft die Extension beide Server parallel:
```typescript
const [coderReady, judgeReady] = await Promise.all([
waitUntilModelReady(pi, ctx, 8001, "qwen3.5-coder"),
waitUntilModelReady(pi, ctx, 8002, "qwen3.5-judge"),
]);
```
Dies muss auf einen einzigen Check umgestellt werden:
```typescript
const ready = await waitUntilModelReady(pi, ctx, 8001, "qwen3.5-single");
```
---
## Verifikation
1. `cd ~/pi_coder_v2 && ./start-single.sh` → Server startet auf Port 8001
2. `nvidia-smi` → nur eine GPU belegt, ~20 GB VRAM
3. `./install_servers_and_pi_coder_extension.sh` → Extension in `~/.pi/agent/` deployen
4. In pi: `/reload` → Extension neu laden
5. `/optimize <kleiner Testauftrag>` → Loop läuft durch
6. In der Statuszeile: `switchModel` wechselt ohne spürbare Pause
7. Qualitätscheck: Judge-Urteil und Coder-Fix verhalten sich wie erwartet

View file

@ -0,0 +1,16 @@
#!/usr/bin/env bash
# Kopiert die versionierten Dateien aus dem Repo nach ~/.pi/agent/.
# Nach jeder Änderung im Repo ausführen, damit pi agent die neue Version lädt.
set -euo pipefail
REPO="$(cd "$(dirname "$0")" && pwd)"
mkdir -p ~/.pi/agent/extensions
cp "$REPO/pi-coder-judge-extension.ts" ~/.pi/agent/extensions/pi-coder-judge-extension.ts
echo "Kopiert: pi-coder-judge-extension.ts → ~/.pi/agent/extensions/"
cp "$REPO/models.json" ~/.pi/agent/models.json
echo "Kopiert: models.json → ~/.pi/agent/"
echo ""
echo "Fertig. Bitte /reload in pi agent ausführen."

143
models.json Normal file
View file

@ -0,0 +1,143 @@
{
"providers": {
"ollama": {
"baseUrl": "http://localhost:11434/v1",
"api": "openai-completions",
"apiKey": "ollama",
"compat": {
"supportsDeveloperRole": false,
"supportsReasoningEffort": false
},
"models": [
{ "id": "qwen2.5-coder:7b", "name": "Qwen2.5 Coder 7B (schnell)" },
{ "id": "qwen3-coder-30b-gpu:latest", "name": "Qwen3 Coder 30B GPU (Standard)" },
{ "id": "mistral-small3.2:24b", "name": "Mistral Small 3.2 24B" },
{ "id": "deepseek-r1:32b", "name": "DeepSeek R1 32B (Reasoning)" }
]
},
"llama-cpp": {
"baseUrl": "http://127.0.0.1:8000/v1",
"api": "openai-completions",
"apiKey": "none",
"compat": {
"supportsDeveloperRole": false,
"supportsReasoningEffort": false,
"maxTokensField": "max_tokens",
"thinkingFormat": "qwen-chat-template"
},
"models": [
{
"id": "qwen35b-uncensored",
"name": "Qwen3.6 35B Uncensored (llama.cpp :8000)"
},
{
"id": "qwen35b-moe-tools",
"name": "Qwen3.6 35B MoE Tools (llama.cpp :8000)"
}
]
},
"llama-cpp-single": {
"baseUrl": "http://127.0.0.1:8001/v1",
"api": "openai-completions",
"apiKey": "none",
"compat": {
"supportsDeveloperRole": false,
"supportsReasoningEffort": false,
"maxTokensField": "max_tokens",
"thinkingFormat": "qwen-chat-template"
},
"models": [
{
"id": "qwen3.5-coder",
"name": "Qwen3.6 27B Single-Server Coder (llama.cpp :8001)",
"reasoning": true,
"input": ["text"],
"contextWindow": 262144,
"maxTokens": 16384,
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
},
{
"id": "qwen3.5-judge",
"name": "Qwen3.6 27B Single-Server Judge (llama.cpp :8001)",
"reasoning": true,
"input": ["text"],
"contextWindow": 262144,
"maxTokens": 16384,
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
}
]
},
"llama-cpp-coder": {
"baseUrl": "http://127.0.0.1:8001/v1",
"api": "openai-completions",
"apiKey": "none",
"compat": {
"supportsDeveloperRole": false,
"supportsReasoningEffort": false,
"maxTokensField": "max_tokens",
"thinkingFormat": "qwen-chat-template"
},
"models": [
{
"id": "qwen3.5-coder",
"name": "Qwen3.6 27B Coder (llama.cpp :8001)",
"reasoning": true,
"input": ["text"],
"contextWindow": 262144,
"maxTokens": 16384,
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
}
}
]
},
"llama-cpp-judge": {
"baseUrl": "http://127.0.0.1:8002/v1",
"api": "openai-completions",
"apiKey": "none",
"compat": {
"supportsDeveloperRole": false,
"supportsReasoningEffort": false,
"maxTokensField": "max_tokens",
"thinkingFormat": "qwen-chat-template"
},
"models": [
{
"id": "qwen3.5-judge",
"name": "Qwen3.6 27B Judge (llama.cpp :8002)",
"reasoning": true,
"input": ["text"],
"contextWindow": 262144,
"maxTokens": 16384,
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
}
}
]
},
"openrouter": {
"models": [
{ "id": "qwen/qwen3-235b-a22b:free", "name": "Qwen3 235B (Free)" },
{ "id": "deepseek/deepseek-r1:free", "name": "DeepSeek R1 (Free)" },
{ "id": "google/gemini-2.5-pro-exp-03-25:free", "name": "Gemini 2.5 Pro (Free)" },
{ "id": "meta-llama/llama-4-maverick:free", "name": "Llama 4 Maverick (Free)" },
{ "id": "microsoft/phi-4:free", "name": "Phi-4 (Free)" },
{ "id": "qwen/qwen-2.5-coder-32b-instruct", "name": "Qwen2.5 Coder 32B (günstig)" },
{ "id": "deepseek/deepseek-r1", "name": "DeepSeek R1 Full (Reasoning)" },
{ "id": "qwen/qwen3-235b-a22b", "name": "Qwen3 235B Full" }
]
}
}
}

1617
pi-coder-judge-extension.ts Normal file

File diff suppressed because it is too large Load diff

30
start-servers.sh Executable file
View file

@ -0,0 +1,30 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
LOG_CODER=$(mktemp /tmp/coder_XXXXXX.log)
LOG_JUDGE=$(mktemp /tmp/judge_XXXXXX.log)
echo "[*] Starte beide Server parallel ..."
bash "$SCRIPT_DIR/start-coder.sh" > "$LOG_CODER" 2>&1 &
PID_CODER=$!
bash "$SCRIPT_DIR/start-judge.sh" > "$LOG_JUDGE" 2>&1 &
PID_JUDGE=$!
wait_result() {
local PID="$1" NAME="$2" LOG="$3"
if wait "$PID"; then
echo "[✓] $NAME bereit"
else
echo "[✗] $NAME fehlgeschlagen — Log:"
cat "$LOG"
return 1
fi
}
RC=0
wait_result "$PID_CODER" "Coder (:8001)" "$LOG_CODER" || RC=1
wait_result "$PID_JUDGE" "Judge (:8002)" "$LOG_JUDGE" || RC=1
rm -f "$LOG_CODER" "$LOG_JUDGE"
exit $RC

79
start-single.sh Executable file
View file

@ -0,0 +1,79 @@
#!/usr/bin/env bash
set -euo pipefail
HF_HOME="${HF_HOME:-/home/dschlueter/nvme2n1p7_home/huggingface}"
MODEL_REL_PATH="models/qwen3/Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ4_XS.gguf"
IMAGE="ghcr.io/ggml-org/llama.cpp:server-cuda"
CONTAINER_NAME="qwen36-27b-single"
HOST_PORT=8001
CONTAINER_PORT=8000
MODEL_ALIAS="qwen3.5-single"
GPU_DEVICE="${GPU_DEVICE:-1}"
echo "[*] Verwende HF_HOME = $HF_HOME"
echo "[*] GPU device = $GPU_DEVICE (überschreibbar: GPU_DEVICE=2 ./start-single.sh)"
if [ ! -f "$HF_HOME/$MODEL_REL_PATH" ]; then
echo "[!] Modell-Datei nicht gefunden: $HF_HOME/$MODEL_REL_PATH" >&2
exit 1
fi
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}\$"; then
echo "[*] Stoppe existierenden Container $CONTAINER_NAME ..."
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
fi
echo "[*] Starte llama.cpp-Single-Server (Coder + Judge in einem) ..."
docker run -d \
--gpus "\"device=${GPU_DEVICE}\"" \
--name "$CONTAINER_NAME" \
--restart unless-stopped \
-e HF_HOME="/hf_home" \
-v "$HF_HOME:/hf_home:ro" \
-p "${HOST_PORT}:${CONTAINER_PORT}" \
"$IMAGE" \
-m "/hf_home/${MODEL_REL_PATH}" \
--alias "${MODEL_ALIAS}" \
-c 262144 \
-n 16384 \
--jinja \
--reasoning on \
--no-context-shift \
--temp 0.65 \
--top-p 0.80 \
--top-k 20 \
--min-p 0.01 \
--repeat-penalty 1.05 \
--main-gpu 0 \
-ngl 999 \
-fa on \
--kv-unified \
--cache-type-k q4_0 \
--cache-type-v q4_0 \
--batch-size 1024 \
--ubatch-size 512 \
--parallel 1 \
--cont-batching \
--host 0.0.0.0 \
--port "$CONTAINER_PORT"
echo "[*] Warte auf Modell-Bereitschaft (Completion-Check, max. 300 s) ..."
MODEL_READY=0
for i in {1..150}; do
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 \
-X POST "http://localhost:${HOST_PORT}/v1/chat/completions" \
-H "Content-Type: application/json" \
-d "{\"model\":\"${MODEL_ALIAS}\",\"messages\":[{\"role\":\"user\",\"content\":\"ping\"}],\"max_tokens\":1,\"temperature\":0.0,\"stream\":false}")
if [ "$HTTP_CODE" = "200" ]; then MODEL_READY=1; break; fi
echo " [${i}/150] HTTP ${HTTP_CODE:-000} — Modell lädt noch, warte 2s ..."
sleep 2
done
if [ "$MODEL_READY" -ne 1 ]; then
echo "[!] Modell wurde nicht rechtzeitig bereit (kein HTTP 200 auf Completion)." >&2
docker logs --tail 200 "$CONTAINER_NAME" || true
exit 1
fi
echo "[*] Modell bereit — erster Completion-Request erfolgreich (HTTP 200)."
echo "[*] Server läuft auf http://0.0.0.0:${HOST_PORT} (Coder + Judge)"
echo "[*] Stoppen mit: docker rm -f ${CONTAINER_NAME}"

34
status.sh Executable file
View file

@ -0,0 +1,34 @@
#!/usr/bin/env bash
check_server() {
local NAME="$1"
local PORT="$2"
local ALIAS="$3"
printf "%-28s" "$NAME (Port $PORT):"
# Docker-Status
if docker ps --format '{{.Names}}' | grep -q "^${NAME}\$"; then
printf " Container=\033[32mRUNNING\033[0m"
elif docker ps -a --format '{{.Names}}' | grep -q "^${NAME}\$"; then
printf " Container=\033[33mSTOPPED\033[0m"
else
printf " Container=\033[31mNOT FOUND\033[0m"
echo
return
fi
# HTTP-Erreichbarkeit
if curl -s --max-time 3 "http://localhost:${PORT}/health" >/dev/null 2>&1 || \
curl -s --max-time 3 "http://localhost:${PORT}/v1/models" >/dev/null 2>&1; then
printf " HTTP=\033[32mOK\033[0m"
else
printf " HTTP=\033[31mNOT READY\033[0m"
fi
echo
}
echo "=== LLaMA-Server Status ==="
check_server "qwen36-27b-coder" 8001 "qwen3.5-coder"
check_server "qwen36-27b-judge" 8002 "qwen3.5-judge"

14
stop-servers.sh Executable file
View file

@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
CODER="qwen36-27b-coder"
JUDGE="qwen36-27b-judge"
for NAME in "$CODER" "$JUDGE"; do
if docker ps -a --format '{{.Names}}' | grep -q "^${NAME}\$"; then
docker rm -f "$NAME" >/dev/null
echo "[*] Gestoppt: $NAME"
else
echo "[-] Nicht gefunden: $NAME"
fi
done