AIAgent / init_agent
Responsabilité
init_agent est l'« atelier d'assemblage » de l'agent : il injecte dans l'instance agent la configuration du provider, des toolsets, des seuils de compression (compression), du extra_body personnalisé du provider, des composants de mémoire et d'apprentissage, afin qu'il dispose de toutes les dépendances nécessaires pour exécuter un tour (turn) de conversation. La classe AIAgent elle-même n'est qu'une coquille fine autour du produit qu'il crée.
Mot de conception
La configuration et le runtime sont séparés pour que le chemin de code « lancer un tour de conversation » soit aussi stateless que possible. init_agent fige en une fois toutes les décisions de démarrage (choix du provider, stratégie de compression, activation des toolsets, fusion du extra_body) ; ensuite run_conversation entre dans le rythme « alimente un message → lance la boucle » sans avoir à revenir modifier la configuration. L'avantage direct : run_conversation peut être lancé en concurrence sereinement — quand la même instance agent est pilotée par plusieurs messages entrants simultanés, il n'y a pas de race « la config est en train d'être modifiée ».
L'autre motivation à résoudre le seuil de compression au démarrage plutôt qu'au moment où la compression se déclenche : la fenêtre de 272K de la famille Codex gpt-5.x exige de « monter automatiquement » le seuil pour exploiter toute la fenêtre, et cette montée doit être notifiée une fois à l'utilisateur. Le calculer à l'exécution à chaque fois, c'est soit re-spammer la notification, soit l'oublier. Au démarrage on le calcule une fois, on écrit un fichier marker, et plus loin on lit juste le marker pour décider.
Fichiers clés
class AIAgent:400-497— définition de classe, les champs sont la configuration__init__ forwarder:497-560—from agent.agent_init import init_agent; init_agent(...)def init_agent:276— entrée de la fonction principale d'assemblagehelpers extra_body provider custom:183-275—_normalized_custom_base_url/_custom_provider_model_matches/_merge_custom_provider_extra_body_resolve_compression_threshold:93-127— résolution du seuil de compressionzone de helpers:62-91—_ra, notice autoraise, etc.
Flux de données
AIAgent.__init__(run_agent.py:423) collecte tels quels les paramètres du constructeur.- Appelle
init_agent:276, qui dans l'ordre :- résout le nom et l'URL de base du provider (
agent/agent_init.py:183) - fusionne le
extra_bodydu provider personnalisé (agent/agent_init.py:257) - résout le seuil de compression (
agent/agent_init.py:93) - injecte les toolsets, le gestionnaire de mémoire, le graphe d'apprentissage, etc.
- résout le nom et l'URL de base du provider (
- L'agent assemblé expose la méthode
run_conversation(run_agent.py:6350), transférée aurun_conversationau niveau module (agent/conversation_loop.py:588). - Le
GatewayRunnerde la passerelle (gateway) (gateway/run.py:3029) détient l'agent assemblé et lui confie les messages entrants.
Dans init_agent, le nom du provider est normalisé en minuscules et les espaces sont strippés ; provider sert à la fois d'indice de routage et de base pour inférer api_mode :
agent.base_url = base_url or ""
provider_name = provider.strip().lower() if isinstance(provider, str) and provider.strip() else None
agent.provider = provider_name or ""
...
if api_mode in {"chat_completions", "codex_responses", "anthropic_messages", "bedrock_converse", "codex_app_server"}:
agent.api_mode = api_mode
elif agent.provider == "openai-codex":
agent.api_mode = "codex_responses"
elif agent.provider in {"xai", "xai-oauth"}:
agent.api_mode = "codex_responses"La résolution du seuil de compression se fait dans _resolve_compression_threshold. L'autoraise de Codex est unidirectionnelle — elle ne peut que monter, jamais baisser un seuil plus élevé déjà fixé par l'utilisateur :
if model_cthresh is None:
return global_threshold, None
if is_codex_autoraise:
if model_cthresh <= global_threshold + 1e-9:
# Autoraise never lowers; keep the user's higher/equal threshold.
return global_threshold, None
return model_cthresh, {
"model": model,
"from": global_threshold,
"to": model_cthresh,
}
return model_cthresh, NonePour la fusion du extra_body du provider personnalisé, le cœur est de matcher l'entrée de config sur base_url + model, puis de fusionner extra_body dans request_overrides en préservant les clés déjà posées par l'utilisateur :
merged_extra_body = dict(extra_body)
existing_extra_body = overrides.get("extra_body")
if isinstance(existing_extra_body, dict):
merged_extra_body.update(existing_extra_body)
overrides["extra_body"] = merged_extra_body
agent.request_overrides = overridesLa direction de l'update est « l'extra_body de la config sert de base, l'extra_body déjà présent chez l'utilisateur passe par-dessus » ; ainsi les champs injectés à l'exécution dans request_overrides["extra_body"] ne sont pas écrasés par la config de démarrage.
Limites et échecs
- Nom de provider qui ne résout pas vers un api_mode : si l'utilisateur passe un
provider="foobar"inconnu,api_moden'est pas inféré et retombe sur le chemin par défaut chat_completions. Si foobar ne supporte en réalité que codex_responses, le premier message à l'exécution renvoie un 400.init_agentne valide pas que le provider est dans une whitelist, il normalise seulement la casse. - Conflit de fusion custom extra_body : si plusieurs entrées sont configurées sous le même base_url et qu'elles matchent le même model,
_custom_provider_extra_body_for_agentapplique « la première entrée avec un match explicite de model gagne », les suivantes sont ignorées. Une entrée sans champmodelsert de fallback et n'est utilisée que si aucune correspondance explicite ne matche. Si l'utilisateur modifie la config sans redémarrer l'agent, il croit le nouvel extra_body actif alors que l'ancien fallback est encore en vigueur. - Valeur illégale du seuil de compression : si
global_thresholdestNoneou négatif,_resolve_compression_thresholdne fait pas de validation de plage et le transmet tel quel. Un négatif désactive définitivement la compression ;Nonelève unTypeErrorà la comparaison. La couche config (hermes config) a une vérification de plage, maisinit_agentne la re-double pas et fait confiance au produit de config. - Échec d'écriture du marker autoraise : si
$HERMES_HOMEest en lecture seule ou disque plein,_record_codex_gpt55_autoraise_noticesaute silencieusement, et la conséquence est que la notification re-pop au prochain init. C'est un trade-off assumé : un échec d'écriture du marker ne doit pas empêcher l'agent de démarrer. - Import paresseux via
_ra(): dansinit_agent, les helpers accèdent au modulerun_agentvia_ra()plutôt qu'unimporten tête, pour permettre aux tests de monkeypatcherrun_agent.OpenAIetc. puis d'appelerinit_agent. Le prix : un import supplémentaire à la première invocation, et si le timing du patch arrive trop tard les attributs déjà liés ne sont pas remplacés par le nouveau patch.
Résumé
init_agent est la couche de traduction configuration → runtime : toutes les décisions de démarrage (choix du provider, stratégie de compression, activation des toolsets) sont figées ici en une fois. Ensuite l'agent entre dans un rythme sans état « alimente un message → lance la boucle » et ne revient plus modifier la configuration.