把任务交给云端之后:Open Nua Cloud Agent Runtime 的工程实践
Open Nua Engineering
把一个 Agent 放到服务器上,让它调用模型、执行工具、把结果流回客户端,并不难。真正改变系统设计的,是接下来的几个问题:用户离开以后,谁负责让任务继续?请求超时了,能不能再发一次?发布了新版本,昨天的会话应该用哪份指令?任务等待人工确认时,执行身份的权限被撤销了怎么办?
这些问题决定了 Cloud Agent Runtime 能否成为产品里的基础能力。
在执行与客户端解耦的文章里,我们讨论过如何让桌面任务不再依附某个窗口。这次把执行交给独立 Runtime,边界又向前推进了一步:客户端可以离开,执行服务要继续拥有任务;进程可以替换,已经确认的事实必须留下。
这篇文章记录 Open Nua 在这个过程中做出的工程选择。重点是任务从交付、受理、执行到恢复的完整责任链。
先分清谁拥有哪一份事实
最容易走的捷径,是把登录、模型代理、会话连接、任务执行、沙箱管理都放进同一个服务。最初只需串通一次请求,后来每次重启、扩展或者授权变更,却都会牵动整条链路。
我们把职责拆成几部分:
| 角色 | 拥有的事实 |
|---|---|
| 交付控制面 | 固定版本、部署配置、发布入口和访问策略 |
| Gateway | 身份授权、短期执行凭证、受控模型与工具代理、配额和用量 |
| Session Host | 客户端可见的会话、消息、工具、人工输入与状态投影 |
| Job Supervisor | 持久任务、执行尝试、所有权、取消、期限与恢复 |
| Worker 与 Sandbox | 本次推理和工具执行、checkpoint,以及隔离的工具工作环境 |
客户端提交意图、展示结果。Host 让它看见一致的会话;Supervisor 管理实际执行;Gateway 在调用经过权限边界时验证授权。
图里把持久状态放在一起,是为了强调它们都不能只留在进程内存中。这些状态仍有不同的所有者:会话投影用于重建页面,Job 事件用于追踪执行,checkpoint 用于继续计算。它们不能相互替代。
拆分以后,登录或模型代理服务无需同时成为长任务调度器。客户端连接的生命周期,也不再决定 Worker 的生命周期。这里仍有明确前提:执行服务和它依赖的基础设施必须可用;“不依赖窗口”并不等于任务可以脱离运行环境继续。
交付给 Runtime 的,应当是一份确定的输入
任务运行到一半时,如果指令、工具权限或者依赖悄悄变化,恢复执行就失去了明确含义。
Open Nua 当前用 Plugin 作为新 Cloud Agent 的交付来源。Plugin 固定指令、所选 Skills、服务声明、访问策略和依赖;工作空间另外配置模型、执行身份、工具连接、凭证引用与资源限制。平台校验固定包版本和摘要,再生成执行快照。
这里有两种需要分开的变化:
- 修改 Agent 的指令、权限声明或包内依赖,需要发布新的 Plugin 版本。
- 调整部署的模型、身份或资源限制,产生新的部署配置,由平台重新校验。
稳定的发布入口指向当前版本,已有会话保留绑定的执行快照。发布新版影响后续新会话,不会把旧历史里的指令和版本换掉。回滚也只是改变后续的选择,不能抹掉已经发生的执行。
我们在反馈助手的实际验收中做过升级、回滚、再次升级,并核对新旧运行各自记录的版本和摘要。这个检查比“版本下拉框切换成功”更有意义:它证明发布入口和执行历史确实是两种不同的事实。
请求被接受,与任务执行完成,是两个时刻
客户端发送请求后没有收到响应,可能有几种原因:请求没有到达;服务拒绝了请求;任务已经被持久接收,只是回执在网络上丢了。
如果一律显示“执行失败,点击重试”,第三种情况就可能制造第二次执行。
因此,受理必须有稳定身份。平台给逻辑命令固定幂等键,Runtime 持久记录对应任务和回执。重复传输先找回原请求的结果;同一个键也不能被当成提交不同内容的通行证。
“已受理”只意味着系统接下了这项工作,还不意味着模型成功、工具成功或业务结果已经交付。任务状态、执行事件和业务回写会继续发生。
如果回执暂时无法确认,我们保留这个不确定性,沿用原请求身份查询或重放受控命令。这不能被扩大成“任意工具都能安全重试”。发消息、改工单等外部写入仍需要业务自己的幂等和结果核对;Runtime 的受理去重,不构成外部副作用恰好发生一次的保证。
重连、恢复和重试需要不同的规则
这三个词常常都被压缩成一个“重试”按钮,但它们改变的是不同层次的状态。
客户端重连是在找回观察位置。Host 从持久会话状态和事件恢复消息、工具、人工输入与执行进度。客户端无需重新启动任务,也不应该通过再发一条用户消息来恢复页面。
Worker 恢复是在替换执行进程。Supervisor 找回原任务,确认 checkpoint 兼容性,取得当前执行所有权,再以新的 attempt 继续。旧进程的 PID 没有恢复价值,原任务、快照、checkpoint 和已经消耗的时间才有。
显式重试是一个新的执行决定。它需要自己的准入和来源关系,不能伪装成一次网络重传,更不能默认把已经发生的业务写入从头再做。
这也解释了为什么一个会话标识不够用。Session 连接用户正在继续的工作,执行标识对应一次受理,Attempt 标识区分实际承担它的进程尝试。观察者换了、进程换了和工作重新发起了,应当留下不同的记录。
恢复的前提也必须诚实:checkpoint 保存的是可继续的计算状态,不能让外部世界回到过去。兼容性不满足时应当阻止恢复;副作用无法确认时,需要核对结果,而不是用自动重跑掩盖不确定性。
能接管任务,也必须能阻止旧执行继续写入
只把任务存进数据库,还不足以解决故障恢复。旧 Supervisor 可能暂时失联,新的 Supervisor 已经接管;旧进程随后恢复,又试图写入结果。
我们用持久执行所有权、租约和递增的 epoch 约束这件事。状态提交要带上当前所有者和 epoch;接管后,旧执行携带的值无法继续提交。心跳无法证明自己仍拥有任务时,运行侧会停止当前执行。
恢复也不会重新获得一份完整的时间预算。原来的绝对 deadline 继续生效,等待人工输入和替换 Worker 都不能成为无限延长任务的办法。
但数据库能够拒绝旧写入,不代表它能撤回已经发往外部服务的请求。执行所有权、受控代理和业务幂等需要共同约束副作用。已有恢复与撤权测试证明的是具体控制路径,我们仍需要继续扩大失联、并发和清理时序的故障覆盖。
谁触发任务,不等于任务拥有什么权限
一个后台 Agent 可能由管理员创建,由业务事件触发,再独立运行。如果直接借用创建者的账户,它就会悄悄继承管理员能访问的一切。
Open Nua 区分三件事:
- 执行主体决定权限、资源归属和用量记在谁名下。
- 触发者记录是谁或哪个事件引发了这次工作。
- 执行负载标识实际运行的 Agent 版本、配置摘要和实例。
触发关系记录因果,不自动授予权限。独立 Agent 主体需要自己的授权,也不会继承创建者对私有 Plugin 的可见性。
短期执行授权绑定主体、工作空间、版本快照、模型和资源上限。开始执行时检查,续租和恢复时还要重新检查当前权限。等待人工确认期间也不能豁免:用户作出回答时,系统仍需确认这项工作可以继续。
凭证同样按用途处理。通过受控远程工具代理调用时,部署保存的是凭证引用,由 Gateway 验证调用上下文并注入认证信息;这条路径不把长期密钥交给模型或 Sandbox。需要向执行环境注入凭证的能力具有另一种暴露边界,必须单独授权和治理,不能借用代理路径的结论宣称所有执行都“看不到密钥”。
撤权能够阻止后续授权和调用,并推动执行停止;它不能收回已经被外部服务接受的操作。这是权限控制需要面对的时间边界。
恢复计算、恢复文件和交付结果,是三份契约
另一个容易过度承诺的词是“持久化”。
checkpoint 能让 Agent 继续计算,不意味着 Sandbox 里的文件都已经安全保存。启用工作区保存后,文件在明确的执行边界提交,异常退出仍可能丢失尚未提交的改动。恢复依据是已提交的工作区记录,不能任意选择存储系统里的“最新快照”。
交付给用户的 Artifact 又是另一种资源。它需要显式发布、登记持久引用,并在下载时重新授权;临时沙箱目录不能直接充当长期下载地址。即使任务状态是 completed,也要分别确认文件是否发布成功、能否实际下载。
这样拆开以后,每一种状态都能给出准确承诺:页面能否重建,计算能否继续,文件保存到了哪里,用户最终能拿走什么。
用反馈助手检验平台,也看清平台的边界
反馈助手把这些边界串在了一个具体场景里:业务系统接收反馈和附件,组装允许提供给 Agent 的上下文;Cloud Runtime 生成内部分析与回复草稿;结果回到业务系统,经过人工审核后再发布给反馈人。
Plugin 拥有 Agent 的指令,Runtime 拥有执行。反馈系统继续拥有工单、触发、分派幂等、审核与公开回复。我们没有因为引入云端 Agent,就把整套反馈业务迁进 Runtime。
这条链路已经用维护者自己的合成反馈走通过,包括实际模型调用、草稿回写、审核和回复读回。但现在的反馈助手仍主要证明链路成立。它还没有完成基于真实证据调查问题、关联已有问题、持续推进处理的完整能力,后台自动消费也保持关闭。
这是一个很有用的验收结论:基础设施可用与场景产品好用,有各自的完成条件。下一步应该围绕真实反馈工作补齐调查和处理能力,而不是继续向通用 Runtime 填入工单规则。
我们现在能承诺到哪里
当前实现已经支持独立的云端执行服务、固定版本交付、持久会话与任务、人工输入、受控恢复,以及对应的授权和运行观测。我们在维护者实际使用的服务组合和客户端中验证了主要流程,并用自动化测试与受控故障场景补充证据。
这还不等于经过大规模多租户生产运行验证。跨租户工作区备份隔离、长任务预算治理、更多失联时序和持续运维仍有工作要做;客户端也仍有个别超时问题需要继续追踪。已有测试和一次成功的业务链路,不能替这些范围作保证。
走到这里,我们对 Cloud Agent Runtime 的理解变得更具体了:它要保存一次工作已经被接受、依据什么版本运行、由谁授权、目前停在哪里,以及哪些结果仍然无法确认。
模型可以继续变强,客户端也可以继续变化。只有这些事实有明确的归属,应用才有可靠的基础去构建自己的 Agent 产品。