Skip to content

AIAgent / init_agent

源码版本v2026.7.20

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

Flujo de datos

  1. AIAgent.__init__ (run_agent.py:423) recoge los parámetros de construcción sin modificarlos.
  2. Llama a init_agent:276, que en orden:
  3. El agent ensamblado expone run_conversation (run_agent.py:6350), que reenvía al agent/conversation_loop.py:588 a nivel de módulo.
  4. El GatewayRunner del 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:

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

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

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

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 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 pasa provider="foobar" desconocido, api_mode no 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_agent no valida que el provider esté en una lista blanca; solo normaliza el casing.
  • Conflictos al fusionar extra_body: si bajo un mismo base_url hay varias entradas y todas matchean el mismo model, _custom_provider_extra_body_for_agent aplica «gana la primera con match explícito de model»; el resto se ignora. Una entrada sin campo model hace 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 nuevo extra_body ya corre, en realidad sigue el fallback antiguo.
  • Valores inválidos del umbral de compresión: si global_threshold es None o negativo, _resolve_compression_threshold no hace validación de rango y lo pasa tal cual. Un número negativo implica que la compresión no se dispara nunca; None levanta TypeError al comparar. La capa de config (hermes config) sí valida el rango, pero init_agent no repite la validación: confía en el producto de config.
  • Fallo al escribir el marker del autoraise notice: si $HERMES_HOME es read-only o el disco está lleno, _record_codex_gpt55_autoraise_notice se 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 de init_agent obtienen el módulo run_agent vía _ra() en lugar de un import en la cima, para que los tests puedan hacer monkeypatch de run_agent.OpenAI y similares antes de llamar a init_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.

Sitio de aprendizaje comunitario no oficial. Basado en el código fuente de NousResearch/hermes-agent (licencia MIT).