trading-system-docs/notes/trading/system-docs/modul-16-paperclip.md

147 lines
9.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Modul-16: Paperclip (Agent-Orchestrierungsebene)
**Status: FREIGEGEBEN (21.08.2026)** · Container `Modul-16-Paperclip` · Image `trading-modules-modul16-paperclip:latest`
> ⚠️ **Achtung Namenskollision:** Es existiert ein **Fremdsystem** `paperclip-dn8l-paperclip-1`
> (Coolify, Hostinger hvps-paperclip) im `coolify`-Netz. Dies ist **NICHT** unser Modul-16.
> Unser Modul-16 läuft im `trading-modules`-Netz als `Modul-16-Paperclip`. Fremdsystem nie anfassen.
## Zweck
Paperclip ist die **übergeordnete Agent-/Orchestrierungsebene** des Trading-Systems. Er nimmt
Research-, Analytics-, Strategieüberprüfungs-, Backtest-, Optimization- und Monitoring-Aufträge
entgegen, wählt eine **Agent-Rolle** (Strategy/Research/Analytics/Validation/System) und führt sie
deterministisch aus. Später (V2) können daraus Hermes-Agent-Aufgaben entstehen.
**Kritische Grenze:** Paperclip ist **NICHT Teil des kritischen Trading-Pfads.**
- ✅ Er liest READ-ONLY aus Modul-0315 und startet klar begrenzte, nicht-kritische Writes (Backtest, Optimization).
- ❌ Er DARF KEINE Broker-Orders senden, Modul-09 nicht zur Orderausführung anweisen, Risk-/Portfolio-Regeln nicht
überschreiben, M15-Safety nicht überschreiben, Strategien nicht automatisch produktiv aktivieren,
Optimierungsparameter nicht automatisch deployen.
- **Trading-Core (M0315) läuft bei komplettem Ausfall von Modul-16 unverändert weiter.**
## Architektur / Datenfluss
```
Client/Auftrag
│ POST /tasks (task_type + payload)
Modul-16-Paperclip (Port 55016, NUR intern/expose)
├── app/core/orchestrator.py Rollen-Wahl + deterministische Ausführung
├── app/core/proposal_gate.py Proposal-Gate (NIE Auto-APPROVED/aktiviert)
├── app/storage/storage.py Persistenz agent_task/agent_run/agent_proposal/agent_audit
├── app/clients/clients.py Read-Clients + 2 begrenzte Write-Clients
└── app/api/main.py REST-API (/health, /tasks, /proposals)
├── Read-Only (tolerant): Modul-03/04/06 (research), M05+M11 (strategy_review),
│ M11 (analytics), M15 (monitoring, NUR GET)
└── Write (begrenzt): Modul-12 POST /backtest, Modul-13 POST /optimization
```
**API (Docker-intern, Port 55016):**
- `GET /health` — Liveness (`{"status":"ok","service":"paperclip","version":"0.1.0"}`)
- `GET /health/ready` — Readiness (DB + Schema vorhanden) → 200 `{"status":"ready"}`
- `POST /tasks` — Auftrag einreichen (validiert; unbekannte/`execute_order` → 422)
- `GET /tasks` / `GET /tasks/{id}` — Aufträge + Status/result_refs
- `POST /tasks/{id}/run` — Aufgabe synchron ausführen (erzeugt `agent_run`)
- `POST /proposals` — Proposal als DRAFT erzeugen (APPROVED via Paperclip → 422)
- `GET /proposals` / `GET /proposals/{id}`
Kein RabbitMQ: synchroner request/response. Port 55016 ist **nur intern** (`expose:`), kein öffentliches Mapping.
## Agent-Rollen (V1, vorbereitet)
| Rolle | task_type | liest | schreibt |
|-------|-----------|-------|----------|
| `analytics` | `analytics` | Modul-11 (portfolio/strategy/regime) | — |
| `research` | `research` | Modul-03/04/06 | — |
| `strategy` | `strategy_review` | Modul-05 (Strategien) + Modul-11 | — |
| `validation` | `backtest`, `optimization`, `validation` | Modul-12/13 | M12 POST /backtest, M13 POST /optimization |
| `system` | `monitoring` | Modul-15 (NUR GET) | — |
Alle Rollen sind per `ENABLED_ROLES` registriert; es werden **nicht unnötig viele aktiviert**.
`TASK_TO_ROLE` mappt `task_type → Rolle`.
## Proposal-Prinzip (ProposalGate)
Wenn Paperclip eine Änderung empfiehlt, wird sie **als Proposal** gespeichert, niemals direkt angewendet:
```
STRATEGY_CHANGE_PROPOSAL mit:
proposal_id, source/agent, strategy, aktuelle Version, vorgeschlagene Änderung (JSON),
Begründung (rationale), Analytics-/Backtest-Referenzen, Confidence, created_at, status
Status: DRAFT / PROPOSED / REVIEWED / APPROVED / REJECTED
```
- `create_proposal` läuft ausschließlich über `ProposalGate` (nur `status in {DRAFT, PROPOSED}` erlaubt).
- Paperclip kann `APPROVED` **NIE selbst setzen** (validiert in `proposal_gate.py`; API gibt 422).
- Proposal wird mit `auto_activated=false` persistiert — **keine automatische Aktivierung**.
- **Keine automatische Übernahme optimierter Parameter** in Produktion.
## Sicherheit
- **Keine öffentlichen Ports**: 55016 nur `expose` (Docker-intern), kein Host-Mapping.
- **Internes Docker-Netzwerk** `trading-modules`.
- **Secrets nur ENV/Secret** (`PG_PASSWORD` aus Compose `environment`); keine Secrets in Logs/API/DB.
- **Keine Broker-Credentials.**
- **Kein Docker-Socket-/Root-Recht, kein Shell-Zugriff auf andere Container.**
- **M15-Ausfall kann Paperclip nicht dazu bringen, Safety zu umgehen** (Paperclip hat KEINE M15-Control-Write-Methode).
- **Paperclip-Ausfall stoppt Trading nicht** (eigene DB-Tabellen, kein Einfluss auf Trading-Core).
## Persistenz / Audit (Migration `migrations/001_paperclip.sql`)
- `agent_task` — Aufträge (id, agent_role, task_type, status, payload, result_refs, error_*).
- `agent_run` — Ausführungen (run_id, task_id, agent_role, status, result, idempotent).
- `agent_proposal` — Vorschläge (proposal_id, source, strategy, proposed_change, rationale, references, confidence, status, auto_activated).
- `agent_audit` — lückenloses Audit-Log (actor, action, detail, task_id, run_id, proposal_id).
Jede Agent-Aktion (task_created, run_started, run_succeeded, run_failed, run_rejected, proposal_*) wird in `agent_audit` protokolliert.
## Read-Clients (tolerant)
Einzelne Quellen-Fehler (404/5xx/Timeout) werden über `_safe_get` als `{"_error": code, "_message": …}`
Teil-Ergebnis gekapselt → der Gesamt-Task bleibt SUCCEEDED. Nur wenn ein Service **komplett unreachable**
(`UNREACHABLE`/`TIMEOUT`) ist, liefert er `None`. Write-Aktionen (Backtest/Optimization) bleiben strikt FAILED bei Fehlern.
**Ausnahme — Monitoring (FAIL-CLOSED):** Der `monitoring`-Task ruft M15 **nicht** über `_safe_get`,
sondern direkt auf. M15 ist die autoritative Trading-Status-Quelle — ist sie nicht erreichbar, wird der
Monitoring-Task **FAILED** (kein „ok"-Status bei ausgefallenem M15). Bei M12/M13-Ausfall → betroffener
Task (Backtest/Optimization) FAILED, M16 bleibt healthy, Trading-Core unbeeinflusst.
## Tests & E2E-Verifikation (VPS, 21.08.2026)
- **Unit-Tests** `tests/test_paperclip.py`: 10/10 grün (ProposalGate-Safety, Task-Validierung, Worker, Idempotenz, Auto-Aktivierungs-Verbot).
- **Backtest-Flow**: M16 → M12 `POST /backtest`**SUCCEEDED**, `agent_run` + `run_id`/`result_refs` korrekt (`kind:"backtest"`).
- **Optimization-Flow**: M16 → M13 `POST /optimization`**SUCCEEDED**, `run_id`/`result_refs` korrekt (`kind:"optimization"`).
- **Monitoring-Flow**: M16 → M15 `/monitoring/status` (READ-ONLY) → **SUCCEEDED**, Ergebnis gespeichert.
- **Research-Flow**: M16 → M03/M04/M06 → **SUCCEEDED** (M04 404 „Kein Regime" als Teil-Ergebnis gekapselt, kein Crash).
- **Strategy-Review-Flow**: M16 → M05+M11 → **SUCCEEDED**.
- **Proposal**: erstellt als `DRAFT`/`auto_activated=false`; `APPROVED` → 422. **NICHT auto-aktiviert.**
- **Kein M09-Zugriff**: `task_type=execute_order` → 422; kein `ExecutionClient`, keine `/order`-Endpunkte, kein `execution_base_url`.
- **Kein M15-Override**: Paperclip hat nur GET-Methoden auf M15, keine Control-Write.
- **Ausfall-Toleranz**: M12 down → backtest FAILED, M13 down → optimization FAILED, M15 down → monitoring
**FAILED (FAIL-CLOSED)**, M15 up → SUCCEEDED. M16 bleibt in allen Fällen **healthy**, Trading-Core unbeeinflusst,
Erholung → SUCCEEDED.
- **Idempotenz**: erneuter `run` erzeugt neuen `agent_run`, überschreibt frühere Runs nicht.
- **Ungültige Aufgabe**: `task_type=delete_database` → 422.
- **Keine Secrets in Logs/API**: `docker logs` frei von password/secret/token/api_key.
- **Health/Ready**: beide 200.
## Bugs / Fixes (modul-16, dokumentiert)
1. **`references`-SQL-Keyword** → Spalte in Migration + Queries als `"references"` gequoted.
2. **Migration-Commit**: psycopg2 braucht `conn.autocommit = True` (Muster wie M15), sonst bleiben Tabellen
uncommittet → `relation agent_task does not exist` bei `/health/ready`. Behoben in `_connect`.
3. **create_task RETURNING-Mismatch**: RETURNING listete 7 Spalten, `_task_row` erwartete 10 →
`IndexError`. Behoben: vollständige Spaltenliste.
4. **Research-Einzelfehler-Crash**: Einzelne fehlende Quelle (M04 404) failte den ganzen Research-Task.
Behoben: `_safe_get` kapselt Einzelquellen-Fehler, Task bleibt SUCCEEDED mit Teil-Ergebnissen.
5. **Monitoring-FAIL-CLOSED (21.08.2026)**: `monitoring`-Task nutzte `_safe_get`, was M15-`UNREACHABLE`
zu `None` kapselte → Task blieb SUCCEEDED trotz ausgefallenem M15. Fix: `_monitoring` ruft M15 **direkt**
auf, damit ein M15-Ausfall als `ClientError` weiterfaellt und der Task **FAILED** wird. Verifiziert:
M15 down → monitoring FAILED, M15 up → SUCCEEDED.
## Compose / Betrieb
- Netzwerk `trading-modules`, DB-Host `Modul-01-PostgreSQL` (Modul-01), Port 55016 nur intern (`expose`),
`restart: unless-stopped`. Kein öffentliches Port-Mapping.
- **Kein Teil des kritischen Trading-Pfads:** kein M09-, kein M15-Control-Zugriff.
---
## Geändert
```
Geändert von: Rain Ocampo
Datum: 21.08.2026
Grund: Modul-16-Paperclip implementiert + E2E verifiziert (Backtest/Optimization/Monitoring/Research/Strategy-Success, Safety 422, ProposalGate, Ausfall-toleranz, Idempotenz). Monitoring-FAIL-CLOSED-Fix: M15-Ausfall -> monitoring-Task FAILED statt SUCCEEDED. Ausfall-Toleranz-E2E (M12/M13/M15 down -> FAILED, healthy) gruen. Image neu gebaut (Fix persistent). FREIGEGEBEN. Modul-17 NICHT begonnen.
```