Zurück zur Bibliothek
A2A

A2A auf Hermes Agent bereitstellen: Ein Docker-Gateway-Referenzstack

Zuletzt aktualisiert: 2026年7月23日

A2A in Produktion zu betreiben bedeutet, zwei Protokolle zu überbrücken, die nie dafür gedacht waren, miteinander zu kommunizieren — und das hinter einem Gateway, das Authentifizierung, Tenant-Isolation und Streaming handhabt, ohne Tokens zu verlieren. Dieser Referenz-Stack löst die drei Probleme, die A2A-Deployments blockieren: Protokollübersetzung (JSON-RPC 2.0 zu OpenAI-kompatiblen Chat Completions), Streaming-Aussöhnung (A2A-Artifact-SSE zu Hermes-Run-Event-SSE) und Multi-Tenant-Sicherheit (PostgreSQL Row-Level Security auf jede Query). Wenn Sie Agenten auf verschiedenen Frameworks benötigen, die einander Arbeit delegieren, ist das Muster hier der Deploy-Pfad.

Kernpunkte

  • 3 Schichten, 1 Docker-Compose-Stack — SilvaEngine Gateway übernimmt Transport und Authentifizierung, A2A Daemon Engine übernimmt die Protokolllogik, HermesAgentHandler bindet an die OpenAI-kompatible API von Hermes Agent an. Das docker-a2a-hermes-agent-gateway-Repo paketiert alle drei.
  • 5 Protokolloberflächen auf einem Port — JSON-RPC 2.0, GraphQL, SSE-Stream, SSE-Push und Agent-Card-Discovery, alle hinter einem einzigen Gateway auf Port 8765 mit JWT- oder AWS-Cognito-Authentifizierung.
  • PostgreSQL Row-Level Security erzwingt Tenant-Isolation — der zusammengesetzte Schlüssel partition_key = "{endpoint_id}#{Part-Id}" wird auf Datenbankebene über RLS-Richtlinien auf allen vier A2A-Tabellen erzwungen, nicht nur im Anwendungscode.
  • A2A SDK v2s Single-Message-Einschränkung prägt die Streaming-Architektur — die Bridge emittiert Token-Chunks in Echtzeit an SSE und eine einzige akkumulierte Message an das SDK-EventQueue nach Stream-Abschluss, wodurch InvalidAgentResponseError vermieden wird.
  • 15 E2E-Test-Checks über 5 Skripte — von Non-Streaming-Rauchtests bis zu vollständigen SSE-Streaming-Pipelines mit HTTP-Fallback-Verifikation, alle ausführbar mit pip install requests.

Das Agent2Agent Protocol (A2A) definiert, wie KI-Agenten einander über JSON-RPC 2.0 entdecken, delegieren und Aufgaben streamen. Hermes Agent (von Nous Research) stellt einen OpenAI-kompatiblen API Server unter /v1/chat/completions sowie eine run-basierte SSE-Streaming-Schnittstelle unter /v1/runs und /v1/runs/{id}/events bereit. Beide sprechen nicht dieselbe Sprache. A2A sendet message/send mit strukturierten Parts; Hermes empfängt Chat-Completion-Payloads. A2A streamt Aufgaben-Artefakte via SSE; Hermes streamt Token-Deltas via Run-Events.

Das Repository docker-a2a-hermes-agent-gateway überbrückt diese Lücke in einem einzigen Container-Image und docker compose-Stack. Es betreibt das SilvaEngine Gateway mit ausschließlich dem registrierten Modul a2a_daemon_engine, stellt die vollständige A2A-Protokolloberfläche bereit und bindet A2A-Aufgaben an eine Hermes-Agent-API-Server-Instanz über HTTP + SSE an. Der Zustand persistiert in einem mitgelieferten PostgreSQL-Backend mit Row-Level Security zur Tenant-Isolation.

Dieser Artikel ordnet die Drei-Schichten-Architektur zu, den Anfrage-Lebenszyklus vom A2A-Client zu Hermes und zurück, die Konfigurations- und Bereitstellungsmuster sowie die betrieblichen Aspekte für den Produktivbetrieb von A2A auf Hermes Agent. Es handelt sich um eine Referenz-Deployment-Beschreibung — das allgemeine Muster (gateway-vermittelte A2A-Bridge zu einem beliebigen Agent-Framework) ist der Gegenstand; der Docker-Stack ist die ausgearbeitete Implementierung.

Die Drei-Schichten-Architektur

Der Stack trennt die Belange in drei Schichten, die jeweils einer eigenständigen Komponente gehören:

A2A auf Hermes Agent — Drei-Schichten-Stack docker-a2a-hermes-agent-gateway Referenz-Deployment 1 A2A-Client Beliebiger Agent oder Anwendung mit JSON-RPC 2.0 message/send · tasks/get · SSE 2 SilvaEngine Gateway Transport · Auth · Routing · SSE-Client-Lebenszyklus JWT / Cognito-Auth Part-Id Tenant-Routing SSE-Client-Registry Ratenbegrenzung 3 A2A Daemon Engine Protokolllogik · Task-Zustandsmaschine · Handler-Dispatch Agent-Card-Auslieferung Task-Lebenszyklus HermesAgentHandler Dual-Path-Streaming Hermes Agent API Server OpenAI-kompatibel · SSE-Runs /v1/chat/completions /v1/runs + /events PostgreSQL Persistenz · RLS-Tenant-Isolation a2a_agents / tasks messages / settings Gateway ist der einzige Always-On-Service — Hermes und PostgreSQL sind profilgesteuerte Geschwister — ideabosque.com/library

Das Gateway ist der einzige Always-On-Service. Sowohl Hermes als auch PostgreSQL sind profilgesteuerte Geschwister — binden Sie sie für einen in sich geschlossenen Stack ein, oder schalten Sie die Profile ab und verweisen Sie mit HERMES_API_URL und PG_HOST auf externe Instanzen. Dies ist für die Produktion relevant: Sie können das Gateway in Ihrer VPC betreiben und auf einen verwalteten Postgres (RDS, Cloud SQL) sowie eine Hermes-Instanz verweisen, die auf einem GPU-Knoten anderswo läuft.

Schicht 1: SilvaEngine Gateway — Transport und Auth

Das SilvaEngine Gateway ist ein FastAPI-Gateway für authentifizierten, prozessinternen Zugriff auf installierte Module. Es stellt Modul-GraphQL- und REST-Routen über ein konfigurierbares YAML-Routenmanifest bereit — das Hinzufügen eines neuen Moduls erfordert ausschließlich Manifeständerungen, kein Gateway-Python-Code. In diesem Stack ist ausschließlich der A2A Daemon Engine registriert.

Das Gateway ist verantwortlich für:

  • Authentifizierung — lokales JWT (HS256) oder AWS Cognito (RS256 + JWKS), ausgewählt durch GATEWAY_AUTH_PROVIDER
  • Routing — YAML-Manifest ordnet URL-Pfade den Modul-Dispatch-Funktionen zu
  • SSE-Client-Lebenszyklus — der sse_manager wird pro Modul aufgelöst und verwaltet langlebige Client-Verbindungen
  • Ratenbegrenzung — pro-IP In-Memory-Ratenbegrenzung (GATEWAY_RATE_LIMIT Anfragen pro GATEWAY_RATE_WINDOW Sekunden)
  • Thread-Pool-Dispatch — synchrone Modul-Dispatch-Funktionen laufen in einem konfigurierbaren Thread-Pool (GATEWAY_DISPATCH_WORKERS, Standard 32 im Docker-Image)

Das Gateway erzeugt partition_key = "{endpoint_id}#{Part-Id}" aus dem URL-Pfadsegment und dem Part-Id-Anfrage-Header. Jede Tenant-bezogene Anfrage benötigt diesen Header. Der Agent-Card-Endpunkt unter /{ep}/.well-known/agent-card.json ist gemäß A2A-Spezifikation öffentlich (keine Auth), erfordert jedoch weiterhin Part-Id, da die Karte pro Partition aufgelöst wird.

Schicht 2: A2A Daemon Engine — Protokolllogik

Der a2a_daemon_engine ist kein eigenständiger Service. Er wird als registriertes Gateway-Modul über deploy() in main.py geladen, das drei Gateway-zugewandte Einstiegspunkte deklariert:

Einstiegspunkt Gateway-Route Methode Zweck
a2a_core_graphql POST /{ep}/a2a_core_graphql POST GraphQL-CRUD für Agents, Tasks, Messages, Settings
a2a POST /{ep}/a2a POST A2A-JSON-RPC-Protokoll (message/send, tasks/get, tasks/cancel, tasks/list)
sse_message POST /{ep}/a2a_sse POST A2A-JSON-RPC-Message + Push an SSE-Clients

Das Gateway stellt zusätzlich GET /{ep}/a2a_sse für den SSE-Stream bereit. Der Daemon lauscht im Produktivbetrieb nicht auf einem eigenen Port und betreibt keinen eigenen HTTP-Server. Jeglicher Transport, Auth und SSE-Client-Lebenszyklus liegt beim Gateway.

Der Daemon stellt bereit:

  • A2A SDK v1.0 — JSON-RPC über HTTP, aufgebaut auf dem offiziellen A2A-SDK-Server-Muster
  • Öffentliche Agent Card unter /.well-known/agent-card.json mit ETag- und Last-Modified-Unterstützung
  • Task-Zustandsmaschinesubmittedworkinginput-required | completed | failed | canceled
  • Dual-Backend-Persistenz — DynamoDB (PynamoDB) oder PostgreSQL (SQLAlchemy + Alembic). Das Docker-Image erzwingt PostgreSQL.
  • Multi-Tenant-Isolation — zusammengesetzte Partitionsschlüssel ({endpoint_id}#{part_id}) mit PostgreSQL Row-Level Security
  • Austauschbare LLM-Handler — pro-Agent module_name / class_name-Auswahl im Agenten-Verzeichnis

Schicht 3: HermesAgentHandler — die Bridge

Der Hermes-Bridge-Handler (a2a_daemon_engine/handlers/a2a_hermes_handler.py) ist der einzige Framework-spezifische Code. Er implementiert eine ask_model()-Schnittstelle: A2A-Message-Parts und Kontext entgegennehmen, den Hermes-Agent-API-Server aufrufen, die Antwort zurück in A2A-Message-Parts konvertieren und beim Streaming Token-Deltas an den SSE-Kanal weiterleiten.

Der Handler unterstützt zwei Ausführungsmodi:

Non-Streaming ordnet POST /v1/chat/completions auf dem Hermes-API-Server zu — dem OpenAI-kompatiblen Endpunkt. Die Anfrage trägt konvertierte A2A-Message-Parts als Chat-Completion-Payload. Hermes verarbeitet die Anfrage und gibt eine einzelne Antwort zurück. Der Handler konvertiert die Antwort in eine A2A-Message mit ROLE_AGENT und emittiert sie an das SDK-EventQueue. Der Client erhält eine einzelne JSON-RPC-Antwort mit dem vollständigen Agent-Text.

Streaming ordnet POST /v1/runs zu, um einen Run zu erzeugen, und öffnet dann eine SSE-Verbindung zu GET /v1/runs/{id}/events. Hermes streamt Events, sobald sie eintreten: Token-Deltas (message.delta), Reasoning-Metadaten (reasoning.available), Tool-Call-/Ergebnis-Benachrichtigungen, Approval-Anfragen (approval.required) und Lebenszyklus-Events (run.created, run.completed, run.failed). Der Handler betreibt eine Drain-Schleife in einem Hintergrund-Thread. Jedes message.delta-Event wird an den SSE-Manager des Gateways weitergeleitet und in Echtzeit an verbundene Clients geliefert. Wenn run.completed eintrifft, wird der akkumulierte Text als einzelne A2A-Message an das SDK-EventQueue emittiert.

Der Anfrage-Lebenszyklus

Ein einzelnes message/send mit stream=true durchläuft acht Schritte:

1. Client         POST /{ep}/a2a  {jsonrpc, method:"message/send", params}
                  Headers: Authorization: Bearer *** Part-Id: 
2. Gateway        Auth (lokales JWT / Cognito) → Routenabgleich aus routes.yaml
                  → partition_key = "{ep}#{Part-Id}"
3. a2a_daemon     dispatch_a2a → A2ADaemonExecutor
                  → resolve_agent(): Agent-Metadaten (DB) > Setting-Dict > Config (Env)
4. Handler        HermesAgentHandler (A2A_AI_AGENT_MODULE / _CLASS)
5. Hermes         POST {HERMES_API_URL}/v1/runs   (Bearer HERMES_API_KEY)
                  GET  {HERMES_API_URL}/v1/runs/{id}/events   (SSE)
6. Broadcast      Token-Chunks → Subscriber auf GET /{ep}/a2a_sse
7. Persist        Task + Messages in PostgreSQL geschrieben (a2a_*-Tabellen, RLS-bezogen)
8. Response       Akkumulierte Antwort auch im HTTP-JSON-RPC-Ergebnis zurückgegeben

Schritt 8 ist bedeutsam: Auch beim Streaming trägt die HTTP-Antwort die vollständige Antwort. Ein Client, der SSE-Frames verpasst, kann auf die HTTP-Antwort zurückfallen. Die E2E-Test-Suite (test_hermes_sse_live.py) verifiziert diesen Fallback explizit in Schritt 06.

Priorität der Agenten-Auflösung

Die Konfigurationsauflösung folgt einer Prioritätskette: Agent-Metadaten (DB) → Setting-Dict → Config-Standards (Env-Vars). Pro-Agent-Overrides gewinnen über globale Standards. Zwei Agenten können auf unterschiedliche Frameworks verweisen — einer auf Hermes für reasoning-lastige Aufgaben, ein anderer auf einen anderen Handler für Workflow-Orchestrierungs-Aufgaben — und die A2A-Protokolloberfläche sieht für den aufrufenden Agenten identisch aus.

Für einen Hermes-gestützten Agenten sehen die in der a2a_agents-Tabelle gespeicherten Metadaten so aus:

{
  "module_name": "a2a_daemon_engine.handlers.a2a_hermes_handler",
  "class_name": "HermesAgentHandler",
  "hermes_api_url": "http://127.0.0.1:8642",
  "hermes_api_key": "hermes-local-key",
  "hermes_model": "hermes-agent",
  "hermes_timeout": 300.0
}

Die Env-Var-Defaults (HERMES_API_URL, HERMES_API_KEY, HERMES_MODEL) ermöglichen der Bridge, Hermes ohne einen DB-Agent-Eintrag zu erreichen — die Funktion resolve_agent() in a2a_ai_agent_utility.py fällt auf Env-Vars zurück, wenn kein Agent-Eintrag existiert. Das bedeutet, Sie können den Stack starten und ein message/send senden, ohne einen Agenten zu registrieren, und es wird an Hermes mit den Env-Defaults geroutet.

A2A-Zustandsabbildung

Die Bridge ordnet Hermes-SSE-Events A2A-Task-Zuständen zu. Diese Tabelle ist das Herz der Bridge — jede Framework-Integration erzeugt eine äquivalente Tabelle:

Hermes-SSE-Event A2A-Task-Zustand Bridge-Aktion
run.created (run_id zurückgegeben) WORKING run_id für Cancel-Unterstützung registrieren
message.delta WORKING Token akkumulieren; pro-Chunk an SSE emittieren
reasoning.available WORKING Reasoning-Metadaten — keine Token-Emission
tool.call / tool.result WORKING Nur Tool-Ausführungs-Metadaten
approval.required INPUT_REQUIRED Approval-Chunk emittieren; pending_approval speichern
run.completed COMPLETED Stream-Event setzen; finalen Text akkumulieren
run.failed FAILED Fehler-Chunk emittieren; FAILED-Zustand setzen
POST /v1/runs/{id}/stop CANCELED Externer Cancel via tasks/cancel
POST /v1/runs/{id}/approval (setzt Run fort) Aufgelöst via operation="approval_response"

Das Dokument HERMES_INTEGRATION.md im a2a_daemon_engine-Repository dokumentiert das exakte Hermes-Event-Format, die Konfigurationsschlüssel und die End-to-End-Ablaufdetails.

Human-in-the-Loop-Approval über Agent-Grenzen hinweg

Hermes unterstützt Human-in-the-Loop-Approval-Gates — wenn ein Agent die Erlaubnis zur Ausführung einer sensiblen Aktion benötigt, pausiert er und emittiert eine Approval-Anfrage. Die Bridge übersetzt dies in den A2A-INPUT_REQUIRED-Zustand, der dem aufrufenden Agenten (oder menschlichen Operator) signalisiert, dass eine Eingabe erforderlich ist. Die Antwort kommt über POST /v1/runs/{id}/approval zurück, und der Run wird fortgesetzt.

Hier zeigt das Bridge-Muster seinen Wert. A2A definiert INPUT_REQUIRED als erstklassigen Task-Zustand. Hermes hat seinen eigenen Approval-Mechanismus. Die Bridge ordnet das eine dem anderen zu, und der aufrufende Agent — der selbst ein A2A-Client auf einem völlig anderen Framework sein kann — sieht einen standardmäßigen Protokoll-Zustandsübergang, kein Hermes-spezifisches Detail. Eine Agent-Delegationskette kann einen Schritt enthalten, der eine menschliche Freigabe erfordert (eine Kaufautorisierung, eine Datenzugiffs-Entscheidung, eine Angebot-Freigabe), und das A2A-Protokoll trägt dieses Gate transparent über Framework-Grenzen hinweg.

Die A2A-SDK-v2-Einschränkung und der Dual-Path-Fix

Das A2A SDK v2 (a2a-sdk==1.0.2) legt dem on_message_send-Pfad zwei Einschränkungen auf, die die Bridge-Implementierung geprägt haben:

  1. Nur eine einzelne Message. Das Emitieren mehrerer Message-Objekte an das SDK-EventQueue löst InvalidAgentResponseError: Multiple Message objects received. aus.
  2. Kein TaskStatusUpdateEvent. Status-Events lösen InvalidAgentResponseError: Received TaskStatusUpdateEvent in message mode. aus.

Eine naive Bridge würde pro Token-Delta eine Message emittieren — das natürliche Streaming-Muster. Das SDK lehnt dies ab. Es lehnt auch Status-Events (WORKING, COMPLETED) auf dem message/send-Pfad ab.

Der Fix ist ein Dual-Path-Ausgabekanal:

  • SSE (Gateway-verwaltet): Token-Chunks werden in Echtzeit an SSE gepusht. Verbundene Clients sehen die Streaming-Ausgabe, sobald sie entsteht. Status-Events (WORKING, COMPLETED, FAILED) gehen ebenfalls nur an SSE.
  • SDK-EventQueue: Nach Stream-Abschluss wird eine einzelne akkumulierte Message mit dem vollständigen Antworttext an das SDK-EventQueue emittiert. Dies ist, was die JSON-RPC-message/send-Antwort zurückgibt.

Der Client erhält Echtzeit-Streaming via SSE und eine saubere Single-Message-JSON-RPC-Antwort via SDK. Beide Kanäle funktionieren; keiner verletzt die SDK-Einschränkungen.

Protokolloberflächen auf einem Port

Das Gateway stellt fünf Protokolloberflächen auf einem einzelnen Port (Standard 8765) bereit:

Protokoll Route Auth Zweck
GraphQL POST /{ep}/a2a_core_graphql Ja A2A-Core-Queries/Mutationen (Agents, Tasks, Messages, Settings)
JSON-RPC 2.0 POST /{ep}/a2a Ja A2A-Protokoll: message/send, tasks/get, tasks/cancel, tasks/list
SSE (Stream) GET /{ep}/a2a_sse Ja Langlebiger pro-Partition A2A-Task-Event-Stream
SSE (Push) POST /{ep}/a2a_sse Ja JSON-RPC-Message + Push an verbundene SSE-Clients
Agent Card GET /{ep}/.well-known/agent-card.json Öffentlich A2A-Discovery-Dokument (Part-Id-Header weiterhin erforderlich)

Die Form der JSON-RPC-message/send-Parameter, die von den Test-Harnesses verwendet wird:

{
  "message": {
    "role": "ROLE_USER",
    "parts": [{ "text": "Say hello from A2A" }]
  },
  "metadata": {
    "operation": "task_execution",
    "agent_uuid": "a2a-hermes-agent",
    "stream": true,
    "task_data": { "task_id": "my-task-001", "task_type": "hermes_test" },
    "system_prompt": "You are a concise assistant.",
    "conversation_history": []
  }
}

Das Feld metadata.operation wählt den Ausführungspfad: task_execution für Agent-Runs mit Streaming-Unterstützung, message_response für Non-Streaming-Chat. Das agent_uuid zielt auf einen bestimmten Agenten im Verzeichnis. Das stream-Flag aktiviert SSE-Broadcasting. Die task_data.task_id ist eine vom Aufrufer gelieferte ID, die von tasks/get und tasks/cancel verwendet wird.

SSE ist pro-Partition, nicht pro-Task. Jede Aufgabe in {ep}#{Part-Id} sendet an alle Subscriber dieser Partition. Die Reihenfolge der Operationen ist wichtig: Verbinden Sie den SSE-Listener, bevor Sie die Message senden, sonst gehen frühe Token-Chunks verloren.

Persistenz und Multi-Tenancy

Das Docker-Image erzwingt db_backend=postgresql — DynamoDB wird nicht unterstützt. Der Daemon verwendet literale, ungepräfixte Tabellennamen:

Tabelle Enthält
a2a_agents Agent-Einträge + pro-Agent Handler-/Modell-Metadaten
a2a_tasks Task-Lebenszyklus + Status
a2a_messages Message-Züge pro Task
a2a_settings Pro-Partition Setting-Dicts

Da die Namen ungepräfixt sind, teilen Sie PG_DB nicht mit einem anderen Modul, das dieselben Namen verwendet.

Die Tenant-Isolation nutzt PostgreSQL Row-Level Security. Die Session-Variable app.tenant_id wird auf den partition_key der Anfrage ("{endpoint_id}#{Part-Id}") gesetzt, und RLS-Richtlinien begrenzen jede Query darauf. Tabellen und Richtlinien werden beim Gateway-Start automatisch erstellt, wenn initialize_tables=1. Das bedeutet, ein vergessener partition_key-Filter im Anwendungscode kann keine Cross-Tenant-Zeilen leaken — die Datenbank erzwingt die Grenze.

Die RLS-Implementierung liegt in a2a_daemon_engine/utils/rls.py (set_rls_context und create_rls_policies) sowie der Migration 0005_enable_rls_policies. Die Funktion set_rls_context führt pro-Anfrage ein SET app.tenant_id auf der Verbindung aus, und create_rls_policies aktiviert und erzwingt RLS mit einer tenant_isolation-Richtlinie auf allen vier A2A-Tabellen. RLS ist im DynamoDB-Modus inaktiv.

Deployment mit Docker Compose

Der Stack hat einen Always-On-Service und zwei optionale profilgesteuerte Geschwister:

Service Container-Name Always-On? Profil Zweck
a2a-gateway a2a-hermes-gateway Ja SilvaEngine Gateway (nur A2A-Routen) + Hermes-Bridge
postgres a2a-postgres Optional postgres Mitgeliefertes PostgreSQL-Persistenz-Backend
hermes container-hermes Optional hermes Mitgelieferter Hermes Agent (OpenAI-kompatible API + Dashboard)

COMPOSE_PROFILES ist der einzelne Schalter für beide Geschwister:

Wert Gestartete Services
leer nur Gateway (externer Postgres + externes Hermes)
postgres Gateway + mitgelieferter Postgres
hermes Gateway + mitgeliefertes Hermes (externer Postgres)
postgres,hermes Gateway + mitgelieferter Postgres und Hermes (Standard)

Wenn ein Geschwister mitgeliefert wird, behalten Sie dessen Host-Referenz beim Service-Namen: PG_HOST=postgres und HERMES_API_URL=http://hermes:<API_SERVER_PORT>. Wenn ein Geschwister extern ist, verweisen Sie auf Ihre eigene Instanz (z. B. PG_HOST=host.docker.internal, HERMES_API_URL=http://host.docker.internal:8642).

Schnellstart

cp .env.example .env

# Ausfüllen: JWT_SECRET_KEY, ADMIN_PASSWORD, API_SERVER_KEY,
# HERMES_API_KEY (= API_SERVER_KEY), HERMES_MODEL_PROVIDER + Provider-Key, HERMES_MODEL

mkdir -p www/hermes www/projects

DOCKER_BUILDKIT=1 docker compose build
docker compose up -d           # COMPOSE_PROFILES=postgres,hermes ist der Standard

docker compose ps              # auf (healthy) warten
curl -f http://localhost:8765/health

pip install requests
python test_hermes_hello.py    # End-to-End-Rauchtest

Sowohl silvaengine_gateway als auch a2a_daemon_engine werden per pip aus Git in das Image installiert (kein Host-Source-Mount). Das Image ist generisch und vollständig Env-gesteuert — keine Secrets sind eingebettet. Die Module werden aus öffentlichen GitHub-Repos unter ideabosque via git+https geklont — keine Credentials oder SSH-Deploy-Key erforderlich.

Die .env-Inline-Kommentar-Falle

Der env_file-Parser von Docker Compose entfernt keine Inline-Kommentare. Eine Zeile wie:

HERMES_API_KEY=hermes-local-key   # Token für Hermes

setzt HERMES_API_KEY auf die literale Zeichenkette hermes-local-key # Token für Hermes (Kommentar inklusive), was die Authentifizierung stillschweigend bricht. Die Regeln: Setzen Sie nichts nach dem Wert auf eine KEY=value-Zeile. Notizen gehören auf eigene #-Kommentarzeilen oberhalb der Variablen.

Verifikation: 15 E2E-Checks über 5 Skripte

Der Stack wird mit eigenständigen Python-Test-Harnesses ausgeliefert (einzige Abhängigkeit: requests). Sie laden ./.env, lösen ein Gateway-JWT aus oder erzeugen eines, und kommunizieren mit dem laufenden Stack:

Skript Art Was es tut
test_hermes_hello.py Smoke Non-Streaming message/send, gibt die Antwort aus
test_hermes_hello_sse.py Smoke Ein Prompt über SSE zurückgestreamt
test_hermes_gateway_live.py E2E-Suite 9 Checks: Hermes-Health, Gateway-Health, Agent Card, GraphQL-Ping, message/send, tasks/get, tasks/list, tasks/cancel, Fehlerpfad
test_hermes_sse_live.py E2E-Suite 6 Checks: Health x2, SSE-Connect, Live-Token-Chunks, COMPLETED-Status, HTTP-Fallback
test_hermes_chatbot.py Interaktiv REPL gegen die A2A-Oberfläche mit Live-SSE-Streaming

Alle nicht-interaktiven Skripte geben PASS/FAIL pro Schritt aus und beenden sich bei Fehlern mit Non-Zero-Exit, sodass sie als CI-Gates funktionieren. Die Unit-Test-Suite (test_hermes_handler.py) führt 24 Tests mit gemocktem HTTP via httpx.MockTransport aus — keine Services erforderlich.

Betriebsmuster

Routenänderungen ohne Rebuilds

routes.yaml wird read-only in den Container bind-mounted. Bearbeiten Sie die Host-Datei und starten Sie den Gateway-Prozess neu — kein Rebuild nötig:

make restart

Nach einer Upstream-Änderung

Da silvaengine_gateway und a2a_daemon_engine zur Build-Zeit per pip aus Git installiert werden, erfordert eine Upstream-Änderung einen Rebuild mit --no-cache, damit die Git-Schicht das neueste @main erneut klont:

DOCKER_BUILDKIT=1 docker compose build --no-cache
docker compose up -d --force-recreate

Es gibt kein Version-Pinning — @main ist ein bewegliches Ziel. Pinnen Sie einen Tag oder Commit in requirements-modules.txt, wenn Sie Reproduzierbarkeit benötigen.

Skalierung über einen Worker hinaus

In-Memory-Task-Zustand, Ratenbegrenzungs-Zähler und die SSE-Client-Registry sind pro-Prozess. Mit GATEWAY_WORKERS > 1 wechseln Sie zu gemeinsamen Backends (GATEWAY_TASK_BACKEND=dynamodb, GATEWAY_RATE_LIMIT_BACKEND=dynamodb, plus region_name und aws_*-Credentials) und verwenden Sie Sticky-Sessions für SSE. Die Standardkonfiguration startet einen Uvicorn-Prozess.

Zu ändernde Security-Defaults

  • JWT_SECRET_KEY=change-me-in-production — ersetzen Sie durch openssl rand -hex 32
  • ADMIN_PASSWORD=change-me — ersetzen Sie durch ein echtes Passwort
  • POSTGRES_PASSWORD=silvaengine — ersetzen Sie durch ein echtes Passwort
  • GATEWAY_CORS_ORIGINS=* erlaubt beliebige Origins ohne Credentials. Setzen Sie eine explizite Liste, wenn Sie Cookies/Credentials benötigen.
  • Der mitgelieferte Hermes mountet den Host-Docker-Socket (/var/run/docker.sock). Das entspricht effektiv Root auf dem Host für alles innerhalb dieses Containers. Betreiben Sie das hermes-Profil nur auf einem Host, den Sie kontrollieren, und entfernen Sie den Mount, wenn der Agent keine Container starten muss.
  • Das Hermes-Dashboard ist standardmäßig auf Port 9119 mit leeren Basic-Auth-Credentials aktiviert. Setzen Sie HERMES_DASHBOARD_BASIC_AUTH_* oder binden Sie den Port an localhost, bevor Sie den Host freigeben.
  • RLS ist die Tenant-Grenze. Ein Aufrufer, der ein beliebiges Part-Id setzen kann, liest die Daten dieser Partition — behandeln Sie Part-Id als autorisierungsrelevanten Input in jedem Frontend, das Sie davor schalten.

Was dies ermöglicht

Der Drei-Schichten-Stack gibt Ihnen drei Fähigkeiten, die schwer von Grund auf zusammenzustellen sind:

1. A2A-Protokoll-Compliance ohne Hermes-Neuentwicklung. Jeder A2A-Client kann den Hermes-gestützten Agenten über seine Agent Card entdecken, Aufgaben via message/send senden, Antworten via SSE streamen und den Task-Lebenszyklus über Standard-A2A-Zustände verfolgen. Der Client weiß nicht, dass der Remote-Agent Hermes betreibt — er sieht einen A2A-Endpunkt mit einer JSON-RPC-Schnittstelle.

2. Multi-Tenant-Agent-Bereitstellung von einem Gateway. Der Part-Id-Header kombiniert mit PostgreSQL-RLS bedeutet, dass eine Gateway-Instanz mehrere Tenants mit harter Isolation auf Datenbankebene bedient. Jeder Tenant erhält ein eigenes Agenten-Verzeichnis, eine eigene Task-Historie und einen eigenen Message-Store — alles in denselben vier Tabellen, begrenzt durch partition_key.

3. Human-in-the-Loop-Approval über Agent-Grenzen hinweg. Hermes-Approval-Gates ordnen sich A2A-INPUT_REQUIRED-Zuständen zu. Eine Agent-Delegationskette kann einen Schritt enthalten, der eine menschliche Freigabe erfordert, und das A2A-Protokoll trägt diesen Zustandsübergang zurück zum ursprünglichen Agenten oder Operator — unabhängig davon, auf welchem Framework jeder Agent in der Kette läuft.

Die Referenzimplementierung unterstützt zudem AWS-Lambda-Dispatch für serverless A2A, experimentelles gRPC-Transport mit bidirektionalem Streaming und Dual-Backend-Persistenz (DynamoDB oder PostgreSQL). Das a2a_daemon_engine-Repository und sein Hermes-Integrationsleitfaden enthalten die vollständige Implementierung, die Konfigurationsreferenz und die Zustandsabbildungsdetails. Das SilvaEngine-Gateway-Repository dokumentiert das Routen-Manifest-System, die Authentifizierungs-Provider und die Modul-Auto-Initialisierung.

Weiterführende Lektüre


Ein mittelständischer Distributor benötigt Quoting-Agenten, die mit Katalog-Agenten sprechen, die mit Bestands-Agenten sprechen — jeder gestützt durch ein anderes Framework, jeder im Besitz eines anderen Teams. A2A gibt diesen Agenten ein gemeinsames Protokoll. Eine Bridge-Schicht lässt Hermes Agent teilnehmen, ohne dessen Innereien neu zu schreiben. Das docker-a2a-hermes-agent-gateway paketiert diese Bridge in ein einzelnes Container-Image mit PostgreSQL-Persistenz, RLS-Multi-Tenancy und 15 E2E-Test-Checks.

Einen abgegrenzten Build anfragen

Einwöchige Discovery. Sie erhalten ein System-Inventar, eine Workflow-Karte und einen festen Umfang — ob Sie mit uns bauen oder nicht.

Möchten Sie dies für Ihre Systeme gebaut?

Jedes Dokument hier stammt aus echter Produktionsarbeit. Wenn Sie ein Zielsystem und einen Workflow im Sinn haben, können wir in einer Woche einen Build umreißen.

Build mit festem Umfang anfragen

Einwöchiges Discovery. Sie erhalten ein Systeminventar, eine Workflow-Mappe und einen festen Umfang — unabhängig davon, ob Sie mit uns bauen.