# AI 代理访问 API：直接令牌还是经过中介的操作？

AI 编程代理不应仅仅因为需要调用 API，就拿到一个通用的 SaaS 令牌。直接交付令牌会让代理进程变成凭据持有者，凭据也就可能通过各种常见方式泄露：详细输出的命令、子进程、上传的诊断包、工具结果，或者一条要求代理打印配置的指令。

这并不意味着每个 API 调用都需要繁琐的流程。关键是要把**执行操作的权限**和**持有授权凭据**分开。给代理一种受限的方式来请求有用的工作，让真正执行操作的组件保管凭据，并在后果值得人工判断时加入人的决策。

这种区别很容易被忽略，因为一条成功的 curl 命令看起来没有什么危险。问题从代理能够创建生产部署、关闭客户工单、修改问题或运行远程命令时开始。到了这一步，令牌就不再是配置细节，而是没有判断能力的操作权限。

## 直接交付令牌会让代理成为凭据边界

如果把 `SAAS_TOKEN` 放进代理的环境变量、配置文件、工具定义，或代理可以访问的密钥存储中，代理就能发起经过身份验证的请求，而不需要另一个组件判断每个请求是否属于当前任务。令牌可能拥有合理的权限范围，但它仍然会对该进程的所有行为开放，也常常会对它启动的程序开放。

开发者经常说代理无法「看到」环境变量。普通的工具使用就足以推翻这种说法。代理可以要求 Shell 检查环境，调用继承环境变量的脚本，运行带调试日志的测试工具，或者把配置快照写入代码仓库。具体暴露路径取决于代理及其工具，所以安全的假设很简单：如果进程可以直接使用 Bearer 令牌，它通常也能让令牌出现在你没有计划的位置。

Bearer 令牌还有一个令人不安的特点。SaaS 服务无法区分预期的代理和复制了字符串的其他人。RFC 6750，也就是 OAuth 2.0 Bearer 令牌使用规范，指出 Bearer 令牌必须防止在存储和传输中泄露，因为拥有令牌就足以使用它。这不是学术表述。代理一旦把令牌打印到构建日志中，服务看到的就是一个有效调用方，而不是一次错误。

直接访问还会让责任追踪变得模糊。服务审计日志可能只能识别出一个机器人账户，却很少能告诉你是哪次代理运行生成了请求、代理接收了哪些指令、是谁启动了它，或者最终影响是否经过人工批准。你得到的是事后 API 事件，而不是能够解释这次操作的决策记录。

在一次性本地沙盒中，如果以下条件全部满足，直接使用令牌有时可以接受：

- 令牌很快过期，而且只有最低限度的非生产权限。
- 目标中没有客户、员工或生产数据。
- 代理运行在可以直接丢弃的隔离环境中。
- 人员可以撤销凭据，而不会影响共享工作。

团队常常因为复制令牌很快，就把这个例外变成日常做法。速度确实重要，但令牌进入产物，或代理执行了嵌入问题描述中的恶意指令后，清理工作同样真实存在。

## 权限范围限制权限，但无法控制意图

OAuth 权限范围、API 角色和代码仓库权限回答的是「这个身份可以做什么？」它们无法回答「现在应该执行这个请求吗？」这是两种不同的控制。把它们当成同一件事，就会留下很大的漏洞。

例如，一个拥有单个项目问题编辑权限的问题跟踪器令牌，对于负责错误分诊的代理来说可能完全合适。但导入的问题中若包含提示词注入，仍然可以要求代理关闭所有未解决问题、修改优先级或发布误导性评论。每个请求都符合权限范围，但每个请求仍然可能是错误的。

部署平台也有同样的问题。只限于某个应用的令牌并不知道代理现在应该部署当前提交、回滚版本、修改环境变量，还是删除预览环境。服务看到的是经过授权的 API 调用。只有你的工作流才能判断这些调用是否符合任务，以及目标是否可以接受。

IETF 的 OAuth 2.0 安全最佳当前实践建议使用短期访问令牌，在可能时使用发送方约束令牌，并缩小权限，以减少 Bearer 令牌泄露造成的损害。这些做法都很有价值，它们能缩短复制凭据的有效时间并限制影响范围。但它们不会为一个权限范围正确、却具有破坏性的操作增加审批，也不会解释代理的意图。

不要因此创建一个试图预测每个端点和参数的庞大规则目录。团队往往先建立这样的目录，然后花数月为新的服务 API 和特殊发布流程维护例外。一个狭窄的操作接口，加上在合适时刻进行的人工审批，通常比一套没人能有把握读懂的策略语言更能适应实际工作。

## 问题跟踪器需要保留人工判断的写入路径

问题跟踪器看起来风险不高，直到代理开始批量修改内容。关闭问题可能压下客户报告。修改标签可能破坏分诊报表。发布评论可能把内部判断暴露给外部协作者。把用户添加到工单可能扩大敏感上下文的访问范围。

把代理的工作拆成观察和修改两部分。让它获取问题、搜索标签、检查关联的拉取请求，并起草拟议更新。最终修改通过一个操作执行，该操作明确列出项目、问题、修改字段和评论内容。服务收到内容前，审查人员应先看到实际文本。

请求契约可以把这个边界变得具体。代理应发送结构化意图，而不是拼出携带凭据的命令行。

```json
{
  "service": "issue-tracker",
  "action": "update_issue",
  "issue": "APP-184",
  "changes": {
    "labels_add": ["needs-reproduction"],
    "comment": "I reproduced this on the current release and attached the failing test."
  }
}
```

执行器应注入自己的凭据，并返回范围受限的结果：

```json
{
  "ok": true,
  "issue": "APP-184",
  "updated_fields": ["labels", "comment"],
  "request_id": "service-request-id"
}
```

不要返回原始 Authorization 标头、完整 HTTP 跟踪，或包含密钥的调试对象。事情看似显而易见，但在困难的集成过程中，有人开启详细 HTTP 诊断后，问题就会出现。把诊断放在由人工操作的排查路径后面，并使用经过测试而不是想当然的脱敏机制。

即使人工批准了会话，代理仍可能做出糟糕判断。因此，具有破坏性的工单修改应有单独的审批选项。会话授权回答的是这个运行中的程序是否可以使用集成。逐次调用审批回答的是这次具体修改是否应该发生。当代理能够读取工单、文档或拉取请求评论中的不可信文本时，这种区别尤其重要。

## 部署 API 的影响不止发布按钮

部署 API 往往不只是「部署这个版本」。它可能修改环境变量、触发构建、重启工作负载、创建域名、回滚版本、获取日志或删除资源。一个范围很广的部署令牌，会变成代理在计划出错后可以随手使用的远程控制器。

为部署工作设置不同的操作类别。读取构建状态和获取部署的公开元数据通常属于常规操作。将产物提升到生产环境、回滚版本、修改密钥引用和删除环境会带来不同后果。不要把它们全部放在一个名为「部署访问」的审批后面。

合理的请求应包含不可变的产物引用和明确目标。当服务能够解析提交、镜像摘要或构建标识时，应拒绝「latest」这类模糊输入。可变标签会在审批和执行之间制造时间差：审查人员批准的是一个对象，实际操作执行的却可能是另一个对象。

```json
{
  "service": "deployment-platform",
  "action": "promote_release",
  "application": "billing-api",
  "environment": "production",
  "artifact": {
    "git_commit": "8cf4f3a",
    "build_id": "build-4921"
  },
  "reason": "Fixes the confirmed invoice retry failure"
}
```

执行器应检查已批准的标识是否与它发送的请求一致。它还应记录服务响应标识和目标环境。只记录「部署成功」，到了凌晨两点有人询问哪个产物被发布、谁批准了操作时，几乎没有帮助。

不要为了避开 API 设计，就让代理抓取网页控制台。浏览器自动化会把细节隐藏在审查之外，可能毫无预警地失效，还可能点击已经过时的页面状态。如果平台提供 API，就通过 API 使用狭窄的中介操作。如果平台只有控制台，那么在建立可靠连接器之前，应接受有些操作仍然需要人工完成。

## 客服工具需要在自动化前先减少数据

客服系统把操作和个人数据结合在一起。一张工单可能包含账户详情、联系信息、附件、订单历史、日志以及情绪强烈的消息。让代理直接访问会带来两个问题：代理是否会读到不需要的材料，以及它是否会以公司名义发出有害回复？

不要默认把完整工单记录发送给代理。只获取任务所需的字段。如果代理需要对工单分类，可能只需要主题、经过脱敏的正文和产品领域。它可能不需要所有历史内部备注、账单记录或附件。

写入操作需要比分类更严格的审查。一个实用模式是先起草，后发送。代理创建回复草案，并标明工单以及使用过的内部来源。人员检查语气、事实陈述、账户相关细节，以及回复是否意外泄露内部备注。之后执行器才发布消息。

关闭、合并或重新分配工单同样需要明确的操作语义。「解决工单」过于模糊，因为它可能暗中发送关闭邮件、改变服务级别协议计时，或删除草稿。请求模式应明确客服服务将执行的副作用。

这正是广泛服务账户特别诱人的地方。它避免了权限摩擦，也让代理可以处理任何队列。但这也意味着一条有缺陷的指令就能跨越账户边界。如果服务商支持，应为队列或团队分配服务身份，然后把中介操作接口限制为该团队真正需要的操作。

## SSH 是执行权限，不是换了语法的 API 凭据

SSH 需要单独处理，因为私钥可能通向 Shell、文件传输、端口转发，以及能够使用自身凭据的工具。部署 API 可能只提供有限操作，而远程 Shell 可以即时组合出新的操作。

把 SSH 私钥交给编程代理会同时产生两种风险。密钥可能泄露，代理还可以生成任意远程命令。限制账户权限会有所帮助，但如果受限账户能够访问部署脚本、云 CLI 凭据或生产配置，它仍然可以做出远超原始任务范围的事情。

使用一个持有私钥的中介，接收请求的主机、命令和参数。让操作记录经过参数处理后解析出的主机和确切命令。不要批准一个代理之后还能通过嵌套引号、命令替换，或从不可信分支获取的远程脚本重新解释的 Shell 字符串。

对于敏感主机，应优先使用固定的远程操作，而不是通用 Shell 访问。像 `release-status --service billing-api` 这样的命令比 `bash -lc '...'` 更容易审查。如果必须允许通用命令，应准确显示它将如何执行，并要求逐次调用审批。把 `sudo`、安装软件包、读取密钥文件、Shell 重定向和向外复制命令视为高风险情况，不要把它们当作普通维护操作。

SSH 主机验证同样重要。客户端必须根据受管理的 known-hosts 条目验证服务器主机密钥。自动接受新的主机指纹，会让网络或 DNS 错误变成凭据使用事件，从而抵消隐藏私钥的意义。

## 经过中介的操作可以封存凭据，并建立决策点

中介操作系统把 SaaS 令牌或 SSH 私钥留在执行器中，为代理提供一个请求执行特定工作的接口。执行器附加凭据、发出请求并返回结果。代理永远不会拿到密钥，也不会拿到可能被它误填进命令的占位符。

这会改变故障模式。直接访问的代理如果接受了恶意指令，既能决定操作，也能使用可重复利用的凭据执行操作。经过中介的代理仍然可能请求错误操作，因为没有安全控制能让语言模型完美判断意图。但执行器可以识别调用方，要求人员批准调用，让凭据对代理保持不可用，并记录请求和结果。

Sallyport 使用这种模式处理 HTTP API 调用和 SSH 命令：支持 MCP 的代理通过 `sp mcp` 连接，而 macOS 应用把 API 和 SSH 凭据保存在加密保险库中，并自行执行操作。保险库锁定时会拒绝所有操作。当负责人员离开机器时，这正是正确行为。

不要把中介和中间人代理混为一谈。代理会观察或转发通用流量。操作网关接收具体操作请求，应用授权控制，使用自己保留的凭据，并返回结果。这种更紧密的结构可以提供审批点和审计记录，而不是等代理已经形成请求后，再试图解释所有流量。

最好的中介接口往往刻意保持简单。它们只暴露少量易懂的动词，接受结构化参数，拒绝模糊目标，并返回足以验证影响的信息。一个接受任意 URL、标头和正文的万能请求端点，可能只是用更多步骤悄悄重建了直接访问。

## 当人无法判断请求时，审批设计就会失败

审批提示应该帮助人员做决定，而不只是打断流程。「代理请求访问客服工具」要求审查人员批准一个未知的未来。「以客服团队身份向工单 4821 发布这条回复」则给出了可以检查的具体操作。

对于新代理进程的首次调用，应显示进程身份和代码签名权限。进程身份不是装饰。在开发机器上，多个终端、扩展和辅助程序都可能请求同一个集成。授予会话前，审查人员应该知道哪个已签名程序正在请求权限。

然后在正确层级进行授权：

- 对于一次代理运行期间需要重复读取或执行常规调用的低影响工作，使用会话审批。
- 对外消息、状态修改、部署、远程命令和不可逆操作，使用逐次调用审批。
- 离开机器时锁定凭据存储，让所有操作失败，而不是等待无人值守的审批。
- 任务变化、代理行为异常或无法确认进程身份时，撤销当前会话。

审批疲劳是设计失败。如果人员必须批准每次无害读取，就会养成一路点击通过的习惯。如果一次审批让代理获得整个下午修改生产环境的权限，界面就是把过多权限隐藏在便利性之后。根据后果拆分操作类型，让普通活动保持安静，让重要活动具体明确。

避免审批文字只是重复代理自己模糊的描述。操作层已经拥有结构化字段，应当直接使用它们。显示服务、经过身份验证的账户或角色、目标项目或主机、操作，以及将离开组织的面向人员的内容。对审查人员不需要看到的凭据和私密字段进行脱敏。

## 日志必须把代理运行连接到每个外部影响

仅有服务提供商日志不足以记录代理工作，因为它们从 API 边界才开始。仅有代理对话记录也不够，因为它们可能省略实际请求，或被编辑。你需要运行级记录和调用级记录，并让两者通过可靠关联连接起来。

运行级日志应标明代理进程、会话起止时间、允许运行的审批，以及立即撤销会话的方法。调用级日志应记录操作请求、授权结果、执行结果、目标，以及服务请求标识（如果存在）。如果请求正文包含客户内容，应避免在常规界面显示敏感内容，但要保留足够的受保护证据来调查事件。

如果日志可能用于处理争议，防篡改证据很重要。普通本地文本文件可以展示有用历史，但用户或已被入侵的进程可以改写它。哈希链让每条记录依赖前一条记录，因此后续编辑会破坏验证。这不会让日志绝对可靠，但会让不被察觉的改写更加困难，并为调查人员提供可测试的完整性属性。

Sallyport 从一个不可写入的加密哈希链审计日志中生成 Sessions 和 Activity 日志。无需保险库密钥，就可以使用 `sp audit verify` 离线验证密文链。健康的验证结果应类似这样：

```text
Audit chain: valid
Records checked: 184
First sequence: 1
Last sequence: 184
```

如果验证报告序列中断或哈希不匹配，应保留文件并先展开调查，不要继续依赖该日志。不要通过删除可疑末尾记录来「修复」日志，因为这可能删除记录何时以及如何发生变化的唯一证据。

## 直接令牌迁移应从风险最高的凭据开始

不要试图在一周内重新设计所有集成。先处理滥用后最难恢复的凭据：生产部署权限、广泛的客服访问权限，或能够进入共享系统的 SSH 密钥。在尝试完善每个工作流之前，迁移应先从代理中移除可使用的密钥。

为每个集成执行以下步骤：

1. 盘点代理现在从哪里获取凭据，包括环境变量、代码仓库文件、CI 密钥、Shell 配置文件、工具配置和复制过的提示词。
2. 列出代理实际执行的操作，再区分读取、起草、修改和远程执行。大多数直接令牌允许的权限远远超过这份清单。
3. 为所需操作创建结构化请求。每次写入都绑定到命名目标，每次部署都绑定到不可变的产物引用。
4. 把凭据移入代理无法读取的执行器。确认新路径正常后，轮换旧凭据。
5. 有意测试失败情况：锁定保险库、拒绝审批、撤销会话、提交无效目标，并运行审计验证。只演示成功路径几乎无法证明什么。

轮换步骤可以发现团队经常犯的错误：他们增加了中介，却为了「备用」把原令牌继续留在代理环境中。这样就留下了通往同一权限的两条路径，而控制较弱的那条最终一定会被使用。删除备用路径。如果中介路径暂时无法支持某项必需操作，就记录临时例外，限制它的范围和有效期，并指定负责人移除它。

直接交付令牌之所以容易，是因为它把困难的决定交给了进程环境中的一个字符串。对于一次性沙盒，这种取舍可能合理。对于会影响客户、部署或共享基础设施的服务，应让凭据远离代理，并让操作本身在发生前变得可见。
