AIAgent / init_agent
Responsabilidad
init_agent es el «taller de ensamblaje» del agent: inyecta en la instancia del agent la configuración de provider, toolsets, umbral de compresión (compression), extra_body de provider personalizado, componentes de memoria y aprendizaje, etc., para que disponga de todas las dependencias necesarias para correr una ronda de conversación. La clase AIAgent es solo la cáscara del objeto que init_agent devuelve.
Motivo de diseño
Mantener la configuración separada del runtime persigue que el código que «corre una ronda de conversación» sea lo más stateless posible. init_agent fija de una sola vez todas las decisiones del arranque (elección de provider, estrategia de compresión, activación de toolsets, fusión de extra_body); después run_conversation entra en el ritmo de «recibe mensaje → corre bucle» sin volver a tocar la configuración. La ventaja directa: run_conversation puede invocarse de forma concurrente sin que un mismo agent accionado por varios mensajes entrantes a la vez sufra una race de «la configuración se está cambiando».
El umbral de compresión se resuelve en el arranque y no en el momento de comprimir por otro motivo: la ventana de 272K de la familia Codex gpt-5.x exige «subir automáticamente» el umbral para aprovecharla, y ese aumento debe notificarse al usuario una sola vez. Recalcularlo en runtime significaría o bien reventar de notificaciones o bien olvidar alguna. En el arranque se calcula una vez y se escribe un marker; después basta leerlo.
Archivos clave
class AIAgent:400-497— definición de la clase; los campos son la configuración__init__ reenvía:497-560—from agent.agent_init import init_agent; init_agent(...)def init_agent:276— entrada de la función principal de ensamblajehelper extra_body de provider personalizado:183-275—_normalized_custom_base_url/_custom_provider_model_matches/_merge_custom_provider_extra_body_resolve_compression_threshold:93-127— resolución del umbral de compresiónzona de funciones auxiliares:62-91—_ra, autoraise notice, etc.
Flujo de datos
AIAgent.__init__(run_agent.py:423) recoge los parámetros de construcción sin modificarlos.- Llama a
init_agent:276, que en orden:- resuelve el nombre y
base_urldel provider (agent/agent_init.py:183) - fusiona el
extra_bodydel provider personalizado (agent/agent_init.py:257) - resuelve el umbral de compresión (
agent/agent_init.py:93) - inyecta toolsets, gestor de memoria, grafo de aprendizaje, etc.
- resuelve el nombre y
- El agent ensamblado expone
run_conversation(run_agent.py:6350), que reenvía alagent/conversation_loop.py:588a nivel de módulo. - El
GatewayRunnerdel gateway (gateway/run.py:3029) mantiene el agent ensamblado y le entrega los mensajes entrantes.
El nombre del provider se normaliza a minúsculas y se le quitan los espacios dentro de init_agent; provider sirve a la vez de pista de enrutado y de base para inferir el 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 resolución del umbral de compresión vive en _resolve_compression_threshold. El autoraise de Codex es unidireccional — solo sube, nunca baja un umbral que el usuario ya había fijado más alto:
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, NoneLa fusión del extra_body de un provider personalizado empareja entradas por base_url + model y, tras encontrar la entrada, vuelca extra_body dentro de request_overrides, conservando primero los valores que el usuario ya hubiera puesto:
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 dirección del update es «el extra_body de la config queda como base, el extra_body que el usuario ya tuviera se superpone», así que lo que runtime inyecte en request_overrides["extra_body"] no es sobreescrito por la config del arranque.
Límites y fallos
- El nombre del provider no se resuelve a ningún
api_mode: si el usuario pasaprovider="foobar"desconocido,api_modeno se infiere y se cae al camino por defecto de chat_completions. Si foobar realmente solo soporta codex_responses, el primer mensaje en runtime devolverá 400.init_agentno valida que el provider esté en una lista blanca; solo normaliza el casing. - Conflictos al fusionar
extra_body: si bajo un mismobase_urlhay varias entradas y todas matchean el mismo model,_custom_provider_extra_body_for_agentaplica «gana la primera con match explícito de model»; el resto se ignora. Una entrada sin campomodelhace de fallback y solo se usa si no hay match explícito. Si el usuario cambió la config pero no reinició el agent creyendo que el nuevoextra_bodyya corre, en realidad sigue el fallback antiguo. - Valores inválidos del umbral de compresión: si
global_thresholdesNoneo negativo,_resolve_compression_thresholdno hace validación de rango y lo pasa tal cual. Un número negativo implica que la compresión no se dispara nunca;NonelevantaTypeErroral comparar. La capa de config (hermes config) sí valida el rango, peroinit_agentno repite la validación: confía en el producto de config. - Fallo al escribir el marker del autoraise notice: si
$HERMES_HOMEes read-only o el disco está lleno,_record_codex_gpt55_autoraise_noticese salta sin ruido; el coste es que el próximo init vuelve a sacar la notificación. Es un trade-off intencionado: un fallo escribiendo el marker no debería impedir que el agent arranque. - Import perezoso en
_ra(): las funciones auxiliares dentro deinit_agentobtienen el módulorun_agentvía_ra()en lugar de unimporten la cima, para que los tests puedan hacer monkeypatch derun_agent.OpenAIy similares antes de llamar ainit_agent. El coste: una llamada de import más la primera vez, y si el patch llega tarde, los atributos ya fijados no se reemplazan por el nuevo.
Resumen
init_agent es la capa de traducción de configuración → runtime; todas las decisiones del arranque (elección de provider, estrategia de compresión, activación de toolsets) se fijan aquí de una sola vez. Después, el agent entra en el ritmo sin estado de «recibe mensaje → corre bucle» y no vuelve a tocar la configuración.