对 AI 说“你看着办”,是事故的开始——怎么给它搭一个能干活的“脚手架”
AI 代理不乱来,靠的不是模型多聪明,而是“脚手架”。从能直接复制运行的最小示例,到怎么安全地把活交出去,讲得通俗易懂。
“这个仓库有点乱,你帮我收拾收拾。”
我这么吩咐完,第二天早上一看,AI 确实“收拾”了——它一口气改了 40 个文件。能跑的代码是有,可连那些删了就出大事的配置文件,也被它干干净净地“整理”掉了。
那一瞬间后背发凉的感觉,你有过吗?
明明那么聪明的 AI,怎么会面不改色地闯祸?原因其实很简单:“聪明”和“能安全地干活”,完全是两回事。就像考试门门满分、第一天打工却把收银机弄坏的新人——不是能力问题,是没人给他搭好台阶。
这个“台阶”,最近大家管它叫 harness(脚手架)。今天我就尽量不用术语,把它讲清楚。
脚手架到底是个啥?
说白了,脚手架就是放在 AI“外面”的一小段程序。
最好理解的类比,是工地上系在身上的那根安全绳,或者小孩学自行车时装的辅助轮。它不改变你本身的能力,只是保证你摔不下去。放到 AI 身上,它干的就是这么几件事:
- 决定让它读什么(不是全摊开给它看)
- 决定让它做什么(把目标说清楚)
- 决定哪些能自作主张,哪些必须停下来问人
- 做完之后,用机器去检查成果有没有坏掉
“写一条漂亮的 prompt”,只是这里面很小的一块。光磨 prompt 却挡不住事故,就像不装辅助轮硬要练独轮车——方向根本就反了。
为什么偏偏现在火起来了
不久之前,找 AI 干的活无非是“写段文字”“写段代码”。东西出来了,人读一遍、判断一下就行。
可现在不一样了,我们开始把**“干活”这件事本身**交给它:读文件、挑一个跟旧文章不重样的选题、确认改了哪些地方、去外部服务上注册、失败了还得报告原因——到这一步,人就没法每一步都插在中间盯着了。所以“它自己跑偏、自己闯祸”的风险,一下子就上来了。
Claude Code 口碑好,与其说是模型聪明,不如说是这个脚手架做得到位。读文件的工具、搜索的工具、拦住危险操作的机关、把项目规矩记下来的机制——正是这些“不起眼的周边”足够扎实。真正流行的从来不是什么魔法 prompt,而是这些不起眼的周边。
先跑起来:30 行的最小脚手架
讲再多,不如直接跑一遍。我们来搭一个最小的脚手架:只准 AI“读和写”,而且让它绝对碰不到指定文件夹之外的东西。只要有 Node.js 和一个 Anthropic 的 API key 就能跑。
先做准备。
mkdir harness-demo && cd harness-demo
npm init -y
npm install @anthropic-ai/sdk
mkdir sandbox
echo "# 备忘" > sandbox/note.md
接着写一份“许可清单”。这是脚手架的心脏,就一句声明:sandbox 之外,不许碰。
{
"workspace": "./sandbox",
"maxSteps": 6
}
然后是主程序(harness.mjs)。你只要记住一个地方就够了——safePath 就是那个“一旦想往文件夹外面跑就当场拦下”的门卫。光是有它在,开头那场“40 文件惨案”就不会发生。
import Anthropic from "@anthropic-ai/sdk";
import { readFile, writeFile } from "node:fs/promises";
import path from "node:path";
const client = new Anthropic();
const policy = JSON.parse(await readFile(new URL("./policy.json", import.meta.url), "utf8"));
const root = path.resolve(policy.workspace);
// 门卫:只要想跑到工作文件夹外面,就地拦下
function safePath(p) {
const resolved = path.resolve(root, p);
if (resolved !== root && !resolved.startsWith(root + path.sep)) {
throw new Error(`${p} 在工作文件夹之外。只能碰 sandbox 里面的东西。`);
}
return resolved;
}
const tools = [
{ name: "read_file", description: "读 sandbox 里的文本",
input_schema: { type: "object", properties: { path: { type: "string" } }, required: ["path"] } },
{ name: "write_file", description: "往 sandbox 里写文本",
input_schema: { type: "object", properties: { path: { type: "string" }, content: { type: "string" } }, required: ["path", "content"] } },
];
async function useTool(name, input) {
if (name === "read_file") return await readFile(safePath(input.path), "utf8");
if (name === "write_file") { await writeFile(safePath(input.path), input.content, "utf8"); return "写入成功"; }
throw new Error(`不认识的工具: ${name}`);
}
const messages = [{ role: "user", content: process.argv.slice(2).join(" ") || "读一下 note.md,把摘要写到 summary.md 里。" }];
for (let step = 0; step < policy.maxSteps; step++) {
const res = await client.messages.create({
model: process.env.ANTHROPIC_MODEL || "claude-sonnet-4-6",
max_tokens: 1024,
tools,
system: "你是一个谨慎的文件管理员。只在需要时才用工具,所有操作都限定在 sandbox 之内。",
messages,
});
messages.push({ role: "assistant", content: res.content });
const calls = res.content.filter((b) => b.type === "tool_use");
if (calls.length === 0) { console.log(res.content.find((b) => b.type === "text")?.text ?? ""); break; }
const results = [];
for (const c of calls) {
try { results.push({ type: "tool_result", tool_use_id: c.id, content: String(await useTool(c.name, c.input)).slice(0, 4000) }); }
catch (e) { results.push({ type: "tool_result", tool_use_id: c.id, is_error: true, content: e.message }); }
}
messages.push({ role: "user", content: results });
}
运行就这一行。
node harness.mjs
总共才几十行,可“AI 本体”“能用的工具”“许可的范围”“重试的上限”“坏了就停的机关”已经各就各位了。这就是脚手架的骨架。往后你只要在上面接上搜索、跑测试、等审批、发通知,它就会慢慢长成 Claude Code 那个样子。
这三个场景里它最管用
1. 批量产出内容时的质检 一句“帮我写篇博客”扔过去,AI 会面不改色地量产一堆又水又“几乎一个选题”的文章。这时候让脚手架带上流程:读现有标题 → 挑一个不撞车的切入角度 → 写正文 → 用机器核对字数和链接。这样一来,还轮不到人去纠结好坏,门卫就先把水文给挡回去了。我自己靠这个,每个月都有好几篇草稿在发布前被拦下。被拦下,我反而感激。
2. 给咨询邮件分类 “把进来的咨询读一遍,只把像生意的挑给我看。”读,自动来就行。但往客户名单里录入这一步,必须保留到人按下按钮为止。这一条用脚手架强制执行:读取自动、写入只做草稿(dry-run)、最终录入只由人来。把分错类的客户私自塞进正式数据库——这种事故就此消失了。
3. 部署前那口缓一缓的气 按下发布键之前,先逼它确认一遍:build 过没过、环境变量齐不齐、改动跟预想的是不是一致、有没有回滚的步骤。AI 老爱只盯着报错日志的“最后一行”,然后修到八竿子打不着的地方去。所以诀窍是——提前把“该看哪里”定好。别把整份日志全丢给它,只截相关的那几十行。光这一下,跑偏的修法就少了一大半。
从 Claude Code 身上能“偷”的 3 个设计
自己搭脚手架,不用从零硬想。Claude Code 就是一座样板房。不必照单全收,把下面这 3 条早点搬过来,整个东西会稳上一大截。
第一,把规矩分层。每次都不变的约定,写进配置文件;只管这一回的指示,就放在当下的便签里;长期的偏好,单独存一处。要是每回都一股脑塞进 prompt,越塞越长,精度反而往下掉。
第二,确定性的活儿交给命令。格式化、检查、跑测试,与其求着 AI 来做,不如用 npm test 这样的命令直接跑,又快又稳。只把“需要动脑”的活儿留给 AI。
第三,重的调研甩给别人去做。把又长的日志、又多的文件一股脑灌进主对话,关键的判断就被冲糊了。让调研在另一个进程里跑,你只接结论。光这一下,判断的锋利劲儿就回来了。
我亲手翻过的 3 次车
老实交代。我第一版脚手架,全是事故。
第一桩,工具给太多了。想着多备点方便,我一口气塞了三十来个工具,结果 AI 直接犯选择困难,“到底用哪个?”,一连串选错。现在我一开始只留 5 到 10 个。
第二桩,报错信息太不近人情。只回一句 Error: failed,AI 啥也修不了。后来我改成像 找不到 README.md。sandbox 里只有 note.md 这样,把原因和下一步一起写清楚,它居然突然就开始自己解决问题了。
第三桩,把检查全压在人眼上。“最后我再把把关就行”——这句话,一到忙起来的日子准崩。自从我摆上字数、死链、类型错误这类机器能判的门卫之后,半夜还在核对的次数就明显少了。
要上手,从这里开始
千万别一上来就造一个“全自动的聪明代理”。挑一件就算搞砸也能退回去的小活儿:草稿文章的质检、PR 的初审、咨询分类、上预发布前的确认。这个量级,刚刚好。
顺序永远是同一个。① 把让它读的范围划窄 → ② 把目标(成品)说清楚 → ③ 检查尽量交给命令去跑 → ④ 危险操作(删除、生产数据库、扣费、force push)一开始全部设成“先问人”。只把确认安全的操作,往后再升级成自动。光是守住这个顺序,事故就会少得吓你一跳。
权限怎么定,我整理在了 Claude Code 权限设置指南 里;团队一起用时怎么打地基,写在 CLAUDE.md 最佳实践。想把长任务拆开,也可以顺手看看 子代理使用模式。官方的思路,Claude Agent SDK 文档 是一手资料。
我实际试下来的结果
自从开头那场“40 文件惨案”,我就不再纠结“到底该不该信任 AI”了。我改看的是:这次是被哪个门卫拦下来的。最小脚手架里只加了一个 safePath,跑到文件夹外的事故就归零了;再加上字数和链接的自动核对,水文在发布前就被拦住。与其去找一个更聪明的 AI,不如先搭好那个“摔了也不会受伤”的脚手架。看着像绕远路,但这是我现在最真切的体会——这才是最快的一条路。
小结
脚手架工程,不是给 prompt 描眉画眼的技术。它是设计“给 AI 看什么、让它做什么、在哪里叫停、靠什么来确认”的技术。先把上面那 30 行跑起来,给自己的某件活儿装上一个“门卫”,从这儿开始。AI 干活的质量,与其取决于模型多聪明,不如说取决于它外面那圈脚手架。
想更成体系地把 AI 安全地嵌进自己的业务,可以翻翻 教程与模板一览;想在团队层面把权限、审查、验证一并理顺,欢迎看看 培训与落地咨询。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
让 Claude Code 真正进入可验证的工作流
先用免费 PDF 固定基础,再用 Gumroad 教材复用工作流;如果涉及团队导入、权限或收入路径,可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
怎么写指令,让 Claude Code 只改一个文件
从一句「改得更好一点」却被动了 40 行的翻车经历,总结出一套把改动范围、验证、回滚打包在一起的 Claude Code 请求模板。
Claude Code 权限拒绝后的恢复流程:不要削弱护栏
把 Claude Code 被拒绝的命令拆成拒绝原因、安全替代步骤、证据命令和重试条件,而不是立刻放宽权限。
Claude Code Harness Smoke Test:信任代理前的15分钟验证循环
用15分钟确认 Claude Code 的范围、禁止区域、验证命令、公开 URL 和收入 CTA。