Para agentes de IA: um índice de documentação está disponível em https://www.mongodb.com/pt-br/docs/llms.txt — as versões de markdown de todas as páginas estão disponíveis anexando .md a qualquer caminho de URL.
See how MongoDB 9.0 delivers up to 2x higher throughput.
MongoDB Branding Shape
Register now >
Menu Docs

Runner SDK - Arquitetura técnica

SDK de tempo de execução do Tenant independente de estrutura para o Atlas Agent Engine.

Este é um pacote principal interno ; os agentes não o instalam diretamente. Instale um SDK de framework (agent-engine-sdk-langgraph ou agent-engine-sdk-adk), que depende dele.

O SDK do Runner fornece os componentes de integração AER, Pod de Ferramentas e Servidor de Memória da arquitetura da plataforma. O mecanismo de orquestração é um serviço separado do mecanismo do Atlas Agent.

Componente
Porta
Nível de confiança
Acesso à rede
Acesso a segredos
Propósito

AER (Tempo de Execução do Agente)

8001

Médio

Somente APIs de LLM

Somente chaves LLM

Plano de execução - executa a lógica do agente

Ferramenta

8002

Varia

Conforme necessário

Conforme necessário

Plano de dados - execução de ferramenta isolada

Servidor de memória

8081

Médio

MongoDB

Credes de banco de dados

Serviço de memória para memória semântica/episódica/taxonômica

O Corredor Compartilhado detecta automaticamente a ligação do ouvinte na inicialização: :: (IPv6 pilha dupla — o uvicorn renderiza isso como http://[::]:port no formato de URL ) quando o kernel tiver IPv6 habilitado com IPV6_V6ONLY=0, caso contrário 0.0.0.0. Nenhuma configuração necessária — a produção implanta em redes IPv6first (namespaces de locatários de células) obtém :: automaticamente e o desenvolvimento local do Docker (onde a ponte IPv6 geralmente está desabilitada) volta para 0.0.0.0. O host resolvido pela sondagem é registrado na inicialização (listener bind resolved: host=...) portanto, a verificação pós-implementação não precisa inferir da própria linha de inicialização do uvicorn.

Defina APP_HOST no ambiente para substituir a sondagem — necessário em contêineres onde o kernel relata IPv6 como disponível, mas a rede circundante roteia apenas IPv4 (por exemplo, encaminhamento de porta Docker com host_ip: 127.0.0.1). O conjunto de modelos de testes de integração APP_HOST=0.0.0.0 por esse motivo.

O ouvinte usa a porta padrão fixa para o RUNNER_MODE ativo (8001/8002/8081). RUNNER_MODE é necessário e injetado pelo tempo de execução da plataforma; dev local ignora substituições do usuário .env para ele, e segredos do espaço de trabalho reservam o nome inteiramente.

A imagem CMD é python -m agent_engine_runner_shared.launcher (TypeScript: node …/launcher.js). O iniciador instala o registro antes de importar AGENT_ENTRYPOINT, portanto, uma falha no tempo de importação ou uma exceção da função de ponto de entrada é um registro ERROR (JSON estruturado quando STRUCTURED_LOGGING=true) em vez de um rastreamento bruto que os registros de espaço de trabalho mostrariam como INFO. TenantRuntime ainda liga para setup_logging / setupLogging mais tarde; essa segunda instalação é idempotente. As falhas que ocorrem antes do início do processo do iniciador (erros de intérprete/imagem) ainda dependem da heurística de leitura do API Gateway documentada em `docs/api-gateway/README.md <../../../.././docs/api- gateway/README.md# agente--operator-logs-s3-read-path>'__.

Essa separação permite: - Registro de auditoria - O OE vê todas as chamadas de ferramentas/LLM sem executá-las - Aplicação de políticas - OE pode bloquear chamadas antes da execução - Menor privilégio - Cada componente só tem o acesso necessário - Repetição - OE pode retornar resultados em cache para retomada determinística


Contrato normativo completo: `docs/runner/README.md <../../../../../docs/current/README.md#execution-Lifecycle--secret-availability>`__. As regras que um autor agente precisa para escrever o código correto:

  • O código de nível superior do módulo (qualquer coisa fora de um corpo de função) é executado uma vez na inicialização do processo, tanto no AER quanto no Pod da Ferramenta. Ele pode confiar na configuração do MCP OAuth e nos segredos de inicialização entregáveis pelo locatário, incluindo MONGODB_URI e chaves de API LLM. Sessões de invocação e credenciais de solicitação delegadas não estão disponíveis durante a inicialização. As gravações em cache do MCP OAuth (~/.agentic/mcp-oauth/<server>.json ou AGENTIC_MCP_OAUTH_DIR em tempos de execução implantados) são serializadas em um arquivo de aviso <server>.json.lock para que as atualizações simultâneas de token do mesmo servidor não possam se sobrepor umas às outras.

  • A construção de aplicação de ferramentas tenta a função @app.entrypoint durante a inicialização para descobrir cada registro app.llm(llm_id=...). As solicitações de modelo reutilizam o registro preparado. A construção usa configuração de inicialização estática e não deve executar uma curva comercial, chamada de modelo ou corpo de ferramenta. A construção com falha avisa e restaura os registros anteriores, deixando uma solicitação de modelo posterior capaz de tentar novamente. A construção bem-sucedida é armazenada em cache mesmo sem LLMs nomeados. O cache e o aquecimento de gráficos AER permanecem de propriedade da estrutura.

  • ``app.llm(llm, llm_id=...)`` deve ser chamado enquanto seu ponto de entrada estiver na pilha de chamadas — diretamente em seu corpo ou em uma função assistente que ele chamar. Chamando-o de qualquer lugar fora disso (nível superior do módulo, um corpo da ferramenta, um assistente invocado de algum lugar diferente do ponto de entrada) levanta RuntimeError imediatamente, nomeando o site de chamada ofensivo.

  • Corpos de ferramenta locais (is_local=True) são executados somente no processo da própria AER. As cargas de trabalho de AER e de ferramenta recebem segredos entregáveis pelo locatário na inicialização.

  • Corpos de ferramentas remotas (is_local=False, ou ferramentas que exigem credenciais delegadas) são executados somente no Pod de Ferramentas, sob demanda, quando a OE direciona uma chamada para eles — nunca durante a construção. as declarações required_secrets documento requisitos secretos; eles não restringem o ambiente secreto da inicialização.

  • As ferramentas credenciadas permanecem remotas mesmo que o SDK forneça seu padrão local, porque a autorização delegada e a injeção de segredo com escopo de ferramenta ocorrem somente na rota do Pod de Ferramentas.


┌────────┐ ┌─────┐ ┌─────┐
│ 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} │ │
│ ◄──────────────────────────│ │
│ │ │

Observação

Retomar por meio do gateway de API: o diagrama acima usa o caminho /resume/{id} interno da OE por brevidade. Os desenvolvedores chamam o gateway de API em POST /api/v1/projects/{project_id}/executions/{execution_id}/resume com um corpo JSON plano:

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

A forma {"human_review": {"decision": "..."}} envolta é o contrato de endpoint interno da OE; o gateway o rejeita com HTTP 400.

Quando o OE cancela uma execução de forma duradoura, ele POSTA /drain para o tempo de execução da Ferramenta/AER que o possui. O contrato do receptor (fixado pelo dispositivo compartilhado em client-libraries/test-fixtures/drain/contract.json):

  • POST /drain {request_id, execution_id, reason, deadline_at_ms, workspace_id} — em um tempo de execução com escopo de trabalho (conjunto APP_ID, ou seja, todo tempo de execução gerenciado), workspace_id é obrigatório e deve corresponder exatamente: o token do portador autentica o chamador, mas não comprova que a execução nomeada pertence a esse tempo de execução.

  • 202 {"outcome": "accepted"} durante a drenagem; 200 com o resultado final (completed / timed_out / delivery_failed) depois. accepted é somente aceitação em nível de HTTP — completed é o único sinal de inatividade.

  • Idempotente em request_id, uma drenagem por execução: uma nova tentativa releia o resultado registrado em vez de executar novamente os efeitos colaterais.

  • deadline_at_ms é absoluto e nunca estendido; o tempo de execução rejeita prazos além de RUNNER_DRAIN_MAX_DEADLINE_MS (padrão 60s). Os registros finalizados sobrevivem RUNNER_DRAIN_RECORD_TTL_S (padrão 900s) para que as novas tentativas de chamador permaneçam idempotentes durante sua janela de reconciliação — mantenha-a igual ou superior à janela de novas tentativas do chamador (OE tenta novamente 15 minutos): um TTL mais curto despeja o registro enquanto o chamador ainda está tentando novamente e os reenvios atrasados obtêm execution_not_found em vez do resultado registrado.

  • Uma execução que esse tempo de execução nunca atendeu responde delivery_failed com reason_code=execution_not_found - uma execução pode abranger os tempos de execução de AER e ferramentas de forma independente, portanto, o direcionamento exato de OE não torna um completed falso seguro.

Os limites por idioma fazem parte do contrato, não são um detalhe de implementação. O Python cancela as tarefas assíncronas monitoradas, de modo que o trabalho conjunto se estabelece como completed. Uma promessa não tem cancelamento, então o tempo de execução do TypeScript (agent-engine-runner-shared, que espelha este contrato e deve mudar junto com ele) aborta apenas AbortController s registrados — a vez AER e fluxos LLM — enquanto as funções de ferramenta simples não recebem sinal: elas são rastreados e têm entrada bloqueada, e ultrapassar o prazo é um timed_out verdadeiro. O estado é o processo local por design: um tempo de execução reiniciado perdeu seu trabalho ativo e responde delivery_failed; a reconciliação entre reinicializações pertence ao registro durável do chamador, não a esse registro.

POST /interrupt/call é o irmao cirurgico da drenagem: a interrupção por chamada do OE nomeia uma chamada por {execution_id, step_number} e o tempo de execução cancela exatamente o identificador rastreado dessa chamada — o Python cancela a tarefa de assíncrono, o tempo de execução TS cancela a AbortController da chamada — enquanto a execução permanece aberto e as chamadas posteriores prosseguem (nunca trava de entrada). Sempre 200 com um resultado: interrupted (sinalizada ou gravada para disparar no anexar), not_found (nenhuma chamada a bordo), already_settled (a chamada foi encerrada primeiro), not_cancellable (trabalho rastreado sem canal de sinal — um corpo de ferramenta de sincronização em um thread, uma função simples de ferramenta TS — relatados abertamente, nunca declararam ter sido abandonados). O escopo do espaço de trabalho corresponde à rota de drenagem e os vetores compartilhados residem em client-libraries/test-fixtures/interrupt-call/contract.json.


Chamadas de ferramenta interceptadas e invoke_llm agora usam o mesmo fluxo de execução de propriedade da OE. Quando o agente em execução no AER precisa chamar uma ferramenta ou um LLM, o wrapper envia a solicitação interceptada para o OE e aguarda que o OE retorne o resultado final. OE roteia as chamadas de ferramentas por meio de Tool Pod /execute e as chamadas LLM por meio de Tool Pod /invoke_llm e, em seguida, retorna o resultado final de sucesso, suspensão ou erro de volta para AER.

Diagrama de Sequência:

┌─────┐ ┌─────┐ ┌──────────┐
│ 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} │ │
│ │ │

Pontos principais:

  1. Cada ferramenta interceptada ou chamada LLM atravessa primeiro o OE para propriedade de política, registro e repetição.

  2. OE possui roteamento — ferramentas remotas usam Tool Pod /execute; as ferramentas locais aprovadas retornam à pilha de chamadas original do AER para que o contexto nativo da estrutura permaneça ativo; chamadas LLM interceptadas usam Tool Pod /invoke_llm.

  3. O replay usa resultados em cache — Na retomada após SUSPEND, OE retorna o resultado final armazenado para evitar efeitos colaterais duplicados.

  4. A suspensão ainda se desenrola no wrapper — OE retorna a carga útil serializada da suspensão e, em seguida, SecureToolWrapper a converte de volta à interrupção do framework.

  5. ``invoke_llm`` agora corresponde ao contrato da ferramenta — OE retorna a carga útil final de status/result/error em vez de solicitar à AER que execute após a aprovação.

  6. Os blocos de fluxo LLM preservam metadados da mensagem final — id, name, additional_kwargs e response_metadata viagens através do evento de fluxo digitado e são coletados de volta na mesma forma de resposta final usada para repetição.

O carregamento de configuração de tempo de execução agora suporta interpolação ${VAR} para uma lista limitada de caminhos agent.yaml de propriedade do locatário, atualmente mcp.servers.*.url.

Como funciona:

  • load_runtime_agent_config(env_vars=...) substitui referências ${VAR} somente em caminhos permitidos.

  • O tempo de execução passa tenant_env_vars() em vez de os.environ bruto, portanto, os envelopes e segredos de propriedade da plataforma são excluídos da substituição.

  • Se um caminho da lista de permissões fizer referência a uma variável não definida ou contiver um marcador ${...} malformado, o carregador gerará um erro de validação nítido.

  • mcp.servers.*.auth.token_env não deve ponto para um env var de propriedade da plataforma.

Exemplo:

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

No tempo de execução, o ${TABLEAU_HOST} é resolvido do subconjunto de ambientes de propriedade do locatário. Uma referência como ${OPENAI_API_KEY} é rejeitada porque essa variável pertence à plataforma e é filtrada antes da interpolação.

O componente de segurança central que intercepta chamadas de ferramentas:

# 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 chave: nenhuma ferramenta ou chamada LLM é executada sem que o OE saiba disso.


A OE pode bloquear chamadas de ferramenta/LLM com base nas regras da política:

A aplicação da política é tratada pelo Go OE. Consulte docs/orchestration-engine/README.md.

Quando um agente retoma após SUSPEND, a execução do LangGraph é repetida desde o início. Sem o cache, isso reexecutaria ferramentas e chamadas LLM, causando efeitos colaterais duplicados (por exemplo, enviando notificações duas vezes).

Como funciona:

  1. OE rastreia todas as etapas - Cada chamada de ferramenta/LLM é registrada com seu número de etapa, nome e resultado

  2. Ao retomar, o agente repete - o LangGraph é executado novamente desde o início, fazendo as mesmas chamadas em ordem

  3. OE retorna resultados em cache - Em vez de executar novamente, o OE retorna o resultado armazenado

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)

O cache de replays é tratado pelo Go OE. O AER/SecureToolWrapper lida com respostas em cache de forma transparente:

Manuseio 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

Isso garante a retomada determinística - o agente vê exatamente os mesmos resultados que via antes de suspender, evitando efeitos colaterais duplicados.

Suporta checkpointers na memória e 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

Embora adicione latência, o roteamento por OE fornece: 1. Trilha de auditar completa - Todas as chamadas são registradas 2. ponto de aplicação da política - Local único para bloquear chamadas 3. Capacidade de repetição - OE pode retornar resultados em cache para retomar 4. Agnóstico em termos de framework - OE não conhece o LangGraph

  • OAER precisa de acesso à API LLM, mas não deve ter credenciais de banco de dados

  • Aferramenta pode precisar de acesso ao banco de dados , mas não deve ter chaves LLM

  • Diferentes ferramentas podem ser executadas em diferentes ferramentas com diferentes permissões


O SDK do Runner agora oferece paridade de recursos com o monolítica agent-runtime:

funcionalidade
Agent-runtime
SDK do executor

@app.tool decorator

✅

✅

Metadados explícitos de rede/tempo limite

✅

✅

supressão de campo

✅

✅

Checkpoint do MongoDB

✅

✅

Registro de execução (JSONL + MongoDB)

✅

✅

Rastreamento OpenTelemetry

✅

✅

Integração de memória

✅

✅

Incorporações de IA do Voyage

✅

✅

Multilocatário (org_id/user_id)

✅

✅

Streaming de eventos SSE

✅

✅

Servidor gRPC

✅

✅

Suporte de transmissão

✅

✅

Human-in-the-loop

❌

✅

Arquitetura de três componentes

❌

✅

Cache de repetição

❌

✅

Instale recursos conforme necessário. Use uv add se seu projeto for gerenciado com uv ou pip install de outra forma:

# 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]"
Avalie esta página