Manus / architecture explainer

Manus 整体架构:
让模型拥有“电脑”和执行循环

这是基于公开资料的能力与数据流推演图,不是 Manus 内部私有微服务拓扑。点击节点查看解释,开启“运行数据流”观察一次任务如何从输入走向产物;切换底部标签可对比 Agent Loop、端云协同以及端侧沙箱、Capability Broker 以及分级记忆与长任务管理伪代码。

更新:2026-10-24
证据:官方文档、博客与 Google ADK
官方明确结构推断未公开 / 留白
01 / 入口与任务管理 02 / Agent Loop(核心控制面) 03 / Tools + Sandbox(执行面) 04 / 状态、并行与交付 用户 / API自然语言任务 Task / Project任务状态与上下文 Orchestrator调度 / 恢复 / 等待(结构推断) Model GatewayLLM + tool call(具体路由未公开) Agent Loop观察 → 选择动作 → 执行 → 追加上下文append-only / KV-cache / 工具可用性状态机 Tool Router工具选择与约束 Shell / Code写代码、运行脚本文件与软件工具 Cloud Browser隔离浏览与网页操作研究 / 自动化 Connectors外部数据与服务MCP / OAuth 等 Manus Sandbox隔离云 VM网络 / FS / 浏览器 My Browser本地已登录会话需用户授权 File-system Context文件化记忆 / todo.md可恢复的外部化状态 Wide Research并行 VM / 通用子 Agent规模化协作协议 Artifacts / Deliverables报告、代码、网站、文件从 Sandbox 输出 API / Webhooks异步任务与事件通知系统外部集成
读图要点:真正的“核心架构”不是单一 LLM,而是 Agent Loop + 可操作的云电脑 + 文件化上下文 + 可组合工具。模型决定下一步,Sandbox 承担真实执行,文件系统把长任务状态外置,产物再回到用户或外部系统。

1. 控制面:把自然语言变成持续决策

用户任务先进入 Task / Project 语义容器,再由控制逻辑把模型调用、工具执行、等待事件和恢复串起来。公开资料没有给出内部服务名,因此图中 Orchestrator 与 Model Gateway 使用虚线,表示合理的系统分层推断。

2. 执行面:云电脑是“手”

Sandbox 不是普通代码运行器,而是具备网络、文件系统、浏览器和软件工具的隔离云 VM。它让 Agent 能执行脚本、访问网页、生成网站和持续产出文件。

3. 上下文面:文件系统是外置记忆

Manus 官方明确提出用文件系统承载长期上下文:网页内容可以从当前上下文移出,但保留 URL;文档可以从上下文省略,但保留路径。todo.md 则把全局计划重新写回注意力末端。

4. 扩展面:Skills、Connectors、并行 Agent

Skills 提供可复用操作手册,Connectors 连接外部数据,Wide Research 通过并行 VM 和通用子 Agent 扩大计算规模。三者分别对应知识复用、数据接入和横向扩展。

任务运行时序

  1. 接收:用户/API 创建任务,任务上下文和附件进入 Task / Project。
  2. 规划:Agent Loop 读取目标、已有文件和可用工具,选择下一步动作。
  3. 执行:Tool Router 把动作分派给 Shell、Browser、Connector 或 Sandbox 内的程序。
  4. 观察:工具结果、错误和产物追加回上下文;错误不应被简单抹掉,因为它是下一轮决策的证据。
  5. 外置:长内容写入文件,todo.md 复述计划;上下文保持 append-only 以利于缓存。
  6. 交付:文件、报告、网站等 Artifact 输出给用户,API 任务则可通过状态事件 / Webhook 接收完成通知。

共同点:都是“观察—决策—行动”循环

ReAct 的经典循环是 Thought → Action → Observation。模型通过行动访问外部环境,再根据观察调整后续计划。Manus 的 Agent Loop 保留了这一认知骨架,但把它放进了更完整的运行时:上下文、工具状态、文件系统和云端执行环境共同参与下一步决策。

关系判断:Manus 不是 ReAct 的对立方案,更像是把 ReAct-like loop 工程化为可长时间运行的 Agent Harness。

核心差别:范式 vs. 生产系统

ReAct 主要回答“模型如何边推理边行动”;Manus 还要回答“如何让这个循环低成本、可恢复、可扩展地运行”。因此 Manus 把 KV-cache、工具约束、外部化记忆、错误轨迹和并行 Sandbox 纳入核心设计。

官方Manus 公开文章明确讨论 append-only 上下文、KV-cache、工具可用性状态机和文件系统上下文。
边界Manus 内部是否直接采用某个 ReAct prompt 模板,官方没有公开;这里比较的是可观察的循环结构与系统机制。

ReAct:重点在显式推理轨迹

ReActReasoning trace:通常显式产生 Thought,用于拆解目标、跟踪进度、处理异常。
ReActAction space:一般由特定任务环境定义,规模相对有限。
ReActMemory:主要依赖轨迹上下文维持工作记忆。
ReActEnvironment:可以是 Wikipedia API、网页环境、文本游戏或模拟器。

Manus:重点在可持续执行

ManusState:用 todo.md、工作文件、产物路径和工具记录外置部分任务状态。
ManusTools:保持工具定义稳定,使用状态机和解码约束屏蔽当前不可用动作。
ManusRuntime:在隔离云 VM 中执行 Shell、浏览器、文件操作和软件工具。
ManusScale:通过 Wide Research 并行启动通用子 Agent 和独立 Sandbox。
一句话:ReAct 让模型学会“边想边做”;Manus 让这种“边想边做”成为一个能处理长任务、真实文件、失败恢复、工具扩展和并行执行的生产系统。
设计结论:端侧 Agent Runtime 可以复用 ADK 的 Runner / Event Loop / Session / Memory / Artifact 抽象,但需要额外增加能力沙箱、端云 Placement、离线队列、同步协议和资源预算。推荐把“分发决策”放在 Runner 之前,把“执行”留在本地 Runner 或云端 Runner 中。
01 / 端侧 Runtime 分层

一台设备上的最小闭环

ADK 映射Runner + Event Loop:本地执行内核,接收事件,驱动 Agent / Tool,提交状态后再恢复执行。
ADK 映射SessionService:保存当前任务的事件日志、状态和 checkpoint。
ADK 映射MemoryService:查询跨会话长期知识;端侧优先本地加密索引,云端可做可选同步。
端侧新增Capability Broker:把相机、麦克风、文件、网络、通知等系统能力变成带权限和配额的工具。
端侧新增Sandbox Manager:为每个任务分配隔离 workspace、CPU / 内存 / 电量预算与网络策略。
02 / 端侧沙箱

不要直接给模型一台“裸机”

端侧沙箱的目标不是复刻云 VM,而是把高风险能力包在受控执行器后面。默认使用进程隔离或 WebAssembly;高风险原生能力通过 Capability Broker 暴露,所有调用都携带 capability token、用户授权、超时和资源配额。

安全基线任务独立工作目录;禁止默认读全盘;网络默认关闭或白名单;文件和密钥按任务命名空间隔离。
安全基线危险动作分级:读设备状态可自动执行,写入/外发/支付/删除需要确认或云端策略批准。
实现选择Android / iOS 可用原生沙箱 + 权限代理;桌面端可用进程隔离、容器或 WASM;不要把容器当作唯一安全边界。
03 / 端侧记忆

三层存储,不把所有内容都塞给模型

ADK Session工作记忆:当前窗口内的 Event + State,支持 append-only 和恢复。
端侧持久化任务记忆:本地加密 SQLite / 文件日志,存 todo、checkpoint、工具结果索引和 artifact URI。
ADK Memory长期记忆:通过 MemoryService 查询跨会话知识;敏感事实默认只留端侧,用户明确同意后再同步。
同步策略同步“记忆条目 + provenance + version”,而不是盲目同步完整对话;冲突用版本向量或事件序列解决。
04 / 长时间任务

把运行过程做成可恢复的 Durable Task

长任务不应依赖一个常驻进程或一次 HTTP 请求。每个步骤都要有稳定的 task_id、step_id、输入摘要、输出引用、幂等键和 checkpoint。

任务日志事件先写本地 durable log,再异步同步云端;设备重启后从最后一个已提交事件恢复。
调度器前台 / 后台 / 充电时运行 / 网络可用时运行分级;用 lease、heartbeat、超时和重试避免重复执行。
交接端侧低电、离线或算力不足时,在步骤边界生成 handoff manifest,把任务交给云端 Runner,完成后再回传事件和产物引用。
05 / 端云协同拓扑

通信路由总线要有,但不要让所有调用都上总线

本地层:使用进程内 Event Bus 或 ADK Event Loop 传递 Agent、Tool、Service 事件。低延迟、无需序列化,不适合拆成网络微服务。

边缘网关:用一个 Sync / Transport Gateway 负责鉴权、断线重连、事件序号、压缩、加密和 ACK。长连接可选 WebSocket / QUIC;请求响应可用 HTTP/2。

云端层:只有需要跨设备、跨服务、异步 fan-out 或高吞吐时才接入 durable event bus。不要用消息总线替代任务状态库。

A2A 的位置:当端侧 Agent 要调用“独立部署的云 Agent”时,用 A2A 或等价的远程 Agent 协议;当只是调用本地函数或同进程子 Agent 时,直接函数 / local sub-agent 更简单。

数据平面与控制平面分离:事件和任务控制走同步通道,大文件走 Artifact Store / 分块上传,不要把二进制内容塞进每个事件。

总线消息最小格式:task_id · step_id · event_id · epoch · source · target · capability · payload_ref · deadline · idempotency_key

06 / 分发模块放在哪里

放在 Runner 之前:Placement & Task Dispatcher

推荐入口链路:

1Task Intake:接收用户任务,生成 task envelope 和隐私等级。
2Capability Registry:读取端侧能力、模型版本、沙箱能力、网络、电量、温度、预算和云端可用性。
3Policy / Placement:根据隐私、延迟、成本、上下文大小、工具位置和可靠性,选择 local、cloud 或 hybrid。
4Task Dispatcher:把任务交给本地 Runner、云端 Runner,或拆成带依赖的 child tasks。
ADK 映射Runner:只负责已分发执行的 invocation;不要让模型自己决定是否跨云,也不要让每个 Tool 各自实现路由。
推荐决策:粗粒度地按“任务 / 子任务 / 工具阶段”分发,而不是按每个 token 或每一轮模型调用切换端云。这样可以减少上下文迁移、避免状态分裂,也更容易做幂等和审计。
07 / 三种运行模式

Local-first / Cloud-first / Hybrid

Local-first默认在端侧运行:隐私敏感、低延迟、离线可用、任务规模小;云端只同步授权后的记忆和产物。
Cloud-first端侧只做 UI、设备能力和会话代理;适用于大模型、重计算、长时间任务或端侧无合适模型。
Hybrid端侧做意图识别、敏感数据预处理和轻量工具;云端做复杂规划、批处理和大产物生成;通过 handoff manifest 交接。
08 / 端侧模型职责

小模型不是“缩水版云端大脑”

端侧模型适合做低延迟、隐私优先、频繁调用的工作:意图分类、工具参数补全、短规划、设备控制和本地记忆检索。云端大模型适合处理长上下文、复杂规划、跨文档综合和高风险决策复核。

建议:给端侧模型固定工具 schema、短上下文和严格输出约束;复杂任务由 Placement 层升级,不要依赖端侧模型自行判断所有系统级路由。

09 / 推荐最小实现

先做一个能恢复的单设备版本,再接云

Local App → Placement Policy → Local Runner → Event Log → Capability Broker → Sandbox / Tools → Artifact Store

                                                        ↘ Sync Gateway ↔ Cloud Runner / A2A Agent

  1. 第一阶段:本地 Runner + append-only Event Log + SQLite SessionService + 任务 workspace。
  2. 第二阶段:加入 MemoryService、ArtifactService、进程/WASM 沙箱和能力授权。
  3. 第三阶段:加入 Placement & Task Dispatcher、Sync Gateway、断线恢复和云端 handoff。
  4. 第四阶段:加入 A2A、云端 durable queue、并行 child tasks 和跨设备任务接管。
10 / ADK 对照表
Runner端侧和云端各自拥有一个 Runner;Placement 层决定调用哪个 Runner。
Event所有状态变化、工具请求、工具结果和 handoff 都统一为可序列化事件。
SessionService短期会话与 checkpoint;端侧先落盘,云端可保存副本。
MemoryService跨会话可搜索知识;同步前做隐私过滤和用户授权。
ArtifactService
大文件、图片、音频、模型输出和中间结果;用 URI / hash 在事件中引用。
A2A独立远程 Agent 的正式通信协议;不要替代同进程 local sub-agent 或普通工具函数。
PlacementADK 文档中的应用层扩展,不是 Runner 内部隐式魔法;它应位于 Task Intake 与 Runner 之间。
先说边界:下面是端侧 Agent Runtime 的工程伪代码,不是某个平台的可直接部署安全组件。生产实现必须叠加操作系统级 sandbox、签名校验、密钥隔离、资源配额、系统权限、审计和独立安全评审。Capability Broker 的核心原则是:模型永远不直接接触系统 API,所有高风险能力都经过策略、令牌和审计。
01 / 数据模型

把能力、策略和请求做成显式对象

enum Capability {
  FILE_READ, FILE_WRITE,
  NET_CONNECT, PROCESS_EXEC,
  CAMERA, MICROPHONE, NOTIFY
}

type TaskPolicy = {
  taskId: String
  userId: String
  capabilities: Set<Capability>
  allowedPaths: List<PathRule>
  allowedHosts: List<HostRule>
  maxCpuMs: Long
  maxMemoryMb: Int
  maxDiskMb: Int
  maxWallTimeMs: Long
  requireApproval: Set<Capability>
  allowCloudHandoff: Boolean
}

type CapabilityToken = {
  tokenId: String
  taskId: String
  capability: Capability
  scope: Json
  expiresAt: Instant
  nonce: Bytes
  signature: Bytes
}

type ToolRequest = {
  taskId: String
  stepId: String
  toolName: String
  args: Json
  tokenId: String?
  idempotencyKey: String
}
02 / 最小权限策略

策略不是提示词,要在执行边界强制检查

function buildPolicy(task): TaskPolicy {
  risk = classifyRisk(task.goal, task.tools)

  // 默认:无网络、无原生进程、只读任务目录
  p = TaskPolicy(
    taskId = task.id,
    userId = task.userId,
    capabilities = { FILE_READ },
    allowedPaths = [
      PathRule("workspace/" + task.id,
               read=true, write=true)
    ],
    allowedHosts = [],
    maxCpuMs = 30_000,
    maxMemoryMb = 512,
    maxDiskMb = 256,
    maxWallTimeMs = 120_000,
    requireApproval = {},
    allowCloudHandoff = true
  )

  if task.needsWeb:
    p.capabilities += NET_CONNECT
    p.allowedHosts = normalizeAllowlist(task.hosts)

  if risk >= HIGH:
    p.requireApproval += { FILE_WRITE, NET_CONNECT,
                           PROCESS_EXEC }

  return p
}

注意:路径必须 canonicalize 后再匹配,不能用字符串前缀判断;域名解析、重定向和代理也必须重新检查。

03 / Broker 签发令牌

模型只能申请能力,不能自行制造权限

class CapabilityBroker {
  policyStore: PolicyStore
  approval: ApprovalService
  signer: KeyStore
  audit: AuditLog

  async function authorize(req, ctx)
      : Result<CapabilityToken> {
    policy = policyStore.get(ctx.taskId)
    cap = toolToCapability(req.toolName)

    if cap not in policy.capabilities:
      audit.deny(req, "capability_not_granted")
      return Denied

    scope = deriveScope(cap, req.args, policy)
    if scope.invalid:
      audit.deny(req, "scope_out_of_policy")
      return Denied

    if cap in policy.requireApproval:
      decision = await approval.request(
        taskId=ctx.taskId, summary=redact(req),
        risk=cap, timeoutMs=60_000)
      if decision != APPROVED:
        audit.deny(req, "user_not_approved")
        return Denied

    token = CapabilityToken(
      tokenId=randomId(), taskId=ctx.taskId,
      capability=cap, scope=scope,
      expiresAt=now() + 60.seconds,
      nonce=randomBytes(32), signature=null)
    token.signature = signer.sign(canonicalJson(token))
    audit.grant(req, token)
    return Granted(token)
  }
}
04 / Broker 执行入口

每次工具调用都重新验证,不信任模型参数

async function execute(req, ctx): ToolResult {
  await rateLimiter.check(ctx.taskId)
  await replayGuard.check(req.idempotencyKey)

  decision = await authorize(req, ctx)
  if decision is Denied:
    return ToolError("DENIED", decision.reason)

  token = decision.token
  if !signer.verify(token):
    return ToolError("INVALID_TOKEN")
  if token.taskId != ctx.taskId:
    return ToolError("TOKEN_TASK_MISMATCH")
  if token.expiresAt < now():
    return ToolError("TOKEN_EXPIRED")

  safeArgs = enforceScope(req.args, token.scope)
  audit.start(req, token)
  try:
    result = await sandbox.run(
      capability=token.capability,
      scope=token.scope, args=safeArgs,
      budget=ctx.policy.budget)
    audit.finish(req, result.summary)
    return redactForModel(result)
  catch e:
    audit.error(req, classify(e))
    return ToolError("EXECUTION_FAILED")
}
05 / Sandbox Manager

用任务级工作区和受限执行器隔离 Agent

async function createSandbox(task, policy): SandboxHandle {
  workspace = fs.createPrivateDir(
    root="agent-workspaces/", id=task.id,
    mode=0700)

  // 生产实现:这里应调用 OS / WASM / 容器适配器
  handle = await sandboxProvider.create({
    rootfs: readOnlyRuntimeImage(),
    workdir: workspace,
    uid: freshUnprivilegedUid(),
    noNewPrivileges: true,
    seccompProfile: "agent-minimal",
    filesystem: {
      readWrite: [workspace],
      readOnly: ["/usr", "/tmp/readonly-tools"]
    },
    network: {
      mode: policy.allowedHosts ? "proxy" : "none",
      egressAllowlist: policy.allowedHosts,
      dnsRecheck: true
    },
    limits: {
      cpuMs: policy.maxCpuMs,
      memoryMb: policy.maxMemoryMb,
      diskMb: policy.maxDiskMb,
      wallTimeMs: policy.maxWallTimeMs,
      pids: 64
    },
    env: minimalEnvironment(task.id)
  })

  return handle
}

async function destroySandbox(handle) {
  await handle.stop(graceMs=2_000)
  await handle.collectLogs(to="artifact://audit/")
  await handle.destroy()
}
关键:容器 / WASM 只是适配器。真正的安全边界来自 OS 权限、syscall 过滤、网络代理、文件系统隔离、密钥不进入环境变量,以及 Broker 对每一次调用的重新校验。
06 / 端云升级与交接

在安全的步骤边界生成 handoff manifest

type HandoffManifest = {
  taskId: String
  parentStepId: String
  checkpointHash: String
  eventCursor: String
  policyDigest: String
  inputArtifactRefs: List<Uri>
  outputArtifactRefs: List<Uri>
  allowedCapabilities: Set<Capability>
  privacyClass: "LOCAL_ONLY" | "CLOUD_OK"
  expiresAt: Instant
}

async function handoffToCloud(ctx): HandoffResult {
  if ctx.policy.allowCloudHandoff == false:
    return Rejected("policy")
  if ctx.privacyClass == LOCAL_ONLY:
    return Rejected("privacy")

  checkpoint = eventLog.commitCheckpoint(ctx)
  refs = artifactStore.uploadEncrypted(
    only=checkpoint.referencedArtifacts)
  manifest = sign(HandoffManifest(
    taskId=ctx.taskId,
    parentStepId=ctx.stepId,
    checkpointHash=checkpoint.hash,
    eventCursor=checkpoint.cursor,
    policyDigest=ctx.policy.digest,
    inputArtifactRefs=refs.inputs,
    outputArtifactRefs=[],
    allowedCapabilities=cloudSafeCaps(ctx),
    privacyClass=ctx.privacyClass,
    expiresAt=now()+10.minutes))
  return transport.send("cloud", manifest)
}
07 / 可恢复执行

事件先落盘,结果幂等,再允许 Agent 继续

async function runStep(ctx, step): Event {
  stepKey = `${ctx.taskId}:${step.id}`
  if eventLog.hasCompleted(stepKey):
    return eventLog.getResult(stepKey)

  started = eventLog.append({
    type: "STEP_STARTED", stepId: step.id,
    idempotencyKey: stepKey})

  try:
    result = await broker.execute(
      request=step.toolRequest,
      ctx=ctx.withBudget(step.budget))
    completed = eventLog.append({
      type: "STEP_COMPLETED", stepId: step.id,
      resultRef=artifactStore.save(result),
      idempotencyKey=stepKey})
    return completed
  catch e:
    failed = eventLog.append({
      type: "STEP_FAILED", stepId: step.id,
      retryable=isRetryable(e), errorClass=classify(e)})
    scheduler.scheduleRetryOrHandoff(failed)
    return failed
}
08 / 设计审计清单

上线前至少验证这些失败路径

必须路径穿越、符号链接逃逸、压缩包解压穿越、重定向到非白名单域名、DNS rebinding。
必须令牌过期、跨任务复用、重放请求、撤销后的 token、并发审批和重复支付 / 写入。
必须设备重启、进程崩溃、网络断开、云端重复投递、artifact 上传一半和 checkpoint 损坏。
必须敏感数据进入日志、模型上下文、错误消息、遥测或云端 handoff manifest。
建议对每个能力定义 policy test、fuzz test、kill-and-resume test 和红队用例;安全拒绝应该是可解释事件,而不是静默失败。
核心实现原则:向量数据库只负责“找什么”,事件日志负责“发生了什么”,Task State 负责“下一步做什么”,Artifact Store 负责“大文件在哪里”。长任务恢复不是恢复最后一个 prompt,而是 Checkpoint + Event Replay + Memory Retrieval + Artifact References + Policy Revalidation。
01 / Canonical Records

先定义不可变事实与可重建索引

enum EventType {
  TASK_CREATED, GOAL_CHANGED,
  STEP_STARTED, STEP_COMPLETED, STEP_FAILED,
  MEMORY_CANDIDATE, MEMORY_CONFIRMED,
  CHECKPOINT_CREATED, TASK_PAUSED,
  HANDOFF_REQUESTED, HANDOFF_ACCEPTED,
  ARTIFACT_SAVED, TASK_COMPLETED
}

type Event = {
  eventId: String
  taskId: String
  seq: Long
  type: EventType
  createdAt: Instant
  payload: Json
  payloadRef: Uri?
  idempotencyKey: String?
  prevHash: String?
  hash: String
}

type TaskState = {
  taskId: String
  status: TaskStatus
  goal: String
  phase: String
  eventCursor: Long
  completedSteps: Set<String>
  pendingSteps: List<Step>
  blockedReasons: List<String>
  checkpointId: String?
  policyDigest: String
}

type ArtifactRef = {
  uri: Uri
  sha256: String
  mimeType: String
  sizeBytes: Long
  sensitivity: Sensitivity
}
02 / 分级存储

热状态、任务日志、向量记忆和产物分工

class StorageLayout {
  // L0:当前模型调用,永不作为事实源
  contextBuilder: ContextBuilder

  // L1:当前任务热状态,可从事件重建
  sessionDb: SQLite<SessionState>

  // L2:append-only canonical event log
  eventLog: DurableEventLog

  // L3:可删除、可重建的语义索引
  vectorIndex: LocalVectorIndex
  memoryRecords: SQLite<MemoryRecord>

  // L4:大文件、原始输出、checkpoint payload
  artifactStore: EncryptedBlobStore

  // L5:可选云端副本,不是本地事实源
  syncQueue: SQLite<SyncEnvelope>
}

function persistEvent(event): CommitResult {
  // 事务:写入事件后才更新 cursor / 热状态
  tx = eventLog.beginTransaction()
  try:
    tx.append(event)
    next = reducer.apply(sessionDb.load(event.taskId), event)
    tx.upsertSession(next)
    tx.commit()
    return Committed(next.eventCursor)
  catch e:
    tx.rollback()
    return Failed("DURABLE_WRITE_FAILED")
}
03 / 事件日志与 Reducer

任何状态变化都通过事件提交

async function appendEvent(taskId, type, payload, opts={}): Event {
  last = eventLog.last(taskId)
  event = Event(
    eventId = uuid(), taskId = taskId,
    seq = last.seq + 1, type = type,
    createdAt = clock.now(), payload = payload,
    payloadRef = opts.payloadRef,
    idempotencyKey = opts.idempotencyKey,
    prevHash = last.hash,
    hash = hashCanonical(last.hash, type, payload))

  if opts.idempotencyKey:
    old = eventLog.findByIdempotency(opts.idempotencyKey)
    if old != null: return old

  await persistEvent(event)
  return event
}

function reduce(state, event): TaskState {
  switch event.type:
    case STEP_COMPLETED:
      state.completedSteps.add(event.payload.stepId)
      state.pendingSteps.remove(event.payload.stepId)
    case TASK_PAUSED:
      state.status = PAUSED
    case HANDOFF_ACCEPTED:
      state.status = RUNNING_ON_CLOUD
    case TASK_COMPLETED:
      state.status = COMPLETED
  state.eventCursor = event.seq
  return state
}
04 / Checkpoint

保存语义状态,不保存不可移植的 KV-cache

type Checkpoint = {
  checkpointId: String
  taskId: String
  eventCursor: Long
  stateDigest: String
  goalSummary: String
  currentPhase: String
  completedSteps: List<String>
  pendingSteps: List<String>
  decisions: List<DecisionRef>
  openQuestions: List<String>
  memoryRefs: List<String>
  artifactRefs: List<ArtifactRef>
  policyDigest: String
}

async function createCheckpoint(taskId): Checkpoint {
  state = sessionDb.load(taskId)
  cp = Checkpoint(
    checkpointId=uuid(), taskId=taskId,
    eventCursor=state.eventCursor,
    stateDigest=hash(state),
    goalSummary=summarizeGoal(state),
    currentPhase=state.phase,
    completedSteps=state.completedSteps,
    pendingSteps=state.pendingSteps,
    decisions=extractDecisionRefs(taskId),
    openQuestions=state.blockedReasons,
    memoryRefs=memoryDb.refsForTask(taskId),
    artifactRefs=artifactStore.refsForTask(taskId),
    policyDigest=state.policyDigest)
  ref = await artifactStore.putJson(cp)
  await appendEvent(taskId, CHECKPOINT_CREATED,
      {checkpointId: cp.checkpointId}, {payloadRef: ref.uri})
  return cp
}
05 / 记忆记录与提取

候选记忆先验证,再进入长期记忆

type MemoryRecord = {
  memoryId: String
  scope: "task" | "user" | "device"
  kind: "fact" | "constraint" | "preference" | "decision" | "lesson"
  content: String
  embedding: Vector?
  embeddingModel: String?
  sourceEventId: String
  sourceArtifactRef: Uri?
  confidence: Float
  confirmedByUser: Boolean
  validFrom: Instant
  validUntil: Instant?
  sensitivity: Sensitivity
  supersedes: String?
  status: "candidate" | "active" | "superseded" | "expired"
}

async function extractMemory(event): List<MemoryCandidate> {
  if event.type in {STEP_STARTED, STEP_FAILED}:
    return [] // 默认不把噪声写入长期记忆
  candidates = localModel.extractStructured(
    text=event.payload.text,
    schema=MemoryCandidateSchema,
    instruction="只提取未来可能复用的事实、约束、决策或经验")
  return filter(candidates,
    x => x.confidence >= 0.65 and x.hasEvidence)
}

async function promoteMemory(candidate, event): MemoryRecord? {
  similar = memoryDb.findSimilar(candidate.content,
    filters={scope: candidate.scope, status: "active"})
  merged = resolveConflict(similar, candidate)
  if merged.requiresUserConfirmation:
    return memoryDb.insertCandidate(merged)
  record = memoryDb.activate(merged, sourceEventId=event.eventId)
  await appendEmbeddingJob(record.memoryId)
  return record
}
06 / 本地向量检索

混合检索:语义 + 关键词 + 新鲜度 + 任务相关性

async function retrieveMemory(query, ctx): List<MemoryRecord> {
  parsed = queryParser.parse(query)
  filters = {
    status: "active",
    scope: inSet(["user", "device", "task"]),
    sensitivity: allowedBy(ctx.policy),
    taskId: ctx.taskId or ANY
  }

  vectorHits = vectorIndex.search(
    embed(query), topK=40, filters=filters)
  keywordHits = memoryDb.fullTextSearch(
    parsed.terms, topK=40, filters=filters)
  candidates = deduplicate(vectorHits + keywordHits)

  scored = candidates.map(m => ({
    memory: m,
    score: 0.45 * m.semanticScore
      + 0.20 * m.lexicalScore
      + 0.15 * taskRelevance(m, ctx)
      + 0.10 * m.confidence
      + 0.10 * freshness(m, clock.now())
  }))

  ranked = rerank(scored, query, ctx)
  return diversifyAndBudget(
    ranked, maxTokens=ctx.memoryTokenBudget)
}

function freshness(m, now): Float {
  halfLife = {
    preference: 180.days,
    lesson: 30.days,
    tool_failure: 14.days,
    task_state: INFINITE
  }[m.kind]
  return validAt(m, now)
    ? expDecay(now - m.validFrom, halfLife)
    : 0.0
}
07 / 上下文构建

把检索结果变成有来源的上下文片段

async function buildContext(taskId, step): Context {
  state = sessionDb.load(taskId)
  recent = eventLog.tail(taskId, maxEvents=20)
  memories = await retrieveMemory(
    query=step.goal, ctx=state.context)

  // 模型看到的是证据包,不是无来源的散文
  evidence = memories.map(m => {
    id: m.memoryId,
    text: m.content,
    source: m.sourceEventId,
    artifact: m.sourceArtifactRef,
    confidence: m.confidence,
    validUntil: m.validUntil
  })

  return Context(
    stableSystemPrompt=systemPrompt,
    goal=state.goal,
    checkpoint=state.checkpointSummary,
    activeConstraints=state.activeConstraints,
    recentEvents=redact(recent),
    retrievedMemory=evidence,
    artifactRefs=state.artifactRefs,
    toolSchema=toolRegistry.for(step))
}
重要:记忆检索结果必须携带来源和有效期。模型不能把“候选记忆”当成没有不确定性的系统事实。
08 / 断点恢复

Checkpoint + Event Replay + Policy Revalidation

async function resumeTask(taskId): ResumeResult {
  cp = checkpointStore.latest(taskId)
  if cp == null:
    cp = checkpointStore.fromTaskCreated(taskId)

  currentPolicy = policyStore.current(taskId)
  state = checkpointToState(cp)
  tail = eventLog.readAfter(taskId, cp.eventCursor)
  for event in tail:
    state = reduce(state, event)

  if cp.policyDigest != currentPolicy.digest:
    state = revalidatePendingSteps(state, currentPolicy)
  if state.status in {COMPLETED, CANCELLED}:
    return Done(state)

  next = planner.nextRunnableStep(state)
  ctx = await buildContext(taskId, next)
  return runner.resume(
    taskId=taskId, state=state,
    nextStep=next, context=ctx)
}
09 / 长任务调度

拆成可抢占、可重试、可休眠的小步骤

async function schedulerTick() {
  tasks = taskStore.findRunnable(limit=20)
  for task in tasks:
    device = deviceMonitor.snapshot()
    if !resourcePolicy.allows(task, device):
      await appendEvent(task.id, TASK_PAUSED,
        {reason: resourcePolicy.reason(device)})
      continue

    lease = leaseStore.acquire(task.id,
      owner=device.id, ttl=60.seconds)
    if !lease.acquired: continue

    try:
      step = planner.nextRunnableStep(task.state)
      result = await runIdempotentStep(task, step)
      if shouldCheckpoint(task, result):
        await createCheckpoint(task.id)
    finally:
      leaseStore.release(lease)
}

async function runIdempotentStep(task, step) {
  key = `${task.id}:${step.id}:${step.inputHash}`
  old = eventLog.completedByIdempotency(key)
  if old: return old.payload
  await appendEvent(task.id, STEP_STARTED,
    {stepId: step.id}, {idempotencyKey: key})
  result = await toolRunner.execute(step)
  await appendEvent(task.id, STEP_COMPLETED,
    {stepId: step.id, resultRef: save(result)},
    {idempotencyKey: key})
  return result
}
10 / 压缩与淘汰

压缩历史,但保留恢复路径

async function compactTask(taskId) {
  state = sessionDb.load(taskId)
  events = eventLog.all(taskId)
  if events.length < 50: return

  summary = localModel.summarize({
    goal: state.goal,
    completed: state.completedSteps,
    events: events.slice(-200)
  }, schema=StageSummarySchema)

  summaryRef = await artifactStore.putJson({
    summary, sourceRange: [events[0].seq, events[-1].seq],
    sourceHash: merkleRoot(events)
  })

  // 删除的是热上下文,不是 canonical event log
  await sessionDb.setSummary(taskId, summaryRef)
  await createCheckpoint(taskId)
  await memoryExtractor.promoteFacts(taskId)
  return summaryRef
}

function evictionScore(m, now): Float {
  if m.confirmedByUser: return -INFINITE
  if m.kind == "task_state" and taskOpen(m.taskId):
    return -INFINITE
  return 0.30 * age(m, now)
    + 0.25 * inverseAccess(m)
    + 0.20 * inverseConfidence(m)
    + 0.15 * storageCost(m)
    + 0.10 * lowReuseProbability(m)
}
11 / 端云同步

同步 canonical records,不直接复制向量索引

async function syncOutbox() {
  batch = syncQueue.nextBatch(maxBytes=1_000_000)
  for item in batch:
    if !privacyPolicy.allows(item):
      syncQueue.markBlocked(item, "LOCAL_ONLY")
      continue

    envelope = encryptAndSign({
      deviceId: device.id,
      taskId: item.taskId,
      cursor: item.cursor,
      type: item.type,
      payloadRef: item.payloadRef,
      previousHash: item.previousHash
    })
    ack = await transport.send(envelope)
    if ack.accepted:
      syncQueue.ack(item.id, ack.serverCursor)
}

async function mergeCloudDelta(delta) {
  verifySignature(delta)
  for event in delta.events:
    if !eventLog.has(event.eventId):
      await persistEvent(event)
  for memory in delta.memoryRecords:
    memoryDb.merge(resolveConflict(
      memoryDb.find(memory.memoryId), memory))
  await rebuildDerivedIndexesIfNeeded(delta)
}
12 / 索引重建

向量数据库是派生数据,损坏后可重建

async function rebuildVectorIndex(modelVersion) {
  next = LocalVectorIndex.createTemporary(
    modelVersion=modelVersion)
  cursor = 0
  while true:
    batch = memoryDb.activeRecordsAfter(cursor,
      limit=128, allowed=privacyPolicy.localIndex())
    if batch.empty: break
    vectors = await embedder.embed(
      batch.map(x => x.content))
    next.upsert(batch.zip(vectors))
    cursor = batch[-1].rowId
    await scheduler.yieldIfNeeded()
  next.commitAtomically()
}

async function deleteMemory(memoryId) {
  memoryDb.markDeleted(memoryId)
  vectorIndex.delete(memoryId)
  syncQueue.enqueue({type: "MEMORY_DELETE", memoryId})
  // 原始来源事件按审计/合规策略保留或擦除
  await audit.record("MEMORY_DELETED", memoryId)
}
13 / 端侧完整闭环

从任务创建到数周后恢复

async function runLongTask(task) {
  await appendEvent(task.id, TASK_CREATED,
    {goal: task.goal, policyDigest: task.policy.digest})

  while !taskStore.isTerminal(task.id):
    try:
      result = await resumeTask(task.id)
      if result.needsApproval:
        await waitForUser(task.id)
      if result.needsNetwork:
        await waitForNetwork(task.id)
      if result.needsCloud:
        manifest = await handoffToCloud(task.id)
        await waitForCloudMerge(manifest)
      if result.stepFinished:
        await extractAndPromoteMemory(result.events)
      if shouldCheckpoint(task.id):
        await createCheckpoint(task.id)
      if storageNearLimit():
        await compactTask(task.id)
    catch CrashOrShutdown:
      // 进程可直接退出;事件已落盘,下一次 tick 会恢复
      return Paused

  await appendEvent(task.id, TASK_COMPLETED,
    {finalArtifacts: artifactStore.refsForTask(task.id)})
  await memoryExtractor.commitReusableLessons(task.id)
}

官方明确

  • 每个任务分配隔离的 Manus Sandbox 云虚拟机。
  • Sandbox 具备网络、文件系统、浏览器和软件工具。
  • Agent Loop 采用工具调用、追加上下文、文件系统上下文和工具可用性状态机等设计。
  • Skills 以文件系统资源形式渐进披露;Connectors 与 Skills 分工互补。
  • Wide Research 使用并行处理和通用子 Agent。
  • 可使用 My Browser 连接本地已登录浏览器,但需用户授权。

未公开 / 不应臆测

  • 内部微服务数量、服务边界、数据库类型和队列实现。
  • 具体使用哪些基础模型、路由策略和 fallback 规则。
  • 沙箱底层虚拟化技术、网络隔离细节和资源调度算法。
  • 权限、审计、密钥托管的完整内部实现。
  • Wide Research 的精确拓扑、聚合协议和成本控制算法。

因此本图适合做产品理解、技术交流和竞品分析;不应当作为 Manus 内部部署图或安全合规证明。

Sources
  1. Context Engineering for AI Agents: Lessons from Building Manus — Agent Loop、KV-cache、工具状态机、文件系统上下文、todo.md 与错误保留。
  2. Understanding Manus sandbox - your cloud computer — 隔离云 VM、网络、文件系统、浏览器、生命周期与安全边界。
  3. Manus AI Embraces Open Standards: Integrating Agent Skills — Skills、渐进披露、MCP/Connectors 的分工。
  4. Introducing Wide Research — 并行处理、通用子 Agent 与云端计算扩展。
  5. Introducing Manus Browser Operator — 云浏览器与本地浏览器连接器的边界。
  6. Manus API Introduction 与 Task Lifecycle — API 任务、异步状态与事件集成。
  7. ReAct: Synergizing Reasoning and Acting in Language Models 与 Google Research 对 ReAct 的说明 — Thought / Action / Observation 循环与外部环境交互。
  8. Google ADK Agent Runtime、Event Loop、MemoryService、Artifacts 与 A2A — Runner、事件循环、会话记忆、文件产物和远程 Agent 协作。

注:日期和产品能力会随版本变化;本页面采用 2026-10-24 可访问的公开资料,内部实现部分以“结构推断”明确标注。