对于 AI 代理:可在 https://www.mongodb.com/zh-cn/docs/llms.txt 获取文档索引—通过在任何 URL 路径后添加 .md 可获取所有页面的 Markdown 版本。
See how MongoDB 9.0 delivers up to 2x higher throughput.
MongoDB Branding Shape
Register now >
Docs 菜单

Runner SDK — 技术架构

适用于Atlas助手引擎的与框架无关的租户运行时 SDK。

这是一个内部核心包;代理不会直接安装它。安装依赖于它的框架SDK(agent-engine-sdk-langgraph 或 agent-engine-sdk-adk)。

Runner SDK 提供平台架构的 AER、Tool Pod 和内存服务器集成组件。编排引擎是一项独立的Atlas助手引擎服务。

组件
端口
信任级别
网络访问
密钥访问
用途

AER(代理执行运行时)

8001

中型

仅限 LLM API

仅限法学硕士密钥

执行平面 — 运行代理逻辑

工具

8002

不定

按需

按需

数据平面 — 隔离工具执行

内存服务器

8081

中型

MongoDB

数据库信用

用于语义/情景/分类记忆的内存服务

共享亚军会在初创企业时自动检测监听器绑定:当内核启用了带有 IPV6_V6ONLY=0 的 IPv6 时,为 ::(IPv6 双栈 — uvicorn 将其呈现为URL形式的 http://[::]:port),否则0.0.0.0。无需配置 — 在 IPv6 优先网络(单元租户命名空间)上部署的生产环境会自动获取 ::,而本地Docker开发者(通常禁用网桥 IPv6)则回退到 0.0.0.0。探测解析的托管会在初创企业(listener bind resolved: host=...) 时进行记录,因此部署后验证不必从 uvicorn 自己的初创企业行进行推断。

在环境中设置 APP_HOST 以覆盖探测器 — 在内核报告 IPv6 可用但周围网络仅路由 IPv4(例如使用 host_ip: 127.0.0.1 的Docker端口转发)的容器中是必需的。因此,集成测试 Compose 模板设立为 APP_HOST=0.0.0.0。

监听器使用活动 RUNNER_MODE (8001/8002/8081) 的固定默认端口。 RUNNER_MODE 是必需的,由平台运行时注入;本地开发人员会忽略用户 .env 对其进行的覆盖,工作区密钥会完全保留该名称。

映像 CMD 为 python -m agent_engine_runner_shared.launcher(TypeScript:node …/launcher.js)。启动器在导入 AGENT_ENTRYPOINT 之前安装日志记录,因此导入时崩溃或入口点函数的异常是 ERROR记录(STRUCTURED_LOGGING=true 时为结构化JSON ),而不是工作区日志显示为 INFO。 TenantRuntime 稍后仍会调用 setup_logging / setupLogging;第二次安装是幂等的。在API器进程启动之前发生的故障(解释器/映像错误)仍然依赖于 `docs/api-gateway/README.md <../../../../../docs/api- gateway/README.md# 代理--operator-logs-s3-read-path>`__.

这种分离可以实现: - 审核日志记录 - OE 可以看到每个工具/LLM 调用而不执行它们 - 策略执行 - OE 可以在执行前区块调用 - 最小权限- 每个组件仅具有其需要的访问权限- 重放 - OE 可以返回缓存的结果以实现确定性恢复


完整的规范合同:`docs/runner/README.md <../../../../../docs/runner/README.md#execution-lifecycle--secret-availability>`__。代理作者写入正确代码所需的规则:

  • 在 AER 和 Tool Pod 中,模块顶层代码(函数体之外的任何代码)会在进程初创企业时运行一次。它可以依赖 MCP OAuth 配置和租户可交付的初创企业密钥,包括 MONGODB_URI 和 LLM API密钥。初创企业期间,调用会话和委派请求凭证不可用。 MCP OAuth缓存写入(~/.agentic/mcp-oauth/<server>.json 或已部署的运行时中的 AGENTIC_MCP_OAUTH_DIR)在咨询 <server>.json.lock文件上进行序列化,因此同一服务器的并发令牌刷新不会相互干扰。

  • 工具应用程序构建会在初创企业期间尝试使用 @app.entrypoint 函数来发现每个 app.llm(llm_id=...) 注册。模型请求会重复使用准备好的注册表。构建使用静态初创企业配置,不得执行业务轮换、模型调用或工具主体。构建失败会发出警告并恢复之前的注册,以便后续的模型请求能够重试。即使没有命名 LLM,也会缓存成功的构造。 AER图表缓存和预热仍由框架所有。

  • ``app.llm(llm, llm_id=...)`` must be called while your entrypoint is on the call stack — directly in its body, or in a helper function it calls. Calling it from anywhere outside that (module top level, a tool body, a helper invoked from somewhere other than the entrypoint) raises RuntimeError immediately, naming the offending call site.

  • 本地工具体 (is_local=True) 仅在 AER 自身的进程中运行。 AER 和工具工作负载都会在初创企业时接收租户可交付的密钥。

  • 远程工具体(is_local=False 或需要委派凭证的工具)仅在 OE 将调用路由到它们时按需在 Tool Pod 中运行,而不会在构造期间运行。 required_secrets 声明文档密钥要求;它们不会缩小初创企业密钥环境的范围。

  • 即使 SDK 提供了本地默认,凭证工具仍处于远程状态,因为委托授权和工具范围的密钥注入仅发生在 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} │ │
│ ◄──────────────────────────│ │
│ │ │

注意

通过API网关恢复:为简洁起见,上图使用了 OE 的内部 /resume/{id} 路径。开发者使用平面JSON正文调用位于 POST /api/v1/projects/{project_id}/executions/{execution_id}/resume 的API网关:

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

封装的 {"human_review": {"decision": "..."}} 形状是 OE 的内部端点协定;网关使用HTTP 400 拒绝它。

当操作环境持久取消执行时,它会向拥有该执行的工具/AER 运行时发布 /drain。接收者合约(由 client-libraries/test-fixtures/drain/contract.json 中的共享装置固定):

  • POST /drain {request_id, execution_id, reason, deadline_at_ms, workspace_id} — 在工作区范围的运行时(APP_ID设立,即每个托管运行时)上,workspace_id 是强制性的,并且必须完全匹配:持有令牌对调用者进行身份验证,但不证明命名执行属于此运行时。

  • 202 {"outcome": "accepted"} 同时排干;随后将 200 替换为最终结果 (completed / timed_out / delivery_failed)。 accepted 是仅HTTP级接受 — completed 是唯一的静止信号。

  • 在 request_id 上幂等,每次执行一次排空:重试会重新读取记录的结果,而不是重新运行副作用。

  • deadline_at_ms 是绝对的且从不扩展;运行时会拒绝超过 RUNNER_DRAIN_MAX_DEADLINE_MS(默认60)的截止日期。最终确定的记录可在 RUNNER_DRAIN_RECORD_TTL_S(默认900)后继续存在,因此调用者重试可在其协调窗口中保持幂等— 将其保持在调用者的重试窗口或之上(OE 在 15 分钟内重试排空):较短的TTL会逐出记录当调用者仍在重试时,延迟重新发送得到 execution_not_found 而不是记录的结果。

  • 此运行时的执行从未使用 reason_code=execution_not_found 提供 delivery_failed 的答案 — 一次执行可以独立跨越 AER 和工具运行时,因此精确的 OE 目标不会使错误的 completed 安全。

每种语言的限制是合同的一部分,而不是实施细节。 Python取消跟踪的异步任务,因此协作工作结算为 completed。 Promise 无法取消,因此 TypeScript 运行时(agent-engine-runner-shared,镜像此合同,必须随之更改)仅中止注册的 AbortController — AER 轮次和 LLM 流 — 而普通工具函数不会收到信号:会被跟踪并阻止准入,超过截止日期则为诚实的 timed_out。状态设计为进程本地化:重新启动的运行时已丢失其活动工作并回答 delivery_failed;重启后的协调属于调用者的持久性记录,而不是此注册表。

POST /interrupt/call 是排出的外科同级:OE 的每次调用中断以 {execution_id, step_number} 命名一次调用,运行时准确中止该调用的跟踪处理— Python取消异步任务,TS 运行时中止调用的 AbortController — 而保持打开状态,后续调用将继续进行(永远没有准入锁存器)。始终 200,其结果为:interrupted(在连接时发出信号或记录为触发)、not_found(无此类进行中调用)、already_settled(先结束调用)、not_cancellable(跟踪工作)没有信号渠道— 线程上的同步工具体、普通 TS 工具函数 — 如实报告,从未声称已被放弃)。工作区范围与排出路由的范围匹配,共享向量位于 client-libraries/test-fixtures/interrupt-call/contract.json 中。


截获的工具调用和 invoke_llm 现在使用相同的 OE 拥有的执行流。当在 AER 中运行的代理需要调用工具或 LLM 时,包装器会将拦截的请求发送到 OE,并等待 OE 返回最终结果。 OE 通过 Tool Pod /execute 路由工具调用,通过 Tool Pod /invoke_llm 路由 LLM 调用,然后将最终的成功、暂停或错误结果返回 AER。

序列图:

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

要点:

  1. 每个截获的工具或 LLM 调用都会首先跨越 OE 以实现策略、日志记录和重放所有权。

  2. OE 拥有路由 — 远程工具使用 Tool Pod /execute;已批准的本地工具返回到原始 AER 调用堆栈,因此框架原生上下文保持活动状态;截获的 LLM 调用使用 Tool Pod /invoke_llm。

  3. 重放使用缓存的结果 — 在暂停后恢复时,OE 会返回存储的最终结果以防止重复的副作用。

  4. 挂起仍然通过包装器展开 — OE 返回序列化的挂起有效负载,然后 SecureToolWrapper 将其转换回框架中断。

  5. “invoke_llm” 现在与工具合同匹配 — OE 返回最终的 status/result/error 有效负载,而不是要求 AER 在批准后执行。

  6. LLM流段保留最终消息元数据— id、name、additional_kwargs 和 response_metadata 遍历类型化流事件,并收集回用于重放的相同最终响应形状。

运行时配置加载现在支持对租户拥有的 agent.yaml 路径(目前为 mcp.servers.*.url)的狭窄允许列表进行 ${VAR} 插值。

作用方式:

  • load_runtime_agent_config(env_vars=...) 仅在已列入允许路径的路径替换 ${VAR} 引用。

  • 运行时传递 tenant_env_vars() 而不是原始 os.environ,因此平台拥有的环境变量和密钥不被替换。

  • 如果列入白名单的路径引用了未设置的变量,或包含格式错误的 ${...} 标记,则加载程序会引发明显的验证错误。

  • mcp.servers.*.auth.token_env 不得点平台拥有的环境变量。

示例:

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

在运行时,${TABLEAU_HOST} 是从租户拥有的环境子集解析得到的。像 ${OPENAI_API_KEY} 这样的引用会被拒绝,因为该变量是平台拥有的,并在插值之前被过滤掉。

拦截工具调用的中央安全组件:

# 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

键不变量:任何工具或 LLM 调用的执行都是 OE 不知道的。


OE 可以根据策略规则区块工具/LLM 调用:

策略执行由Go操作环境处理。请参阅 docs/orchestration-engine/README.md。

当代理在 SUSPEND 后恢复时,LangGraph 执行会从头开始重放。如果没有缓存,这将重新执行工具和 LLM 调用,从而导致重复的副作用(例如,发送通知两次)。

作用方式:

  1. OE 跟踪所有步骤 - 每个工具/LLM 调用都记录其步骤编号、名称和结果

  2. 恢复时,代理会重放 - LangGraph 从头开始重新运行,按顺序进行相同的调用

  3. OE 返回缓存的结果 — OE 返回存储的结果,而不是重新执行

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)

重放缓存由Go OE 处理。 AER/SecureToolWrapper 透明地处理缓存的响应:

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

这确保了确定性的恢复 —代理看到的结果与挂起之前看到的结果完全相同,从而防止重复的副作用。

支持内存检查点和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

尽管增加了延迟,但通过 OE 进行的路由可提供:1。完整的Atlas 审核跟踪 — 每次调用都会被记录 2。策略执行点-区块调用 3 的单个位置。重放功能— OE 可以返回恢复 4 的缓存结果。与框架无关 — OE 不了解 LangGraph

  • AER 需要 LLM API访问权限,但不应具有数据库凭证

  • 工具可能需要访问权限数据库,但不应具有 LLM 密钥

  • 不同的工具可以在具有不同权限的不同工具中运行


Runner SDK 现在提供与单片 agent-runtime 相同的功能:

功能
代理运行时
Runner SDK

@ 应用.tool 装饰器

✅

✅

显式网络/超时元元数据

✅

✅

字段编辑

✅

✅

MongoDB检查点

✅

✅

执行日志记录 (JSONL + MongoDB)

✅

✅

OpenTelemetry 跟踪

✅

✅

内存集成

✅

✅

Voyage AI嵌入

✅

✅

多租户 (org_id/user_id)

✅

✅

SSE 事件流

✅

✅

gRPC Server

✅

✅

流媒体支持

✅

✅

人机交互

❌

✅

三组件架构

❌

✅

重放缓存

❌

✅

根据需要安装功能。如果您的项目使用 uv 进行托管,则使用 uv add,否则使用 pip install:

# 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]"
给本页内容打分