Einstieg und Startfluss
Verantwortung
Aus «einem hermes-Befehl / einer Gateway-Nachricht (gateway message)» wird «ein im Speicher laufender Agent». Die Einstiegsschicht ist bewusst dünn gehalten – run_agent.py tut nur Weiterleiten und CLI-Zusammenbau; die echte Dependency-Injection steckt in agent_init.py. So teilen sich Gateway, TUI, CLI, ACP und cron denselben Initialisierungspfad.
Designmotiv
Einstiegsschicht dünn, Assembly zentralisiert — damit alle Aufrufpfade nur ein einziges Mal lernen. Würde AIAgent.__init__ selbst Provider (provider) auflösen, extra_body mergen, Kompressionsschwelle (compression threshold) berechnen, müssten Gateway, TUI und cron diese Logik jeweils nachbauen; eine Änderung an einer Stelle würde viele Stellen berühren. __init__ degeneriert zum reinen Forwarder; die eigentliche Entscheidung konvergiert in einer Funktion init_agent. Ein neu angedocktes Frontend (etwa ein künftiger MCP-Daemon) bekommt durch Konstruktion eines AIAgent die volle Fähigkeit, ohne die Assembly-Details zu kennen.
Dasselbe gilt für die Platzierung von hermes_bootstrap.py ganz vorne: Windows-UTF-8 ist eine «so früh wie möglich»-Korrektur; nachdem ein Kindprozess gespawned wurde, ist es zu spät. Also ist es ein eigenes Modul, das jeder Einstiegspunkt in der ersten Zeile importiert.
Schlüsseldateien
Gateway-Main:22965— Gateway-Prozesseinstiegmain()AIAgent-Klasse:400-497— dünne Einstiegsklasse,__init__speichert nur ParameterAIAgent.__init__ Weiterleitung:497-560— der Kommentar sagt explizit "Forwarder — see agent.agent_init.init_agent"run_conversation-Methode:6350— InstanzmethodeAIAgent.run_conversation, ruft diemodulweite run_conversation:588aufCLI-Main:6434— CLI-Einstieginit_agent:276-400— Dependency-Injection-Hauptfunktion_resolve_compression_threshold:93-127— Beispiel für Konfigurationsauflösung beim Starthermes_bootstrap.py— früheste Pfad-/Umgebungsführung (Repo-Wurzel)
Datenfluss
- Der Nutzer führt
hermes(CLI) aus oder das Gateway startet mitmain()(run_agent.py:22965). - Der Prozess durchläuft
hermes_bootstrap.pyfür Pfad-/Umgebungskorrekturen und tritt in denCLI-Main:6434oder die Gateway-Schleife ein. - Es wird eine
AIAgent:400-Instanz erzeugt; deren__init__leitet aninit_agent:276weiter. init_agentlöst Provider, Toolsets, Kompressionsschwellen, Custom-Provider-extra_bodyusw. auf und baut ein lauffähiges Agent-Objekt zusammen.- Das Gateway hält diesen Agenten in
GatewayRunner(gateway/run.py) und füttert jede eingehende Nachricht anrun_conversation:588.
Der Funktionskörper von AIAgent.__init__ ist die wörtliche Ausführung von «Forwarder» — die Konstruktionsparameter werden unverändert an init_agent weitergereicht, ohne eigene Auflösung:
"""Forwarder — see ``agent.agent_init.init_agent``."""
from agent.agent_init import init_agent
init_agent(
self,
base_url=base_url,
api_key=api_key,
provider=provider,
api_mode=api_mode,
...
)Und das hermes_bootstrap.py, das als Erstes mit dem Prozess in Berührung kommt, macht nur auf Windows eine Codierung-Reparatur; auf POSIX gibt es direkt zurück — die Logik ist kurz:
if not _IS_WINDOWS:
return False
if _bootstrap_applied:
return False
os.environ.setdefault("PYTHONUTF8", "1")
os.environ.setdefault("PYTHONIOENCODING", "utf-8")
for stream_name in ("stdout", "stderr"):
stream = getattr(sys, stream_name, None)
...
reconfigure(encoding="utf-8", errors="replace")Dass setdefault statt = verwendet wird, ist Absicht: Ein Nutzer kann vorab PYTHONUTF8=0 in die Umgebungsvariablen legen, um aktiv opt-out, und wird nicht überschrieben.
Grenzen und Fehler
- Einstieg direkt konstruiert: Jemand umgeht
hermes_bootstrapund startet auf Windows direktpython -m gateway.run; stdio bleibt cp1252, und nicht-ASCII-Output wirftUnicodeEncodeError. Die Position des Bootstrap ist Konvention, nicht erzwungen; die Doku verlangt, dass jeder Einstiegspunkt ihn in der ersten Zeile importiert. AIAgent.__init__wirft vorinit_agent: Da der__init__-Körper nur aus Import + Aufruf besteht, kommt jeder Fehler aus dem Inneren voninit_agent; der Stackframe wirkt «verschoben». Beim Debuggen ist der letzte Frame des Tracebackinit_agent— nicht von dem «Forwarder»-Kommentar in__init__in die Irre führen lassen, als sei der Forwarder selbst buggy.init_agent-Import ist lazy:from agent.agent_init import init_agentsteht im__init__-Körper, nicht auf Modulebene, um Circular Imports zu vermeiden —agent_initimportiert wiederumrun_agent. Kosten: ein zusätzlicher Import bei der ersten Konstruktion einesAIAgent(Größenordnung Millisekunden); danach läuft alles über den Modul-Cache.hermes_bootstrap-reconfigurekann scheitern: Ist stdout ersetzt durchBytesIO(Testszenario) oder bereits geschlossen, wirftreconfigureOSError/ValueError; der Code fängt das und überspringt stillschweigend. Der Umgebungsvariablen-Pfad greift trotzdem, sodass Kindprozesse nicht betroffen sind.
Zusammenfassung
Die Einstiegsschicht ist «Weiterleiten + Zusammenbauen» und trägt keine Geschäftslogik. AIAgent ist eine dünne Hülle, init_agent ist das eigentliche Assembly-Zentrum. Wenn diese Kette verstanden ist, beantwortet die Hauptschleife des Agenten die Frage «was passiert, nachdem eine Nachricht gefüttert wurde».