docs: LICENCE.md-Feature, .gitignore, Single-GPU-Doku

- pi-coder-judge-extension.ts: licenceMd() + writeLicenceMd() — erzeugt
  proprietäre LICENCE.md in der Doku-Phase (nur wenn noch nicht vorhanden)
- tests: Guards und pure-logic-Tests für LICENCE.md ergänzt (38 Tests, alle grün)
- .gitignore: Node, TypeScript-Artefakte, Backups, Editor-Dateien, OS-Metadaten
- README.md + BEDIENUNGSANLEITUNG.md: vollständig auf Single-GPU-Architektur
  aktualisiert (ein Server, System-Prompt-Rollen, kein Quick-Judge,
  PASS WITH CONCERNS-Verhalten, LICENCE.md in /update_doku, start-single.sh)
- SINGLE_SERVER_PLAN.md: entfernt (umgesetzt, kein Mehrwert mehr)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-06-15 05:03:50 +02:00
commit 485ab30265
7 changed files with 297 additions and 357 deletions

21
.gitignore vendored Normal file
View file

@ -0,0 +1,21 @@
# Node (aktuell keine Dependencies, aber falls welche hinzukommen)
node_modules/
npm-debug.log*
*.log
# TypeScript-/Build-Artefakte
*.tsbuildinfo
dist/
# Backup-Dateien (z. B. settings.json.bak aus dem Install-Skript)
*.bak
# Editor / IDE
.vscode/
.idea/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db

View file

@ -1,8 +1,8 @@
# Bedienungsanleitung: pi_coder # Bedienungsanleitung: pi_coder
pi_coder ist ein Werkzeug, das zwei lokale KI-Modelle als **Coder** und **Judge** einsetzt, pi_coder ist ein Werkzeug, das ein lokales KI-Modell abwechselnd als **Coder** und **Judge**
um Software automatisch zu schreiben, zu prüfen und zu verbessern — alles gesteuert über einsetzt, um Software automatisch zu schreiben, zu prüfen und zu verbessern — alles gesteuert
einfache Slash-Kommandos in der pi-Agent-Oberfläche. über einfache Slash-Kommandos in der pi-Agent-Oberfläche.
--- ---
@ -25,17 +25,22 @@ einfache Slash-Kommandos in der pi-Agent-Oberfläche.
## 1. Konzept: Coder und Judge ## 1. Konzept: Coder und Judge
pi_coder verwendet zwei Rollen: pi_coder verwendet zwei Rollen — bedient von **einem einzigen Modell**, das je nach Aufgabe
eine andere Persona annimmt:
**Coder** (Port 8001): Schreibt und repariert Code. Liest die Aufgabe aus `TASK.md`, **Coder-Persona**: Schreibt und repariert Code. Liest die Aufgabe aus `TASK.md`, implementiert
implementiert sie, führt Tests aus und erstellt Git-Commits. sie, führt Tests aus und erstellt Git-Commits.
**Judge** (Port 8002): Überprüft den Code mit dem Blick eines skeptischen Senior-Entwicklers. **Judge-Persona**: Überprüft den Code mit dem Blick eines skeptischen Senior-Entwicklers.
Prüft Korrektheit, Robustheit, Randfälle, Sicherheit und Produktionsreife. Gibt ein Urteil: Prüft Korrektheit, Robustheit, Randfälle, Sicherheit und Produktionsreife. Gibt ein Urteil:
- `PASS` — Code ist in Ordnung - `PASS` — Code ist in Ordnung
- `PASS WITH CONCERNS` — grundsätzlich akzeptabel, aber mit Anmerkungen - `PASS WITH CONCERNS` — hat Anmerkungen, die behoben werden müssen (wird wie `FAIL` behandelt,
außer du verwendest `--approve-concerns`)
- `FAIL` — enthält Blocker, die behoben werden müssen - `FAIL` — enthält Blocker, die behoben werden müssen
Die Umschaltung zwischen den Rollen erfolgt über unterschiedliche System-Prompts, die die
Extension automatisch vor jeder Inference einsetzt. Es läuft kein zweiter Server.
Der Grundgedanke: Coder und Judge haben keine „Höflichkeitsschranke" zueinander — Der Grundgedanke: Coder und Judge haben keine „Höflichkeitsschranke" zueinander —
der Judge kritisiert direkt und konkret, der Coder repariert ohne Widerspruch. der Judge kritisiert direkt und konkret, der Coder repariert ohne Widerspruch.
@ -47,14 +52,14 @@ der Judge kritisiert direkt und konkret, der Coder repariert ohne Widerspruch.
```bash ```bash
cd ~/pi_coder cd ~/pi_coder
./start-servers.sh ./start-single.sh
``` ```
Ausgabe bei Erfolg: Ausgabe bei Erfolg:
``` ```
[*] Starte beide Server parallel ... [*] Verwende HF_HOME = /home/.../huggingface
[✓] Coder (:8001) bereit [*] GPU device = 1
[✓] Judge (:8002) bereit [✓] Single-Server (:8001) bereit
``` ```
Dauer: bis zu 5 Minuten (Modell wird in GPU-VRAM geladen; neuere llama.cpp-Versionen Dauer: bis zu 5 Minuten (Modell wird in GPU-VRAM geladen; neuere llama.cpp-Versionen
@ -68,8 +73,7 @@ prüfen beim Start die VRAM-Verfügbarkeit, was zusätzliche Zeit kostet).
``` ```
=== LLaMA-Server Status === === LLaMA-Server Status ===
qwen36-27b-coder (Port 8001): Container=RUNNING HTTP=OK qwen36-27b-single (Port 8001): Container=RUNNING HTTP=OK
qwen36-27b-judge (Port 8002): Container=RUNNING HTTP=OK
``` ```
### pi agent öffnen ### pi agent öffnen
@ -86,53 +90,41 @@ pi
## 3. Server starten und stoppen ## 3. Server starten und stoppen
### GPU-Zuordnung (3-GPU-Setup) ### GPU-Zuordnung
Bei einem System mit drei GPUs sind die Rollen fest in den Startskripten verankert — Der Server läuft standardmäßig auf **GPU 1** (überschreibbar):
**keine manuelle Konfiguration nötig:**
| GPU | Modell | Rolle | ```bash
|-----|--------|-------| ./start-single.sh # GPU 1 (Standard)
| GPU 0 (NVIDIA T600) | — | Display / Monitor — wird von den Skripten nicht angetastet | GPU_DEVICE=0 ./start-single.sh # GPU 0
| 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, Auf einem System mit mehreren GPUs empfiehlt es sich, GPU 0 (Display) freizuhalten
auch wenn beide gleichzeitig laufen. und GPU 1 für den KI-Server zu verwenden.
**Wichtig:** Andere GPU-Prozesse (z. B. TTS-Dienste, Ollama) auf GPU 1 oder GPU 2 können **Wichtig:** Andere GPU-Prozesse (z. B. TTS-Dienste, Ollama) auf der verwendeten GPU
VRAM belegen und dazu führen, dass ein Server nicht startet. Im Zweifel prüfen: können VRAM belegen und dazu führen, dass der Server nicht startet. Im Zweifel prüfen:
```bash ```bash
nvidia-smi --query-gpu=index,name,memory.used,memory.free --format=csv nvidia-smi --query-gpu=index,name,memory.used,memory.free --format=csv
``` ```
### Beide starten (empfohlen) ### Server starten
```bash ```bash
cd ~/pi_coder cd ~/pi_coder
./start-servers.sh ./start-single.sh
``` ```
Erwartete Ausgabe: Erwartete Ausgabe:
``` ```
[*] Starte beide Server parallel ... [*] Verwende HF_HOME = /home/.../huggingface
[✓] Coder (:8001) bereit [*] GPU device = 1
[✓] Judge (:8002) bereit [✓] Single-Server (:8001) bereit
``` ```
Dauer: bis zu 5 Minuten. Danach sind beide Modelle im VRAM und sofort antwortbereit. Dauer: bis zu 5 Minuten. Danach ist das Modell im VRAM und sofort antwortbereit.
### Einzelnen Server neu starten ### Server stoppen
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 ```bash
./stop-servers.sh ./stop-servers.sh
@ -149,7 +141,7 @@ Z.B. wenn nur der Judge-Server abgestürzt ist:
Falls die GGUF-Datei an einem anderen Ort liegt: Falls die GGUF-Datei an einem anderen Ort liegt:
```bash ```bash
HF_HOME=/mnt/daten/huggingface ./start-servers.sh HF_HOME=/mnt/daten/huggingface ./start-single.sh
``` ```
--- ---
@ -235,7 +227,7 @@ Der Judge:
3. Führt Tests aus 3. Führt Tests aus
4. Gibt ein strukturiertes Urteil aus 4. Gibt ein strukturiertes Urteil aus
**Beispiel-Ausgabe PASS:** **Beispiel-Ausgabe PASS WITH CONCERNS:**
``` ```
Urteil: PASS WITH CONCERNS Urteil: PASS WITH CONCERNS
@ -326,17 +318,22 @@ Empfohlene Sofortmaßnahmen: keine
- `--continue` — überspringt die Implementierungsphase und startet direkt mit dem - `--continue` — überspringt die Implementierungsphase und startet direkt mit dem
Judge→Fix-Zyklus ab dem aktuellen Code-Stand. Nützlich wenn man bereits manuell 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. `/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. - `--interactive` — pausiert nach erstem PASS für einen menschlichen Checkpoint.
Details: siehe [Interactive-Modus](#interactive-modus) weiter unten. Details: siehe [Interactive-Modus](#interactive-modus) weiter unten.
- `--no-tests` — überspringt die automatische Test-Erkennung. Sinnvoll wenn keine - `--no-tests` — überspringt die automatische Test-Erkennung. Sinnvoll wenn keine
Test-Suite vorhanden ist oder Tests über externe Infrastruktur laufen. Test-Suite vorhanden ist oder Tests über externe Infrastruktur laufen.
- `--approve-concerns` — behandelt „PASS WITH CONCERNS" wie „PASS": kein ShipIt-Call, - `--approve-concerns` — behandelt „PASS WITH CONCERNS" wie „PASS": kein ShipIt-Call,
direktes SHIP. Für Projekte, bei denen du dem Judge-Urteil vertraust. direktes SHIP. Standardmäßig löst PASS WITH CONCERNS immer einen Coder-Fix aus.
- `--test-cmd "befehl"` — überschreibt die automatische Test-Erkennung mit einem - `--test-cmd "befehl"` — überschreibt die automatische Test-Erkennung mit einem
eigenen Befehl (z.B. `--test-cmd "pytest tests/ -x"`). eigenen Befehl (z.B. `--test-cmd "pytest tests/ -x"`).
- `--test-timeout N` — maximale Laufzeit pro Test-Befehl in Sekunden (Standard: 120). - `--test-timeout N` — maximale Laufzeit pro Test-Befehl in Sekunden (Standard: 120).
### PASS WITH CONCERNS — wichtig
Ohne `--approve-concerns` gilt `PASS WITH CONCERNS` **wie `FAIL`** — der Coder bekommt
einen Fix-Auftrag und der Loop geht weiter. Erst sauberes `PASS` beendet die Schleife.
Mit `--approve-concerns` gilt PASS WITH CONCERNS als bestanden.
### Beispiel: einfacher Auftrag ### Beispiel: einfacher Auftrag
``` ```
@ -346,31 +343,33 @@ Empfohlene Sofortmaßnahmen: keine
Was im Hintergrund passiert: Was im Hintergrund passiert:
``` ```
Phase 1: Coder implementiert... Phase 1: Coder implementiert...
Phase 2: Runde 1/2: Quick-Check (kompakter Erstcheck)... Phase 2: Runde 1/2: Judge — TASK.md + letzter Commit + Tests...
→ Urteil: FAIL (2 Blocker) → Urteil: FAIL (2 Blocker)
Phase 3: Runde 1/2: Coder fixt... Phase 3: Runde 1/2: Coder fixt...
Phase 4: Runde 2/2: Judge — TASK.md + letzter Commit + Tests... Phase 4: Runde 2/2: Judge — TASK.md + letzter Commit + Tests...
→ Urteil: PASS WITH CONCERNS → Urteil: PASS
✓ PASS WITH CONCERNS nach Runde 2 ✓ PASS nach Runde 2 → SHIP
Finale ShipIt-Prüfung... (nur bei PASS WITH CONCERNS, nicht bei klarem PASS)
→ SHIP
[Dialog: Version → v0.1.0 (empfohlen)] [Dialog: Version → v0.1.0 (empfohlen)]
``` ```
**Runde 1 = Quick-Check:** Kompakter Prompt ohne TASK.md-Analyse — erkennt offensichtliche Bei `PASS WITH CONCERNS` (ohne --approve-concerns):
Fehler schnell. Erst ab Runde 2 (oder bei `--continue`) kommt der vollständige Judge-Prompt. ```
→ Urteil: PASS WITH CONCERNS (Major: fehlendes --help)
Phase 3: Runde 2/2: Coder fixt Concerns...
Phase 4: Runde 3/3: Judge...
→ Urteil: PASS
✓ PASS → SHIP
```
Bei klarem `PASS` entfällt die ShipIt-Runde — es wird direkt SHIP ausgelöst. 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, 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] ◉ Coder implementiert: Login-Flow mit JWT [01:23]
◉○ Runde 1/2: Quick-Check [00:47] ◉○ Runde 1/2: Judge — TASK.md + letzter Commit + Tests [00:47]
●◉ Runde 2/2: Coder fixt — fehlendes Null-Check [01:05] ●◉ Runde 1/2: Coder fixt — fehlendes Null-Check [01:05]
●● ✓ PASS nach Runde 2/2 — ShipIt… ●● ✓ PASS nach Runde 2/2 — SHIP
🚀 SHIP produktionsreif 🚀 SHIP produktionsreif
``` ```
@ -392,6 +391,7 @@ Nach SHIP werden automatisch ausgeführt:
1. Code-Kommentare einfügen 1. Code-Kommentare einfügen
2. README.md schreiben 2. README.md schreiben
3. BEDIENUNGSANLEITUNG.md schreiben 3. BEDIENUNGSANLEITUNG.md schreiben
4. LICENCE.md anlegen (falls noch nicht vorhanden)
### Vom manuellen Workflow in den automatischen wechseln ### Vom manuellen Workflow in den automatischen wechseln
@ -415,7 +415,8 @@ Stand und der Fix-Zyklus läuft automatisch bis PASS oder max. N Runden.
### Loop-Erkennung ### Loop-Erkennung
Wenn zweimal hintereinander genau dieselben Blocker auftreten, bricht `/optimize` ab: Wenn zweimal hintereinander genau dieselben Blocker oder Concerns auftreten, bricht
`/optimize` ab:
``` ```
⚠ Derselbe Blocker tritt erneut auf Schleife abgebrochen. Bitte manuell prüfen. ⚠ Derselbe Blocker tritt erneut auf Schleife abgebrochen. Bitte manuell prüfen.
``` ```
@ -563,11 +564,14 @@ Fix: Validierung in die Funktion generate_password() verschieben statt in parse_
## 8. Dokumentation generieren: /update_doku ## 8. Dokumentation generieren: /update_doku
Nach Abschluss der Entwicklung (nach `/shipit` oder `/optimize`) erstellt `/update_doku` Nach Abschluss der Entwicklung (nach `/shipit` oder `/optimize`) erstellt `/update_doku`
drei Dinge automatisch: vier Dinge automatisch:
1. **Code-Kommentare** — erklärt das WARUM in den Quelldateien (Deutsch) 1. **Code-Kommentare** — erklärt das WARUM in den Quelldateien (Deutsch)
2. **README.md** — Entwicklerperspektive: Installation, Build, Verwendung 2. **README.md** — Entwicklerperspektive: Installation, Build, Verwendung
3. **BEDIENUNGSANLEITUNG.md** — Endnutzerperspektive: einfach, ohne Jargon 3. **BEDIENUNGSANLEITUNG.md** — Endnutzerperspektive: einfach, ohne Jargon
4. **LICENCE.md** — proprietäre Lizenz mit aktuellem Jahr und Inhaber aus `git config user.name`
(wird nur angelegt wenn noch keine `LICENCE.md` vorhanden ist — existierende Lizenzen
werden nie überschrieben)
``` ```
/update_doku /update_doku
@ -582,6 +586,7 @@ Nur geänderte Quelldateien werden neu kommentiert — unveränderte bleiben una
Code-Kommentare: keine Änderungen seit letztem Lauf übersprungen. Code-Kommentare: keine Änderungen seit letztem Lauf übersprungen.
README.md: 2 Datei(en) geändert wird geprüft README.md: 2 Datei(en) geändert wird geprüft
BEDIENUNGSANLEITUNG.md: 2 Datei(en) geändert wird geprüft BEDIENUNGSANLEITUNG.md: 2 Datei(en) geändert wird geprüft
LICENCE.md: bereits vorhanden übersprungen.
``` ```
### Zusammen mit /optimize ### Zusammen mit /optimize
@ -772,7 +777,7 @@ Die Checkboxen werden automatisch abgehakt:
# Für Projekte wo "PASS WITH CONCERNS" ausreicht: # Für Projekte wo "PASS WITH CONCERNS" ausreicht:
/optimize "Kleines Refactoring" --approve-concerns /optimize "Kleines Refactoring" --approve-concerns
# Kombination: kein Test, kein ShipIt bei Concerns, 1 Runde # Kombination: kein Test, PASS WITH CONCERNS akzeptieren, 1 Runde
/optimize "Typo-Fix in Fehlermeldungen" --rounds 1 --no-tests --approve-concerns /optimize "Typo-Fix in Fehlermeldungen" --rounds 1 --no-tests --approve-concerns
``` ```
@ -851,7 +856,7 @@ Die Checkboxen werden automatisch abgehakt:
ls $HF_HOME/models/qwen3/ ls $HF_HOME/models/qwen3/
# Oder mit explizitem Pfad starten: # Oder mit explizitem Pfad starten:
HF_HOME=/korrekter/pfad ./start-servers.sh HF_HOME=/korrekter/pfad ./start-single.sh
``` ```
### Server startet nicht / HTTP nicht erreichbar ### Server startet nicht / HTTP nicht erreichbar
@ -864,26 +869,26 @@ HF_HOME=/korrekter/pfad ./start-servers.sh
1. Zu wenig VRAM — Container bricht beim Laden ab: 1. Zu wenig VRAM — Container bricht beim Laden ab:
```bash ```bash
docker logs qwen36-27b-coder | tail -50 docker logs qwen36-27b-single | tail -50
# Suche nach: "CUDA out of memory" oder "failed to allocate" # Suche nach: "CUDA out of memory" oder "failed to allocate"
``` ```
→ Kontext reduzieren: `-c 32768` statt `-c 131072` → Kontext reduzieren: `-c 65536` statt `-c 262144` in `start-single.sh`
2. GPU nicht verfügbar: 2. GPU nicht verfügbar:
```bash ```bash
nvidia-smi # alle drei GPUs sichtbar? nvidia-smi # GPU sichtbar?
# GPU 1 (Coder) testen: # GPU 1 testen:
docker run --gpus '"device=1"' --rm nvidia/cuda:12.0-base nvidia-smi docker run --gpus '"device=1"' --rm nvidia/cuda:12.0-base nvidia-smi
# GPU 2 (Judge) testen: # Andere GPU testen:
docker run --gpus '"device=2"' --rm nvidia/cuda:12.0-base nvidia-smi GPU_DEVICE=0 ./start-single.sh
``` ```
3. Port bereits belegt: 3. Port 8001 bereits belegt:
```bash ```bash
ss -tlnp | grep 800[12] ss -tlnp | grep 8001
docker ps -a # alter Container noch vorhanden? docker ps -a # alter Container noch vorhanden?
./stop-servers.sh ./stop-servers.sh
./start-servers.sh ./start-single.sh
``` ```
### "Agent is already processing a prompt" ### "Agent is already processing a prompt"
@ -914,8 +919,8 @@ Lies die Datei neu ein und wende die Änderungen als unified diff mit apply_patc
⚠ Derselbe Blocker tritt erneut auf Schleife abgebrochen. ⚠ Derselbe Blocker tritt erneut auf Schleife abgebrochen.
``` ```
**Ursache:** Der Coder kann einen bestimmten Blocker nicht beheben — z.B. weil die **Ursache:** Der Coder kann einen bestimmten Blocker oder Concern nicht beheben — z.B.
Aufgabe einen Widerspruch enthält oder ein externes System fehlt. weil die Aufgabe einen Widerspruch enthält oder ein externes System fehlt.
**Lösung:** Manuell eingreifen: **Lösung:** Manuell eingreifen:
``` ```
@ -927,9 +932,14 @@ Dann den Blocker analysieren und entweder:
- Die Aufgabe in TASK.md präzisieren - Die Aufgabe in TASK.md präzisieren
- Bei komplexen Aufgaben mit mehr Runden wiederholen: `/optimize --continue --rounds 5` - Bei komplexen Aufgaben mit mehr Runden wiederholen: `/optimize --continue --rounds 5`
Falls der Judge wiederholt Concerns findet, die du akzeptierst:
```
/optimize --continue --approve-concerns
```
### Server läuft, aber pi wechselt nicht das Modell ### Server läuft, aber pi wechselt nicht das Modell
**Ursache:** `models.json` wurde nach einer Änderung nicht neu deployt. **Ursache:** `models.json` oder `settings.json` wurde nach einer Änderung nicht neu deployt.
**Lösung:** **Lösung:**
```bash ```bash

236
README.md
View file

@ -1,8 +1,9 @@
# pi_coder — Automatisierter Coder/Judge-Workflow für pi agent # pi_coder_2 — Automatisierter Coder/Judge-Workflow für pi agent
Dieses Repository enthält die Konfiguration und Skripte für einen automatisierten 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, Coding-Workflow mit einem lokalen LLaMA-Modell, das abwechselnd als Coder und Judge
gesteuert über [pi agent](https://github.com/earendil-works/pi). agiert — gesteuert über Pi Agent. Er dreht solange Optimierungsschleifen, bis das
Programm Produktionsreife hat.
--- ---
@ -12,37 +13,40 @@ gesteuert über [pi agent](https://github.com/earendil-works/pi).
Nutzer gibt Auftrag Nutzer gibt Auftrag
/coder → qwen3.5-coder (:8001) → Implementierung + git commit /coder → qwen3.5-single (:8001, Coder-Persona) → Implementierung + git commit
/judge → qwen3.5-judge (:8002) → Review: PASS / FAIL + Blocker /judge → qwen3.5-single (:8001, Judge-Persona) → Review: PASS / FAIL + Blocker
FAIL? ▼ FAIL? ▼
/fix → qwen3.5-coder (:8001) → Fixes + git commit /fix → qwen3.5-single (:8001, Coder-Persona) → Fixes + git commit
PASS WITH CONCERNS? ▼
│ (ohne --approve-concerns: wie FAIL → weiterer Fix-Zyklus)
PASS? ▼ PASS? ▼
/shipit → qwen3.5-judge (:8002) → Finale Freigabe: SHIP / NO-SHIP /shipit → qwen3.5-single (:8001, Judge-Persona) → Finale Freigabe: SHIP / NO-SHIP
(nur bei "PASS WITH CONCERNS" klares PASS → direkt SHIP) (nur bei "PASS WITH CONCERNS" --approve-concerns; klares PASS → direkt SHIP)
/optimize = Coder→Judge→Fix-Schleife automatisch (bis PASS oder max. N Runden) /optimize = Coder→Judge→Fix-Schleife automatisch (bis PASS oder max. N Runden)
--interactive: pausiert nach PASS für menschlichen Checkpoint + optionale Zusatzaufträge --interactive: pausiert nach PASS für menschlichen Checkpoint + optionale Zusatzaufträge
``` ```
Beide Modelle laufen als **separate llama.cpp-Docker-Container** und sprechen eine Ein **einzelner** llama.cpp-Docker-Container bedient beide Rollen. Die Rollenumschaltung
OpenAI-kompatible API (`/v1/chat/completions`). pi agent wechselt automatisch zwischen (Coder ↔ Judge) erfolgt über unterschiedliche System-Prompts, die der `before_agent_start`-Hook
den Endpunkten wenn du ein `/judge`-, `/fix`- oder `/coder`-Kommando aufrufst. der Extension vor jeder Inference injiziert. pi agent wechselt keine Server mehr —
nur die Persona im System-Prompt ändert sich.
--- ---
## Modelle ## Modelle
| Rolle | Modell | Port | Container | Alias | GPU | | Rolle | Modell | Port | Container | Alias | GPU |
|--------|---------------------------------------------------|------|------------------|----------------|----------| |----------------|---------------------------------------------------|------|--------------------|----------------|----------|
| Coder | Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ4_XS | 8001 | qwen36-27b-coder | qwen3.5-coder | device=1 | | Coder + Judge | Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ4_XS | 8001 | qwen36-27b-single | qwen3.5-single | 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 Coder- und Judge-Rollen werden durch verschiedene System-Prompts realisiert —
Serverparametern (Kontext, Temperatur, Parallelität). dasselbe Modell, derselbe Container, unterschiedliche Persona.
--- ---
@ -58,12 +62,12 @@ Serverparametern (Kontext, Temperatur, Parallelität).
sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
sudo systemctl restart docker sudo systemctl restart docker
``` ```
- NVIDIA-GPUs: empfohlen 2 × RTX 3090 (je 24 GB VRAM) für Coder und Judge parallel. - NVIDIA-GPU: empfohlen RTX 3090 (24 GB VRAM). Standard: GPU 1. Überschreibbar mit
Bei 3 GPUs: GPU 0 für Display reservieren, GPU 1 → Coder, GPU 2 → Judge. `GPU_DEVICE=0 ./start-single.sh`. Bei 3 GPUs: GPU 0 für Display reservieren.
- GGUF-Modell vorhanden unter: - GGUF-Modell vorhanden unter:
`$HF_HOME/models/qwen3/Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ4_XS.gguf` `$HF_HOME/models/qwen3/Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ4_XS.gguf`
- Standard-Pfad: `HF_HOME=/home/dschlueter/nvme2n1p7_home/huggingface` - Standard-Pfad: `HF_HOME=/home/dschlueter/nvme2n1p7_home/huggingface`
- Überschreibbar: `HF_HOME=/anderer/pfad ./start-servers.sh` - Überschreibbar: `HF_HOME=/anderer/pfad ./start-single.sh`
- [pi agent](https://github.com/earendil-works/pi) installiert (`~/.pi/`) - [pi agent](https://github.com/earendil-works/pi) installiert (`~/.pi/`)
--- ---
@ -75,16 +79,20 @@ Serverparametern (Kontext, Temperatur, Parallelität).
git clone https://kitux.de/forgejo/dschlueter/pi_coder.git ~/pi_coder git clone https://kitux.de/forgejo/dschlueter/pi_coder.git ~/pi_coder
cd ~/pi_coder cd ~/pi_coder
# 2. Extension und Modell-Config nach ~/.pi/agent/ deployen # 2. Extension, Modell-Config und settings.json nach ~/.pi/agent/ deployen
./install_servers_and_pi_coder_extension.sh ./install_servers_and_pi_coder_extension.sh
# 3. pi agent neu laden (in der pi-Oberfläche) # 3. pi agent neu laden (in der pi-Oberfläche)
# /reload # /reload
# 4. Server starten # 4. Server starten
./start-servers.sh ./start-single.sh
``` ```
Das Install-Skript sichert eine vorhandene `~/.pi/agent/settings.json` als `.bak` und
kopiert dann `settings.json` aus dem Repo. Damit ist der korrekte Default-Provider
(`llama-cpp-single/qwen3.5-single`) automatisch konfiguriert.
Nach späteren Änderungen an `pi-coder-judge-extension.ts` oder `models.json`: Nach späteren Änderungen an `pi-coder-judge-extension.ts` oder `models.json`:
```bash ```bash
./install_servers_and_pi_coder_extension.sh # kopiert nach ~/.pi/agent/ ./install_servers_and_pi_coder_extension.sh # kopiert nach ~/.pi/agent/
@ -96,95 +104,71 @@ Nach späteren Änderungen an `pi-coder-judge-extension.ts` oder `models.json`:
## Server starten / stoppen / status ## Server starten / stoppen / status
```bash ```bash
# Beide Server parallel starten (empfohlen — dauert 13 Minuten) # Server starten (empfohlen — dauert 13 Minuten)
./start-servers.sh ./start-single.sh
# Einzeln starten (z.B. nur einen neu starten) # Server auf anderer GPU starten
./start-coder.sh # Port 8001 GPU_DEVICE=0 ./start-single.sh
./start-judge.sh # Port 8002
# Beide stoppen # Stoppen
./stop-servers.sh ./stop-servers.sh
# Status beider Server prüfen # Status prüfen
./status.sh ./status.sh
``` ```
`start-servers.sh` startet beide Container gleichzeitig und wartet bis beide `start-single.sh` startet den Container und wartet bis HTTP-ready (max. 5 Minuten — die
HTTP-ready sind (max. 5 Minuten — die neue llama.cpp-Version prüft beim Start, ob neue llama.cpp-Version prüft beim Start, ob Modell + KV-Cache in den VRAM passen, was
Modell + KV-Cache in den VRAM passen, was zusätzliche Zeit kostet). Logs werden zusätzliche Zeit kostet).
getrennt gesammelt und nur bei Fehler ausgegeben.
Wenn Server bereits laufen und du `start-servers.sh` (oder ein Einzelskript) Wenn der Server bereits läuft und du `start-single.sh` aufrufst, wird der laufende
aufrufst, werden die laufenden Container zuerst per `docker rm -f` gestoppt Container zuerst per `docker rm -f` gestoppt und dann neu gestartet — ein laufender
und dann neu gestartet — ein laufender Inference-Request wird dabei abgebrochen. Inference-Request wird dabei abgebrochen.
--- ---
## llama.cpp-Serverparameter im Detail ## llama.cpp-Serverparameter im Detail
### Gemeinsame Parameter ### Parameter des Single-Servers (Port 8001)
| Parameter | Wert | Bedeutung | | Parameter | Wert | Bedeutung |
|---|---|---| |---|---|---|
| `--jinja` | — | Verwendet das im GGUF eingebettete Jinja-Chat-Template (Qwen-Format). Notwendig für korrekte `<\|im_start\|>`-Tokens. | | `--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}'`. | | `--reasoning on` | — | Aktiviert das interne Thinking-Budget des Modells (Qwen3-spezifisch). |
| `--no-context-shift` | — | Kontextfenster wird **nicht** verschoben wenn es voll ist — stattdessen Fehler. Verhindert stille Datenverluste. | | `--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. | | `--repeat-penalty 1.05` | — | Leichte Penalty für Wiederholungen. |
| `--top-k 20` | — | Nur die 20 wahrscheinlichsten nächsten Tokens werden berücksichtigt. | | `--top-k 20` | — | Nur die 20 wahrscheinlichsten nächsten Tokens. |
| `--min-p 0.01` | — | Tokens mit Wahrscheinlichkeit < 1 % des wahrscheinlichsten Tokens werden ausgeschlossen. | | `--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. | | `-ngl 999` | — | Alle Layer auf die GPU laden. |
| `-fa on` | — | Flash Attention — schnellere Attention-Berechnung, weniger VRAM für den Attention-Pass. | | `-fa on` | — | Flash Attention. |
| `--kv-unified` | — | Einheitlicher KV-Cache über alle Schichten. Effizienter bei langen Kontexten. | | `--kv-unified` | — | Einheitlicher KV-Cache über alle Schichten. |
| `--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-k q4_0` | — | KV-Cache Keys 4-Bit quantisiert. Nötig für 256K Kontext auf einer 24-GB-GPU. |
| `--cache-type-v q4_0` | — | KV-Cache Values ebenfalls 4-Bit quantisiert. | | `--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. | | `--cont-batching` | — | Continuous Batching. |
| `--main-gpu 0` | — | GPU-Index (0 = erste des Containers) für Nicht-Tensor-Operationen. | | `-c 262144` | 256K | Großes Kontextfenster für langen Gesprächsverlauf im Optimize-Loop. |
| `--gpus '"device=1"'` (Coder) | — | Docker: nur GPU 1 dem Coder-Container zuweisen. | | `-n 16384` | 16K | Maximale Ausgabelänge. |
| `--gpus '"device=2"'` (Judge) | — | Docker: nur GPU 2 dem Judge-Container zuweisen. | | `--temp 0.2` | — | Deterministisch — Coder-Rolle. Der Judge-System-Prompt gleicht die etwas höhere Temperatur aus. |
| `--parallel 2` | — | 2 Slots, damit pi agent Folgeanfragen schnell hintereinander stellen kann. |
### Coder-Server (Port 8001) — optimiert für Coding-Aufgaben | `--gpus '"device=1"'` | — | Standard: GPU 1. Überschreibbar: `GPU_DEVICE=0 ./start-single.sh`. |
| 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 ## GPU-Konfiguration und VRAM
### Aktuelles Setup (1 GPU pro Server) ### Aktuelles Setup (1 GPU, 1 Server)
Jeder Server bekommt eine dedizierte GPU — dadurch kein VRAM-Konflikt: Ein einzelner Container bedient beide Rollen sequenziell:
``` ```
GPU 0 NVIDIA T600 → Display / Monitor (nicht für KI genutzt) GPU 0 NVIDIA T600 → Display / Monitor (nicht für KI genutzt)
GPU 1 RTX 3090 (24 GB) → qwen36-27b-coder (Port 8001) GPU 1 RTX 3090 (24 GB) → qwen36-27b-single (Port 8001, Coder + Judge)
GPU 2 RTX 3090 (24 GB) → qwen36-27b-judge (Port 8002)
``` ```
Die Skripte verwenden `--gpus '"device=1"'` (Coder) bzw. `'"device=2"'` (Judge). Da Coder und Judge nicht gleichzeitig inferieren, gibt es keinen VRAM-Konflikt.
Innerhalb des Containers ist dies stets `CUDA:0`, daher `--main-gpu 0` ohne `--tensor-split`. Die Umschaltung zwischen den Rollen erfolgt ausschließlich über den System-Prompt —
der Container läuft ohne Unterbrechung durch.
### VRAM-Abschätzung für das 27B IQ4_XS-Modell (eine GPU) ### VRAM-Abschätzung für das 27B IQ4_XS-Modell
| Komponente | Größe (ca.) | | Komponente | Größe (ca.) |
|---|---| |---|---|
@ -194,36 +178,28 @@ Innerhalb des Containers ist dies stets `CUDA:0`, daher `--main-gpu 0` ohne `--t
| KV-Cache bei 65 536 Tokens (q4_0) | ~23 GB | | KV-Cache bei 65 536 Tokens (q4_0) | ~23 GB |
| **Summe (262k Kontext)** | **~2224 GB** | | **Summe (262k Kontext)** | **~2224 GB** |
Bei einer **24-GB-GPU** ist 262k Kontext knapp kalkuliert. Wichtig: keine anderen Prozesse Bei einer **24-GB-GPU** ist 262k Kontext knapp kalkuliert. Keine anderen Prozesse
dürfen nennenswert VRAM belegen (z. B. TTS-Dienste, Ollama). Prüfen mit: dürfen nennenswert VRAM belegen (z. B. TTS-Dienste, Ollama). Prüfen mit:
```bash ```bash
nvidia-smi --query-gpu=index,memory.used,memory.free --format=csv 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. Falls der VRAM nicht reicht: Kontext in `start-single.sh` auf `131072` oder `65536` reduzieren.
### Anpassung für andere GPU-Konfigurationen ### Anpassung für andere GPU-Konfigurationen
```bash ```bash
# 2 GPUs (beide für KI, keine Display-GPU): # Andere GPU verwenden:
--gpus '"device=0"' # Coder GPU_DEVICE=0 ./start-single.sh
--gpus '"device=1"' # Judge
# 1 GPU (beide Server auf gleicher GPU — nicht empfohlen, VRAM-Konflikt): # 2 GPUs für einen Server (sehr großer Kontext):
--gpus '"device=0"' # beide Server # start-single.sh anpassen:
--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"' \ --gpus '"device=0,1"' \
--tensor-split 0.5,0.5 \ --tensor-split 0.5,0.5 \
--main-gpu 0 \ --main-gpu 0 \
-c 262144 -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 ## Parameter-Tuning-Guide
@ -232,23 +208,18 @@ Entweder ein kleineres Modell verwenden oder die Quantisierung erhöhen (IQ3_XS,
| Wert | Eignung | | Wert | Eignung |
|---|---| |---|---|
| `0.00.1` | Maximale Reproduzierbarkeit. Gut für Judge/Review. | | `0.00.1` | Maximale Reproduzierbarkeit. |
| `0.10.3` | Guter Kompromiss für Coding. **Empfohlen für Coder.** | | `0.10.3` | Guter Kompromiss für Coding. **Aktuell gesetzt: 0.2.** |
| `0.40.6` | Kreativere Lösungen, mehr Varianz. Sinnvoll für Prototyping. | | `0.40.6` | Kreativere Lösungen. |
| `0.71.0` | Kreativschreiben, Brainstorming. Für Coding meist zu viel Rauschen. | | `0.71.0` | Für Coding meist zu viel Rauschen. |
### Kontextgröße (`-c`) ### 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 | | Kontext | KV-Cache (q4_0) | Empfehlung |
|---|---|---| |---|---|---|
| 32 768 | ~1,9 GB | 1 × 16-GB-GPU | | 32 768 | ~1,9 GB | 1 × 16-GB-GPU |
| 65 536 | ~3,7 GB | 1 × 24-GB-GPU | | 65 536 | ~3,7 GB | 1 × 24-GB-GPU (konservativ) |
| 131 072 | ~7,5 GB | 2 × 16-GB-GPU | | 131 072 | ~7,5 GB | 1 × 24-GB-GPU |
| 262 144 | ~810 GB | 1 × 24-GB-GPU (q4_0) — **aktuell gesetzt** | | 262 144 | ~810 GB | 1 × 24-GB-GPU (q4_0) — **aktuell gesetzt** |
### KV-Cache-Quantisierung ### KV-Cache-Quantisierung
@ -257,31 +228,35 @@ KV-Cache ≈ `context_size × 28 × 128 × 32 × 2 × 1 Byte ≈ context_size ×
|---|---|---| |---|---|---|
| `f16` | 100 % (Basis) | Referenz | | `f16` | 100 % (Basis) | Referenz |
| `q8_0` | ~50 % | Kaum merklich schlechter | | `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.** | | `q4_0` | ~25 % | Merklicher Qualitätsverlust bei langen Kontexten — aber nötig für 256K Kontext auf 24 GB. **Aktuell gesetzt.** |
### Parallelität (`--parallel`) ---
Mehr parallele Slots erhöhen den Durchsatz bei gleichzeitigen Anfragen, aber jeder Slot ## Tests ausführen
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 ```bash
wenn pi agent Folgeanfragen schnell hintereinander schickt. ./run-tests.sh
```
Keine npm-Installation nötig — nutzt den integrierten `node:test`-Runner und
`--experimental-strip-types` für TypeScript. Testet Parsing-Logik, Konfigurationskonsistenz
und Extension-Guards gegen Regressionen.
--- ---
## Dateien ## Dateien
| Datei | Zweck | | Datei / Verzeichnis | Zweck |
|---|---| |---|---|
| `pi-coder-judge-extension.ts` | pi agent Extension (Kommandos, Tools, Hooks) | | `pi-coder-judge-extension.ts` | pi agent Extension (Kommandos, Tools, Hooks, Rollen-Logik) |
| `models.json` | Provider- und Modell-Konfiguration für pi agent | | `models.json` | Provider- und Modell-Konfiguration für pi agent |
| `test-utils.ts` | Unit-Tests für Hilfsfunktionen der Extension | | `settings.json` | pi-agent-Settings (Default-Modell, aktivierte Modelle) — wird von Install-Skript deployt |
| `run-tests.sh` | Unit-Tests ausführen (TypeScript-Stripping + node) | | `run-tests.sh` | Tests ausführen |
| `start-servers.sh` | Beide Server parallel starten (empfohlen) | | `tests/` | Testdateien (pure-logic, config, extension-guards, scripts) |
| `start-coder.sh` | Nur Coder-Container starten (Port 8001) | | `start-single.sh` | Single-Server starten (Port 8001, Coder + Judge) |
| `start-judge.sh` | Nur Judge-Container starten (Port 8002) | | `stop-servers.sh` | Container stoppen |
| `stop-servers.sh` | Beide Container stoppen | | `status.sh` | Laufstatus anzeigen |
| `status.sh` | Laufstatus beider Server anzeigen | | `install_servers_and_pi_coder_extension.sh` | Extension + models.json + settings.json nach `~/.pi/agent/` deployen |
| `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/` | 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 | | `examples/restore-all.sh` | Examples nach Demo-Lauf in Ausgangszustand zurücksetzen |
@ -289,18 +264,18 @@ wenn pi agent Folgeanfragen schnell hintereinander schickt.
## pi-Kommandos (Kurzübersicht) ## pi-Kommandos (Kurzübersicht)
| Kommando | Modell | Beschreibung | | Kommando | Persona | Beschreibung |
|---|---|---| |---|---|---|
| `/coder <auftrag>` | Coder | TASK.md anlegen, Implementierung starten | | `/coder <auftrag>` | Coder | TASK.md anlegen, Implementierung starten |
| `/judge [fokus]` | Judge | Code-Review gegen TASK.md + letzten Commit | | `/judge [fokus]` | Judge | Code-Review gegen TASK.md + letzten Commit |
| `/fix [hinweis]` | Coder | Judge-Kritik beheben, committen | | `/fix [hinweis]` | Coder | Judge-Kritik beheben, committen |
| `/shipit` | Judge | Finale Freigabeprüfung | | `/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 <auftrag> [--rounds N] [--with-doku] [--continue] [--interactive]` | beide | Vollautomatische Schleife bis PASS (Standard: 2 Runden) |
| `/optimize ... [--no-tests] [--approve-concerns] [--test-cmd "cmd"] [--test-timeout N]` | beide | Test-Erkennung überspringen / PASS WITH CONCERNS direkt shippern | | `/optimize ... [--no-tests] [--approve-concerns] [--test-cmd "cmd"] [--test-timeout N]` | beide | Test-Erkennung überspringen / PASS WITH CONCERNS akzeptieren |
| `/patch <änderung>` | Coder | Gezielte Minimaländerung ohne Review | | `/patch <änderung>` | Coder | Gezielte Minimaländerung ohne Review |
| `/quick_check [was]` | Judge | Schnelle Prüfung der letzten Änderung | | `/quick_check [was]` | Judge | Schnelle Prüfung der letzten Änderung |
| `/version` | — | Versionsnummer erhöhen (SemVer + Git-Tag) | | `/version` | — | Versionsnummer erhöhen (SemVer + Git-Tag) |
| `/update_doku` | Coder | Code kommentieren + README + Bedienungsanleitung | | `/update_doku` | Coder | Code kommentieren + README + Bedienungsanleitung + LICENCE.md |
| `/plan <auftrag>` | Coder | Implementierungsplan in PLAN.md (kein Code) | | `/plan <auftrag>` | Coder | Implementierungsplan in PLAN.md (kein Code) |
| `/continue` | Coder | Unterbrochenen Prozess fortsetzen | | `/continue` | Coder | Unterbrochenen Prozess fortsetzen |
| `/cancel` | — | Laufenden Loop nach aktuellem Schritt abbrechen | | `/cancel` | — | Laufenden Loop nach aktuellem Schritt abbrechen |
@ -310,6 +285,15 @@ Ausführliche Beschreibung aller Kommandos mit Beispielen: siehe **BEDIENUNGSANL
--- ---
## PASS WITH CONCERNS — Verhalten
Im `/optimize`-Loop gilt `PASS WITH CONCERNS` **nicht** als bestanden — der Coder erhält
einen weiteren Fix-Auftrag, genau wie bei `FAIL`. Erst sauberes `PASS` beendet die Schleife.
Opt-out: `--approve-concerns` lässt `PASS WITH CONCERNS` als bestanden gelten.
---
## Live-Aktivitätsstatus ## Live-Aktivitätsstatus
Während der Ausführung zeigt pi_coder in der Statuszeile, was gerade passiert — Während der Ausführung zeigt pi_coder in der Statuszeile, was gerade passiert —
@ -320,11 +304,11 @@ inklusive eines laufenden `[MM:SS]`-Timers während jeder LLM-Inference-Phase:
| Coder implementiert | `◉ Coder implementiert: Login-Flow mit JWT [01:23]` | | Coder implementiert | `◉ Coder implementiert: Login-Flow mit JWT [01:23]` |
| edit-Tool aktiv | `Editiere src/main.py…` | | edit-Tool aktiv | `Editiere src/main.py…` |
| git commit | `Git-Commit…` | | git commit | `Git-Commit…` |
| Judge (Quick-Check, Runde 1) | `◉○ Runde 1/2: Quick-Check [00:47]` | | Judge (Runde 1) | `◉○ Runde 1/2: Judge — TASK.md + letzter Commit + Tests [00:47]` |
| Judge (voller Review, Runde 2) | `●◉ Runde 2/2: Judge — TASK.md + letzter Commit + Tests [02:15]` | | Judge (Runde 2) | `●◉ Runde 2/2: Judge — TASK.md + letzter Commit + Tests [02:15]` |
| Tests laufen | `●○ Runde 1/2: Tests laufen (pytest, max. 120s)…` | | Tests laufen | `●○ Runde 1/2: Tests laufen (pytest, max. 120s)…` |
| Fix-Phase | `●◉ Runde 2/2: Coder fixt — fehlendes Null-Check [01:05]` | | Fix-Phase | `●◉ Runde 2/2: Coder fixt — fehlendes Null-Check [01:05]` |
| ShipIt | `●●◉ ShipIt — finale Freigabe [00:33]` | | ShipIt | `●●◉ ShipIt — finale Freigabe [00:33]` |
| Interactive-Pause (--interactive) | `⏸ PASS warte auf /continue…` | | Interactive-Pause | `⏸ PASS warte auf /continue…` |
Der Timer beweist, dass die LLM tatsächlich arbeitet. Steht er still, hängt der Prozess. Der Timer beweist, dass die LLM tatsächlich arbeitet. Steht er still, hängt der Prozess.

View file

@ -1,150 +0,0 @@
# 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

@ -324,6 +324,45 @@ function planPrompt(task: string): string {
// ── Hilfsfunktionen ───────────────────────────────────────────────────────── // ── Hilfsfunktionen ─────────────────────────────────────────────────────────
// Proprietäre Default-Lizenz ("Alle Rechte vorbehalten"), deutsch — passend zur übrigen Doku.
// Reine Template-Funktion (deterministisch): Lizenztext wird NIE vom LLM generiert.
function licenceMd(year: string, holder: string): string {
return [
"# Lizenz",
"",
`Copyright (c) ${year} ${holder}`,
"",
"Alle Rechte vorbehalten.",
"",
"Diese Software und der zugehörige Quellcode sind urheberrechtlich geschützt.",
"Kein Teil dieser Software darf ohne vorherige ausdrückliche schriftliche",
"Genehmigung des Rechteinhabers kopiert, verändert, weitergegeben, veröffentlicht,",
"unterlizenziert oder anderweitig genutzt werden.",
"",
"DIE SOFTWARE WIRD OHNE MÄNGELGEWÄHR UND OHNE JEGLICHE AUSDRÜCKLICHE ODER",
"KONKLUDENTE GEWÄHRLEISTUNG BEREITGESTELLT. DER RECHTEINHABER HAFTET NICHT FÜR",
"SCHÄDEN, DIE AUS DER NUTZUNG DIESER SOFTWARE ENTSTEHEN.",
""
].join("\n");
}
// Legt LICENCE.md mit der proprietären Default-Lizenz an — nur wenn noch keine existiert
// (eine vorhandene Lizenz wird nie überschrieben). Inhaber aus 'git config user.name',
// sonst Platzhalter; Jahr aus dem aktuellen Datum.
async function writeLicenceMd(pi: ExtensionAPI, ctx: ExtensionCommandContext): Promise<void> {
const check = await pi.exec("bash", ["-c", "test -f LICENCE.md && echo exists"], { cwd: ctx.cwd });
if (check.stdout.trim() === "exists") {
ctx.ui.notify("LICENCE.md existiert bereits — unverändert.", "info");
return;
}
const who = await pi.exec("bash", ["-c", "git config user.name 2>/dev/null"], { cwd: ctx.cwd });
const holder = who.stdout.trim() || "[Rechteinhaber eintragen]";
const year = String(new Date().getFullYear());
// content als Argument ($1), nicht interpoliert — kein Shell-Escaping-Problem.
await pi.exec("bash", ["-c", `printf "%s" "$1" > LICENCE.md`, "_", licenceMd(year, holder)], { cwd: ctx.cwd });
ctx.ui.notify(`LICENCE.md angelegt (proprietär, © ${year} ${holder}).`, "info");
}
// Legt TASK.md neu an oder hängt einen Zusatzauftrag an. // Legt TASK.md neu an oder hängt einen Zusatzauftrag an.
async function writeTaskMd( async function writeTaskMd(
pi: ExtensionAPI, pi: ExtensionAPI,
@ -703,10 +742,18 @@ async function runUpdateDoku(pi: ExtensionAPI, ctx: ExtensionCommandContext): Pr
ctx.ui.notify(`3/3 BEDIENUNGSANLEITUNG.md fehlgeschlagen: ${String(e?.message ?? e)}`, "error"); ctx.ui.notify(`3/3 BEDIENUNGSANLEITUNG.md fehlgeschlagen: ${String(e?.message ?? e)}`, "error");
} }
// LICENCE.md: deterministisch anlegen (nur wenn nicht vorhanden) — kein LLM.
try {
ctx.ui.setStatus("update_doku", "LICENCE.md wird geprüft…");
await writeLicenceMd(pi, ctx);
} catch (e: any) {
ctx.ui.notify(`LICENCE.md fehlgeschlagen: ${String(e?.message ?? e)}`, "error");
}
// Abschließender Dokumentations-Commit (immer, auch bei Teilfehlern) // Abschließender Dokumentations-Commit (immer, auch bei Teilfehlern)
await pi.exec( await pi.exec(
"bash", "bash",
["-c", "git add -A && git commit -m 'docs: update comments, README, BEDIENUNGSANLEITUNG' || true"], ["-c", "git add -A && git commit -m 'docs: update comments, README, BEDIENUNGSANLEITUNG, LICENCE' || true"],
{ cwd: ctx.cwd } { cwd: ctx.cwd }
); );
@ -1331,7 +1378,7 @@ export default function (pi: ExtensionAPI) {
if (withDoku) { if (withDoku) {
await runUpdateDoku(pi, ctx); await runUpdateDoku(pi, ctx);
} else { } else {
ctx.ui.notify("Nächster Schritt: /update_doku für Code-Kommentare, README.md und BEDIENUNGSANLEITUNG.md", "info"); ctx.ui.notify("Nächster Schritt: /update_doku für Code-Kommentare, README.md, BEDIENUNGSANLEITUNG.md und LICENCE.md", "info");
} }
} }
} catch (e: any) { } catch (e: any) {
@ -1387,7 +1434,7 @@ export default function (pi: ExtensionAPI) {
// ── Dokumentations-Phase ───────────────────────────────────────────────── // ── Dokumentations-Phase ─────────────────────────────────────────────────
pi.registerCommand("update_doku", { pi.registerCommand("update_doku", {
description: "Inkrementelle Code-Kommentare + README.md + BEDIENUNGSANLEITUNG.md via Git-Tags.", description: "Inkrementelle Code-Kommentare + README.md + BEDIENUNGSANLEITUNG.md via Git-Tags; legt LICENCE.md an (proprietär, falls fehlend).",
handler: async function (_args: string, ctx: ExtensionCommandContext) { handler: async function (_args: string, ctx: ExtensionCommandContext) {
await runUpdateDoku(pi, ctx); await runUpdateDoku(pi, ctx);
} }
@ -1484,7 +1531,7 @@ export default function (pi: ExtensionAPI) {
"/patch <änderung> Gezielte Minimaländerung → Coder", "/patch <änderung> Gezielte Minimaländerung → Coder",
"/quick_check [was] Schnelle OK/PROBLEM-Prüfung → Judge", "/quick_check [was] Schnelle OK/PROBLEM-Prüfung → Judge",
"/plan <auftrag> Implementierungsplan in PLAN.md → Coder", "/plan <auftrag> Implementierungsplan in PLAN.md → Coder",
"/update_doku Code-Kommentare + README.md + BEDIENUNGSANLEITUNG.md", "/update_doku Code-Kommentare + README.md + BEDIENUNGSANLEITUNG.md + LICENCE.md",
"/version Versionsnummer erhöhen (SemVer + Git-Tag)", "/version Versionsnummer erhöhen (SemVer + Git-Tag)",
"/discard Verwirft PLAN.md", "/discard Verwirft PLAN.md",
"/new_project <pfad> Projektverzeichnis + git init + .gitignore", "/new_project <pfad> Projektverzeichnis + git init + .gitignore",

View file

@ -44,3 +44,17 @@ test("Loop-Erkennung greift auch ohne Blocker-Abschnitt (Fallback auf Gesamttext
test("currentRole wird nach jedem optimize-Lauf auf 'coder' zurückgesetzt", () => { test("currentRole wird nach jedem optimize-Lauf auf 'coder' zurückgesetzt", () => {
assert.match(extSource, /currentRole\s*=\s*"coder"/); assert.match(extSource, /currentRole\s*=\s*"coder"/);
}); });
test("LICENCE.md: deterministisch in der Doku-Phase, nur wenn nicht vorhanden, proprietär", () => {
assert.match(extSource, /function licenceMd\b/);
assert.match(extSource, /async function writeLicenceMd\b/);
// wird in runUpdateDoku aufgerufen
assert.match(extSource, /await writeLicenceMd\(pi, ctx\)/);
// existierende Lizenz wird nicht überschrieben
assert.match(extSource, /test -f LICENCE\.md/);
// proprietärer Default
assert.match(extSource, /Alle Rechte vorbehalten/);
// Inhaber aus git config, Jahr aus aktuellem Datum
assert.match(extSource, /git config user\.name/);
assert.match(extSource, /new Date\(\)\.getFullYear\(\)/);
});

View file

@ -8,6 +8,7 @@ import { extractFn } from "./helpers.mjs";
const parseVerdict = extractFn("parseVerdict"); const parseVerdict = extractFn("parseVerdict");
const parseBlockers = extractFn("parseBlockers"); const parseBlockers = extractFn("parseBlockers");
const normalizeForComparison = extractFn("normalizeForComparison"); const normalizeForComparison = extractFn("normalizeForComparison");
const licenceMd = extractFn("licenceMd");
test("parseVerdict: klares PASS", () => { test("parseVerdict: klares PASS", () => {
assert.equal(parseVerdict("Review fertig.\nUrteil: PASS\nKeine Blocker."), "PASS"); assert.equal(parseVerdict("Review fertig.\nUrteil: PASS\nKeine Blocker."), "PASS");
@ -64,3 +65,16 @@ test("normalizeForComparison: gleiche Blocker trotz Formatierungsunterschied →
normalizeForComparison(b) normalizeForComparison(b)
); );
}); });
test("licenceMd: proprietärer Default mit eingesetztem Jahr und Inhaber", () => {
const txt = licenceMd("2026", "Dieter Schlüter");
assert.match(txt, /^# Lizenz/);
assert.match(txt, /Copyright \(c\) 2026 Dieter Schlüter/);
assert.match(txt, /Alle Rechte vorbehalten\./);
assert.match(txt, /ohne vorherige ausdrückliche schriftliche/);
assert.match(txt, /OHNE MÄNGELGEWÄHR/);
});
test("licenceMd: Platzhalter-Inhaber wird unverändert eingesetzt", () => {
assert.match(licenceMd("2030", "[Rechteinhaber eintragen]"), /Copyright \(c\) 2030 \[Rechteinhaber eintragen\]/);
});