15 knowledge objects (log, note, index, module, history) migrated to C3 knowledge schema v1. Bodies unchanged, metadata preserved.
14 KiB
| id | type | role | representation | state | knowledge_schema |
|---|---|---|---|---|---|
| object/3063a680-c35e-a62c-91b4-f382027930a5 | arch | module | canonical | current | 1 |
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 alsModul-17-Hermes-Agent. Fremdsysteme (paperclip-dn8l-paperclip-1u.ä., 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üfungGET /tasks/GET /tasks/{id}— Tasks + Status/result_refsGET /runs/{id}— einzelner Run mit result_refsGET /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_codehermes_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 portleer). - Internes Docker-Netzwerk
trading-modules. - Kein Docker-Socket, kein Root-/Shell-Zugriff auf andere Container.
- Secrets nur ENV/Secret (
PG_PASSWORDaus Composeenvironment); 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 restartready, Daten persistent. - T13 keine Secrets:
docker logsfrei von password/secret/token/api_key. - T14 Port nur intern:
ss -ltnkein Host-Listening auf 55017. - T15 Hermes-Ausfall: bei gestopptem Hermes sind M03/04/05/11/12/13/15/16 alle healthy
(
/healthok) → Trading-Core unbeeinflusst.
E2E-Gesamt: 15/15 grün.
Bugs / Fixes (modul-17, dokumentiert)
- Methodenname
_last_succeeded_runvs_run_last_succeeded_run: Executor referenzierte_last_succeeded_run, Methode hieß anders → benannt in_last_succeeded_run(Idempotenz-Check). - Test-Pfad:
sys.path.insertmusste auf../appzeigen (nicht Modul-Root) fürapp.*-Importe. - M12 422 "Unzureichende Daten": EURUSD/demo lieferte nur 1 Candlestick (< 220 nötig). Fix im
E2E: Symbol
BT_PULL(asset_classstock, 450 Candles) für Backtest/Optimization. - M13 erfordert Pflichtfelder
start_date+end_date(anders als M12): Optimization-Payload musste beide Datumsfelder enthalten, sonst HTTP 422. - E2E-Skript: BusyBox wget im Alpine-Worker kennt kein
--post-file→ JSON perdocker cp--post-data="$(cat …)". Funktionsnamemkpayload→mk_put(aufrufender Code nutztemk_put).
- 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-HostModul-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.
LLM-Schicht (KI-/Analyse-Erweiterung, M16 additiv llm=true)
Hermes kann Tasks zusätzlich über eine LLM-Schicht (Tool-Calling-Loop) ausführen. Die LLM-Schicht
ist additiv (M16 setzt llm=true im Task-Payload); ohne Flag bleibt das deterministische M17-Verhalten
unverändert. Die Tool-Whitelist und Task-Gates bleiben deterministisch außerhalb des LLM — das LLM
führt nie selbst Tools aus, sondern liefert requested_tools[], die durch die bestehende Whitelist geprüft werden.
2-Stufen-Modellkonzept (final, 21.08.2026)
| Stufe | Modell | Provider | Verwendung |
|---|---|---|---|
| STANDARD | deepseek-v4-flash:cloud |
ollama_cloud |
Normale Research-/Analyse-Tasks, voller Tool-Calling-Loop, Structured JSON, vollständiger Audit |
| LOKAL | llama3.1:8b |
ollama_local |
NUR expliziter Privacy-/Offline-Modus (llm_mode=local), Analyse-only (kein Tool-Loop), CPU-/Timeout-Schutz |
Regeln (hart):
- KEIN automatischer Cloud→Local-Fallback. Cloud-Ausfall → Task FAILED (
LLM_UNAVAILABLE), kein minutenlanger Wechsel auf das lokale Modell. - LOKAL nur explizit über
llm_mode=localim Task-Payload (extra.llm_mode). Nicht als Standard verwenden. - LOKAL = Analyse-only: genau EIN Analyse-Call, keine Tool-Anfragen.
llama3.1:8bauf CPU versteht den Tool-Calling-Loop nicht zuverlässig (LLM_MAX_ROUNDS) und ist langsam (~60-90s/Call). - FAIL-CLOSED: LLM down/Timeout/invalid JSON/Schema/Max-Runden → Task FAILED, kein Trading-Core-Effekt.
- Safety unverändert: kein
execution.place_order, keinrisk.set_limits, keinportfolio.rebalance, kein M15 pause/halt/resume, Proposal maximal DRAFT/PROPOSED.
LLM-Audit (hermes_llm_run, Migration 002_hermes_llm.sql)
Fünfte Audit-Tabelle. Je LLM-Run: provider, model, config_version, input_context_hash, requested_tools, executed_tools, rejected_tools, tokens_in/out, latency_ms, structured_result, status, error_code. Vollständig AUCH im FAILED-Pfad (Timeout/Invalid-JSON/Max-Runden persistieren Metriken). Keine vollständigen sensiblen Prompts, keine Secrets.
LLM-ENV (Compose M17-Block)
LLM_PROVIDER=ollama_cloud(Standard = Cloud)LLM_MODEL_CLOUD=deepseek-v4-flash:cloudLLM_MODEL_LOCAL=llama3.1:8bLLM_BASE_URL=http://10.0.4.2:11434(Ollama-Netz)LLM_TIMEOUT_SECONDS=300
LLM-E2E-Verifikation (final, 21.08.2026)
- Cloud-Standard (
llm=true, standard): SUCCEEDED (deepseek-v4-flash:cloud, 4 requested/2 executed, Audit vollständig). - Local explizit (
llm=true,llm_mode=local): SUCCEEDED (llama3.1:8b, Analyse-only, Audit vollständig). - Cloud-down (isoliert, ungültige Base-URL): FAILED (
LLM_UNAVAILABLE), kein Auto-Fallback (Provider bleibt Cloud). - llm=false: SUCCEEDED (deterministisches M17, kein LLM-Run).
- Audit vollständig: requested/executed/rejected/tokens/latency/context_hash persistiert, auch im FAILED-Pfad.
- M03–M15 unbeeinflusst: alle Module healthy während der LLM-Tests.
- Unit-Tests
tests/test_llm.py: 20/20 grün (inkl. 4 REJECTED-Gate-Tests: place_order, monitoring_pause, risk_set_limits, unknown_tool).
LLM-Bugs / Fixes
- Audit-Metriken leer (Cloud-E2E):
finish_llm_runschrieb nur status/result/error → um requested/executed/rejected_tools, tokens_in/out, latency_ms, structured_result, provider/model/config_version erweitert. - FAILED-Pfad verlor Audit-Metriken: Max-Runden-Exception trug keine Metriken → Exception trägt jetzt
Audit-Dict,
execute()persistiert sie auch bei LLM_MAX_ROUNDS/Timeout/Invalid-JSON. - NOT NULL-Constraint:
requested_toolsetc.NOT NULL DEFAULT '[]'→finish_llm_runkoerziertNone→[]. - Structured Output: Cloud-Modell ignoriert Schema ohne
format-Constraint →format-Constraint im Payload. - ToolCall-Format: ToolCall-Modell erwartet
name/args(nichttool/params) → Skript/Executor angepasst. - Lokales Modell ungeeignet für Tool-Loop:
llama3.1:8bauf CPU → LLM_MAX_ROUNDS (fragt endlos Tools an). Fix: lokaler Modus = Analyse-only (kein Tool-Loop), wie vom User empfohlen.
Geändert
Geändert von: Rain Ocampo
Datum: 21.08.2026
Grund: LLM-Schicht (2-Stufen-Konzept) ergaenzt: STANDARD=deepseek-v4-flash:cloud (voller Tool-Loop),
LOKAL=llama3.1:8b (nur explizit llm_mode=local, Analyse-only), KEIN Auto-Fallback, Cloud-down->FAILED
(LLM_UNAVAILABLE), llm=false deterministisch, Audit hermes_llm_run vollstaendig (auch FAILED-Pfad),
Unit 20/20, E2E 5/5 Szenarien gruen, M03-M15 unbeeinflusst. FREIGEGEBEN.
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.