Modul-17: FREIGEGEBEN (21.08.2026) - Hermes-Agent Worker unter Paperclip, Tool-Whitelist strikt, 4 Audit-Tabellen, E2E 15/15 gruen - Rain Ocampo
This commit is contained in:
parent
1009ed20b1
commit
1d11e56ed4
1 changed files with 158 additions and 0 deletions
158
notes/trading/system-docs/modul-17-hermes-agent.md
Normal file
158
notes/trading/system-docs/modul-17-hermes-agent.md
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
# Modul-17: Hermes-Agent (autonomer Research-/Analyse-Worker unter Paperclip)
|
||||
|
||||
**Status: FREIGEGEBEN (21.08.2026)** · Container `Modul-17-Hermes-Agent` · Image `trading-modules-modul17-hermes-agent:latest`
|
||||
|
||||
> ⚠️ **Keine Namenskollision** mit fremden Containern. Hermes läuft im `trading-modules`-Netz
|
||||
> als `Modul-17-Hermes-Agent`. Fremdsysteme (`paperclip-dn8l-paperclip-1` u.ä., Coolify) nie anfassen.
|
||||
|
||||
## Zweck & Rolle
|
||||
Hermes ist die **autonome Research-/Analyse-Ausführungsebene unter Paperclip (M16)**. Er nimmt
|
||||
von Paperclip delegierte Tasks (Research, längere Analysen, Strategie-Review, Analytics-Auswertung,
|
||||
Backtest-/Optimization-Auswertung, Systemdiagnose READ-ONLY) entgegen und führt sie **ausschließlich
|
||||
über die strikte Tool-Whitelist** aus. Das Ergebnis geht als `agent_run`/`result_refs` an Paperclip
|
||||
zurück.
|
||||
|
||||
**Kritische Grenze — Hermes ist NICHT Teil des kritischen Trading-Pfads und darf NICHT:**
|
||||
- direkt auf Modul-09 Execution zugreifen, Orders erzeugen
|
||||
- Risk-/Portfolio-Regeln (M07/M08) überschreiben
|
||||
- M15-Control verändern (PAUSE/HALT/RESUME)
|
||||
- Strategien automatisch produktiv aktivieren
|
||||
- Optimierungsparameter automatisch deployen
|
||||
- Broker-Credentials erhalten
|
||||
|
||||
**Paperclip bleibt Orchestrator.** Hermes arbeitet NUR über die erlaubten Tools/APIs. Unbekannte
|
||||
oder unerlaubte Tools → **REJECTED**.
|
||||
|
||||
## Architektur / Datenfluss
|
||||
```
|
||||
Paperclip (M16) ── Task ──▶ Modul-17-Hermes-Agent (Port 55017, NUR intern/expose)
|
||||
│
|
||||
├── app/core/tool_whitelist.py strikte Tool-Whitelist (Herzstück)
|
||||
├── app/core/executor.py Hermes-Executor (Tool-Ausführung + Audit + Idempotenz)
|
||||
├── app/storage/storage.py Persistenz hermes_task/run/tool_call/result
|
||||
├── app/clients/clients.py Read-Clients + 2 begrenzte Write-Clients
|
||||
└── app/api/main.py REST-API (/health, /tasks, /runs, /tools)
|
||||
│
|
||||
├── READ (tolerant): Modul-03 Market Data, M04 Regime, M05 Strategy,
|
||||
│ M06 Ranking, M10 Journal, M11 Analytics, M15 Monitoring (GET)
|
||||
└── WRITE (begrenzt): Modul-12 POST /backtest, Modul-13 POST /optimization,
|
||||
Proposal/Research-Ergebnis speichern
|
||||
```
|
||||
|
||||
**API (Docker-intern, Port 55017):**
|
||||
- `GET /health` — Liveness (`{"status":"ok","service":"hermes","version":"0.1.0"}`)
|
||||
- `GET /health/ready` — Readiness (DB-Schema vorhanden) → 200 `{"status":"ready"}`
|
||||
- `POST /tasks` — Task einreichen (`task_type` + `payload.tools[]`); Whitelist-Prüfung
|
||||
- `GET /tasks` / `GET /tasks/{id}` — Tasks + Status/result_refs
|
||||
- `GET /runs/{id}` — einzelner Run mit result_refs
|
||||
- `GET /tools` — Tool-Whitelist auflisten
|
||||
|
||||
Kein RabbitMQ: synchroner request/response. Port 55017 **nur intern** (`expose:`), kein öffentliches Mapping.
|
||||
|
||||
## Tool-Whitelist (strikt, `app/core/tool_whitelist.py`)
|
||||
Jeder Tool-Call wird gegen die Whitelist geprüft. Unbekannte/unerlaubte Tools → REJECTED.
|
||||
|
||||
| Tool (namespace.method) | Zielmodul | Richtung | Status |
|
||||
|-------------------------|-----------|----------|--------|
|
||||
| `market_data.prices` | Modul-03 | READ | erlaubt |
|
||||
| `regime.analyze` | Modul-04 | READ | erlaubt |
|
||||
| `strategy.list` | Modul-05 | READ | erlaubt |
|
||||
| `ranking.current` | Modul-06 | READ | erlaubt |
|
||||
| `journal.entries` | Modul-10 | READ | erlaubt |
|
||||
| `analytics.portfolio` | Modul-11 | READ | erlaubt |
|
||||
| `monitoring.status` | Modul-15 | READ | erlaubt (NUR GET) |
|
||||
| `backtest.start` | Modul-12 | WRITE | erlaubt (CONTROLLED) |
|
||||
| `optimization.start` | Modul-13 | WRITE | erlaubt (CONTROLLED) |
|
||||
| `execution.place_order` | Modul-09 | WRITE | **REJECTED** |
|
||||
| `risk.set_limits` | Modul-07 | WRITE | **REJECTED** |
|
||||
| `portfolio.rebalance` | Modul-08 | WRITE | **REJECTED** |
|
||||
| `monitoring.pause/halt/resume` | Modul-15 | Control | **REJECTED** |
|
||||
| alles andere | — | — | **REJECTED** |
|
||||
|
||||
**M09/M07/M08 sind strukturell NICHT in den Clients implementiert** (`clients.py`) — ein Verstoß
|
||||
ist damit zur Laufzeit unmöglich, zusätzlich zur Whitelist-Barriere. M15-Control-Writes fehlen ebenfalls.
|
||||
|
||||
## Persistenz / Audit (Migration `migrations/001_hermes.sql`)
|
||||
Vier Tabellen in Modul-01-PostgreSQL (PostgreSQL-01), NICHT im kritischen Trading-Pfad:
|
||||
- `hermes_task` — Aufgaben (id, task_type, agent_role, status, payload, result_refs, error_code)
|
||||
- `hermes_run` — Ausführungen (run_id, task_id, status, result, result_refs, idempotent)
|
||||
- `hermes_tool_call` — **jeder Tool-Call nachvollziehbar**: task_id, tool, target_module, input_hash,
|
||||
status, timestamps, error_code
|
||||
- `hermes_result` — gespeicherte Ergebnisse/Reports
|
||||
|
||||
Jeder Tool-Call wird in `hermes_tool_call` auditier. Keine Secrets/Prompts mit Credentials in Logs.
|
||||
|
||||
## Sicherheit
|
||||
- **Keine öffentlichen Ports**: 55017 nur `expose` (Docker-intern), kein Host-Mapping (verifiziert: `docker port` leer).
|
||||
- **Internes Docker-Netzwerk** `trading-modules`.
|
||||
- **Kein Docker-Socket, kein Root-/Shell-Zugriff auf andere Container.**
|
||||
- **Secrets nur ENV/Secret** (`PG_PASSWORD` aus Compose `environment`); keine Secrets in Logs/API/DB.
|
||||
- **Keine Broker-Credentials** (kein Client, kein ENV dafür).
|
||||
- **Hermes-Ausfall beeinflusst M03–16 NICHT** (eigene DB-Tabellen, keine Kopplung an Trading-Core).
|
||||
- **M15-Ausfall → Monitoring-/Systemdiagnose-Task FAILED** (FAIL-CLOSED, nicht schönreden).
|
||||
|
||||
## Read-Clients (tolerant)
|
||||
Einzelne Quellen-Fehler (404/5xx/Timeout) werden als Teil-Ergebnis `{"_error": code}` gekapselt,
|
||||
der Gesamt-Task bleibt SUCCEEDED. Komplett unreachable (`UNREACHABLE`/`TIMEOUT`) → `None`.
|
||||
|
||||
**Ausnahme — Monitoring (FAIL-CLOSED):** Der `monitoring.status`-Call ruft M15 direkt auf. Ist M15
|
||||
nicht erreichbar → Task **FAILED** (kein "ok" bei ausgefallenem M15). Gleiches gilt für M12/M13:
|
||||
Ausfall → zugehöriger Backtest-/Optimization-Task **FAILED** (`UNREACHABLE`), Hermes bleibt healthy,
|
||||
Trading-Core unbeeinflusst.
|
||||
|
||||
## Idempotenz
|
||||
Jeder `task_id` erzeugt genau **einen** `hermes_run` (SUCCEEDED). Wiederholte Einreichung derselben
|
||||
Task-Payload → je Task genau 1 Run, kein Doppel-Run. `input_hash` in `hermes_tool_call` identifiziert
|
||||
identische Aufrufe.
|
||||
|
||||
## Tests & E2E-Verifikation (VPS, 21.08.2026)
|
||||
- **Unit-Tests** `tests/test_hermes.py`: 13/13 grün (Whitelist-Gates, Executor, Idempotenz, REJECTED).
|
||||
- **T1 Research-Task** → M03 → **SUCCEEDED**, result_refs mit `tool_output` + `report`.
|
||||
- **T2 Analytics-Task** → M11 → **SUCCEEDED**.
|
||||
- **T3 Backtest-Task** → M12 `POST /backtest` (BT_PULL, stock, 450 Candles) → **SUCCEEDED**.
|
||||
- **T4 Optimization-Task** → M13 `POST /optimization` (BT_PULL) → **SUCCEEDED**.
|
||||
- **T5 Monitoring READ-ONLY** → M15 `GET /status` → **SUCCEEDED**.
|
||||
- **T6 Execution-Tool** (`execution.place_order`) → **REJECTED** (`TOOL_NOT_ALLOWED`).
|
||||
- **T7 M15-Control-Tool** (`monitoring.pause`) → **REJECTED**.
|
||||
- **T8 Risk/Portfolio-Write** (`risk.set_limits`) → **REJECTED**.
|
||||
- **T9 ungültiges Tool** (`bogus.tool`) → **REJECTED**.
|
||||
- **T10 Ausfall**: M15 down → Monitoring FAILED; M12 down → Backtest FAILED; M13 down → Optimization
|
||||
FAILED (jeweils `UNREACHABLE`). Erholung → SUCCEEDED. Hermes bleibt healthy.
|
||||
- **T11 Idempotenz**: je Task genau 1 Run.
|
||||
- **T12 Restart/Reconnect**: nach `docker restart` ready, Daten persistent.
|
||||
- **T13 keine Secrets**: `docker logs` frei von password/secret/token/api_key.
|
||||
- **T14 Port nur intern**: `ss -ltn` kein Host-Listening auf 55017.
|
||||
- **T15 Hermes-Ausfall**: bei gestopptem Hermes sind M03/04/05/11/12/13/15/16 alle healthy
|
||||
(`/health` ok) → Trading-Core unbeeinflusst.
|
||||
|
||||
**E2E-Gesamt: 15/15 grün.**
|
||||
|
||||
## Bugs / Fixes (modul-17, dokumentiert)
|
||||
1. **Methodenname `_last_succeeded_run` vs `_run_last_succeeded_run`**: Executor referenzierte
|
||||
`_last_succeeded_run`, Methode hieß anders → benannt in `_last_succeeded_run` (Idempotenz-Check).
|
||||
2. **Test-Pfad**: `sys.path.insert` musste auf `../app` zeigen (nicht Modul-Root) für `app.*`-Importe.
|
||||
3. **M12 422 "Unzureichende Daten"**: EURUSD/demo lieferte nur 1 Candlestick (< 220 nötig). Fix im
|
||||
E2E: Symbol **`BT_PULL`** (asset_class `stock`, 450 Candles) für Backtest/Optimization.
|
||||
4. **M13 erfordert Pflichtfelder `start_date`+`end_date`** (anders als M12): Optimization-Payload
|
||||
musste beide Datumsfelder enthalten, sonst HTTP 422.
|
||||
5. **E2E-Skript**: BusyBox wget im Alpine-Worker kennt kein `--post-file` → JSON per `docker cp`
|
||||
+ `--post-data="$(cat …)"`. Funktionsname `mkpayload`→`mk_put` (aufrufender Code nutzte `mk_put`).
|
||||
6. **SSH-Quoting**: verschachtelte `$(…)` mit doppelten Anführungszeichen im Inline-Python kappen
|
||||
das Kommando → Inline-Echo entfernt, reiner grep-Check.
|
||||
|
||||
## Compose / Betrieb
|
||||
- Netz `trading-modules`, DB-Host `Modul-01-PostgreSQL` (Modul-01), Port 55017 nur intern (`expose`),
|
||||
`restart: unless-stopped`. Kein öffentliches Port-Mapping.
|
||||
- **Kein Teil des kritischen Trading-Pfads:** kein M09-, kein M07/M08-, kein M15-Control-Zugriff.
|
||||
- Paperclip (M16) bleibt Orchestrator; Hermes ist seine Worker-Ausführungsebene.
|
||||
|
||||
---
|
||||
## Geändert
|
||||
```
|
||||
Geändert von: Rain Ocampo
|
||||
Datum: 21.08.2026
|
||||
Grund: Modul-17-Hermes-Agent implementiert + E2E verifiziert (Research/Analytics/Backtest/Optimization/Monitoring-Success,
|
||||
Safety REJECTED 6/7/8/9, M12/M13/M15-Ausfall -> FAILED (FAIL-CLOSED), Idempotenz, Restart, keine Secrets, Port intern,
|
||||
Hermes-Ausfall beeinflusst M03-16 nicht). Tool-Whitelist strikt, 4 Audit-Tabellen (hermes_task/run/tool_call/result),
|
||||
6 API-Endpunkte. Unit 13/13, E2E 15/15 gruen. FREIGEGEBEN. Keine weiteren Module begonnen.
|
||||
```
|
||||
Loading…
Reference in a new issue