trading-system-docs/notes/trading/system-docs/modul-17-hermes-agent.md

9.6 KiB
Raw Blame History

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_calljeder 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 M0316 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 /statusSUCCEEDED.
  • 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 mkpayloadmk_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.