当 AI 编码代理成为「不可信工作负载」,隔离边界该怎么画?

2026-05-26 16 预计阅读时间:1 分钟
来源:docker.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:11 分钟

今年早些时候,有人用 Claude Code 把整个博客迁移到 Astro——146 篇文章、6024 张图片、Canonical URL、JSON-LD、Sitemap,全套重写。他花了好几个小时写一份 skills 文件,教代理理解博客架构、部署流程、哪些文件绝对不能碰。结果确实跑通了:组件重写、Bug 修复、批量迁移,全由代理完成。

但这个故事的重点不是"AI 多能干",而是另一个更硬的问题:当你把一个能自主决策、自主执行代码的工作负载交给机器,它本质上是一个不可信的自治进程。传统的隔离模型不够用了。

不可信自治工作负载:不只是"沙箱"那么简单

传统意义上的不可信工作负载,我们已经有成熟套路:浏览器里的 JavaScript 有 V8 沙箱;容器里的微服务有 cgroup + namespace;云上的租户有 VPC 和 IAM。这些模型有一个共同前提——工作负载的行为是可预测的、范围是封闭的。你不会指望一个容器突然决定"我觉得应该顺便改一下隔壁服务的配置"。

AI 编码代理打破了这个前提。它:

  • 自主选择行动路径——不是执行固定脚本,而是根据上下文推理下一步该干什么;
  • 行动范围天然是"整个仓库"——读文件、写文件、跑命令、装依赖、改配置,全在同一个工作目录里;
  • 错误模式是语义级的——不是 crash 或 OOM,而是"理解偏差导致它删了不该删的文件、改了不该改的逻辑"。

这意味着,传统的"给个沙箱就安全了"思路失效。沙箱能阻止代理访问宿主文件系统,但阻止不了代理在项目内部把 package.json 的版本号改成不兼容的值、把 .env 文件提交到 Git、或者把生产数据库的连接串硬编码进组件。

防线要画在语义边界上,不只是系统边界

原文作者的做法指向了一个关键思路:用 skills 文件在语义层面画边界。这不是系统级的权限控制(像 chmod 或 SELinux),而是告诉代理"这个项目的规则是什么"——哪些目录是只读的、哪些文件是关键配置不能改、部署流程的步骤顺序是什么。

这实际上是一种声明式约束:你不拦截每一个 syscall,而是在代理的决策层注入规则,让它自己避开禁区。好处是灵活、上下文敏感;坏处是——它是软约束,代理可能忽略或误解

所以实际工程中,你需要两层防线叠加:

  1. 语义层:skills 文件 / prompt 约束——告诉代理规则,减少误操作概率;
  2. 系统层:文件权限、Git hook、CI 校验——即使代理违反语义规则,系统层也能兜底拦截。

实践:给 AI 编码代理画一个双层隔离边界

下面是一个可改造的示例,展示如何为 AI 代理设置语义约束 + 系统约束的双重防线。

第一层:Skills 文件(语义约束)

在项目根目录放一份 .claude/skills.md,作为代理的行为手册:

# Project Skills: Astro Blog Migration

## Architecture Overview
- Framework: Astro 4.x with SolidJS islands
- Deployment: Cloudflare Pages via `wrangler pages deploy ./dist`
- Content: Markdown posts under `src/content/blog/`, images under `src/assets/images/`

## Hard Constraints (DO NOT VIOLATE)
- NEVER modify files in `src/content/blog/` except to update frontmatter fields
- NEVER delete or move files under `src/assets/images/`
- NEVER change `astro.config.mjs` site URL or canonical origin
- NEVER commit `.env` or any file containing secrets
- NEVER run `git push` — I will push manually after review

## Soft Preferences
- Prefer editing existing components over creating new ones
- When adding dependencies, check if Astro has a built-in alternative first
- Keep component files under 150 lines; split if they grow larger

## Deployment Flow
1. `npm run build` — must pass with zero errors
2. `wrangler pages deploy ./dist` — only after manual approval

这份文件的核心不是"教代理怎么写代码"(它已经会了),而是画红线:哪些动作绝对禁止、哪些偏好应该遵循、关键流程的步骤顺序是什么。

第二层:Git Pre-commit Hook(系统约束)

语义约束可能被忽略,所以用 Git hook 兜底。下面是一个 pre-commit 脚本,阻止代理(或任何人)提交敏感文件或破坏关键配置:

#!/usr/bin/env bash
# .git/hooks/pre-commit — enforce hard constraints even if the agent ignores skills

set -e

STAGED_FILES=$(git diff --cached --name-only)

# Block .env or secret files
for f in $STAGED_FILES; do
  if [[ "$f" =~ \.env || "$f" =~ secret || "$f" =~ credentials ]]; then
    echo "BLOCKED: $f looks like a secrets file. Unstage it before committing."
    exit 1
  fi
done

# Block direct edits to astro.config site URL
if grep -q "site.*:" astro.config.mjs; then
  ORIGIN=$(git show HEAD:astro.config.mjs | grep -oP 'site:\s*"[^"]+"')
  NEW_ORIGIN=$(grep -oP 'site:\s*"[^"]+"' astro.config.mjs)
  if [[ "$ORIGIN" != "$NEW_ORIGIN" ]]; then
    echo "BLOCKED: astro.config.mjs site URL changed. This is a hard constraint."
    exit 1
  fi
fi

# Block deletion of image files
for f in $STAGED_FILES; do
  if [[ "$f" =~ src/assets/images/ ]] && ! git show ":$f" > /dev/null 2>&1; then
    echo "BLOCKED: Image file $f was deleted. Images must not be removed."
    exit 1
  fi
done

echo "Pre-commit checks passed."

安装 hook:

# 使 hook 可执行
chmod +x .git/hooks/pre-commit

# 验证:故意暂存一个 .env 文件,应该被拦截
touch .env.test
git add .env.test
git commit -m "test"  # 应输出 BLOCKED 并中止

第三层:CI 校验(远程兜底)

即使本地 hook 被绕过(比如 git commit --no-verify),CI 还能拦最后一道。在 CI 里加一个轻量检查:

# .github/workflows/guard.yml
name: Constraint Guard
on: [push]

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: No secrets in tracked files
        run: |
          ! git ls-files | grep -E '\.env|secret|credentials'

      - name: Site origin unchanged
        run: |
          ORIGIN=$(git diff HEAD~1 HEAD -- astro.config.mjs | grep -oP 'site:\s*"[^"]+"' | head -1)
          if [[ -n "$ORIGIN" ]]; then
            echo "Site origin was modified in this commit — blocked."
            exit 1
          fi

      - name: No image deletions
        run: |
          DELETED=$(git diff HEAD~1 HEAD --diff-filter=D --name-only | grep 'src/assets/images/' || true)
          if [[ -n "$DELETED" ]]; then
            echo "Images were deleted: $DELETED — blocked."
            exit 1
          fi

三层叠加的效果:代理在语义层就知道不该做什么;如果它忽略了,本地 Git hook 拦住;如果 hook 也被绕过,CI 拦住。 每一层都不是完美的,但三层一起,误操作穿透的概率大幅降低。

重新理解"隔离":从容器围墙到语义护栏

传统隔离是围墙式的——画一个边界,里面随便折腾,外面绝对不可达。这对确定性工作负载够用。

AI 编码代理需要的是护栏式隔离——它在一个共享的工作空间里行动,但某些路径被标记为"不可走"。护栏不是墙,它允许代理在安全区域内自由决策,只在关键边界处施加约束。

这带来几个工程上的变化:

维度 传统不可信工作负载 AI 编码代理
隔离模型 围墙(namespace / sandbox) 护栏(语义规则 + 系统兜底)
约束生效点 syscall 拦截 决策层注入 + 文件系统校验
错误模式 crash / 权限越界 语义偏差 / 理解错误
审计方式 日志 / 网络监控 Git diff review / CI check
信任梯度 全不信任或全信任 分区信任(某些目录可写,某些只读)

分区信任是关键概念。你不需要让代理完全只读——那它就没法干活了。你需要的是:src/components/ 可写、src/content/blog/ 只改 frontmatter、astro.config.mjs 只读、.env 不可见。每个区域有不同的信任等级,对应不同的约束强度。

上线前的检查清单

如果你准备把一个大任务交给 AI 编码代理,先跑一遍这个清单:

  • 有没有 skills 文件?——写清楚架构、硬约束、软偏好、关键流程。这不是可选的,是必须的。
  • 硬约束有没有系统层兜底?——每个"绝对不能做"的规则,都要有一个 Git hook 或 CI check 能拦截。否则它只是建议,不是约束。
  • 代理的写权限范围是否明确?——列出哪些目录/文件可写、哪些只读、哪些不可见。分区信任比全局信任安全得多。
  • 有没有 review 流程?——代理的输出必须经过人类 diff review 才能合并。git push 不应该由代理执行。
  • 有没有回滚方案?——代理改坏了东西,你能在 30 秒内 git reset 回到干净状态吗?确保代理在独立分支上工作,不直接改 main。

AI 编码代理正在从"辅助工具"变成"自治工作负载"。它越来越能干,也越来越需要被当作一个不可信但高权限的进程来对待。画好隔离边界,不是限制它的能力,而是让它的能力在安全轨道上释放。


相关推荐