Para agentes de IA: hay un índice de documentación disponible en https://www.mongodb.com/es/docs/llms.txt — versiones en markdown de todas las páginas están disponibles agregando .md a cualquier ruta URL.
See how MongoDB 9.0 delivers up to 2x higher throughput.
MongoDB Branding Shape
Register now >
Docs Menu

SDK del corredor - Arquitectura técnica

SDK de tiempo de ejecución para inquilinos, independiente del framework, para el motor de agentes de Atlas.

Este es un paquete central interno; los agentes no lo instalan directamente. Instale un SDK de framework (agent-engine-sdk-langgraph o agent-engine-sdk-adk), que depende de él.

El SDK de Runner proporciona los componentes de integración AER, Tool Pod y Memory Server de la arquitectura de la plataforma. El motor de orquestación es un servicio independiente de Atlas Agent Engine.

Componente
Puerto
Nivel de confianza
Acceso a la red
Acceso a secretos
Propósito

AER (Entorno de ejecución del agente)

8001

Intermedio

Solo API de LLM

Solo claves LLM

Plano de ejecución: ejecuta la lógica del agente.

Herramienta

8002

Varía

Según sea necesario

Según sea necesario

Plano de datos: ejecución de herramientas aislada

Servidor de memoria

8081

Intermedio

MongoDB

credenciales de la base de datos

Servicio de memoria para memoria semántica/episódica/taxonómica

Runner-shared detecta automáticamente el enlace del oyente al inicio: :: (IPv6 pila dual: uvicorn lo muestra como http://[::]:port en formato URL) cuando el kernel tiene IPv6 habilitado con IPV6_V6ONLY=0, de lo contrario 0.0.0.0. No se requiere configuración: las implementaciones de producción en redes con IPv6 primero (espacios de nombres de inquilinos de celda) obtienen :: automáticamente y el desarrollo local de Docker (donde el IPv6 del puente suele estar deshabilitado) recurre a 0.0.0.0. El host resuelto por la sonda se registra al inicio (listener bind resolved: host=...) para que la verificación posterior a la implementación no tenga que inferirlo de la propia línea de inicio de uvicorn.

Configure APP_HOST en el entorno para anular la sonda; esto es necesario en contenedores donde el kernel informa que IPv6 está disponible, pero la red circundante solo enruta IPv4 (por ejemplo, el reenvío de puertos de Docker con host_ip: 127.0.0.1). Las plantillas de composición de la prueba de integración configuran APP_HOST=0.0.0.0 por este motivo.

El oyente utiliza el puerto predeterminado fijo para el RUNNER_MODE activo (8001/8002/8081). RUNNER_MODE es necesario y lo inyecta el entorno de ejecución de la plataforma; el desarrollador local ignora las anulaciones de .env del usuario para este, y los secretos del espacio de trabajo reservan el nombre por completo.

La imagen CMD es python -m agent_engine_runner_shared.launcher (TypeScript: node …/launcher.js). El lanzador instala el registro antes de importar AGENT_ENTRYPOINT, por lo que un fallo en el momento de la importación o una excepción de la función de punto de entrada es un registro ERROR (JSON estructurado cuando STRUCTURED_LOGGING=true) en lugar de un rastreo sin procesar que los registros del espacio de trabajo mostrarían como INFO. TenantRuntime todavía llama a setup_logging / setupLogging más tarde; esa segunda instalación es idempotente. Los fallos que ocurren antes de que comience el proceso del lanzador (errores del intérprete/imagen) todavía dependen de la heurística de ruta de lectura de API Gateway documentada en `docs/api-gateway/README.md <../../../../../docs/api-gateway/README.md#agent--operator-logs-s3-read-path>`__.

Esta separación permite: - Registro de auditoría: OE ve todas las llamadas a herramientas/LLM sin ejecutarlas.- Aplicación de políticas: OE puede bloquear las llamadas antes de su ejecución. - Privilegios mínimos: cada componente solo tiene el acceso que necesita. - Reproducción: OE puede devolver resultados almacenados en caché para una reanudación determinista.


Contrato normativo completo: `docs/runner/README.md <../../../../../docs/runner/README.md#execution-lifecycle--secret-availability>`__. Las reglas que un autor de agentes necesita para escribir código correcto:

  • El código de nivel superior del módulo (todo lo que esté fuera del cuerpo de una función) se ejecuta una vez al inicio del proceso, tanto en AER como en Tool Pod. Puede basarse en la configuración de MCP OAuth y en secretos de inicio que el inquilino puede entregar, incluidas las claves API MONGODB_URI y LLM. Las sesiones de invocación y las credenciales de solicitud delegadas no están disponibles durante el inicio. Las escrituras de caché de MCP OAuth (~/.agentic/mcp-oauth/<server>.json o AGENTIC_MCP_OAUTH_DIR en entornos de ejecución implementados) se serializan en un archivo <server>.json.lock de aviso para que las actualizaciones de tokens concurrentes para el mismo servidor no se sobrescriban entre sí.

  • Laconstrucción de la aplicación de la herramienta intenta ejecutar la función @app.entrypoint durante el inicio para descubrir cada registro app.llm(llm_id=...). Las solicitudes del modelo reutilizan el registro preparado. La construcción utiliza una configuración de inicio estática y no debe ejecutar un turno de negocio, una llamada al modelo ni el cuerpo de la herramienta. Si la construcción falla, se emite una advertencia y se restauran los registros anteriores, lo que permite que una solicitud de modelo posterior pueda reintentarlo. La construcción exitosa se almacena en caché incluso sin LLM con nombre. El almacenamiento en caché del gráfico AER y el calentamiento siguen siendo propiedad del marco.

  • ``app.llm(llm, llm_id=...)`` debe llamarse mientras el punto de entrada esté en la pila de llamadas, ya sea directamente en su cuerpo o en una función auxiliar a la que llame. Llamarla desde cualquier lugar fuera de eso (nivel superior del módulo, cuerpo de una herramienta, una función auxiliar invocada desde un lugar distinto al punto de entrada) genera RuntimeError inmediatamente, indicando el sitio de llamada que causó el problema.

  • Los cuerpos de herramientas locales (is_local=True) se ejecutan únicamente en el proceso propio de AER. Tanto las cargas de trabajo de AER como las de Tool reciben secretos entregables al inquilino al iniciarse.

  • Las herramientas remotas (is_local=False, o herramientas que requieren credenciales delegadas) se ejecutan únicamente en el Tool Pod, bajo demanda, cuando OE les envía una llamada; nunca durante la construcción. Las declaraciones required_secrets documentan los requisitos de secreto; no restringen el entorno de secreto de inicio.

  • Las herramientas con credenciales permanecen remotas incluso si el SDK proporcionó su configuración predeterminada local, porque la autorización delegada y la inyección de secretos con ámbito de herramienta solo ocurren en la ruta Tool Pod.


┌────────┐ ┌─────┐ ┌─────┐
│ Client │ │ OE │ │ AER │
└───┬────┘ └──┬──┘ └──┬──┘
│ │ │
│ POST /invoke │ │
│ ──────────────────────────►│ │
│ │ │
│ │ Create Execution (PENDING)
│ │ │
│ │ POST /execute │
│ │─────────────────────────►│
│ │ │
│ │ │ Build LangGraph
│ │ │ Start ainvoke()
│ │ │
│ ┌───────────────────────┼──────────────────────────┼───────────────┐
│ │ LOOP: For each tool/LLM call │ │
│ │ │ │ │
│ │ │ POST /tool/execute │ │
│ │ │◄─────────────────────────│ │
│ │ │ {tool, args, step} │ │
│ │ │ │ │
│ │ │ Log + Policy Check │ │
│ │ │ │ │
│ │ │ {proceed, route_to} │ │
│ │ │─────────────────────────►│ │
│ │ │ │ │
│ │ │ │ Execute tool │
│ │ │ │ │
│ │ │ POST /tool/result │ │
│ │ │◄─────────────────────────│ │
│ │ │ {result, status} │ │
│ └───────────────────────┼──────────────────────────┼───────────────┘
│ │ │
│ │ │ Graph completes
│ │ │
│ │ POST /executor/callback │
│ │◄─────────────────────────│
│ │ {COMPLETED, result} │
│ │ │
│ GET /execution/{id} │ │
│ ──────────────────────────►│ │
│ │ │
│ {status: completed, result}│ │
│ ◄──────────────────────────│ │
│ │ │
┌────────┐ ┌─────┐ ┌─────┐
│ Client │ │ OE │ │ AER │
└───┬────┘ └──┬──┘ └──┬──┘
│ │ │
│ POST /invoke │ │
│ ──────────────────────────►│ │
│ │ │
│ │ POST /execute │
│ │─────────────────────────►│
│ │ │
│ │ ... tool calls ... │
│ │ │
│ │ │ app.suspend(reason)
│ │ │ → tool pod reports
│ │ │ status: "suspend"
│ │ │ (out-of-band, not
│ │ │ result content)
│ │ │
│ │ │ interrupt() on the
│ │ │ OE-confirmed status
│ │ │ Save checkpoint
│ │ │
│ │ POST /executor/callback │
│ │◄─────────────────────────│
│ │ {SUSPENDED, metadata} │
│ │ │
│ GET /execution/{id} │ │
│ ──────────────────────────►│ │
│ {status: suspended} │ │
│ ◄──────────────────────────│ │
│ │ │
╠════════════════════════════╬══════════════════════════╣
║ ⏸️ WAITING FOR HUMAN - Reviews and Approves ║
╠════════════════════════════╬══════════════════════════╣
│ │ │
│ POST /resume/{id} │ │
│ {decision: "approved"} │ │
│ ──────────────────────────►│ │
│ │ │
│ │ Store human decision │
│ │ │
│ │ POST /execute │
│ │ {resume: true} │
│ │─────────────────────────►│
│ │ │
│ │ │ Resume from checkpoint
│ │ │
│ ┌───────────────────────┼──────────────────────────┼───────────────┐
│ │ Replayed steps return CACHED results │ │
│ │ │ │ │
│ │ │ POST /tool/execute │ │
│ │ │◄─────────────────────────│ │
│ │ │ │ │
│ │ │ {cached_result} │ │
│ │ │─────────────────────────►│ │
│ └───────────────────────┼──────────────────────────┼───────────────┘
│ │ │
│ │ │ Continue execution
│ │ │ (new steps)
│ │ │
│ │ POST /executor/callback │
│ │◄─────────────────────────│
│ │ {COMPLETED, result} │
│ │ │
│ GET /execution/{id} │ │
│ ──────────────────────────►│ │
│ {status: completed} │ │
│ ◄──────────────────────────│ │
│ │ │

Nota

Reanudar a través de la puerta de enlace API: El diagrama anterior utiliza la ruta interna /resume/{id} del OE para mayor brevedad. Los desarrolladores llaman a la puerta de enlace API en POST /api/v1/projects/{project_id}/executions/{execution_id}/resume con un cuerpo JSON plano:

{"decision": "approved", "reviewer_notes": "optional context"}

La forma {"human_review": {"decision": "..."}} envuelta es el contrato de punto final interno del OE; la puerta de enlace lo rechaza con HTTP 400.

Cuando el OE cancela de forma duradera una ejecución, envía /drain al entorno de ejecución Tool/AER que lo posee. El contrato receptor (fijado por el fixture compartido en client-libraries/test-fixtures/drain/contract.json):

  • POST /drain {request_id, execution_id, reason, deadline_at_ms, workspace_id} — en un entorno de ejecución con ámbito de espacio de trabajo (conjunto APP_ID, es decir, cada entorno de ejecución administrado) workspace_id es obligatorio y debe coincidir exactamente: el token de portador autentica al llamador, pero no prueba que la ejecución nombrada pertenezca a este entorno de ejecución.

  • 202 {"outcome": "accepted"} mientras se drena; 200 con el resultado final (completed / timed_out / delivery_failed) después. accepted es solo aceptación a nivel HTTP; completed es la única señal de inactividad.

  • Idempotente en request_id, un drenaje por ejecución: un reintento vuelve a leer el resultado registrado en lugar de volver a ejecutar los efectos secundarios.

  • deadline_at_ms es absoluto y nunca se extiende; el tiempo de ejecución rechaza plazos más allá de RUNNER_DRAIN_MAX_DEADLINE_MS (predeterminado 60s). Los registros finalizados sobreviven a RUNNER_DRAIN_RECORD_TTL_S (predeterminado 900s) para que los reintentos del llamador permanezcan idempotentes durante su ventana de reconciliación; manténgalo en o por encima de la ventana de reintento del llamador (OE reintenta un drenaje durante 15 minutos): un TTL más corto expulsa el registro mientras el llamador todavía está reintentando, y los reenvíos tardíos obtienen execution_not_found en lugar del resultado registrado.

  • Una ejecución que este entorno de ejecución nunca sirvió responde delivery_failed con reason_code=execution_not_found: una ejecución puede abarcar los entornos de ejecución de AER y Tool de forma independiente, por lo que la selección exacta de OE no hace que un completed falso sea seguro.

Los límites por lenguaje son parte del contrato, no un detalle de implementación. Python cancela las tareas asyncio rastreadas, por lo que el trabajo cooperativo se establece como completed. Una promesa no tiene cancelación, por lo que el entorno de ejecución de TypeScript (agent-engine-runner-shared, que refleja este contrato y debe cambiar junto con él) aborta solo los AbortController registrados —el turno AER y los flujos LLM— mientras que las funciones de herramientas simples no reciben ninguna señal: son rastreadas y bloqueadas por admisión, y sobrevivir al plazo es un timed_out honesto. El estado es local al proceso por diseño: un entorno de ejecución reiniciado ha perdido su trabajo activo y responde delivery_failed; la reconciliación entre reinicios pertenece al registro duradero del llamador, no a este registro.

POST /interrupt/call es el hermano quirúrgico del drenaje: la interrupción por llamada del OE nombra una llamada por {execution_id, step_number} y el tiempo de ejecución aborta exactamente el identificador rastreado de esa llamada (Python cancela la tarea asyncio, el tiempo de ejecución de TS aborta el AbortController de la llamada), mientras que la ejecución permanece abierta y las llamadas posteriores proceden (sin pestillo de admisión, nunca). Siempre 200 con un resultado: interrupted (señalizado o registrado para dispararse al adjuntar), not_found (no hay tal llamada en curso), already_settled (la llamada terminó primero), not_cancellable (trabajo rastreado sin canal de señal (un cuerpo de herramienta de sincronización en un hilo, una función de herramienta TS simple) informado honestamente, nunca declarado abandonado). El alcance del espacio de trabajo coincide con el de la ruta de drenaje, y los vectores compartidos viven en client-libraries/test-fixtures/interrupt-call/contract.json.


Las llamadas a herramientas interceptadas y invoke_llm ahora utilizan el mismo flujo de ejecución propiedad de OE. Cuando el agente que se ejecuta en AER necesita llamar a una herramienta o a un LLM, el contenedor envía la solicitud interceptada a OE y espera a que OE devuelva el resultado final. OE enruta las llamadas a herramientas a través de Tool Pod /execute y las llamadas a LLM a través de Tool Pod /invoke_llm, y luego devuelve el resultado final (éxito, suspensión o error) a AER.

Diagrama de secuencia:

┌─────┐ ┌─────┐ ┌──────────┐
│ AER │ │ OE │ │ Tool Pod │
└──┬──┘ └──┬──┘ └────┬─────┘
│ │ │
│ 1. POST /tool/execute │ │
│ ────────────────────────►│ │
│ {name or invoke_llm, │ │
│ args/messages, step} │ │
│ │ │
│ │ Log + Policy Check │
│ │ │
│ │ 2a. Replay cache hit │
│ 2a. Final cached result │ │
│ ◄────────────────────────│ │
│ {status, result, │ │
│ from_cache} │ │
│ │ │
├──────────────────────────┼────────────────────────────┤
│ if OE must execute the tool │
│ │ │
│ │ 2b. POST /execute or │
│ │ /invoke_llm │
│ │ ───────────────────────────────────────►
│ │ via Tool Pod │
│ │ ◄───────────────────────────────────────
│ │ {status, result} │
│ │ │
├──────────────────────────┼────────────────────────────┤
│ │ │
│ 3. Final OE-owned result │ │
│ ◄────────────────────────│ │
│ {status, result, error, │ │
│ duration_ms, pod_name} │ │
│ │ │

Puntos clave:

  1. Cada herramienta interceptada o llamada LLM pasa primero por OE para la política, el registro y la propiedad de reproducción.

  2. OE controla el enrutamiento: las herramientas remotas usan Tool Pod /execute; las herramientas locales aprobadas vuelven a la pila de llamadas AER original para que el contexto nativo del marco permanezca activo; las llamadas LLM interceptadas usan Tool Pod /invoke_llm.

  3. La función Replay utiliza resultados almacenados en caché: al reanudarse después de SUSPEND, OE devuelve el resultado final almacenado para evitar efectos secundarios duplicados.

  4. La suspensión aún se desenrolla a través del contenedor: OE devuelve la carga útil de suspensión serializada, luego SecureToolWrapper la convierte de nuevo en la interrupción del marco.

  5. ``invoke_llm`` ahora coincide con el contrato de la herramienta: OE devuelve la carga útil final status/result/error en lugar de pedirle a AER que la ejecute después de la aprobación.

  6. Los fragmentos de flujo LLM conservan los metadatos del mensaje final: id, name, additional_kwargs y response_metadata viajan a través del evento de flujo tipificado y se recopilan de nuevo en la misma forma de respuesta final utilizada para la reproducción.

La carga de la configuración en tiempo de ejecución ahora admite la interpolación ${VAR} para una lista permitida reducida de rutas agent.yaml propiedad del inquilino, actualmente mcp.servers.*.url.

Cómo funciona:

  • load_runtime_agent_config(env_vars=...) Sustituye las referencias ${VAR} solo en las rutas permitidas.

  • El entorno de ejecución pasa tenant_env_vars() en lugar de os.environ sin procesar, por lo que las variables de entorno y los secretos propiedad de la plataforma quedan excluidos de la sustitución.

  • Si una ruta permitida hace referencia a una variable no definida o contiene un marcador ${...} mal formado, el cargador genera un claro error de validación.

  • mcp.servers.*.auth.token_env no debe apuntar a una variable de entorno propiedad de la plataforma.

Ejemplo:

mcp:
servers:
tableau:
url: https://${TABLEAU_HOST}/mcp

En tiempo de ejecución, ${TABLEAU_HOST} se resuelve a partir del subconjunto de entorno propiedad del inquilino. Una referencia como ${OPENAI_API_KEY} se rechaza porque esa variable es propiedad de la plataforma y se filtra antes de la interpolación.

El componente de seguridad central que intercepta las llamadas a las herramientas:

# Every tool call goes through this flow:
def execute_tool(tool_name, arguments):
# 1. Send the intercepted tool call to OE
response = request_oe_approval(oe_url, execution_id, tool_name, arguments)
# 2. Check for policy denial
if not response.proceed:
raise PolicyDeniedException(response.reason)
# 3. OE either returns a final outcome or routes the call back in process
if response.route_to == "callback":
result = local_executor()
report_oe_result(result)
return result
if response.status == "error":
raise ToolExecutionError(response.error)
if response.status == "suspend":
interrupt(response.result)
return response.result

Invariante clave: Ninguna herramienta ni llamada a LLM se ejecuta sin que OE lo sepa.


OE puede bloquear las llamadas a herramientas/LLM en función de las reglas de la política:

La aplicación de las políticas está a cargo de Go OE. Véase docs/orchestration-engine/README.md.

Cuando un agente se reanuda tras una suspensión, la ejecución de LangGraph se repite desde el principio. Sin almacenamiento en caché, esto volvería a ejecutar las herramientas y las llamadas a LLM, lo que provocaría efectos secundarios duplicados (por ejemplo, el envío de notificaciones dos veces).

Cómo funciona:

  1. OE realiza un seguimiento de todos los pasos: cada llamada a la herramienta/LLM se registra con su número de paso, nombre y resultado.

  2. Al reanudarse, el agente vuelve a ejecutar LangGraph desde el principio, realizando las mismas llamadas en orden.

  3. OE devuelve los resultados almacenados en caché: en lugar de volver a ejecutar, OE devuelve el resultado almacenado.

Original Execution:
Step 1: lookup_policy("POL-123") → executed, result stored
Step 2: query_mongogpt(...) → executed, result stored
Step 3: human_review(...) → SUSPEND (waiting for human)
Resume Execution:
Step 1: lookup_policy("POL-123") → CACHED (returns stored result)
Step 2: query_mongogpt(...) → CACHED (returns stored result)
Step 3: human_review(...) → CACHED (returns human's decision)
Step 4: send_notification(...) → executed (new step)

El almacenamiento en caché de reproducción lo gestiona Go OE. AER/SecureToolWrapper gestiona las respuestas almacenadas en caché de forma transparente:

Manejo de AER/SecureToolWrapper:

# In secure_wrapper.py - OE already returns the final replay outcome
response = await request_oe_approval(...)
if response.from_cache:
log_cached_result(tool_name, step)
return response.result

Esto garantiza una reanudación determinista: el agente ve exactamente los mismos resultados que veía antes de la suspensión, lo que evita efectos secundarios duplicados.

Admite tanto puntos de control en memoria como en MongoDB:

# In runtime.py
def get_checkpointer(self):
if mongodb_uri:
return MongoDBSaver(client, db_name)
return MemorySaver() # Development only

src/agent_engine_runner_shared/
├── runtime.py # TenantRuntime - main SDK entry point
├── secure_wrapper.py # SecureToolWrapper, SecureWrappedLLM
├── models.py # Pydantic models for all API contracts
├── context.py # Per-execution context (contextvars)
├── metrics.py # Metrics collection and @with_metrics decorator
├── utils.py # Logging utilities, env helpers
├── logging.py # Durable execution logging (JSONL + MongoDB)
├── memory.py # MemoryEngine integration, MemoryWriter
├── voyage.py # VoyageService for embeddings
├── events.py # SSE event streaming, observability
├── types.py # Protocol definitions (MemoryEngineProtocol)
├── tracing/ # OpenTelemetry tracing
│ ├── setup.py # setup_tracing(), get_current_trace_context()
│ └── exporters.py # JSONLSpanExporter, MongoDBSpanExporter
└── server/
├── base.py # BaseServer abstract class
├── aer.py # AER implementation
├── tool.py # Tool Pod implementation
└── memory.py # Memory Server implementation

Aunque añade latencia, el enrutamiento a través de OE proporciona: 1. Registro de auditoría completo: cada llamada se registra 2. Punto de aplicación de políticas: un único lugar para bloquear llamadas 3. Capacidad de reproducción: OE puede devolver resultados almacenados en caché para reanudar 4. Independiente del marco: OE no conoce LangGraph

  • AER necesita acceso a la API de LLM pero no debería tener credenciales de base de datos.

  • La herramienta podría necesitar acceso a la base de datos, pero no debería tener claves LLM.

  • Las distintas herramientas pueden ejecutarse en diferentes entornos con distintos permisos.


El SDK de Runner ahora ofrece paridad de características con el sistema monolítico agent-runtime:

funcionalidad
tiempo de ejecución del agente
SDK de corredor

Decorador @app.tool

✅

✅

Metadatos explícitos de red/tiempo de espera

✅

✅

Redacción de campo

✅

✅

Puntos de control de MongoDB

✅

✅

Registro de ejecución (JSONL + MongoDB)

✅

✅

Rastreo de OpenTelemetry

✅

✅

Integración de memoria

✅

✅

Voyage IA Embeddings

✅

✅

Multiusuario (org_id/user_id)

✅

✅

Transmisión de eventos SSE

✅

✅

Servidor gRPC

✅

✅

Soporte de transmisión

✅

✅

Intervención humana

❌

✅

Arquitectura de tres componentes

❌

✅

Almacenamiento en caché de reproducción

❌

✅

Instala las funciones según sea necesario. Usa uv add si tu proyecto se gestiona con uv, o pip install en caso contrario:

# uv projects
uv add agent-engine-runner-shared
uv add "agent-engine-runner-shared[mongodb,tracing]"
uv add "agent-engine-runner-shared[tracing]"
uv add "agent-engine-runner-shared[mongodb]"
# pip
pip install agent-engine-runner-shared
pip install "agent-engine-runner-shared[mongodb,tracing]"
pip install "agent-engine-runner-shared[tracing]"
pip install "agent-engine-runner-shared[mongodb]"
Califique esta página