Skip to content

AIAgent / init_agent

源码版本v2026.7.20

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

Flux de données

  1. AIAgent.__init__ (run_agent.py:423) collecte tels quels les paramètres du constructeur.
  2. Appelle init_agent:276, qui dans l'ordre :
  3. L'agent assemblé expose la méthode run_conversation (run_agent.py:6350), transférée au run_conversation au niveau module (agent/conversation_loop.py:588).
  4. Le GatewayRunner de 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 :

python
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 :

python
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, None

Pour 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 :

python
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 = overrides

La 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_mode n'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_agent ne 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_agent applique « la première entrée avec un match explicite de model gagne », les suivantes sont ignorées. Une entrée sans champ model sert 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_threshold est None ou négatif, _resolve_compression_threshold ne fait pas de validation de plage et le transmet tel quel. Un négatif désactive définitivement la compression ; None lève un TypeError à la comparaison. La couche config (hermes config) a une vérification de plage, mais init_agent ne la re-double pas et fait confiance au produit de config.
  • Échec d'écriture du marker autoraise : si $HERMES_HOME est en lecture seule ou disque plein, _record_codex_gpt55_autoraise_notice saute 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() : dans init_agent, les helpers accèdent au module run_agent via _ra() plutôt qu'un import en tête, pour permettre aux tests de monkeypatcher run_agent.OpenAI etc. puis d'appeler init_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.

Site d'apprentissage communautaire non officiel. Basé sur le code source de NousResearch/hermes-agent (licence MIT).