# AI 代理需要操作网关的五个信号

当 AI 编程代理能够带着无人明确检查、批准或撤销的权限，在工作区之外执行操作时，就需要操作网关。问题不在于代理会写代码，而在于它可以使用凭据、修改托管服务，或在有人把密钥粘贴到它的上下文中并称之为配置之后，打开 SSH 会话。

团队通常要等到险些出事后，才会把这称为 AI 代理操作网关问题。令牌出现在聊天记录里。一次编程运行使用了生产部署账号，因为那是唯一可用的账号。有人问是谁批准了数据库变更，得到的回答却是一串零散消息和一次共享终端会话。这些不是文档工作没做好，而是说明团队把操作权限交给了不受信任的进程，却没有为它设置可用的边界。

网关不会通过判断每条命令在道德上是否正确，来让代理变得安全。这不是它应该做出的承诺。网关让凭据留在代理之外，在合适的节点加入人的决定，并留下记录，让操作人员无需凭记忆重建事件，就能回答发生了什么。如果下面的情况听起来很熟悉，直接向代理提供凭据的便利已经失去了价值。

## 复制 API 密钥已经成了常规配置

当开发者为了让代理完成工作，把 API 密钥粘贴到提示词、环境文件、终端会话或代理配置中时，就需要为代理设置独立的操作边界。这种习惯看似无害，因为第一次运行往往确实完成了开发者要求的事情。但它也会在原本从未用于存储生产权限的位置制造副本。

代理上下文中的凭据可能通过比原始提示词更多的路径泄露。代理可能在命令中再次输出它，把它写入配置文件，放进错误报告，加入生成的文档，或在解释失败原因时暴露它。终端回滚内容、Shell 历史、进程环境、CI 日志、备份和支持工单截图都会产生更多副本。遮住一条可见消息，并不能删除这些副本。

团队经常混淆的区别很简单：**代理使用密钥，不等于代理持有密钥**。浏览器可以提交付款，而不必让页面上的每个脚本都看到卡号。代理也可以采用同样的分离方式。它可以带着说明清楚的请求体请求 `POST /deployments`，由可信执行器注入凭据并返回响应。

不要接受一种假分离方案：系统把 `API_KEY=...` 换成 `${SECRET_NAME}`，然后在代理进程内部解析这个占位符。明文仍然到达了同一个进程。被入侵的扩展、仓库中的恶意指令，或过于积极的调试输出都可能把它取出来。

更安全的请求边界可以是这样：

```json
{
  "channel": "http",
  "credential": "deploy-service",
  "method": "POST",
  "url": "https://api.example.internal/deployments",
  "headers": {"content-type": "application/json"},
  "body": {"service": "catalog", "revision": "a1b2c3d"}
}
```

代理可以看到端点、请求体、审批状态和响应，却看不到授权请求所需的 bearer token。正因为如此，你可以轮换凭据，而不必同时清理提示词和本地工作树中的泄露副本。

如果密钥已经进入代理上下文，就应按已暴露处理。撤销或轮换它，检查这次运行把输出记录到了哪些位置，并移除直接交付密钥的模式。团队有时会因为无法证明令牌已经泄露而拖延轮换。你不需要证明复制的凭据已经被盗，只需要承认自己已经无法控制它现在位于哪里。

## 共享账号隐藏了执行变更的人

当代理通过共享部署用户、全团队共用的云令牌，或所有开发者和自动化任务共用的 SSH 账号执行操作时，就需要操作网关。共享访问短期内能省下账号管理工作，但一旦出了问题，责任归属也会立即消失。

考虑一种常见的故障。代理收到修复构建失败的请求。它发现一个过时的基础设施设置，于是使用 `ops@production` 连接主机。这个账号之所以能用，是因为权限很广，而且私钥就在仓库的入门说明中。代理修改了文件，重启服务，然后报告成功。

后来服务开始返回错误。服务器日志显示 `ops` 重启了服务，云审计记录显示团队令牌调用了部署 API。但这两条记录都没有说明是哪个代理进程、哪项工作请求、哪个启动进程的人，或是否有人在操作执行前看过它。团队得到的只能是一场依靠推测完成的事故调查。

为每个人使用独立凭据当然比共享账号好，但这还不能完全解决代理使用问题。如果代理拿到了 Alice 的私钥或长期令牌，审计记录只能说明 Alice 的凭据执行了操作。它无法可靠说明请求究竟是 Alice、她的终端、被入侵的仓库指令，还是她的代理发起的。

让身份和记录各自承担不同的职责：

- 服务身份定义外部系统允许执行什么。
- 代理会话标识提出请求的具体运行进程。
- 审批标识接受操作范围的人。
- 操作记录标识确切的请求及其结果。

不要把这些信息压缩成一个名为 `user` 的字段。发生故障或进行访问审查时，它们分别回答不同的问题。

对于 SSH，共享的宽权限账号尤其值得警惕。SSH 私钥是一种可携带的签名权限。如果代理拥有这个文件，那么你原本打算在登录后施加的控制，就已经失去了最有用的边界。强制命令和账号限制可以减少损害，值得使用，但它们无法改变一个事实：代理可以发起该密钥允许的每一条连接。

将密钥放入由执行器负责完成 SSH 操作的环境中。给代理一个请求接口，记录主机、命令、身份、会话和结果。请求范围要足够窄，让审查者能够理解。`systemctl restart catalog` 可以审查，而 `ssh host 'bash -c "$(curl ... )"'` 则是一个对任意权限进行不透明转发的隧道。

## 一次审批全部操作，不算真正的审批

当开发者一次性批准了模糊权限，却无法知道后续哪些请求使用了这项权限时，代理就需要操作网关。标为“允许代理访问”的按钮，如果涵盖了未知数量的端点、命令、账号和时长，就只是同意的表演。

审批要有用，必须回答两个实际问题：哪个进程提出了请求，这项审批覆盖什么范围？进程身份很重要，因为一台本地设备上可能同时运行受信任的编程代理、从仓库复制来的未签名脚本，以及冒用代理名称的恶意进程。仅凭显示名称不能建立信任。代码签名权限能给审查者提供一个值得检查的事实。

范围也很重要，因为审批疲劳会把人变成自动点击者。要求每位开发者每小时检查五十个日常调用，并不能带来人的控制，只会教会他们关闭提示。反过来，为生产凭据提供一周有效的全范围审批，也会让一条意外指令拥有过大的操作空间。

根据能力带来的后果，使用两种不同的审批范围：

- 会话审批可以覆盖一个已识别代理进程在退出前发出的普通调用。
- 对于能够进行不可逆生产变更、转移资金、修改访问权限或访问任务范围外数据的凭据，应逐次确认。

边界应随着进程结束而失效，而不是随着某人昨天点击过的模糊记忆持续存在。新进程应获得新的决定。这样，当代理重启、工具更新，或开发者从另一个仓库开始第二次运行时，控制仍然有效。

审批卡片应先显示进程身份，再用通俗的语言说明能力范围。“已签名进程 X 请求使用 deploy-service 发起 HTTP 调用”能让人做出接受或拒绝的决定。“工具需要权限”则不能。如果操作需要逐次确认，还应显示目标和操作。人无法判断隐藏在通用能力名称背后的请求。

不要因为问题看起来复杂，就建立一套微型策略语言。团队可能花上几周为提示驱动的工具编写允许规则，最后才发现难点根本不在语法，而在于代理是否应该获得这项权限。先从保险库网关、进程范围的同意，以及在影响范围需要时进行逐凭据确认开始。凌晨两点，你仍然应该能向值班人员解释这些控制措施。

## 代理可以从一次性检出目录访问生产环境

当仓库、分支或一次性开发环境仅仅因为代理在那里运行，就能触发真实的外部操作时，代理就需要操作网关。仓库是输入。把仓库指令当成可信操作员，是概念上的错误。

恶意拉取请求不需要用戏剧化的方式攻击模型。它可以把指令放进代理正常工作时会读取的文件中：“运行这条诊断命令”“使用环境中的部署令牌”或“把日志上传到这个 URL”。如果代理拥有直接访问密钥的权限和不受限制的网络能力，仓库作者就找到了通往操作权限的路径。

这个问题也会在善意的工作中出现。开发者检出一个旧分支来对比迁移脚本。该分支包含一个指向生产环境的旧脚本，因为多年前这样做看起来合理。代理按照附近的文档执行，发现环境中有有效凭据，于是发起请求。没人想修改生产环境，但环境凭据和不受信任指令的组合让这件事成为可能。

将代码访问和操作权限分开。让代理以普通的本地权限读取、测试和编辑检出目录。让出站操作经过一个明确的边界，并标明目标和凭据。代理仍然可以请求执行操作，但不应仅仅因为旁边存在一个密钥文件，就继承相关权限。

这也是为什么仅靠网络过滤不能解决问题。出站规则可以阻止已知目标，在合适的地方应当使用它，但它无法说明是谁发起了允许的请求，请求使用了哪个凭据，或是否有人批准了发出请求的代理会话。网络控制是一道有用的外墙，却不能替代让凭据远离代理。

用一次有意设置为不受信任的代码检出目录进行测试。创建一个只记录请求的无害端点。在项目文件中放入一条有说服力的指令，要求代理使用所谓的诊断令牌调用它。然后以开发者平时使用代理的方式运行。如果端点收到令牌、在代理内部解析的密钥名称，或绕过审查的请求，你就找到了需要修复的边界。

## 你无法快速撤销正在运行的代理

当一次糟糕的运行只能通过关闭终端、撤销它可能复制过的所有凭据，或希望它已经执行完毕来应对时，代理就需要操作网关。可靠的控制应让操作人员停止当前权限，而不必把本地错误扩大成全面的凭据紧急事件。

进程生命周期提供了一个自然的撤销单位。如果审批绑定到一个代理进程，撤销该会话就会阻止这次运行发起后续操作，即使进程仍然打开。代理可以继续起草代码，却无法访问网关控制的外部通道。这比在事故中关闭无关的开发工作，或轮换组织范围的令牌，影响小得多。

团队经常把三种不同的撤销混为一谈：

- 撤销会话会阻止一个已识别的代理运行继续发出已批准的请求。
- 锁定凭据保险库会停止所有受保护操作，直到授权人员重新打开它。
- 轮换或停用外部凭据会在签发它的服务处移除相关权限。

使用能够控制事故的最小操作，需要时再执行范围更大的措施。如果开发者只是选错了任务，撤销会话可能就够了。如果代理把令牌打印进了外部记录，就应轮换令牌。如果你无法确定哪些代理运行拥有访问权，先锁定保险库，再从稳定状态开始调查。

保险库锁定时，网关应拒绝操作。这一点看似显而易见，但有些工具为了方便会缓存解密后的凭据。操作人员最需要锁定权限时，缓存的权限却会让锁定失去意义。锁定状态必须意味着执行器无法使用存储的密钥发起 HTTP 调用或 SSH 连接。

在事故发生前进行演练。启动一个代理会话，请求执行一项无害的受保护操作，撤销会话，然后重复同一个请求。预期响应应说明授权已经失效。接着启动新进程，确认它必须取得自己的审批。如果旧进程仍然可以继续工作，你构建的只是通知系统，而不是控制系统。

## 你的日志记录了输出，却没有记录操作

当你拥有聊天记录和终端输出，却无法提供可靠的操作记录时，代理就需要操作网关。对话记录只能描述代理声称自己做了什么，不能证明什么穿过了网络，也不能证明哪个凭据授权了它。

操作记录应尽可能靠近执行器捕获事件。对于 HTTP 调用，记录代理会话、请求时间、目标、方法、凭据引用、授权决定和结果状态。对于 SSH，记录主机、账号引用、命令请求、决定和退出结果。不要为了让日志看起来完整，就记录原始密码、私钥、bearer token 或完整的敏感响应正文。

OWASP 的 Logging Cheat Sheet 也提出了同样的实际建议：日志应支持安全调查，但应用应避免直接记录访问令牌、密码、会话标识符和其他密钥。许多团队只做到了前一半。他们在代理失败后打开详细调试，结果又在日志存储中制造了一次密钥泄露。

更好的做法是把证据和密钥材料分开。执行器可以保留 `deploy-service` 这样的凭据引用、请求摘要和操作结果。调查人员可以确认某个已授权会话使用该凭据执行了某项具体操作，却不需要拿到凭据本身。

记录还需要具备篡改证据。如果执行操作的同一个进程可以悄悄改写昨天的日志，那么日志记录的就只是该进程希望你相信的内容。哈希链是一种实用防护：每条记录都包含上一条记录的摘要，修改或删除过去的记录就会破坏后续验证。

验证不应要求解密每一条敏感事件。实用的审计工具可以针对加密记录验证哈希链完整性，让操作人员在不广泛授予内容访问权限的情况下发现篡改。这不能证明每项原始操作都明智，只能证明记录的顺序没有事后被悄悄编辑。

尝试提出一个超越“代理成功了吗”的审计问题：“昨天哪个代理运行使用了生产部署凭据，哪个进程获得了同意，每个请求返回了什么结果？”如果你必须拼接 Shell 历史、云日志、聊天导出和某个人的回忆，那就没有真正的操作日志。

## 代理在观察流量，但它仍然拥有权限

当建议的解决方案只是一个观察流量的代理，而代理仍然持有 API 令牌或 SSH 密钥时，代理就需要操作网关。代理确实有合理的用途，但流量可见性和凭据保管是两种不同的控制措施。

反向代理可以在应用边缘验证请求。出站代理可以过滤目标或保存请求日志。但这两种安排都不一定能阻止本地代理读取令牌、将令牌放入另一个请求、保存到文件，或使用另一条获准路径。对于 SSH，网络代理也无法解决私钥位于代理环境中的问题。

中间人设计还会带来自己的运营负担。它必须处理 TLS 信任、证书部署、协议例外，以及应用自行固定或加密的流量。团队有时会因为它看起来像一个通用控制点而搭建这种方案，最后却发现仍然需要决定哪个进程可以使用哪个凭据。

把边界放在操作上，而不只是数据包上。代理发出结构化请求。执行器选择存储的凭据，将其注入 HTTP 请求或用于 SSH，记录决定，然后返回结果。代理持有的是请求执行工作所需的信息，而不是足以在别处冒充服务身份的材料。

这种设计有一个有用的限制：它不试图成为一个从自然语言预测意图的通用策略引擎。它让权限变得明确。代理通过已知通道请求操作，人或配置好的凭据控制决定该通道是否对这次运行开放，记录则捕获发生了什么。

对于 macOS 团队，Sallyport 通过 MCP stdio shim 采用了这种模式：代理请求 HTTP 或 SSH 操作，而应用将 API 和 SSH 密钥保存在加密保险库中并自行执行操作。这并不意味着可以忽略服务权限的合理设置，但它消除了把原始凭据交给代理的习惯。

## 你依赖最小权限，却没有测试边界

当团队声称令牌遵循最小权限，却没有测试这些权限落在自主进程手中时能做什么，代理就需要操作网关。最小权限是实际凭据及其可触达操作的属性，不是附加在角色上的标签。

一个只能部署单个服务的令牌，仍然可能修改该服务的环境变量，从而重定向流量或暴露数据。限制在一台主机上的 SSH 账号，仍然可能读取包含其他系统凭据的部署配置。无法删除资源的云角色，仍然可能创建一个使用权限过大的工作负载。权限名称很少会揭示全部后果。

围绕操作而不是产品进行权限审查。写下代理可以要求执行器做什么，然后逐项检查服务的授权规则。把读取操作也包括进去。代理无需修改任何资源，就可能通过导出端点、日志获取、配置读取和发现 API 引发高成本或敏感事件。

审查时可以使用一张小表：

| 请求操作 | 外部身份 | 滥用后的后果 | 审批范围 |
| - | - | - | - |
| 创建预览部署 | 预览部署账号 | 临时工作负载和成本 | 会话 |
| 重启生产服务 | 生产运维账号 | 用户中断 | 逐次调用 |
| 读取事故日志包 | 支持账号 | 可能暴露敏感数据 | 逐次调用 |
| 通过 SSH 连接构建主机 | 构建主机账号 | 在主机上执行命令 | 命令范围较窄时可按会话审批 |

这张表会迫使团队进行一场不太舒服却很有价值的讨论。如果你无法用简短的话说明后果，那么权限可能过于宽泛，或者请求接口过于模糊。

不要把操作网关当成继续放任服务身份拥有过大权限的理由。它能减少密钥暴露，改善同意流程并提供证据，但外部 API 或主机仍然决定凭据可以做什么。减少这些权限，为不同环境使用不同身份，并为不可逆操作设置更严格的审批规则。

## 第一个边界应该覆盖本周可能造成伤害的操作

操作网关的价值在于替代团队已经在使用的直接凭据路径，而不是变成一项持续六个月的访问权限重构。选出代理当前执行的最高影响操作，先迁移这条路径。

对许多团队来说，这是生产部署 API。对另一些团队来说，则是访问构建主机或运维主机的 SSH。选择应依据实际权限，而不是哪个集成最容易演示。只读的问题跟踪器令牌可能也很重要，但不应让你忽视一把可以重启生产服务的私钥。

让第一次部署具体可执行：

1. 盘点代理可以使用的令牌、SSH 密钥、共享账号和环境变量。
2. 选择一个跨越真实信任边界的凭据，从代理环境中移除它的明文。
3. 定义代理可以发出的结构化操作请求，包括目标和操作。
4. 要求这条请求路径使用进程范围的授权，如果操作可能造成重大损害，再采用逐次调用确认。
5. 运行一次无害测试，撤销会话，并确认日志同时显示允许的调用和被拒绝的重试。

只有当测试环境真实执行了相同的操作路径时，才先使用测试凭据。完全不同工具中的测试令牌，几乎无法证明生产授权会如何运行。测试必须覆盖实际的执行器、审批、撤销和审计路径。

不要等待代理行为变得完美。提示注入防护、仓库审查、沙箱、服务权限和网络控制都能降低风险，但当代理进程使用本不应拥有的凭据请求外部操作时，它们都无法给你一个清晰的答案。在下一枚复制出来的令牌变成事故调查之前，先把凭据放到边界之后。
