Architekturüberblick
Hermes Agent ist ein Open-Source-Desktop-Agent (MIT-Lizenz) von Nous Research. Die Kernpositionierung: «ein persistenter Agent, der mit dir wächst». Eine Nachricht kommt herein – von Telegram/Discord/Slack oder einer anderen Oberfläche – durchläuft die Hauptschleife, die Modell + Werkzeuge (tools) aufruft, um die Aufgabe zu erledigen, und die Erfahrung wird in Gedächtnis und Skills destilliert, um beim nächsten Mal wiederverwendet zu werden.
Schichtansicht
Jede Schicht in einem Satz
- UI-Schicht: TUI / CLI / Web / ACP (Editor-Protokoll) — nur Eingänge, halten keine Geschäftslogik.
- Gateway-Schicht:
gateway/run.pyvereinheitlicht den Zugriff; Plattformen sind Adapter (adapter); Sitzung (session) und Zustellung sind getrennt; Streams (streams) und Hooks sind steckbar. - Hauptschleife:
run_conversationorchestriert «Modellaufruf → Werkzeugaufruf → Ergebnis-Rückfüllung → Budget-Dekrement (budget decrement)», mit Fallback (fallback) bei Misserfolg. - Fähigkeitsschicht: Tools sind atomare Funktionen (
tools/registry.py); Skills sind höher geordnete, evolvierende Flüsse (von der Lernschleife umgeschrieben); Provider (provider)/MCP/Plugins (plugins) liefern Modelle und externe Fähigkeiten. - Planung & Erweiterung: Cron treibt geplante Aufgaben, Subagent delegiert isoliert (null Kontextkosten (context cost)), 6 Sandbox-Backends isolieren die Ausführung.
- Lernschleife: Destilliert die Konversationserfahrung in Skills und ein Benutzermodell und bildet so das Flywheel, das den Agenten «besser, je mehr man ihn nutzt» macht.
Top-Down: repräsentative Signaturen jeder Schicht
Um Hermess Schichtung zu verstehen, genügt es, auf die «Signaturen» zu schauen, die jede Schicht im Code zeigt.
UI-Schicht → Weiterleiter an die Hauptschleife. Alle Hüllen rufen AIAgent.run_conversation auf, doch das ist nur eine Weiterleitung (run_agent.py:6350):
def run_conversation(self, user_message, system_message=None,
conversation_history=None, ..., moa_config=None) -> Dict[str, Any]:
"""Forwarder — see ``agent.conversation_loop.run_conversation``."""
from agent.conversation_loop import run_conversation
token = set_conversation_context(self._conversation_root_id())
with scoped_runtime_main({}):
return run_conversation(self, user_message, ...)Die Konversations-ID wird über ein ContextVar nach unten gereicht — alle LLM-Aufrufe innerhalb der Hauptschleife (Kompression (compression), Vision, MoA-Slot, Background-Review-Fork) tragen automatisch das Tag.
Gateway-Schicht → Generaldirektor der Plattformadapter. GatewayRunner setzt Autorisierungs-/Kanban-/Slash-Command-Fähigkeiten über Mixins zusammen (gateway/run.py:22392); der Einstieg start_gateway(replace=True) killt zuerst die alte Instanz — ein Hintertürchen für den Fall, dass beim systemd-Neustart der alte Prozess nicht sauber beendet wurde:
class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, GatewaySlashCommandsMixin):
"""Main gateway controller. Manages the lifecycle of all platform adapters
and routes messages to/from the agent."""Hauptschleife → wo wirklich gearbeitet wird. run_conversation liegt in agent/conversation_loop.py und orchestriert «Modellaufruf → Werkzeugaufruf → Ergebnis-Rückfüllung → Budget-Dekrement» mit Fallback bei Misserfolg; Signatur siehe oben.
Fähigkeitsschicht → Werkzeugregistrierung. Jedes tools/*.py ruft beim Modul-Import registry.register auf (tools/registry.py:365); override=True ist nötig, damit Plugins eingebaute Tools überschreiben dürfen:
def register(self, name, toolset, schema, handler, check_fn=None,
requires_env=None, is_async=False, description="", emoji="",
max_result_size_chars=None, dynamic_schema_overrides=None, override=False):
"""Register a tool. Called at module-import time by each tool file.
``override=True`` is an explicit opt-in for plugins ... Without it,
registrations that would shadow an existing tool are rejected."""Wer ein eingebautes Tool ersetzen will, muss explizit override=True setzen und die allow_tool_override-Konfiguration freischalten — das verhindert, dass ein Plugin heimlich das browser-Tool tauscht. Gleichnamige MCP-Tools laufen über einen separaten Kanal.
Planung & Erweiterung → der cron-tick. tick (cron/scheduler.py:3888) ist ein einzelner, durch eine Dateisperre geschützter Scan; das Gateway ruft ihn alle 60 Sekunden aus einem Hintergrundthread auf. Der Parameter adapters erlaubt Cron-Aufgaben, die bereits aufgebauten Plattformverbindungen des Gateways wiederzuverwenden, statt selbst neu zu verbinden.
Lernschleife → Konsolidierung des Lerngraphen. build_learning_graph (agent/learning_graph.py:248-328) fügt gelernte (nicht eingebaute) Skills und Erinnerungen zu Knoten/Kanten zusammen. Es ruft zuerst build_skill_nodes(_skill_roots()) auf, um zwei Wurzeln (base und profile) zu scannen, und filtert dann Skills mit source != "base" und created_by == "agent" oder use_count > 0 als «gelernte» heraus — eingebaute Skills kommen nicht in den Lerngraphen, damit das Evolutionssignal nicht von Rauschen überdeckt wird.
Schichtungs-Motiv
Warum diese Schichtung? In einem Satz: die Schichten, die sich ändern dürfen, dürfen die Schichten, die sich nicht ändern dürfen, nicht verschmutzen.
- Die UI-Schicht wechselt häufig die Hülle (TUI/Web/IDE-Protokolle haben je eigene Iterationsrhythmen (iteration rhythm)) — ein Hüllenwechsel darf die Geschäftslogik nicht zwingen, sich zu ändern; deshalb hängen die Hüllen nur Callbacks ein (
step_callback/event_callback) und dringen nicht inrun_conversationein. - In der Gateway-Schicht ist das Andocken neuer Plattformen die Norm (QQbot, Enterprise-WeChat, neue Discord-Stile) — aber die Hauptschleife soll von Plattformen nichts merken; deshalb ist die Plattformlogik reine Adapterlogik, und das Gateway entkoppelt nur Nachrichten und Agent-Aufrufe.
- Die Fähigkeitsschicht ist das «Atom», die Lernschleife ist die «Evolution» — Atome dürfen nicht vom Evolutionspfad verschmutzt werden (sonst kollabiert die Hauptschleife mit, wenn ein Background-Review einen Bug in ein Tool schreibt); deshalb ändert
learning_mutationsskills/, nichttools/. - Planung & Erweiterung ist ein Querschnitt: Cron und Subagenten «triggern die Hauptschleife außerhalb der Hauptschleife», sie teilen sich
run_conversation, halten aber keinen direkten Agent-Zustand.
Häufige Missverständnisse
- «Hermes ist ein Agent-Framework» — nein. Es ist eine konkrete Desktop-Agent-Implementierung; Hauptschleife, Tool-Set und Skill-Verzeichnis gehören zu Hermes selbst. Die Framework-artigen Fähigkeiten (MCP-, Plugin-, Provider-Abstraktionen) sind Grenzen, die es zum Erweitern freilegt, nicht sein Wesen.
- «Die Lernschleife optimiert automatisch die Modell-Prompts» — nein. Sie ändert nur
SKILL.mdunterskills/und das Gedächtnis unter~/.hermes/skills/, nicht die System-Prompt-Templates (prompt templates). Prompt-Templates sind feste Dateien im Repo; sie zu ändern erfordert einen Release. - «Subagenten sind verteilt» — nein. Subagenten forken den Agenten innerhalb desselben Prozesses (teilen sich die Runtime); die Sandbox isoliert die Ausführung (Code läuft in Docker/SSH/Modal/Daytona), nicht eine Verteilung des Agent-Zustands.
- «Gateway-Schicht = Nachrichtenplattform-Anbindung» — nur halb richtig. Das Gateway verwaltet auch Sitzungszustand (
session/), Zustellungs-Dedup (delivery_ledger), Stream-Verteilung (stream dispatch) (stream_*), Slash-Commands, Kanban-Watcher — das sind alles Aufgaben «zwischen Nachrichtenplattform und Agent», nicht nur Anbindung.
Empfohlene Lesereihenfolge
- Start und Einstieg — wie ein Befehl zu einem laufenden Agenten wird
- Hauptschleife des Agenten — was beim Eintreffen einer Nachricht passiert
- Fähigkeitsschicht — der Unterschied zwischen Tools und Skills
- Gateway-Schicht — wie die Multi-Plattform-Zustellung funktioniert
- Planung & Erweiterung — Automatisierung und Isolation
- Lernschleife — der Mechanismus der Selbst-Verbesserung