Claude Code/Codex 安全 Agent Harness 实战:权限、验证与回滚
用权限策略、执行计划、验证脚本和回滚日志,为 Claude Code 与 Codex 搭建更安全的 AI Agent 工作流。
Agent 越强,外部约束越重要
刚开始使用 Claude Code 或 Codex 时,很多人会把重点放在提示词上。小修小改时,这确实有效。但一旦你开始让 Agent 处理部署、SaaS API、文件编辑、文章发布、邮件发送,问题就不再只是提示词了。
真正重要的是 Agent 外面的足场,也就是 Agent Harness。
本文把 Agent Harness 定义为:让 AI Agent 安全工作的外部结构,包括权限规则、执行计划、验证脚本、运行日志和回滚路径。Agent 仍然负责理解任务、生成代码和提出改动,但它不能无条件执行所有命令。
如果想先理解概念,可以从 harness engineering 指南 开始。想看具体事故,可以阅读 Claude Code 安全失败案例。如果需要长期上下文管理,也可以结合 Claude Code 与 Obsidian 集成。
这个思路适用于 Claude Code,也适用于 Codex。工具不同,安全结构是相同的。
User request
|
v
Agent
|
v
[1] Policy layer 哪些可以执行,哪些要询问,哪些必须禁止
[2] Plan layer 先做什么,后做什么
[3] Verification layer 如何确认真的成功
[4] Recovery layer 失败后如何恢复
|
v
Files / shell / SaaS APIs / deploy
Claude Code 官方文档中也把 settings、permissions、hooks、MCP 等能力拆开说明。可以参考 Claude Code settings、Hooks reference 和 MCP 文档。
实例1:内容发布 Agent Harness
“发布一篇文章”听起来像一个动作,但实际上是一条工作流。
1. 读取最近7天的访问数据
2. 从高流量主题周边选择新题目
3. 检查是否和旧文章重复
4. 写日文或英文源稿
5. 生成所有语言版本
6. 检查 frontmatter、slug、内部链接
7. 构建网站
8. 检查公开 URL
9. commit 并 push
如果这些步骤只存在于人的记忆中,Agent 很容易只完成“看得见”的部分,比如写正文,却忘记翻译、构建或线上确认。Plan layer 的价值,就是把这些步骤变成固定流程。
实例2:SaaS 连接 Agent Harness
SaaS 连接更需要边界。比如让 Agent 搜集公司信息、生成样例网站、起草销售邮件。
1. 读取公开网页
2. 保存公司名、网站和邮箱
3. 生成样例 landing page
4. 起草邮件
5. 发送邮件
前四步可以自动化,第五步必须要人确认。因为邮件一旦发出,就会影响真实的人,不能简单撤回。
| 操作 | 是否自动 | 原因 |
|---|---|---|
| 读取公开网页 | 可以 | 风险较低 |
| 保存本地CSV | 可以 | 可审查 |
| 生成样例页面 | 可以 | 发布前可检查 |
| 起草邮件 | 可以 | 尚未发送 |
| 发送邮件 | 需要确认 | 外部影响,难以撤回 |
这就是 Agent Harness 的核心:读取、生成、发布、发送要分开。
Policy layer:从 allow、ask、deny 开始
最小的策略文件可以这样写。
{
"allowCommands": [
"npm run build",
"npm run test",
"node scripts/analytics-report.mjs",
"node scripts/content-trend-report.mjs"
],
"askCommands": [
"git push",
"wrangler pages deploy",
"node scripts/outreach-send-mails.mjs --send"
],
"denyCommands": [
"rm -rf",
"git reset --hard",
"curl * | sh",
"npm publish"
],
"protectedPaths": [
".env",
".env.local",
"claudecode-lab-sheets-f54fc47c68f0.json"
]
}
Claude Code 项目设置也可以表达类似边界。
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(npm run build)",
"Bash(npm run test *)",
"Bash(node scripts/content-trend-report.mjs *)"
],
"ask": [
"Bash(git push *)",
"Bash(wrangler pages deploy *)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(git reset --hard *)",
"Read(./.env)",
"Read(./.env.*)",
"Read(./claudecode-lab-sheets-f54fc47c68f0.json)"
]
}
}
不要只写“请小心处理密钥”。应该把秘密文件、破坏性命令、生产部署、外部发送操作写成明确规则。
Verification layer:把成功条件写成脚本
“仔细检查”不是验证。更好的做法是把成功条件变成命令。
// scripts/verify-published-page.mjs
const url = process.argv[2];
if (!url) {
throw new Error("Usage: node scripts/verify-published-page.mjs <url>");
}
const response = await fetch(url, { redirect: "follow" });
if (!response.ok) {
throw new Error(`Page returned ${response.status}: ${url}`);
}
const html = await response.text();
const checks = [
["title", /<title>.+<\/title>/i],
["description", /<meta name="description"/i],
["adsense", /ca-pub-2125588229998303/i],
["analytics", /G-3YR0LE68MJ/i]
];
for (const [name, pattern] of checks) {
if (!pattern.test(html)) {
throw new Error(`Missing ${name} on ${url}`);
}
}
console.log(`OK: ${url}`);
如果文章包含代码块,还应该用 Playwright 检查手机宽度下是否横向溢出。
import { chromium } from "playwright";
const url = process.argv[2];
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 390, height: 844 } });
await page.goto(url, { waitUntil: "networkidle" });
const overflowing = await page.evaluate(() => {
return [...document.querySelectorAll("pre, code, table")]
.filter((el) => el.scrollWidth > el.clientWidth + 4)
.map((el) => el.textContent?.slice(0, 80));
});
await browser.close();
if (overflowing.length > 0) {
console.error(JSON.stringify(overflowing, null, 2));
process.exit(1);
}
Recovery layer:先记录,再回滚
自动化真正危险的地方不是失败,而是失败后不知道改了什么。每次运行都应该留下日志。
{
"runId": "2026-05-19-article-001",
"topic": "agent harness security",
"changedFiles": [
"site/src/content/blog/claude-code-codex-agent-harness-security.mdx",
"site/src/content/blog-zh/claude-code-codex-agent-harness-security.mdx"
],
"commands": [
"node scripts/content-trend-report.mjs --days 7",
"npm run build",
"wrangler pages deploy dist --project-name claudecode-lab"
],
"status": "deployed"
}
Git 工作流中,优先使用有目标的恢复,而不是粗暴 reset。
git status --short
git diff -- site/src/content/blog/target-article.mdx
git revert <bad-commit>
不要让 Agent 随便执行 git reset --hard,尤其是在工作区可能有他人改动时。
最小 Node.js 命令 Harness
下面是一个很小的命令分类器。
const policy = {
allow: [
/^node scripts\/content-trend-report\.mjs( .*)?$/,
/^npm run build$/,
/^npm run test$/
],
ask: [
/^git push( .*)?$/,
/^wrangler pages deploy( .*)?$/
],
deny: [
/rm -rf/,
/git reset --hard/,
/curl .* \| sh/,
/npm publish/
]
};
export function classifyCommand(command) {
if (policy.deny.some((rule) => rule.test(command))) return "deny";
if (policy.allow.some((rule) => rule.test(command))) return "allow";
if (policy.ask.some((rule) => rule.test(command))) return "ask";
return "ask";
}
这个代码很普通,但这正是优点。好的 harness 应该稳定、可预测、容易审查。
总结
AI Agent 的质量不只取决于提示词。真正进入实务后,质量来自模型外面的结构。
- Policy:什么可以做,什么必须问,什么禁止
- Plan:执行前先明确步骤
- Verification:用命令证明成功
- Recovery:失败后知道如何恢复
当你让 Claude Code 或 Codex 接手更多工作时,不要只加长提示词。先搭建 Agent Harness。这样才能从“能修改文件的 AI”走向“可以安全承担一部分业务流程的 AI”。
免费 PDF:5 分钟看懂 Claude Code 速查表
只需留下邮箱,我们就会立即把这份 A4 一页速查表 PDF 发送给你。
我们会严格保护你的个人信息,绝不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
本文作者
Masa
深度使用 Claude Code 的工程师。运营 claudecode-lab.com——一个涵盖 10 种语言、超过 2,000 页内容的科技媒体。
相关文章
Claude Code 子代理 (Subagent) 实战模式 10 选
使用 Claude Code 的子代理功能,掌握 10 种实战模式。学习如何使用并行处理、专业化和上下文隔离来加倍开发速度。
Claude Code Agent SDK入门 ― 快速构建自主智能代理
学习如何使用Claude Code Agent SDK构建自主AI代理。涵盖环境搭建、工具定义和多步执行,附带实践代码示例。
Claude Code 上下文管理技巧完全指南
详解如何最大限度地利用 Claude Code 的上下文窗口。涵盖 Token 优化、对话分割和 CLAUDE.md 的使用方法。