Skip to content

Core zh

pawaca edited this page Aug 30, 2026 · 5 revisions

核心

上游 Agent 循环、Agent 注册表和 turn 生命周期在 Edge 中的适配。

上游参考:Core

上游提供了什么

核心子系统提供每个组合都依赖的基础 Agent 基础设施:

  • AgentRegistryctx.agents)— 创建、恢复和销毁 Agent。每个 Agent 拥有一个 session、一个 inbox(next-turn / next-step 消息队列)和一个作用域 cordis context。销毁器是一个能力——只有创建者能拆除 Agent。
  • AgentLoop — 实现 Agent 契约的具体驱动。通过 step 管线处理 inbox 消息:pre-step 验证 → 模型请求 → 流式 → 工具执行 → post-step。在 idlerunning 状态之间转换。
  • Agent 事件 — 作用域过滤分发:agent/createdagent/disposedagent/pre-stepagent/requestagent/turn-stoppingagent/status 和 inbox 事件。
  • Inbox 管理 — 两个有序消息列表(next-turnnext-step),支持 append、prepend、replace、remove、clear 和 splice 操作,全部记录为持久化的 agent/inbox/spliced 事件。

Edge 改了什么

直接复用 AgentRegistry + AgentLoop

两个插件原封不动安装。Agent 创建、session 绑定、inbox 管理、step 管线、模型流式和工具分发完全是上游代码。

传输桥 Turn 生命周期管理

上游的 agent loop 和调用者在同一个进程——turn 在调用者发消息时开始,Agent 空闲时结束。Edge 在此基础上加了传输层,因为调用者(浏览器)是远程的:

  • activeTurns map — 追踪 DO 内哪些 session 有正在运行的 turn。防止交叉 HTTP 请求对同一 session 发起并发 turn。
  • claimAndOpenAgent() — 原子地占位 session 并在接受 prompt 前打开/恢复 Agent。确保没有两个请求同时拥有同一个 session。
  • runAgentTurn() — 用 flush-then-publish 管线包装上游 turn:监听 session/event,flush 到 DO SQL,然后通过 WebSocket 发布事件。在 turn 期间绑定 shell 后端。
  • 准入控制createDurablePromptAdmitter 在 persistence flush 之后才准入 prompt,确保用户消息在模型看到之前已持久化。

上游的 agent loop 在这个包装器内部原样运行——它不知道自己被观察并通过网络发布。

传输桥 状态广播

Turn 开始时,Edge 广播 host/session-status { running: true }。结束时广播 { running: false }。上游直接从内存读 agent.status;Edge 把这个翻译成网络帧。

消息发送模式

Edge 端到端支持上游的两种消息模式:

模式 时机 UI 触发 后端路径
queue Agent 空闲 用户在无 turn 运行时发消息 session.prompt(mode:'queue')agent.send() → next-turn inbox → turn 开始时处理
steer Agent 运行中 用户在 turn 活跃时发消息 session.prompt(mode:'steer')agent.steer() → next-step inbox → 下一个 step 边界处理

客户端的 composer 策略自动选择模式:resolve(running, gesture, steeringAvailable) — 如果 agent 正在运行且 steering 可用,消息走 steer;否则走 queue。

队列管理updateQueue() 让客户端编辑排队消息文本、删除待处理消息或将其提升为 steer。Edge 在每次 inbox 变更和重连 baseline 时广播 session/queue 帧,确保客户端始终显示当前的待处理状态。

TODO

调查 steer 模式的 UI 行为。#114)后端支持 queue 和 steer 两种模式。客户端 composer 策略根据 agent 运行状态自动选择。但 UI 在 agent 运行时没有显示可见的模式切换或 steer 指示——需要调查确认 steer 是否在透明工作,还是存在 UI 缺口。

Edge 没有改什么

  • Agent 创建、session 绑定和销毁生命周期
  • Inbox 管理和消息队列语义
  • Step 管线(pre-step → request → streaming → tool execution → post-step)
  • 作用域过滤事件分发
  • 模型请求准备和重试策略
  • Turn-stopping 协商(agent/turn-stopping 瀑布)

性能特征

Turn 生命周期开销

Edge 在上游 turn 基础上增加三项代价:

  • 占位检查:每个 prompt 一次 Map 查找验证无并发 turn — O(1),亚微秒。
  • Flush 屏障:每个事件在 WebSocket 发布前 flush 到 DO SQL。每事件增加约 1ms 同步 SQL 写入。上游在检查点边界 flush;Edge 逐事件 flush 以提供更强的持久性保证。
  • Shell 绑定EdgeShellBindings.bind() 在 turn 期间关联 Computer VFS workspace 和 Agent — 一次 Map 插入,在 finally 中释放。

并发 session

一个 Durable Object 实例(名为 'owner')管理所有 workspace 和 session——镜像了上游的单进程架构。activeTurns map 确保每个 session 最多运行一个 turn,不同 session 可在同一 DO 内交错执行。隔离来自应用层的作用域(cordis agent scope、VFS workspace 路径),而非独立的运行时实例。

Agent 跨 turn 复用

当 session 已有活跃 Agent(来自之前未销毁的 turn),openAgentForTurn 复用它——无 Agent 创建开销。冷 session(DO 重启后)需要付 Agent 恢复代价:从 DO SQL 加载 session header + 事件日志 + 重建内存状态。

架构总结

组件 分类 Edge 代码
AgentRegistry 复用 一行 ctx.plugin() 调用
AgentLoop 复用 一行 ctx.plugin() 调用
Turn 生命周期包装 桥接 runAgentTurn() + claimAndOpenAgent()
状态广播 桥接 host/session-status WebSocket 帧

关键观察:核心 Agent 基础设施完全运行上游代码。Edge 用 claim → flush → publish 管线包装每个 turn 来桥接 DO 和浏览器之间的网络间隙。Agent loop 不知道自己运行在 Cloudflare 上——它看到的是同样的 cordis context、同样的 session 和同样的工具注册表。

已知限制与未来方向

当前的单 DO 架构镜像了上游的单进程设计,但受 Cloudflare 平台特有的天花板限制:

  • 内存:每个 DO 128 MB,所有活跃 session 共享——限制大上下文窗口下的并发 turn 数量。
  • SQL 存储:每个 DO 10 GB,存放所有 session 的事件日志——长期使用的部署会逐渐逼近上限。
  • 并发:DO 是单线程的——多个 session 交错执行但无法真正并行。
  • Subagent 隔离:同一 DO 内的 Agent 共享内存,无法做资源隔离。

自然演进方向是 session-per-DO:每个 session 拥有独立的 Durable Object,独立的内存、存储和生命周期。这需要:

  • 路由 DO(或入口 Worker)管理 workspace → session → DO-id 的映射。
  • 分层 VFS:Agent 级共享存储(workspace 代码、配置)可跨 session DO 访问,加上 session 本地存储(临时文件、spill、工具输出)隔离在各自 DO 内。共享层可用 R2 或专用 workspace DO。
  • Subagent via DO fork:子 Agent 作为独立 DO 启动,拥有自己的内存预算,通过 DO-to-DO fetch 将结果回传给父 Agent。

English

中文

Clone this wiki locally