运行框架
模型可以建议下一步做什么,却无法独自把建议变成有边界、可观察的工作。完成这件事的是模型周围的运行框架。它负责整理模型输入、记录进度、分发工具、等待审批、执行限制,并让人能够可靠地暂停或停止一次运行。
本章建立在一条边界之上:模型负责提出建议,运行框架负责排序并记录;策略与身份系统决定权限;工具和外部系统承载实际影响。运行框架可以执行一项决定,却不会授予权限;它可以协调多次尝试,却无法保证外部影响恰好发生一次。只有守住这些边界,才不会把一个方便的智能体循环误当成事务管理器或安全主体。
运行时契约
最小的运行框架只重复三项操作:询问模型、执行获准的工具调用、追加结果。生产运行时还必须回答这个循环没有回答的问题:
| 关注点 | 运行框架必须公开的契约 |
|---|---|
| 身份 | 这一步由哪位用户、哪个租户、哪份智能体定义和哪次运行产生? |
| 状态 | 哪些内容已持久接收,哪些工作仍未完成? |
| 权限 | 此刻究竟是哪项策略和哪次审批授权了这个动作? |
| 外部影响 | 外部操作已经提交、拒绝、重复执行,还是结果未知? |
| 控制 | 暂停、引导、取消、强制终止和分叉分别在哪里生效? |
| 限制 | 还剩多少时间、成本、调用次数、字节数、变更次数和并发额度? |
| 恢复 | 可重放哪些工作,哪些工作必须先核对外部结果? |
可以把这份契约写成一个带版本的状态转换函数:
其中,e_t 是一项已接收事件,可以是用户命令、模型结果、工具结果、审批决定、计时器事件或恢复时的观察结果。C_t 是这次转换发出的命令集合。状态 S_t = (r, h, w, m, p, k, b, q) 包含:
r:运行身份、状态和智能体定义版本;h:持久历史的头部;w:工作区或沙箱引用;m:应用记忆视图及其版本;p:安全主体、策略快照和审批记录;k:已经准入的工具目录版本;b:剩余预算和已预留预算;q:尚未完成的工作、尝试、租约和未解决影响。
归约器带有版本,因为恢复旧历史时,必须沿用产生这段历史时的语义。即使模型和工具的输出不确定,归约器处理已接收事件的结果也应该确定。由此得到四条不变量:
- 终态运行不会发出新命令。
- 每条外部操作命令都要记录行为者、范围、审批、稳定步骤 ID、尝试次数、幂等键和输入指纹。
- 预算只能通过明确授权的追加而增加。
- 同一条尝试血缘只接收一次完成结果;迟到或重复的结果会保留为证据,却不能悄悄推动状态前进两次。
这份事件历史是重启和审计的事实来源。检查点只是优化手段,不是第二份真相。它记录一个经过验证的历史位置,以及继续运行所需的版本、待处理操作 ID、截止时间、工作区引用和已知的外部影响结果。
运行是状态机,不是递归函数
下面的状态有意写得很明确。客户端收到 paused 或 waiting_approval 时,不应该从输出流突然没有新词元来猜测发生了什么。同样,needs_reconciliation 是可见状态,而不是一个诱导系统盲目重试的普通失败。
| 状态 | 含义 |
|---|---|
created |
已记录身份、定义和初始预算。 |
ready |
准入成功,没有正在运行的子操作。 |
running |
归约器正在处理一项已接收事件。 |
waiting_model |
正在等待模型,界面可以明确显示“等待模型”。 |
waiting_tool |
正在等待工具,具体调用和截止时间都可见。 |
waiting_approval |
正在等待审批,尚未分发外部操作。 |
paused |
不再调度新工作,可恢复状态仍然持久保存。 |
cancelling |
已接收取消请求,正在停止活动子操作。 |
completed / failed |
运行成功终止,或以已知失败终止。 |
needs_reconciliation |
外部结果未知或相互矛盾,需要核对。 |
转换日志必须先于命令交付完成提交。这样,工作进程即使崩溃,也不会抹掉调用工具的原因。不过,这个顺序无法消除经典的不确定窗口:外部系统可能已经提交写入,工作进程却在记录回执前崩溃。因此,恢复逻辑必须理解外部影响,不能只会重放函数。
固定定义版本,明确适配协议
实例生命周期与定义版本是两个独立选择。共享运行器可以为每次运行解析不可变配置;刚刚分配的运行器也可能载入一份没有版本的提示词。每次运行都应该固定到一个智能体定义版本,其中包含指令、模型路由、工具模式、策略引用和运行框架归约器版本。每个检查点也要记录该版本。发布策略再把新运行路由到金丝雀版本或稳定版本,并明确现有运行是继续固定原版本、在安全边界迁移,还是停止。
不同提供商的协议不能互换。兼容适配器应该公布功能矩阵,覆盖工具调用、结构化输出、流式传输、取消、用量数据、续接标识和错误语义。某项能力无法表示时,就明确失败或声明降级,不能把“请求已接收”悄悄解释成功能对等。适配器和提供商版本也要与智能体定义一起固定,评估或事故调查才能复现真实调用路径。
控制动词各有含义
暂停、恢复、引导、取消、强制终止和分叉,是不同能力,不是同一种中断由弱到强的不同档位:
- 暂停会在写明的安全点停止调度。活动调用可以完成、被取消,或继续列为未解决。
- 恢复会在定义和策略版本兼容时继续已暂停状态。如果版本发生变化,迁移必须是一项明确的状态转换。
- 引导会把用户命令追加到持久历史。契约必须说明它在下一次模型调用前生效,还是等当前工作完成后再生效。
- 取消会记录
cancel_requested,拒绝新工作,并把请求传播给活动子操作。协作式代码必须主动观察取消;仅仅取消一次 RPC,并不能中断服务器上的任意代码 (gRPC Authors 2024)。 - 强制终止会直接结束运行框架拥有的执行单元,例如进程组或沙箱。它是一条升级路径,不是回滚机制。
- 分叉会从检查点创建新的运行命名空间,隔离可变状态,并使用新的幂等范围。已经提交的外部影响仍然是双方共享的现实。
取消有两个重要里程碑。请求已经持久记录,且不再调度新命令时,取消得到已确认;运行框架拥有的所有子操作都已停止或标为未解决时,系统才归于静止。运行框架停止等待以后,远程工作仍可能继续;取消也无法撤销已经提交的外部影响。对于结果未知的操作,运行时必须核对、补偿,或交给操作人员决定。Temporal 也区分优雅取消和强制终止,并指出远程活动必须通过协作式心跳才能收到取消通知 (Temporal Technologies 2026)。
审批也是一种中断,但还要满足完整性要求。审批界面必须展示确切工具、目标、参数或差异、执行身份、预期影响和成本。决定要绑定到唯一调用 ID 和不可变载荷哈希,并且有范围、会过期且只能使用一次。实际分发前必须再次执行授权与输入防护。如果暂停期间载荷或相关策略发生变化,就必须重新审批。可序列化的暂停与恢复流程已经展示了这种机制,包括由嵌套智能体发起的审批 (OpenAI 2026)。但审批流程不能取代执行授权。
持久工作、重复交付与外部影响
可恢复的运行框架会把持久历史独立于提示词上下文保存。工作进程从队列消费命令,通过有界租约取得命令,并以稳定步骤 ID 和尝试次数写回结果。租约只能限制并发所有权,不能证明上一位持有者已经停止。对于正确性关键资源,应附加单调递增的栅栏令牌,并要求每条写入路径拒绝陈旧令牌。接收端不验证,令牌就只是元数据。
调度也采用同一套模型。给每次触发一个稳定的调度标识,例如 (schedule_id, scheduled_at),再通过唯一的持久记录认领它。即使如此,仍可能重复交付和重复执行。Kubernetes 文档说明,即使 Job 只要求成功完成一次,同一程序也可能启动两次 (Kubernetes Authors 2026)。调度器租约在有效期内可以防止重叠,却不能保证外部影响恰好一次。
外部操作能否安全重试,取决于接收端:
| 操作 | 重试规则 |
|---|---|
| 只读或已经证明幂等 | 使用有界指数退避并加入抖动。 |
| 由接收端幂等键约束的写入 | 重用同一幂等键;同一键对应不同输入指纹时必须拒绝。 |
| 写入结果未知 | 重试前先查询状态并核对;无法查询时,执行补偿或要求人工检查。 |
| 身份验证、授权、校验或业务规则失败 | 通常不可重试,应直接呈现失败。 |
错误类别、下次尝试时间、绝对截止时间和重试预算都要持久保存,避免重启重置策略。自动重试之所以有用,正是因为操作可能暂时失败;但同一活动也可能执行多次,所以必须按这个现实设计 (Temporal Technologies 2026)。
锁也应该放在它真正保护的资源上。会话锁只能串行化一段对话,保护不了被另一次运行触碰的分支、客户账户或部署。资源级并发控制可以采用条件写入、数据库约束、受保护分支、合并队列,或以实际资源为键的锁。Git 工作树只隔离本地文件,既不能串行化远程推送,也不能把 Git 变成事务系统。
工具是有类型、受授权的外部操作边界
只有名称和 JSON 输入模式还不够。运行框架需要一份契约,用它驱动准入、审批、执行、重试和结果处理:
ToolSpec {
name, input_schema, output_schema, side_effect_class,
auth_scopes, approval_rule, timeout, retry_class,
idempotency, max_output_bytes, sandbox_profile
}
ToolResult {
status, payload | artifact_ref, receipt,
error_class, observed_at
}
四道门必须彼此独立:
- 目录准入决定一项工具定义和实现是否足够可信,可以注册。
- 逐轮暴露为模型选择最少且相关的候选工具。检索可以改善选择,不能授予权限。
- 执行授权在分发前立即核对当前安全主体、租户、动作、资源、参数、策略和已经绑定的审批。
- 结果处理验证结构,限制内联大小,把大输出保存为制品,记录回执,并把返回文本当作不可信数据。
工具名称、描述、模式和结果,都是不可信的模型输入。签名和审查可以降低工具投毒风险,但每一次具体调用仍然需要授权,子智能体发起的调用也不例外。MCP 同样用输入模式和可选输出模式定义工具,要求客户端验证结果,并建议让人能够拒绝敏感调用 (Model Context Protocol 2025)。它的授权指南还禁止把任意令牌转交给下游服务器,并要求令牌绑定到目标资源 (Model Context Protocol 2025; Campbell et al. 2020)。
不存在一条通用的工具数量阈值,可以断定扁平目录何时失效。应当使用真实模型、工具模式和任务分布测量选择质量。目录扩大后,检索、命名空间或模型驱动的发现可以减少逐轮暴露的工具,但每种方式都会增加新的故障:漏掉必要工具、暴露危险而无关的工具,或为发现额外消耗调用次数。
有状态工具:区分三类状态
“有状态工具”这个说法掩盖了三种不同责任:
- 连接状态包括传输会话、协商出的能力、游标和背压。
- 持久资源状态存在外部系统中,例如文件、工单、事务或远程作业。
- 模型可见状态是后续轮次显式传回的句柄或摘要。
协议支持时,恢复流程应该重新连接、重新发现能力,并从游标或显式句柄继续。否则就建立新连接,但不能假装持久资源状态也随旧连接消失。协议本身会改变这些机制。例如,MCP 在不同日期的规范版本中修改过传输和授权 (Model Context Protocol 2025; Model Context Protocol 2025)。因此要固定协议版本,并依据规范声明的保证设计系统,而不是假定每条连接都是持久会话。
上下文是视图,不是记录
权威历史记录包含已接收命令、模型与工具结果、审批决定、回执、来源链接和未解决义务。工作上下文是这份历史的有损投影:
其中,P 从权威历史 H 中选择材料,在上下文预算 B 内服务当前任务状态 T。提示词上下文、持久执行历史和 第 40 章 介绍的个性化记忆,是三类不同存储。
压实可以总结早期轮次;遮蔽可以用带类型的占位符替换庞大观察结果;大输出可以变成制品引用;检索可以为下一项决定恢复证据。每种方式都会丢失信息,因此投影视图应该说明省略了什么,并保留指回来源事件的链接。压实工作上下文不会删除权威证据。它尤其要保留活动约束、待处理审批、未完成计划、影响回执、失败,以及所有摘要的来源血缘。
更长的上下文并没有消除这个设计问题。受控研究发现,即使检索结果正确,性能仍可能随输入长度增加而下降 (Du et al. 2025);在软件智能体任务中,简单遮蔽观察结果也可能达到复杂摘要方法的效果 (Lindenbauer et al. 2025)。选择必须由具体任务的实证决定。
沙箱由多道独立边界构成
“在容器里运行”并不是隔离契约。每个维度都要单独说明:
| 维度 | 最低限度要回答的问题 |
|---|---|
| 进程 | 代码能否向宿主或同级进程发送信号、检查其状态或逃逸过去? |
| 文件系统 | 哪些路径被挂载、可写、持久保存或共享? |
| 网络 | 出站连接是否默认拒绝,允许哪些目标和协议? |
| 凭据 | 是否彻底排除长期密钥,并只为已经核对的动作签发能力? |
| 资源限制 | CPU、内存、进程数、磁盘、输出和网络上限分别是多少? |
| 生命周期 | 暂停、重启、取消、强制终止、到期和删除之后,哪些状态仍然存在? |
工作树不是安全边界。它可以减少多个编程任务之间的文件冲突,却无法隔离内核、进程、网络、宿主凭据或容器运行时套接字。不可信代码需要与威胁模型相匹配的沙箱,例如加固容器、用户态内核或微型虚拟机,还要配合非 root 身份、受限挂载、移除多余权限、系统调用策略和默认拒绝的出站网络 (Souppaya et al. 2017)。
不要挂载长期密钥。凭据代理可以先验证具体动作,再签发短期、范围狭窄并绑定受众和资源的凭据。代理还必须对日志脱敏,并支持轮换和撤销。这些控制可以降低凭据被盗的概率和影响范围,却不能让一个已经授权的动作自动变得安全。同样,结束本地沙箱也不会撤销已经使用其凭据提交的外部影响。
预算与准入控制必须协同
限制是一套账本,不是警告计数器。启动模型或工具调用前,先预留其允许的最坏消耗;完成后核对实际用量并释放余额。无法完成预留的工作必须拒绝。挂钟时间与空闲时间、模型调用、输入和输出词元、金额、工具调用、外部变更、字节数、子运行、并发操作和沙箱资源,都要有彼此独立的上限。
子运行从父运行获得子预算,而不是领取一份全新额度。因此,父子两级的预留始终受同一个单调上限约束。系统要保存绝对截止时间,避免重启凭空增加挂钟时间,并把剩余期限传给下游。本地超时只限制运行框架等待多久;远程工作或计费仍可能继续,必须另行取消或核对。
准入控制把身份与策略检查,同原子地预留配额或容量结合起来。调度前查询容量只能提供参考,因为真正调度前容量仍会变化。有界队列、租户级并发、优先级与公平性、启动期限、背压和明确的 pending 状态,可以让资源紧张变得可见。Kubernetes 资源配额限制命名空间的总资源消耗;Pod 请求无法调度时则会保持 Pending (Kubernetes Authors 2026; Kubernetes Authors 2026)。
断路器解决的是另一个问题。它用关闭、打开和试探状态,防止调用方持续请求一个故障依赖。它不能代替运行预算,也不能把“退出码为零”当作任务成功。结果判断应该依赖结构化状态、回执、后置条件检查和明确的目标验证。
把运行框架作为整体评估
运行框架变更本身就是实验处理,不是可以忽略的背景噪声。比较方案时,应固定模型、任务集、工具实现、策略、采样配置和资源范围。记录确切的智能体定义、运行框架与适配器版本、实际请求、工具模式、沙箱配置和环境。除了任务质量,还要报告这些保护机制是否奏效:
- 任务成功率和无效工具调用率;
- 工具选择精确率和召回率;
- 审批绕过率和未授权影响率;
- 取消确认延迟和静止延迟;
- 取消后的影响数量和重复影响率;
- 恢复偏差和未解决影响率;
- 压实后的上下文关键内容丢失率;
- 隔离逃逸率;
- 相对于最小基线的新增延迟和新增成本。
接着要在边界注入故障:让工作进程在外部提交与日志回执之间崩溃;重复交付一条命令;让租约在工作进程暂停时到期;在审批后撤销授权;挂起子进程;填满队列;投毒工具描述;在关键约束即将使用前压实上下文;在外部影响发生后创建分叉。干净的正常路径轨迹无法检验这份契约。
归因于智能体的分数,其实是模型、运行框架、工具和环境的共同性质。基准基础设施可能泄漏答案,也可能暴露评分器,让智能体学会利用它 (Wang et al. 2026)。因此,第 47 章 和 第 52 章 在比较模型时,必须报告并固定运行框架版本。提供商配置文件有参考价值,但当适配器会插入默认值或省略不支持的字段时,实际序列化的请求和观察到的响应,才是更可靠的复现材料。
有两个问题仍无定论。第一,没有任何上下文投影策略能可靠预知未来步骤需要哪项细节。压实、遮蔽、检索和更大窗口,只是在交换不同错误。第二,可移植的运行框架能够统一常见操作,却无法凭空制造提供商能力,也无法让不同提供商拥有完全相同的错误和取消语义。这两项都应该作为明确的产品选择接受测量,不能藏在一个通用的“智能体”接口背后。
运行契约核对表
把一项长时间运行交给运行框架之前,应当确认它能回答:
- 哪一个不可变定义和归约器版本负责这次运行?
- 最后接收的是哪项事件,还有哪些工作或外部影响没有解决?
- 哪一份确切载荷和策略授权下一项外部操作?
- 何时确认取消,运行框架拥有的工作又在何时完全静止?
- 哪些重试类别安全,由谁执行幂等约束?
- 父预算和子预算分别还剩多少?
- 进程、文件系统、网络、凭据、资源和生命周期边界如何约束执行?
- 恢复测试和故障注入测试能否证明以上主张?
运行框架让智能体具备可运维性,而不只是能运行。下一章 第 42 章 会进一步考验这份契约:图形界面会带来更长的动作链、更弱的状态观察、更高的延迟,以及执行前更难识别的外部影响。
延伸阅读
正文引用的来源都收录在本书的参考文献页。下方制品提供了一个精简的工具描述投毒复现实例。
- Invariant Labs, “mcp-injection-experiments,” 2025. github.com该 GitHub 仓库提供代码示例,用于复现针对 AI 智能体的 MCP(模型上下文协议)工具投毒与提示注入攻击。
评论
登录后评论