# AI 编程工作流中的本地操作控制：它适合什么场景

当一名开发者拥有机器、能够持续参与并授权运行，并且需要让智能体在不接触底层密钥的情况下执行仓库之外的操作时，本地操作控制就适合 AI 编程工作流。这解决的是一个常见而具体的问题：智能体在工作区内编辑代码通常足够安全，但它还需要查询 API、获取私有构件，或通过 SSH 执行命令。

如果团队试图把本地控制提升为整个设备群的授权机制，它就不合适了。Mac 端的审批可以确认某个本地进程能够使用某项凭据执行操作，却无法告诉生产服务应该允许哪个租户、环境、变更窗口或业务规则来处理最终请求。这些决定应由资源所在的位置作出。

边界其实很清楚：本地控制保护开发者机器上的凭据，并恢复人的意图；服务端控制保护共享资源，即使没有开发者坐在那台机器前也能继续工作。当团队要求其中一方替另一方完成职责时，问题就会出现。

## 本地控制应归属于由个人拥有的执行点

当启动智能体的机器有明确的负责人，而且这个人能够识别工作内容并中断操作时，本地操作控制才有意义。通常这意味着开发者在 Mac 上运行交互式编程会话，而不是一台只有一个友好主机名、无人值守的构建工作机。

所有权问题听起来很简单，但团队真正观察工作运行方式时，情况往往没那么清楚。笔记本电脑可能分配给某个人，却经常通过远程桌面访问。共享实验室 Mac 可能一周内先后登录过多名工程师。受管控的构建主机可能使用开发者账户运行，却执行由拉取请求触发的任务。这些事实单独来看，都不能形成有意义的人工控制。

在增加本地审批前，先回答四个具体问题：

- 谁能实际解锁机器并批准请求？
- 哪个可执行文件启动智能体，负责人能否识别它的代码签名机构？
- 智能体的工作是否始终处于交互式会话中，还是可以在人离开后继续运行？
- 如果机器被入侵，远程服务能把这项凭据的影响范围限制到什么程度？

前两个问题决定审批是否对应明确的人。后两个问题决定审批是否具有合理的影响范围。如果开发者点击批准的是一个可以运行一整夜并部署到所有环境的进程，那么这次点击承载的权限远远超过了这个人可能想授予的范围。

本地网关应持有密钥，并自行执行出站操作。通过环境变量、配置文件或工具响应把令牌交给智能体，会让本地控制大多只剩下形式。智能体可能把它回显到会话记录中，写入补丁，放进 shell 历史文件，或发送给另一个工具。凭据一旦进入智能体上下文，就无法可靠地取回。

因此，仅仅采用类似代理的安排还不够。HTTP 代理可以转发流量，但流量转发并不能证明密钥从未进入客户端进程。设计必须确保智能体只请求某项操作，本地组件负责注入凭据，而智能体只收到远程结果。

## 机器所有权不只是登录名

开发者账户名称并不能证明获得批准的进程就是实际执行工作的进程。在 macOS 上，编程智能体可能由终端、编辑器扩展、辅助进程或写入仓库的脚本启动。这些来源的可信程度差异很大。

先在一次无害运行期间检查真实的进程树。以下命令会列出进程 ID、父进程、用户、运行时长和命令行：

```sh
ps -axo pid,ppid,user,etime,command | grep -i '[a]gent'
```

输出应该显示一条你能够识别的链路。例如，交互式终端可能显示为已批准智能体进程的父进程。如果看到不受信任仓库中的 shell 脚本、后台启动项或未知辅助程序，就应该重新考虑。不要仅仅因为命令行中包含预期工具的名称，就批准一个进程。

然后检查可执行文件本身，不要只相信文件名：

```sh
codesign -dv --verbose=4 /path/to/executable 2>&1 | \
  grep -E '^(Identifier|TeamIdentifier|Authority)='
```

签名的二进制文件仍然可能存在漏洞，但签名机构提供了一个稳定、可识别的依据。可写项目目录下的裸路径则没有这种保证。审批界面如果首先显示代码签名机构，会比只显示「智能体想要访问权限」好得多。这样，操作员可以在授予凭据操作前拒绝复制出来的二进制文件、本地包装器或意外更新。

这也是 shell 别名和包装脚本值得关注的原因。它们对工作很有用，但别名可能启动另一个程序，而不是用户以为自己批准的可执行文件。应将操作网关的启动命令放在智能体可写的仓库之外。如果智能体能够修改定义自身访问路径的脚本，网关就不再代表操作员的决定。

Model Context Protocol 规范定义了 MCP 客户端和服务器如何交换工具调用，但没有规定调用工具的是否是经过批准的本地进程。MCP 兼容性回答的是接口问题。进程身份、凭据保管和授权仍然需要单独处理。

## 会话审批适合有明确边界的运行

对于交互式编程运行，按会话审批是一个实用的默认选择。新智能体进程启动时审批一次，之后就不必频繁打断。这样既不需要为每次无害读取弹出确认窗口，也保留了一个让人检查请求来源的时机。

这个边界必须对应真实的进程生命周期，而不能是「今天下午这段工作」之类模糊的概念。进程退出时，会话结束。这个规则容易解释、容易撤销，也很难被智能体重新解释。进程重启后必须再次请求。如果用户撤销了会话，后续调用就会失败，不会继承之前点击产生的信任。

如果开发者每次看到智能体检查问题跟踪器、获取软件包或读取预发布 API 时都要确认，审批疲劳就说明边界设错了。反复出现的警告不会带来更好的判断，只会训练人们清除窗口，让工作继续。

会话审批的用途比策略引擎窄。它表达的是：「我识别出这个本地进程，并允许它在存活期间使用获准的操作路径。」它不应试图判断 SQL 查询是否安全、工单标题是否合理，或当前分支是否应该获得生产权限。这些属于远程授权决定或工作流规则，而自然语言提示是存放它们的糟糕位置。

即使是交互式会话，也不一定适合获得一揽子授权。如果智能体可以打开任意本地工作区、接受聊天任务，或运行未经审核仓库中的插件，那么它的输入面就比范围严格限定的仓库会话更大。在这种情况下，应减少该运行可用的操作集合，或要求针对具有风险的具体凭据进行审批。

审批界面还需要可靠的失败行为。如果保险库已锁定，所有操作都应失败。如果 Mac 被锁定或休眠，导致审批界面无法出现，所有操作也应失败。UI 出错时暗中允许请求继续执行，会把人工控制变成装饰。

## 逐次调用审批适合后果严重的操作

当某项凭据的使用可能造成生产变更、产生费用、删除数据，或跨越开发者每次都应认真考虑的边界时，逐次调用审批就很合适。并非所有名称听起来吓人的凭据都需要这种方式。

应根据后果，而不是协议来分类操作。HTTP POST 可能只是创建一条可随时丢弃的预览记录。SSH 命令可能只是读取部署日志。GET 请求却可能导出完整的客户数据集。审批模式不由方法和传输协议决定。

当操作具有以下一个或多个特征时，应使用逐次调用网关：

- 远程服务无法可靠地撤销结果。
- 凭据会影响共享的生产资源。
- 请求可能把敏感数据传输到预期之外的目标。
- 操作足够少见，不会因为反复确认而变成例行点击。

第四点很重要。对高频操作逐次确认，会产生和过度活跃的会话提示一样的机械点击。尽可能拆分凭据。让日常开发使用只限于开发资源的凭据，把能够访问生产环境的凭据留给少量确实值得中断的操作。

智能体绝不能决定自己的哪些调用需要确认。如果智能体可以把请求标记为「只读」，或自行选择凭据类别，那么提示注入或简单的实现错误就可能引导它走更容易的路径。账户负责人必须在智能体工作区之外设置审批要求。

确认信息要足够易读，能够支持真正的判断。用户需要看到凭据名称或用途、目标、请求方法或命令形式，以及发起调用的进程身份。显示原始请求正文可能泄露密钥，或让用户无法处理过多信息。只显示「批准操作？」则没有有用的上下文。好的确认设计不会展示所有内容，但会告诉操作员足够的信息，让他能够拒绝意外调用。

## 让密钥留在本地只能解决一个问题

本地保险库可以防止智能体以明文持有 API 密钥和私有 SSH 密钥，从而大幅减少密钥通过提示、会话记录、工具日志、复制文件和智能体编写的代码意外泄露的机会。它还允许操作员撤销正在运行的会话，而不必立即轮换凭据。

但它不会改变远程凭据能够执行的操作。如果 API 令牌可以删除所有项目，那么本地审批后，服务仍会接受删除请求。如果 SSH 账户拥有广泛的 sudo 权限，保护私钥也不会把这个账户变成权限狭窄的部署身份。远程最小权限仍然决定最大损害。

要明确区分两件事：

1. 凭据保管关注的是智能体能否获得或还原密钥。本地保险库可以很好地解决这个问题。
2. 资源授权关注的是服务是否应在当前条件下接受某项操作。这个问题必须由 API、主机、身份提供商或部署系统回答。

团队经常因为智能体调用 API 时这两个话题同时出现，就把它们混为一谈。结果很容易预料：安装了本地密钥存储器，却在它后面留下一个长期有效的管理员令牌。令牌确实不那么容易泄露了，但操作路径仍然过于宽泛。

SSH 会把这种区别表现得非常直接。本地机器可以保护私钥，但主机会决定公钥对应的账户能执行什么。条件允许时，应为自动化创建独立账户或强制命令限制，并限制主机访问。不要把工程师个人的管理员密钥用作智能体的通用密钥。个人密钥会随着时间积累各种例外，而自主进程不应继承这些例外。

Sallyport 通过将 API 和 SSH 凭据保存在加密应用保险库中，并在不把密钥传入智能体的情况下执行 HTTP 或 SSH 操作，采用了这种本地模式。这种特性对开发者机器很有帮助，但团队仍然需要范围狭窄的凭据和服务端权限。

## 在信任操作路径前先验证

在把操作网关连接到生产能力之前，应使用无害端点和临时凭据进行测试。测试必须证明三件事：智能体没有收到密钥，网关记录了调用，远程服务看到了预期身份。

创建一个临时 HTTP 凭据，让它只能调用非敏感端点，例如返回调用者身份的测试资源。让智能体执行这一次操作。检查它的会话记录和工具结果，确认其中没有原始令牌，也没有看起来像令牌的字符串。智能体应该收到响应正文或经过脱敏的错误，绝不能收到用于认证请求的请求头值。

然后主动测试拒绝行为。锁定本地保险库并重复操作。终止已批准的智能体进程，再启动一个新进程。如果网关支持撤销，请撤销当前会话，然后从原来的进程重试。每次重试都应在本地边界失败。如果调用仍然成功，就查明是否有其他进程保留了凭据、环境变量绕过了网关，或远程服务存在另一条缓存的授权路径。

有用的审计记录应包含足够的信息，以便在不保存密钥本身的情况下还原事件。对于 HTTP 操作，应记录时间戳、进程或会话身份、凭据标签、目标、方法、结果状态，以及远程服务提供的请求标识符。对于 SSH，应记录目标、账户标签、命令结果，以及符合敏感性规则的命令表示。不要为了让审计记录看起来完整，就记录 bearer 值、私钥或完整的敏感负载。

哈希链可以帮助发现本地历史记录是否被重写，但它无法证明原来的操作是明智的。应将网关记录与接收服务的审计日志放在一起。如果两者不一致，应将其视为调查线索，而不是自动认定某一方一定正确。

Sallyport 会将会话和活动日志投影自加密的哈希链式审计日志，`sp audit verify` 可以在离线状态下检查这条链，而无需保险库密钥。可以在事件复盘或发布交接时执行验证，但不要把完整性检查误认为授权。

## 当人员缺席或资源共享时，由服务端控制接管

当操作必须在没有特定开发者批准的情况下继续运行时，就需要服务端控制。这包括 CI 任务、定时修复、服务器托管的智能体、共享运行器和部署工作机。对于必须跨越笔记本没电、出差、休眠或员工离职继续运行的工作，本地 Mac 不能成为最终权威。

出现以下任一情况时，应将授权决定放在受保护资源附近：

- 多个人或系统可以触发同一个工作流。
- 目标是生产环境、客户数据、财务活动或受监管系统。
- 智能体运行在基础设施上，而不是个人拥有的交互式机器上。
- 服务必须执行租户边界、变更窗口、环境规则或职责分离。
- 工作流需要高可用，不能依赖某个人批准对话框。

对于这些情况，应使用权限狭窄的工作负载身份；如果身份系统支持，还应使用较短的有效期，并保留服务端审计记录。在部署系统或 API 中强制执行环境边界。如果组织需要变更记录或人工审批，也应在那里要求。开发者机器仍然可以帮助准备和检查变更，但不能成为生产操作的执行点。

NIST Special Publication 800-207 将零信任描述为一种把访问决策重点放在保护资源，而不是信任网络位置上的模型。对智能体工作流来说，有用的启示不是每个本地工具都需要复杂的策略语言，而是生产 API 或主机必须自行判断调用者和请求的资源。Mac 端审批无法替代这个决定。

不要通过把开发者的本地凭据转发到 CI 来解决问题。这样会把人工控制的凭据变成无人值守的服务凭据，却没有保留任何一种模式的清晰边界。应创建独立的工作负载身份，只授予任务所需的权限。

## 不要在审批提示中重建策略引擎

团队经常提出这样的规则：「允许 GET 请求，但下班后除外」，或「只有分支名称包含 release 时才允许 SSH」。这个要求很受欢迎，因为它看起来可以减少点击，同时保留本地控制。但它通常会制造一个脆弱的策略系统，让人在压力下无法解释其行为。

本地操作控制应有一条简短、清晰的决策阶梯：保险库已锁定还是已解锁，会话已批准还是未批准，凭据是否需要逐次调用审批。每个状态都有直接的操作。用户可以预测结果、进行测试并撤销权限。

一旦本地工具开始解析分支名称、提示文本、URL 模式、工单标签和智能体提供的意图，它就开始根据智能体可以影响的输入作出授权决定。规则会不断增加例外，例外最终变成权限。很快，开发者就在笔记本电脑上构建出了一个不完整的服务端授权系统，却没有服务上下文，无法把这项工作做好。

本地决定应绑定到本地机器能够确认的事实：保险库是否打开，哪个经过签名的进程请求操作，会话是否获准，以及该凭据是否需要新的确认。远程决定应绑定到服务能够确认的事实：目标资源、调用者身份、租户、当前环境、请求内容和组织控制措施。

这种分离也让故障更容易诊断。本地调用被拒绝，说明保险库、会话或逐次调用网关阻止了它。服务端调用被拒绝，说明远程策略拒绝了它。当拒绝可能来自一堆相互重叠的本地规则时，开发者往往会直接关闭控制，而不是修复它们。

## 混合工作流让每项控制各司其职

大多数团队同时需要本地控制和服务端控制。实用的设计不是二选一。

开发者可以在本地运行交互式智能体，检查预发布 API、读取私有软件包元数据，或执行范围狭窄的 SSH 诊断。开发者在检查进程身份后批准运行。本地保险库提供凭据，智能体始终无法看到它。活动记录则让之后的复查成为可能。

同一个智能体可以准备部署变更，却不获得部署权限。随后，服务端流水线使用自己的工作负载身份运行，执行生产限制，并记录最终部署。如果流水线需要人工审批，应将审批放在拥有生产变更的系统中，让负责该环境的人员能够持续看到它。

应将交接视为值得保留的边界。本地智能体可以生成补丁、测试结果或供审核的签名请求，但不应把开发者的会话权限偷偷带入无人值守任务。服务端任务也不应依赖一台保持解锁的笔记本电脑才能完成工作。

先在一个当前工作流中画出所有使用凭据的操作。写下发起操作的进程、保存密钥的机器、接收请求的资源、能够停止操作的人，以及任务是否必须在这个人不在时运行。很快你就会发现哪些操作适合本地控制，哪些需要服务端执行，还有哪些令人不安的操作目前两边都没有控制。
