今年早些时候,有人用 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,而是在代理的决策层注入规则,让它自己避开禁区。好处是灵活、上下文敏感;坏处是——它是软约束,代理可能忽略或误解。
所以实际工程中,你需要两层防线叠加:
- 语义层:skills 文件 / prompt 约束——告诉代理规则,减少误操作概率;
- 系统层:文件权限、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 编码代理正在从"辅助工具"变成"自治工作负载"。它越来越能干,也越来越需要被当作一个不可信但高权限的进程来对待。画好隔离边界,不是限制它的能力,而是让它的能力在安全轨道上释放。