# 由承包商管理的 AI 代理：干净利落地结束生产访问

承包商可以借助 AI 编程代理出色地完成工作，却仍可能让你的生产环境比接手时更糟。问题通常不是从恶意提示开始的，而是从所有人都以为别人负责代理流程、审批和清理开始的。

把承包商运行的代理当作临时生产操作员。为它指定一名负责任的内部发起人、一个可识别的流程身份、一条权限范围狭窄的授权路径，以及一套有人实际演练过的移除流程。如果你说不出今天谁能让它停下来，那就不是委派了工作，而是制造了一个无人负责的系统。

## 承包商的身份不能拥有生产代理

即使承包商负责启动和监督进程，拥有生产系统的公司也必须拥有代理的权限。承包商的雇佣关系、个人账户、笔记本电脑和日历邀请，都可能在你无法直接控制的条件下结束。生产访问不能依赖其中任何一项。

团队经常把两个问题混在一起：

- 在这次合作期间，谁负责操作代理？
- 代理获得的每项能力，最终由谁负责？

承包商可以回答第一个问题。第二个问题必须由内部员工回答。这个员工需要有足够权限暂停工作、调整范围、检查记录，并在无需等待承包商回应的情况下终止访问。

我见过一种非常普通的失误。承包商为了排查问题，获得了源码仓库邀请和生产 API 令牌。他们在自己的电脑上运行代理，检查代码、查询 API 并准备修复方案。合同结束后，承包商的仓库账户被禁用，所有人都觉得事情已经结束。几周后，一个自动化终端配置仍然保留着令牌，代理的旧工作目录中也还留着告诉它如何使用令牌的指令。公司只是从一个系统中移除了一个人，却没有从生产环境中移除一条仍可运行的操作路径。

不要把承包商的用户账户作为代理工作的长期身份。创建一份公司拥有的合作记录，并将访问权限绑定到这份记录上。记录应注明内部发起人、技术保管人、操作员、获准环境、进程身份和结束条件。一个人可能换工作，也可能在周末突然失联。一份记录却能告诉下一位值班工程师系统中有什么，以及如何关闭它。

这不是为了形式而增加的官僚流程。它能避免最昂贵的一种模糊状态：发生事故时，团队无法判断请求来自获准代理、承包商留下的旧配置，还是攻击者重新利用了被遗忘的访问权限。

## 让一名员工负责委派决定

每项承包商合作都需要一名发起人，由他负责决定是否允许代理执行操作，而不只是负责决定是否雇用承包商。发起人通常是工程经理、服务负责人，或对相关生产区域拥有权限的事故指挥人。

在代理获得生产权限前，发起人应回答四个具体问题。代理要完成什么工作？它可以访问哪个环境？工作范围内允许哪些操作？权限什么时候停止？

避免使用「帮助维护服务」或「协助部署」这类模糊范围。代理可以通过工具解释这些表述，模糊范围就会变得危险。应把工作写成可观察的任务，例如检查这个服务的错误响应、创建合并请求、运行指定的诊断命令，或为某个明确的端点提交变更请求。

发起人不需要整天坐在承包商旁边，但必须负责边界。如果承包商说工作现在需要写入数据库、新的基础设施角色，或访问另一个服务，这就是一次新的委派决定。不要让之前的批准悄悄扩大，以适应新的请求。

NIST 特别出版物 800-207 在零信任指导中提出了一个有用的观点：系统不应仅仅因为某个主体位于特定网络，或曾经访问过某项资源，就给予隐式信任。把这个逻辑应用到代理工作中。承包商在你的聊天频道中、连接到你的 VPN，或之前获准执行读取操作，都不意味着另一个进程可以执行生产写入。

团队经常在这里选择错误的简化方案。因为反复审批看起来很慢，他们就给承包商长期的广泛访问权限。审批之所以慢，是因为工作定义得不够清楚。先解决工作边界。长期广泛访问会把规划问题变成事故响应问题。

为发起人指定明确的替代负责人。如果发起人离开，就转移合作记录，并由新发起人重新批准权限。没有当前发起人的流程，应自动失去访问权限，或立即由人工禁用。没有任何正当理由让无人管理的承包商合作继续拥有生产权限。

## 每次审批都必须包含流程来源信息

批准一个人不同于批准一个进程。这个区别经常被忽略，于是审批覆盖的软件范围比审核者原本想要的更大。

人类身份告诉你谁完成了认证。进程身份告诉你哪个可执行文件请求了操作、它在哪里运行，以及它是否与预期的进程一致。当 AI 代理通过 shell、扩展、MCP 服务器、后台辅助程序和脚本工作时，真正接触凭据路径的是进程。

要求提供证据，证明新的代理进程就是获准的进程。在 macOS 上，代码签名主体可以作为一个有用的起点。它能识别可执行文件所呈现的签名主体，但不能证明生产请求属于这次承包商合作，也不能判断请求的操作是否合理。把它当作进程证据，而不是一张可以放行所有操作的通行证。

实用的审批卡不应只显示承包商的显示名称。它还应标明进程或启动器、可用时的代码签名主体、当前合作、目标环境和请求的能力。如果审核者无法区分已知代理进程和复制出来的脚本，审批就不够可靠。

这可以捕捉一种常见故障。承包商在一个终端中启动了获准的编程代理。之后，某个 shell 脚本或另一个代理进程继承环境变量，读取本地配置文件，并使用同一凭据发出相同请求。仅绑定个人的审批无法判断这两个调用者不同。第二个进程也许没有恶意，但没有人证明过这一点。

正确做法不是要求审核者达到不可能的确定性，而是减少一次审批覆盖的范围。将审批绑定到一个会话或一次进程运行，清楚标识这次运行，并在运行退出时让审批失效。新的进程必须重新说明自己的身份。

保留进程命令、工作目录、启动时间、发起人和预期结束条件的本地记录。你不需要收集承包商查看的每条私有提示或每个文件。你需要足够的运行证据来回答事故调查中的基本问题：哪个获准进程执行了这次操作？它得到谁的授权？当时是否仍在指定的合作范围内？

## 审批责任应与影响范围匹配

审批代理操作的人，应与该操作可能造成的后果相匹配。承包商可以批准委派范围内狭窄任务的日常操作，但不应在没有明确授权的情况下，成为能够修改生产数据、权限、客户沟通或基础设施操作的最终负责人。

把审批分为两层。发起人授权工作类别及其持续时间。操作审批人则在某项操作的影响值得人工决定时，批准这一次具体操作。在小团队中，一名员工可以同时担任两个角色，但记录中仍应将职责分开，因为它们之后回答的是不同的问题。

采用简单的责任模型：

- 发起人负责合作的目的、范围和过期时间。
- 承包商操作员负责任务质量，并在范围内请求操作。
- 操作审批人负责决定是否允许敏感调用。
- 技术保管人负责凭据路径和关闭流程。
- 安全或运维团队负责验证撤销是否完成。

不要为了证明有人参与控制，就给每一次无害的读取操作都设置审批仪式。审核者会机械点击通过，之后反而可能漏掉真正值得关注的请求。应把阻力放在会改变状态、扩大权限、暴露受限数据或触达新生产目标的操作上。

逐次调用审批最适合容易识别且难以撤销的操作，例如写入客户记录、生产部署、具有管理效果的 SSH 命令，或修改权限的请求。会话审批适合范围明确的调查工作，此时进程需要执行几次相关读取。开始工作前，记录中就应说明采用哪一种方式。

审批界面还必须写明目标。「允许代理使用 API」几乎无法帮助审核者作出判断。「允许这个获准进程向生产计费端点发送 POST 请求」才是一个审核者能够负责的具体决定。审核者仍然可以拒绝、要求提交变更请求，或要求承包商改用预发布环境。

审批疲劳是设计失败，不是放弃审批的理由。如果队列变成噪音，就收窄工具范围，把安全操作合并到一个短会话中，或把调查移到非生产环境。不要通过永久保留生产权限来解决问题。

## 不要让凭据进入代理上下文

代理应该请求操作，而不是持有授权操作的秘密。这条边界是让下线工作可行的关键。

如果承包商的代理收到了明文 API 令牌、SSH 私钥，或配置文件中的复制秘密，你就已经失去了对凭据传播位置的控制。令牌可能进入终端历史记录、代理记忆、日志、临时文件、代码补丁，甚至发送给其他服务的提示中。轮换凭据或许能清理令牌，但无法还原它曾经去过哪里。

使用操作网关，把凭据留在公司控制的一侧，并代表代理执行获准的 HTTP 或 SSH 操作。代理收到的是结果，而不是秘密。这样并不会让不安全的操作变得安全，但团队有了应用审批、记录调用，以及无需翻查承包商文件系统就能撤销权限的地方。

Sallyport 对 HTTP API 调用和 SSH 命令采用这种模式：秘密保存在其加密保管库中，代理通过 MCP 连接请求操作，而不是接收凭据。这里有价值的是隔离，而不是魔法。被攻破或粗心的代理仍可能在获准范围内请求有害操作，所以责任和审批依然不可缺少。

把每条凭据路径都当作一项清单资产。记录凭据负责人、目标、允许的操作类型、合作引用和关闭方式。不要满足于「承包商使用的生产令牌」这样的备注。它无法告诉事故响应人员令牌在哪里、是否被复制，或必须禁用哪条路径。

SSH 需要格外谨慎，因为团队常常以为承包商是临时的，主机登录也就自然是临时的。检查 authorized_keys、证书、本地代理转发、跳板机访问、shell 配置文件、计划任务以及部署脚本的远程副本。移除一个公钥，并不能撤销仍可通过另一条受信跳转路径访问系统的私钥。

HTTP 凭据也遵循同样的规则。移除或禁用操作路径，然后验证目标会拒绝新的请求。不要因为访问管理界面显示某个用户已禁用，就宣布工作完成。操作网关、服务账户、API 凭据和网络路由可能拥有不同的生命周期。

## 在授予访问权限前设计撤销流程

团队应该能够在不请求承包商配合的情况下，撤销由承包商运行的代理。如果流程需要承包商的笔记本电脑、密码管理器或记忆，那就不是撤销流程。

在第一次生产操作之前写好关闭路径。它应覆盖代理进程、审批会话、凭据路线、源码和基础设施访问、计划任务以及审计记录保留。每一项都需要指定操作员和验证完成的方法。

禁用和撤销并不是一回事。禁用会阻止特定账户或凭据今后使用。撤销则会结束当前的授权关系，并移除可能恢复该关系的路径。承包商账户可能已被禁用，但已经运行的进程仍保留有效会话。令牌可能已经撤销，但 SSH 连接仍然打开。两种状态都要处理。

合作结束或必须立即停止时，按以下顺序执行：

1. 在凭据网关或授权点阻止新的代理操作，然后撤销与这项合作相关的活动会话。
2. 停止已知的本地和远程代理进程，包括终端复用器、启动代理、持续集成任务和计划任务。
3. 禁用或轮换分配给这项合作的公司凭据，并移除源码仓库、云平台、VPN、堡垒机和工单系统的访问权限。
4. 在合作资产清单中查找复制的配置、生成的脚本、部署密钥和临时服务账户，然后将其移除。
5. 尝试一次无害的授权路径检查，验证请求确实被拒绝，并在关闭合作前保留操作记录。

顺序很重要。如果一开始就删除记录或禁用承包商账户，可能会丢失查找活动会话所需的信息。先停止操作路径，保留证据，再清理访问权限。

不要因为每个复选框都有负责人，就声称流程成功。应在合作期间测试流程。让保管人在承包商在场时撤销一个非生产会话。确认代理无法再发出调用，承包商理解哪些内容已停止，并且活动记录显示了拒绝结果。团队第一次发现关闭路径缺失，不应该是在安全事故或合同争议期间。

## 一份简短的合作记录能暴露缺失的决定

一份小而容易审核的记录，比一份没人阅读的长篇访问政策更能发现真实问题。把它与工作工单或受控的运维仓库放在一起，不要放在私人聊天线程中。

下面的示例刻意保持简单。请用自己的标识符替换占位值，但不要因为合作看起来是临时的，就省略字段。

```yaml
engagement: contractor-search-repair-2025-04
sponsor: employee-ops-owner
technical_custodian: employee-platform-owner
contractor_operator: external-developer
purpose: diagnose production search indexing failures
agent_process:
  approved_launcher: signed-local-agent-process
  allowed_workstation: managed-mac-asset-184
  expires_at: 2025-04-30T17:00:00Z
scope:
  environments: [staging, production-read]
  permitted_actions:
    - GET search-service health endpoint
    - GET indexing queue depth endpoint
  prohibited_actions:
    - production writes
    - credential administration
approval:
  session_owner: employee-ops-owner
  per_call_owner: employee-platform-owner
credential_routes:
  - search-api-read-route
  - bastion-diagnostic-route
revocation_owner: employee-platform-owner
verification_owner: employee-security-owner
```

这份记录将人们经常合并为一个标签的内容分开了。`contractor_operator` 不是 `sponsor`。`session_owner` 不一定是 `per_call_owner`。`revocation_owner` 也不是决定需要开展这项工作的人。这样的分离能防止承包商实际上批准自己扩大权限，也能防止一名不在岗的经理成为唯一能够停止访问的人。

`permitted_actions` 字段应使用动词和目标。「只读」太宽泛，因为某些服务的端点会触发导出、暴露个人数据或消耗容量。「生产读取」也需要谨慎理解。读取操作仍可能暴露受监管数据或帮助攻击者了解运营细节。

即使合同没有确切的结束日期，也要设置过期时间。如果工作继续，就通过新的决定延长。明确的续期会迫使发起人检查工作是否仍需要生产访问，以及原来的进程是否仍是当前使用的进程。

## 审计记录必须回答运营问题

代理工作的审计轨迹必须回答：谁授权了操作，哪个进程发出了请求，请求触达了什么目标，是否成功，以及权限何时被撤销。聊天记录无法可靠地回答所有这些问题。

在条件允许时保留两种视图。一种视图以代理运行或会话为线索，让调查人员看到授权生命周期并停止正在运行的任务。另一种视图以单项操作为线索，让操作员检查具体的 API 调用或 SSH 命令。两种视图应关联到同一底层事件记录，不要靠人工维护两套互不相同的故事。

保护审计日志，不能让它被正在记录的进程修改。能够改变自身历史的进程，可能隐藏最重要的证据。追加写入存储、受限写入权限和加密链都很有帮助，但每种机制都有自己的作用。哈希链可以显示记录是否被修改或删除，却无法告诉你原始请求是否是个好主意。

Sallyport 会从加密、哈希链式审计日志中生成会话日志和活动日志。其 `sp audit verify` 命令可以在离线状态下对密文验证哈希链。当存放保管库的机器已锁定，或调查人员不应直接读取秘密时，这项能力很有用。

对每项合作，都要决定由谁以及何时审核记录。敏感的生产写入可能要求承包商继续工作前先完成审核。短暂的诊断任务或许只需在关闭时审核，但团队仍应知道事故期间如何定位相关事件。

同时保留拒绝记录。被拒绝的操作可能表明代理试图超出范围、承包商误解了任务，或下线后仍有过时进程在运行。如果只保留成功调用，就会丢掉经常能够解释下一次事故的证据。

## 合同条款应与访问设计保持一致

合同无法撤销 API 令牌，但可以消除导致团队遗留访问权限的模糊地带。应使用与团队实际控制措施相对应的语言，写明运营义务。

明确公司拥有为这项合作创建的凭据、进程注册信息、审计记录和所有访问配置。明确承包商必须通过公司批准且受公司控制的路径执行生产操作，不得将凭据复制到本地文件、提示、代码仓库或第三方服务中。如果工作需要例外，必须在例外发生前取得发起人的书面批准。

加入工作材料返还和删除义务，但不要把承诺书当作唯一控制措施。承包商可能本意良好，却仍会漏掉 shell 历史文件、缓存的环境变量或备份。技术撤销负责处理你能控制的部分，合同条款则覆盖系统之外仍然存在的义务。

从运营角度定义合作结束：发起人关闭工作，保管人禁用操作路径，验证负责人确认拒绝测试完成，团队按照正常保留规则保存审计记录。如果承包商之后仍需要支持访问，就创建新的合作，不要重新启用旧合作。

避免使用「承包商负责安全」这种没有明确决定和控制措施的条款。这种表述听起来很有力度，却无法回答有人在下午 6 点要求修改生产环境时的关键问题。应写明谁可以批准变更、谁执行变更，以及谁可以叫停变更。

第一份需要准备的文件，不是一份宽泛的 AI 政策，而是下一位需要让代理接近生产环境的承包商的合作记录。让发起人和保管人坐在一起填写。任何空白字段，都直接指出了团队在授予访问权限前仍需完成的工作。
