阅读需 8 分钟

让生产变更更安全的规划与执行代理

当研究、审批、凭据和受限操作彼此分离时,规划与执行代理可以降低生产环境风险。

让生产变更更安全的规划与执行代理

规划代理不应该有能力把自己的建议直接变成生产变更。让它负责检查、比较和论证。再交给独立的执行代理一小组操作权限,并要求它证明请求的操作与已批准的变更相符。

只有当你看到代理把一个看似合理却错误的假设带过 API 边界时,这种分离听起来才不再像官僚流程。大多数故障并不是电影式的攻击。代理可能读到过时的运行手册,把预发布主机名误认成生产主机名,或者执行工单中的不可信文本。如果同一个进程同时持有凭据和操作权限,错误就会在任何人注意到之前变成一次真实变更。

计划是证据,不是授权

规划代理和执行代理需要不同权限,因为规划产生的是关于世界的判断,而执行会改变世界。一个好的计划仍可能建立在过时数据、不完整的代码仓库上下文,或从不可信来源复制的指令之上。把计划当成权限,会把两个本应分别审查的决定混在一起。

规划代理应该收集事实、说明不确定性、提出替代方案,并生成一份范围明确的请求。它不应该仅仅因为报告中需要提到某个端点,就持有生产令牌。如果它需要从受保护系统获取事实,可以提供一个专门的只读操作,只返回所需事实,或者由人类提供相关输出。

执行代理的工作不同。它收到具体请求后,要判断请求是否符合自己的有限权限。它不应重新展开设计讨论,不应浏览任意工单,也不应接受“修复部署”这样的句子。这种说法也许足以用于对话,却无法作为一个持有凭据的进程的操作契约。

这一区分也能纠正团队常说的“人在回路中”这一习惯。有时他们真正的意思只是“有人扫了一眼很长的聊天记录”。审查者无法可靠地从文字中还原代理可能进行的每一次工具调用。他们可以审查一份简短、结构化的请求,其中明确写出目标、操作、输入、预期效果和回滚路径。

NIST SP 800-53 的 AC-5 控制要求分离职责,以降低某个人在不被发现的情况下滥用系统的可能性。该表述针对的是人,但同样适用于代理。不要把旧的审批层级复制进提示词。应在真实凭据和工具接口中分离能力。

执行范围必须小于计划范围

执行代理应该比规划代理拥有更少的自由,而不只是使用不同的提示词。一个能用广泛云凭据执行任意 shell 命令的独立代理,并没有真正降低风险。它仍然可以重新解释模糊请求、发现其他资源,并进行无关变更。

先建立操作目录。每个操作应对应一项具体操作,并只接受少量参数。例如,deploy_service_revision 可以接受服务名称、不可变的版本标识符和目标环境。它不应接受 shell 片段或任意 URL。

最有用的限制往往是语义限制,而不只是技术限制。凭据可能允许部署,但执行代理的封装层仍可以拒绝 latest 这样的可变标签,在缺少变更 ID 时拒绝生产操作,并拒绝不在允许列表中的服务。这些检查能把通常写在运行手册里的假设变成可以拒绝不安全请求的代码。

不要把受限工具和受限结果混为一谈。一个可以更新 billing-api 的 API 令牌,也可能同时改变流量分配、环境变量和自动扩缩容设置。如果 API 支持,应将这些操作拆开。如果不支持,就在它前面放置一个小型网关,只接受你准备自动化的操作。

一个可信的执行代理通常应具备以下边界:

  • 每个环境使用独立身份。
  • 只接收命名操作,不提供通用 shell。
  • 调用服务提供商之前,先验证目标名称和不可变输入。
  • 生命周期短,且没有生成更宽泛凭据的途径。
  • 对请求和结果生成持久记录。

人们常常抵触这些做法,因为通用工具接入更快。第一次演示确实更快。但当有人需要解释代理为什么读取工单中复制来的命令后删除了错误资源,一个宽泛的 run_command 工具很快就会变得代价高昂。

交接需要机器可检查的变更请求

规划代理应交接一份结构化产物,让执行代理无需解释意图就能完成验证。自由格式的计划会诱使执行代理用自己的推理补齐空白,从而把规划权限重新带回操作路径。

一个实用的请求可以是这样:

{
  "request_id": "chg-2025-0417-redis-timeout",
  "environment": "production",
  "action": "deploy_service_revision",
  "target": {
    "service": "checkout-api",
    "revision": "sha256:8f31c2..."
  },
  "expected_effect": "Run the approved checkout-api revision",
  "rollback": {
    "action": "deploy_service_revision",
    "revision": "sha256:31aa09..."
  },
  "approval": {
    "approved_by": "release-manager",
    "approved_request_hash": "b2c4..."
  }
}

哈希很重要。没有它,审查者批准的可能是自己看到的请求,而执行代理收到的却是修改过的版本。审批记录应绑定到执行代理实际使用的字段的规范表示。如果系统在不同位置以不同方式序列化 JSON,应先定义规范化规则。字段顺序或省略的默认值可能改变负载,随意对字符串计算哈希会造成隐患。

执行代理应明确说明拒绝请求的原因。一种有用的响应格式可以暴露失败的检查项,但不暴露机密信息:

{
  "status": "denied",
  "request_id": "chg-2025-0417-redis-timeout",
  "reason": "revision must be an immutable digest",
  "executed": false
}

这种拒绝是设计的一部分,不是令人尴尬的边缘情况。团队会测试代理能否执行操作,却跳过证明代理不能越权的测试。两条路径都要测。

不要让规划代理决定执行代理的操作定义。平台负责人应该定义操作目录、验证规则和凭据映射。规划代理只能从受支持的操作中选择。即使某项任务很紧急,它也不能凭空创造 deploy_anything

分离进程,避免意外共享权限

即使两个代理角色处于同一个进程中,也很容易模糊边界。共享的环境变量、令牌缓存、工作目录和工具注册会绕过你设定的边界。应使用不同的启动配置,将规划代理和执行代理作为两个独立进程运行。

规划代理进程应该获得研究工具,也可以获得经过严格过滤的只读接口。它不应在环境变量、配置文件或工具描述中看到执行代理的凭据。模型不需要直接接触令牌明文,也可能滥用令牌。如果进程可以调用一个持有令牌的工具,真正相关的能力边界就是工具边界。

执行代理应该只接收经过批准的结构化请求,以及尽可能少的工具。它不应接收原始工单正文、任意网页内容、整个代码仓库的搜索工具,或规划代理的对话记录。这些材料可能包含提示注入、错误命令或执行代理没有理由服从的随意指令。

这为调试提供了一条简单规则:如果执行代理需要更多上下文才能决定执行哪项操作,说明交接产物没有定义完整。不要通过授予它广泛的发现权限来解决。应在请求中补充缺失字段、验证规则或人工决定。

进程分离也有助于事故响应。你可以撤销执行代理的会话,同时保留规划代理的研究记录。你还可以检查规划代理提出的目标是否与批准的目标不同。如果两个角色共享一个会话和一个身份,事后重建过程就只能靠猜测。

当一个支持 MCP 的代理需要执行 HTTP 或 SSH 操作,却不应接触底层 API 或 SSH 凭据时,Sallyport 很适合构建这条边界。应用会将机密信息保存在加密保管库中,并自行执行调用,因此即使规划代理收到错误指令,也无法提取凭据。

在后果发生变化的地方进行审批

运行 SSH 而不接触密钥
使用内置的无状态 sp-ssh helper 执行 SSH 操作,同时将 SSH 密钥留在应用保管库中。

人工审批应覆盖一项具体决定,而不是对代理接下来可能做的一切给出模糊许可。会话审批适合确认一个已知代理进程在本次运行期间可以使用一组范围明确的低风险操作。当操作可能改变生产数据、身份、网络暴露面或资金时,会话审批就不能替代审查。

对于后果重大的凭据,应在每次调用时审批。调用不可逆、不寻常,或很难撤销时,多点一下是值得的。生产删除密钥、修改访问控制的凭据或支付操作,都适合采用这种方式。不要对每个无害的状态查询都弹出审批。提示出现得太频繁,就会变成背景噪声,人们会不看内容直接批准。

审批界面应展示审查者真正能够判断的内容:操作名称、目标环境、目标资源、不可变输入,以及计划中的回滚方式。只显示“代理请求访问”的对话框只是做样子,它没有告诉审查者操作会造成什么后果。

审批范围必须过期。绑定到进程运行的审批,应在进程退出时结束。绑定到变更请求的审批,应与请求内容绑定,不能默默授权后续版本。长期有效的审批授权看起来很方便,因为它减少了摩擦,但也重新制造了原本想通过分离来避免的长期权限。

Sallyport 的决策阶梯很适合这个模型:保管库锁定时,所有操作都会被拒绝;会话授权用于识别新的代理进程;选定的单次调用密钥可以在每次使用时请求确认。它有意比策略引擎更狭窄。团队仍然需要决定哪些凭据值得逐次调用审查。

提示注入先到达规划代理,再影响执行代理

提示注入经常从规划代理的正常工作中进入。代码仓库评论要求运行某条命令,支持工单包含窃取配置的伪造指令,网页要求代理忽略之前的指示。规划代理看到的不可信文本,远多于执行代理应该看到的内容。

常见的错误做法,是花几个月试图写出一条完美指令,告诉规划代理不要受骗。模型行为可以改进,但文字指令无法替代权限边界。假设规划代理可能会在计划中重复错误指令,然后让执行代理拒绝任何不在操作目录、权限范围和审批绑定内的请求。

考虑一种常见故障。规划代理调查延迟时,读到一份旧事故记录,其中建议在排空流量前将某服务的副本数设为零。由于记录使用了复制来的主机名,规划代理在计划中写入了错误环境。如果它可以调用通用部署工具,就可能把过时建议变成一次停机。

在分离设计下,故障会在多个位置被拦截。交接请求必须明确写出 production。执行代理只接受部署版本操作,不接受修改副本数的操作。人工审查者可以看到实际目标和请求效果。如果请求不符合要求,执行代理日志会记录拒绝。上述控制都不要求规划代理完美识别被污染或过时的文本。

将规划代理的输出标记为执行路径中的不可信输入。这个标记应该影响数据处理,而不只是界面上的警告。不要把规划代理的文字插入 shell 命令。没有类型检查和允许列表,也不要让它填充 HTTP 路径、标头或查询字段。JSON Schema 有帮助,但单靠 Schema 验证无法告诉你 production 是否是获授权的目标。

只读权限也可能暴露造成损害的路径

把操作交给 Sallyport
Sallyport 自行执行经过批准的 HTTP 和 SSH 操作,只向代理返回结果。

团队常常因为“它什么都改不了”,就给规划代理广泛的只读权限。这句话已经造成了许多本可避免的问题。只读访问可能暴露客户数据、内部主机名、部署历史、功能开关、访问模式,以及高权限系统的名称。它还可能提供攻击者构造可信操作请求所需的全部信息。

应根据敏感程度和可能产生的影响对读取操作分类。返回服务状态的健康检查端点,与数据库导出端点不同。列出公开名称的服务清单,与返回机密标识符和元数据的凭据管理器 API 也不同。不要把它们都放在同一个通用的 read_only 工具后面。

尽可能向规划代理提供经过提炼的事实。与其允许它访问每一条部署事件,不如提供一个操作,让它针对指定服务返回当前版本、健康状态和最近一次已批准的变更 ID。与其授予广泛的数据库查询权限,不如提供一个能回答诊断问题的指标。这样既能减少意外泄露,也能减少可能携带恶意指令的材料数量。

这也是团队容易矫枉过正、让规划代理失去实用性的地方。答案不是让代理变盲,而是确定任务需要哪些事实,并为这些事实创建读取接口。如果规划代理经常需要额外字段,就在审查使用场景后有意添加。不要每次遇到缺口,都把生产控制台交给它。

日志必须能还原分歧过程

让规划代理不接触凭据
将生产 API 和 SSH 凭据保存在 Sallyport 的加密保管库中,不让规划代理进程接触它们。

审计轨迹不应只回答“是否发生了 API 调用”。发生争议变更后,你需要比较规划代理提出的产物、人工批准的产物、执行代理验证过的请求、实际发出的外部调用,以及返回结果。缺少其中任何一项,留下的就可能是一种说法,而不是证据。

让稳定的请求 ID 贯穿整个流程。规划代理分配或接收这个 ID。审批与 ID 及其内容哈希绑定。执行代理记录该 ID、进程身份和操作结果。外部网关将它与出站目标和响应状态一起记录。不要仅仅为了便于关联,就记录 bearer 令牌、密码、私钥或完整的敏感负载。

防篡改证据很重要,因为普通应用日志通常存放在管理员可以编辑的存储中。如果保留预期的链状态,使用哈希关联的事件序列就能检测后续篡改。它不会神奇地证明每个事件都是真实的,但会让悄悄改写历史记录更难隐藏。当日志存储的访问权限与被审查人员重叠时,这正是你需要的特性。

Sallyport 根据加密的哈希链式审计日志生成 Sessions 和 Activity 日志,sp audit verify 可以在离线状态下对密文检查链条,无需保管库密钥。这对调查很有帮助,因为验证记录不必向审查者交出操作凭据。

在事故迫使你这么做之前,先进行一次重建演练。选一项已批准的变更,让同事仅根据记录回答五个问题:哪个代理提出了变更,谁批准了确切请求,哪个执行代理运行了它,发生了什么出站操作,以及返回了什么结果。如果任何答案依赖记忆或聊天记录,就改进记录。

第一个自动化操作应该简单且可回滚

从一项负责人明确、目标范围狭窄、且你已经演练过回滚的操作开始。使用不可变版本将部署发送到非生产环境,比修改访问规则或删除过期账户更适合作为第一个案例。简单的工作能让你在不拿生产环境冒险的情况下,发现设计缺口。

让同一任务反复经过这套流程,直到请求格式不再因为琐碎原因频繁变化。留意那些可预见的失败:规划代理漏写目标,审查者批准了泛泛的描述,执行代理需要未声明的上下文,日志无法把审批与调用关联起来。每个失败都说明权限仍在哪里跨越了角色边界。

不要用审批点击次数减少了多少来衡量成功。应衡量执行代理是否会拒绝错误目标、未经批准的版本和不支持的操作,同时完成预期操作。一个让所有操作都很容易的系统,很可能允许了太多操作。

当流程经受住考验后,一次扩展一个操作类别。让规划代理保持探索性,让执行代理保持单调可控。生产自动化要赢得信任,靠的是它的拒绝和成功一样经过深思熟虑。

常见问题

AI 工作流中的规划代理是什么?

当任务需要广泛阅读、检查代码仓库或比较不同设计方案时,可以使用规划代理。让它远离凭据和修改工具。它仍然可以生成具体计划、供人审查的命令,以及一份假设清单。

规划代理可以访问生产环境的只读数据吗?

如果只读服务不具备修改生产状态的能力,规划代理可以调用这些服务。当只读访问会暴露客户数据、令牌、内部拓扑或部署元数据时,也应将它视为敏感权限。只读是一类权限,并不自动意味着安全。

将规划代理和执行代理分开,就能保证生产变更安全吗?

不能。拆分规划代理和执行代理可以缩小错误计划或被操纵计划的影响范围,但执行代理仍可能执行一个错误但已获批准的变更。你还需要狭窄的凭据权限、目标验证、对高影响操作进行人工审查,以及完整的审计记录。

规划代理应该如何把工作交给执行代理?

让执行代理接收结构化的变更请求,其中包含目标、操作、参数、预期结果、回滚条件和审批引用。不要把自由文本当作执行契约。当必填字段缺失,或请求内容与已批准版本不一致时,执行代理应直接失败。

每次代理操作都应该经过人工审批吗?

这取决于操作本身以及撤销操作的成本。向预发布服务发送预览请求,可能可以在会话审批下执行;删除数据或修改身份设置,则应在实际执行该调用时要求审批。对无害操作反复弹出提示,会让人形成不加阅读就点击批准的习惯。

一个 AI 代理可以同时担任规划者和执行者吗?

将两个提示词放在同一个聊天中,并不等于身份隔离。应将规划代理和执行代理作为两个独立进程运行,使用不同凭据和不同工具清单。执行代理不能继承规划代理的环境变量、缓存令牌或 shell 历史记录。

如何将执行代理限制在一个环境中?

为每个环境创建独立的机器身份,并将权限限制在实际任务所需的最小范围内。生产执行代理不应拿到可以修改预发布环境的凭据,预发布执行代理也不能仅通过更改 URL 参数就访问生产环境。测试拒绝路径,而不只是测试成功路径。

AI 代理变更的审计轨迹应该包含哪些内容?

记录规划代理生成的产物、审批决定、执行代理身份、完整请求、返回结果和最终状态。如果管理员之后可以修改事件流,仅有时间戳并不能证明太多。需要解决审计争议时,应使用追加写入或经过加密关联的记录。

执行代理做出错误变更时,谁应负责?

人类应对意图、风险接受,以及是否授权不可逆或高影响操作负责。执行代理可以在这一决定之后执行受限操作。如果组织无法指出谁对某项生产变更负责,就还没有解决审批问题。

执行代理最安全的第一个使用场景是什么?

不要从授予代理广泛的生产权限开始。选择一项重复、可回滚、负责人明确的操作,定义狭窄的请求格式,并先强制执行代理在非生产环境中运行。只有在日志显示请求、审批和结果符合预期后,才逐步扩大范围。

Sallyport

Sallyport 替你的 AI 智能体执行 API 调用和 SSH 命令。密钥留在你 Mac 上的本地密钥库里;每次运行由你批准,每个操作都落入一份密封的审计日志。

© 2026 Sallyport · 依据 Apache-2.0 开源 · Oleg Sotnikov