最近开发者圈子里,关于“AI 能不能真正替程序员写代码”的讨论,已经从“能不能补全几行代码”升级到了一个新话题:让 AI Agent 长期独立完成编码工作,连续几周、几个月都只用 Agent 写代码,不手敲业务代码,最终能不能上线产品。有一类实践分享的标题很直接——Six months of writing code exclusively with agents。这类分享说的不是“偶尔用 AI 辅助”,而是把绝大部分业务编码工作交给编程 Agent,人只负责拆需求、写规格、做验收。很多人的第一反应是“这有点冒险”,第二反应是“那我应该怎么复现这套玩法”。本文就把这套工作流拆开来讲,从概念、工具、工作方法到完整示例,最后给出排错清单和工程建议,尽量让零基础的开发者也能照着搭出一套自己的纯 Agent 编程环境。
1. 背景与核心概念
1.1 什么是 Agent 编程
Agent 编程,核心是一个“编程智能体”。这里说的 Agent,不是那种只能聊天的对话机器人,而是能真正操作代码库的程序:它可以读取项目目录里的文件、搜索函数定义、修改代码、在终端里运行命令、看到报错后继续调整,直到任务完成。你可以把它理解成一个“手脚麻利、执行力强,但需要你把需求说清楚”的虚拟同事。
以往我们用 AI 写代码,更多是“自动补全”或“聊天助手”。自动补全根据当前光标位置预测后续代码;聊天助手根据你贴进去的代码片段给出修改建议。而 Agent 的能力边界要大得多:它会自己去翻项目结构,确认依赖版本,跑测试,修复编译错误。你告诉它“做什么”,它负责把过程走完。
常见的 Agent 工具有 Claude Code、Cursor 里的 Agent 模式、Codex CLI 等。它们侧重点不同,但底层逻辑相似:以大语言模型为核心,把文件读写、终端执行、信息搜索这些能力包装成“工具”,让模型按步骤调用。这样一来,AI 就从“写一段代码”变成了“执行一个开发任务”。
1.2 Agent 和普通 AI 编码工具有什么区别
很多人会把 Copilot、代码补全和 Agent 混为一谈,实际上它们的定位完全不同。看下面这张表会更清楚:
| 工作模式 | 代表形态 | 你能做什么 | 适合范围 |
|---|---|---|---|
| 代码补全 | IDE 中的自动联想 | 手写主体,AI 补局部 | 单个函数、单行代码 |
| 聊天助手 | Copilot Chat 等 | 提问、贴代码、讨论方案 | 单文件、局部问题 |
| 编程 Agent | Claude Code、Cursor Agent、Codex CLI | 拆分需求、验收结果 | 多文件功能、完整小项目 |
这个区别很关键。聊天助手通常只改你贴出来的代码,遇到跨文件重构时,你需要自己来回搬运上下文;Agent 则会直接读取整个项目,理解路由、配置、依赖关系,自己决定改哪些文件。
换句话说,使用聊天助手时,你的角色还是“写代码的人”,AI 更像一本会说话的参考书;使用 Agent 时,你的角色变成了“项目经理 + 验收员”,AI 是干活的人。这个角色转变,就是“只用 Agent 写代码”半年之后最核心的变化。
1.3 连续几个月只用 Agent 写代码,到底在练什么
很多开发者看到这种半年实践分享,第一反应是问“AI 真的能独立写项目吗”。答案比想象中复杂:能,但不是把需求一句话扔进去就完事。
这些实践分享真正强调的,不是“AI 多厉害”,而是人在整个流程里如何调整自己的角色。连续几个月只用 Agent 写代码,其实是在锻炼三种能力:
- 把模糊需求拆成可执行切片。
- 写出机器能理解的规格说明和验收标准。
- 在 Agent 产出结果后快速定位问题、引导修复。
也就是说,编写代码的“体力活”被替代了,但“脑力活”并没有消失,只是换了一种形式。理解这一点,你就不会觉得“纯 Agent 编程”是万能的,也不会误以为“只要装了工具就能躺着收钱”。
2. 纯 Agent 编程的整体工作流拆解
2.1 一个典型迭代循环长什么样
如果你看过几篇“只用 Agent 写代码半年”的分享,会发现大家的工作流大同小异,基本围绕一个循环展开:
- 规划:把一个功能拆成若干个小切片,写下每个切片的预期行为和验收标准。
- 启动:打开 Agent 会话,加载项目说明和规格文档。
- 执行:Agent 读取相关文件,按规格实现功能,运行测试。
- 验证:你检查 git diff、跑一遍测试、在浏览器里点一遍关键流程。
- 修复:如果测试失败或行为不符,把错误信息反馈给 Agent,缩小修改范围。
- 提交:确认无误后提交一个可回滚的 commit。
这个循环看起来不复杂,但难点在于“规划”和“验证”。因为 Agent 的执行速度很快,真正决定项目质量的是你有没有把需求拆到位、有没有写清楚验收标准。
2.2 任务切片:把功能切到 Agent 能一口吃掉
“任务切片”是纯 Agent 编程里最重要的方法论。所谓切片,不是按代码层横切,比如“先把接口写完再写页面”,而是按功能纵切,比如“做一个能新增待办事项的完整链路”。
反例是这种提示词:
帮我把一个待办管理应用做完。这个任务太大了。Agent 会自由发挥,选择自己熟悉的技术栈,设计出你并不想要的数据库表,最后生成一个“看起来能跑但根本不符合预期”的项目。正例是这种提示词:
实现 POST /api/todos 接口,接收 { title: string },把数据写入 SQLite,返回创建后的完整对象,状态码为 201。一个合格切片应该满足三个条件:
- 可以独立验收,不依赖后面的功能。
- 改动范围可控,最好只涉及少量文件。
- 有明确的完成标准,比如“接口返回正确 JSON”“测试通过”。
把大需求拆成切片,再逐个交给 Agent,你才能精确控制每一步的产出,而不是等到最后收到一个无法定位问题的巨大 diff。
2.3 规格先行:Agent 最需要的不是灵感,是边界
和人沟通时,对方可以根据表情、语气、背景补全你的意图;但 Agent 没有这种“默契”。它面对模糊需求时,会自己脑补一个最可能的方案,而这个方案往往不是你想要的。
所以,在启动 Agent 之前,你应该先写一份规格文档。这份文档不需要很复杂,但要包含项目目标、技术约束、接口定义、验收标准、禁止事项。规格文档的作用是让每一次会话都有一个“稳定锚点”。Agent 会话可能每天重置,上下文可能丢失,但只要规格文档存在,就可以反复加载。
规格先行听起来有点像传统软件工程里的“需求文档”,但在 Agent 编程里它更重要。因为传统开发中,需求文档最终要由人来读,人具备补全能力;而 Agent 是程序,补全能力弱,指令边界越清楚,产出越稳定。
3. 环境准备与工具选型
3.1 工具链清单
在开始用 Agent 写代码之前,先把运行环境准备好。下面是一份通用工具清单:
- 操作系统:macOS、Linux,或 Windows 搭配 WSL。
- 运行时:Node.js 18 或更高版本。
- 版本控制:Git。
- 编辑器:VS Code 或其他你熟悉的编辑器。
- 编程 Agent:本文以 Claude Code 为例。
- 项目依赖:按项目本身的技术栈来,比如 Express、SQLite。
版本需要根据你的项目实际情况调整,这里重点演示配置思路。如果你只是想在现有项目里试用,不需要重新创建工程,直接进入 3.2 安装 Agent 即可。
3.2 安装并初始化 Claude Code
Claude Code 是 Anthropic 官方推出的终端编程 Agent,使用 npm 就可以安装。打开终端,执行下面两条命令:
npm install -g @anthropic-ai/claude-code claude --version第一条命令会全局安装 Claude Code,第二条命令用来验证是否安装成功。如果提示找不到命令,说明 npm 全局目录没有加入 PATH,或者 Node.js 版本过低。建议先确认 Node 版本:
node -v如果node -v输出版本低于 18,先升级 Node.js。这里更推荐用 nvm 管理 Node 版本,避免直接使用 sudo 修改系统目录。
安装完成后,进入你的项目目录并启动交互式会话:
cd ~/projects/your-app claude首次启动通常会引导你登录账号或配置 API Key。如果你已经有 API Key,也可以通过环境变量注入:
export ANTHROPIC_API_KEY="你的密钥"注意:API Key 属于敏感信息,不要写进 Git 仓库,也不要复制到公开提问中。如果你遇到账号、配额或服务范围相关的报错,请以官方文档说明为准,不要尝试使用任何非官方手段绕过限制。
3.3 VS Code 集成与项目权限配置
Claude Code 本身是终端工具,最方便的使用方式是在 VS Code 的集成终端里启动。这样 Agent 修改代码时,文件树、编辑器、终端输出可以同时看到,不用来回切换窗口。
你还可以在 VS Code 的设置里调整终端回滚行数,方便查看 Agent 输出的大量日志。.vscode/settings.json示例:
{ "terminal.integrated.scrollback": 10000, "terminal.integrated.defaultProfile.windows": "Git Bash" }回滚行数调大之后,即使 Agent 在终端里输出了很多日志,也不会被截断,排错时更从容。
切换到 Agent 开发之后,还有一个安全点是权限控制。Claude Code 支持在项目级配置里声明允许和禁止的命令。以.claude/settings.json为例:
{ "permissions": { "allow": [ "Read", "Edit" ], "deny": [ "Bash(rm -rf *)", "Bash(git push)" ] } }这个配置的意思是:Agent 可以读取和编辑文件,但禁止运行rm -rf以及git push这类高风险命令。不同版本的 Claude Code 对权限配置的语法可能有差异,使用前务必查看官方文档,按当前版本调整。基本原则是“非必要不授权,危险操作一律拒绝”。
3.4 用 CLAUDE.md 固化项目记忆
Claude Code 会在每次会话时读取项目根目录下的CLAUDE.md文件,把它当作项目级别的“长期记忆”。这是纯 Agent 编程里非常重要的配置。
你可以在CLAUDE.md里写清楚项目技术栈、目录结构、常用命令、代码风格和禁止事项,避免每次新开会话都重新解释一遍。示例:
# 项目约定 - 技术栈:Node.js + Express + better-sqlite3 - 目录说明:src/api 放路由,src/services 放业务逻辑,public 放前端静态文件 - 代码风格:ESM 也可以,但当前项目统一使用 CommonJS;2 空格缩进 - 常用命令: - npm start:启动服务 - npm test:运行冒烟测试 - 禁止事项: - 不要在代码中硬编码数据库密码 - 不要修改 docs/requirements.md 里的接口协议有了CLAUDE.md,哪怕会话被重置,Agent 重新启动后依然能快速理解项目背景,不会因为上下文丢失而“失忆”。
4. 核心方法:如何把需求高效地喂给 Agent
4.1 写一份可执行的规格说明
规格说明是 Agent 开发的基础文档。以一个小型待办应用为例,docs/requirements.md可以这样写:
# 待办应用规格 ## 功能目标 1. 用户可以在页面上新增待办事项。 2. 可以勾选事项为已完成。 3. 可以删除事项。 4. 刷新页面后数据仍然存在。 ## 技术约束 - 后端使用 Express 提供 JSON API。 - 使用 better-sqlite3 存储数据。 - 前端使用原生 HTML + JavaScript,不引入前端框架。 ## 接口定义 - GET /api/todos:返回全部待办事项数组。 - POST /api/todos:创建待办事项,请求体为 { title: string }。 - PATCH /api/todos/:id:切换完成状态,请求体为 { done: boolean }。 - DELETE /api/todos/:id:删除待办事项,成功返回 204。 ## 验收标准 - 运行 npm install && npm start 后,访问 http://localhost:3000 可以看到页面。 - 新增、勾选、删除操作生效。 - 重启服务后数据不丢失。 - 所有接口返回 JSON,错误场景返回合适的 4xx 状态码。这份规格文档覆盖了目标、约束、接口、验收四个维度。Agent 读到之后,就不太可能把项目做成 Vue 前端或者 MongoDB 存储,因为技术约束写得很明确。验收标准则是后续测试和人工检查的依据。
4.2 小步迭代与自动测试
规格文档写好后,不要一次性把所有功能都丢给 Agent。最好一个切片一个切片地推进。
一次性运行方式可以用 Claude Code 的 print 模式:
claude -p "阅读 docs/requirements.md,先实现 GET /api/todos 和 POST /api/todos 这两个接口。完成后运行 npm test,确保测试通过。不要修改规格文档。"这里的关键是“每完成一步运行一次测试”。测试就是最清晰的完成标准,Agent 不需要猜测“做成什么样才算好”,只需要让测试变绿。
如果项目还没有测试,也可以反过来要求 Agent 先写测试再写实现。这一步很重要,后面实战案例会演示具体写法。
4.3 上下文管理与会话策略
Agent 的上下文窗口有限,一旦对话过长,早期约定就可能被遗忘。更常见的情况是:你昨天让 Agent 实现了某个接口,今天新开会话,它完全不记得以前的项目约定。
解决这个问题有三个要点:
- 把所有长期约定放进
CLAUDE.md和规格文档。 - 每次会话只做一个切片,不要塞入太多无关需求。
- 如果项目决策发生变化,把新的决定写进
docs/decision-log.md,而不是只写在对话里。
你还可以在完成一个阶段后,让 Agent 自己总结当前进度,然后把总结保存到文档。下次新会话直接读取这份进度文档,上下文就能无缝衔接。
5. 实战案例:从零做一个待办管理应用
下面用一个完整的小项目演示“只有一个 Agent 承包开发”的流程。项目很简单,但足够说明规格、测试、审查和排错的全过程。
5.1 需求与验收标准
需求:做一个待办管理应用,用户可以新增待办、标记完成、删除待办,刷新页面后数据不丢失。
验收标准:
- 启动后访问
http://localhost:3000能操作页面。 - 新增、勾选、删除三类操作对应三个接口,状态码符合语义。
- 重启服务后数据仍在。
5.2 项目骨架与依赖
先创建项目目录并初始化:
mkdir todo-agent-demo && cd todo-agent-demo git init npm init -y npm install express better-sqlite3 mkdir -p src public test docs data cat > .gitignore <<'EOF' node_modules/ data/ .env EOF说明一下为什么要创建这些目录:“src”放后端代码,“public”放前端静态文件,“test”放测试脚本,“docs”放规格文档,“data”放 SQLite 数据库文件。
接着创建docs/requirements.md,内容直接用 4.1 小节的规格文档。再创建CLAUDE.md,写入 3.4 小节的项目约定。这样 Agent 启动时,就能看到完整背景。
5.3 给 Agent 的启动提示词
进入项目后,启动 Claude Code:
cd todo-agent-demo claude在会话里输入下面这样的提示词:
请阅读 docs/requirements.md,按规格实现这个待办应用。 步骤: 1. 先实现后端 API:GET /api/todos、POST /api/todos、PATCH /api/todos/:id、DELETE /api/todos/:id。 2. 再实现 public/index.html 页面,使用原生 JS 调用后端 API。 3. 用 better-sqlite3 存储数据,数据库文件放到 data 目录。 4. 每完成一个接口,运行一次 npm test 确认。 5. 不修改规格文档,不修改 CLAUDE.md。 完成后请列出所有新增文件,并说明如何启动项目。这个提示词有目标、有步骤、有约束、有完成动作,Agent 不容易跑偏。
5.4 参考实现与测试基线
下面这段代码是“人类编写者”准备用来验收 Agent 产出的参考基线,也是你检查 Agent 代码时心里的一杆秤。如果你完全不懂这些代码也没关系,重点是学会“用测试卡 Agent 的产出”。
src/server.js参考实现:
// 文件路径:src/server.js const express = require('express'); const Database = require('better-sqlite3'); const path = require('path'); const fs = require('fs'); const app = express(); const dbDir = path.join(__dirname, '..', 'data'); fs.mkdirSync(dbDir, { recursive: true }); const db = new Database(path.join(dbDir, 'todo.db')); app.use(express.json()); app.use(express.static(path.join(__dirname, '..', 'public'))); db.exec(` CREATE TABLE IF NOT EXISTS todos ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, done INTEGER NOT NULL DEFAULT 0 ) `); app.get('/api/todos', (req, res) => { const rows = db.prepare('SELECT * FROM todos ORDER BY id ASC').all(); res.json(rows); }); app.post('/api/todos', (req, res) => { const title = String(req.body.title || '').trim(); if (!title) return res.status(400).json({ error: 'title is required' }); const info = db.prepare('INSERT INTO todos (title) VALUES (?)').run(title); res.status(201).json({ id: info.lastInsertRowid, title, done: 0 }); }); app.patch('/api/todos/:id', (req, res) => { const id = Number(req.params.id); const row = db.prepare('SELECT * FROM todos WHERE id = ?').get(id); if (!row) return res.status(404).json({ error: 'not found' }); const done = req.body.done ? 1 : 0; db.prepare('UPDATE todos SET done = ? WHERE id = ?').run(done, id); res.json({ id, title: row.title, done }); }); app.delete('/api/todos/:id', (req, res) => { db.prepare('DELETE FROM todos WHERE id = ?').run(Number(req.params.id)); res.status(204).end(); }); const port = process.env.PORT || 3000; app.listen(port, () => console.log(`server running at http://localhost:${port}`));public/index.html参考实现:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <title>待办管理</title> </head> <body> <h1>我的待办</h1> <input id="title" placeholder="输入待办事项" /> <button id="add">添加</button> <ul id="list"></ul> <script> const list = document.getElementById('list'); const input = document.getElementById('title'); async function load() { const res = await fetch('/api/todos'); const todos = await res.json(); list.innerHTML = ''; for (const todo of todos) { const li = document.createElement('li'); li.textContent = todo.title; li.style.textDecoration = todo.done ? 'line-through' : 'none'; li.addEventListener('dblclick', async () => { await fetch(`/api/todos/${todo.id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ done: todo.done ? 0 : 1 }) }); load(); }); li.addEventListener('contextmenu', async (e) => { e.preventDefault(); await fetch(`/api/todos/${todo.id}`, { method: 'DELETE' }); load(); }); list.appendChild(li); } } document.getElementById('add').addEventListener('click', async () => { const title = input.value.trim(); if (!title) return alert('请输入内容'); await fetch('/api/todos', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title }) }); input.value = ''; load(); }); load(); </script> </body> </html>test/api.test.js冒烟测试脚本:
// 文件路径:test/api.test.js // 运行前需要先执行 npm start,让服务在 3000 端口运行。 const BASE = 'http://localhost:3000/api/todos'; async function main() { const r1 = await fetch(BASE); const before = await r1.json(); console.log('GET /api/todos ->', before); if (!Array.isArray(before)) throw new Error('GET 接口应返回数组'); const r2 = await fetch(BASE, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title: '写一篇 Agent 编程复盘' }) }); const created = await r2.json(); console.log('POST /api/todos ->', created); if (!created.id) throw new Error('POST 应返回创建后的对象'); const r3 = await fetch(`${BASE}/${created.id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ done: true }) }); const updated = await r3.json(); console.log('PATCH /api/todos/:id ->', updated); if (updated.done !== 1) throw new Error('PATCH 应更新 done 状态'); const r4 = await fetch(`${BASE}/${created.id}`, { method: 'DELETE' }); console.log('DELETE /api/todos/:id ->', r4.status); if (r4.status !== 204) throw new Error('DELETE 应返回 204'); console.log('smoke test passed'); } main().catch((err) => { console.error(err); process.exit(1); });在package.json的 scripts 里补上启动和测试命令:
{ "scripts": { "start": "node src/server.js", "test": "node test/api.test.js" } }这里要说明:上面的代码不是“必须让 Agent 逐字生成”的标准答案。Agent 可能写出风格不同但功能等价的实现。你真正要做的是把test/api.test.js固定住,要求 Agent 的实现必须通过这套测试。测试通过的代码,才算合格。
5.5 运行、验证与审查要点
启动项目:
npm install npm start打开另一个终端,运行冒烟测试:
npm test预期输出类似:
GET /api/todos -> [] POST /api/todos -> { id: 1, title: '写一篇 Agent 编程复盘', done: 0 } PATCH /api/todos/:id -> { id: 1, title: '写一篇 Agent 编程复盘', done: 1 } DELETE /api/todos/:id -> 204 smoke test passed测试通过后,打开http://localhost:3000手动操作一遍。即使测试通过,也要人工检查几件事:
- 路由路径是否和规格一致:GET、POST、PATCH、DELETE 是否齐全。
- 状态码是否符合语义:创建返回 201,删除返回 204,参数错误返回 400。
- 数据库是否使用参数化查询:防止 SQL 注入。
- 前端接口路径是否与后端一致:比如
fetch('/api/todos')不能多一个前缀。 - 持久化是否生效:重启服务后,数据是否还在。
这个环节叫“审查 Agent 产出”。它是纯 Agent 编程流程中最不能省略的一步。
6. 常见问题与排查思路
6.1 启动失败与依赖问题
现象:npm start或claude启动失败,提示找不到模块、命令不存在。
排查顺序:
- 检查 Node 版本:
node -v,低于 18 建议先升级。 - 在项目根目录执行
npm install,确保依赖安装完整。 - 用
cat package.json确认 scripts 是否正确。 - 用
which claude或claude --version确认 Agent 已安装。
如果全局安装 Claude Code 时遇到权限报错,优先用 nvm 管理 Node.js,而不是直接 sudo 安装。sudo 安装虽然能解决当前报错,但很容易造成后续全局包权限混乱,生产环境更不推荐。
6.2 Agent 忘记约定:上下文丢失
现象:新开一个会话,Agent 完全不知道之前确定的接口规范或代码风格。
原因:Agent 不会自动保留上一个会话的对话内容,上下文窗口也会被长对话占满。
解决方案:
- 把长期约定写进
CLAUDE.md。 - 把当前任务契约写进规格文档。
- 会话开始时就要求 Agent 先读取这两份文档。
避免方法:一次会话只处理一个切片。任务完成后,立刻记录当前进度到docs/decision-log.md。
6.3 Agent 死循环或反复修改同一问题
现象:Agent 连续多次修改同一个文件,测试依然不通过,或者它不停尝试同样错误的方案。
原因:任务范围过大、错误信息不够具体、Agent 在信息不足的情况下盲目重试。
解决方案:立即中断当前操作,要求 Agent 先输出问题分析,再给出修改计划,而不是直接改代码。指令越具体越好,比如“只检查 src/server.js 第 40 行到第 50 行之间的路由注册逻辑”。
预防方法:在权限配置里限制 Agent 的操作范围,禁止它读取或修改不相关的目录。重要节点手动保存,不要让 Agent 在一次会话里连续改动太多文件。
6.4 账号、权限与服务可用性报错
现象:启动 Claude Code 时提示认证失败,或 API 请求返回账号、配额、服务范围相关的错误。
排查步骤:
- 确认 API Key 已正确配置,且没有无意中提交到 Git。
- 确认账号已通过官方要求的认证流程。
- 查阅官方文档,确认当前工具或 API 在哪些区域、哪些账号类型下可用。
- 不要使用任何官方不支持的手段绕过限制。
Agent 工具的可用范围是它自己的服务条款和发布策略决定的,作为开发者,合法、合规地使用工具是基本前提。遇到这类问题,最好的处理方式是查阅官方支持文档,而不是在社区里寻找绕过方案。
6.5 高频问题速查表
| 常见现象 | 可能原因 | 解决思路 |
|---|---|---|
| 无法启动 Claude Code | Node 版本太低,或全局命令未安装 | 升级 Node、重新全局安装、检查 PATH |
| Agent 生成的代码报 Cannot find module 'express' | 依赖未安装 | 在项目根目录执行 npm install |
| 接口返回 404 | 前端请求路径与后端路由不一致 | 对照规格文档核对路径 |
| 数据没有持久化 | 数据库文件被写入临时目录或内存 | 确认数据库路径在 data 目录,并确认重启后目录存在 |
| Agent 反复改同一处却仍报错 | 任务粒度过大,上下文不足 | 中断并给出精确指令,缩小修改范围 |
| API 返回权限或位置相关错误 | 账号状态或服务范围问题 | 确认官方支持范围,按官方文档处理 |
7. 最佳实践与工程建议
7.1 把自己定位成“验收者”而不是“打字员”
纯 Agent 编程最大的认知转变是:你的价值不再是逐行写代码,而是定义问题、拆分任务、验收结果。用 Agent 开发时,代码质量和项目走向由你控制的部分其实是“验收”。
每次 Agent 完成任务后,不要直接合并代码。先看 git diff,再跑测试,最后手动验证关键路径。对于那些很难自动化验证的部分,比如页面交互细节、极端输入情况,更要人工把关。
7.2 测试优先:让 Agent 先写测试再写实现
Agent 很容易写出“看起来正确”的代码,浪费最多时间的也是“看起来正确但实际错误”的代码。为了减少这种情况,可以反过来要求 Agent 先写测试,再写实现。
测试本身就是最明确的规格:接口返回什么状态码、字段叫什么、数据怎么存,全都在测试里写死了。Agent 照着测试实现,跑绿即完成,效率比反复对话高得多。
7.3 权限、密钥与安全边界
Agent 工具拥有修改文件、执行命令的能力,这意味着它也需要被严格约束。
- 密钥不要写入代码,使用
.env或环境变量,并把.env加入.gitignore。 - 在
.claude/settings.json中关闭 Agent 执行高危命令的权限。 - 涉及删除、批量更新、生产数据库等高风险操作时,先在一台隔离的测试环境验证,再做操作。
- 不要把生产环境的数据库连接串随意喂给 Agent;如果必须使用,遵循最小权限原则。
还有一个安全常识值得反复提醒:不要盲目执行 Agent 给出的终端命令,也不要从任何地方复制你不理解的代码粘贴到 DevTools 控制台或终端里执行。AI 生成的内容看起来很像代码,但它未必是安全的,也未必是你想运行的命令。
7.4 哪些项目适合纯 Agent 工作流
纯 Agent 编程并不适合所有场景。从这段时间的实践和公开分享来看,适合的项目有这些特征:
- 需求边界清晰,比如原型、内部工具、脚本、独立小产品。
- 技术栈通用,生态资料多,Agent 容易理解。
- 有明确的验收方式,比如接口测试、页面流程、命令行输出。
- 团队小或单人开发,沟通成本低。
需要谨慎的项目:
- 大型遗留系统,存在大量隐性业务规则。
- 强一致性要求高的金融、医疗等系统。
- 安全敏感项目,涉及权限、加密、账户体系。
- 需要大量领域知识沉淀才能动手的场景。
对大多数项目来说,更稳妥的折中方案是混合模式:让 Agent 负责独立模块,比如报表页、批量脚本、API 接口;关键路径和核心算法仍然由人类主导。
7.5 保持代码可回滚、可审计
Agent 写代码的速度快,意味着错误的传播速度也快。因此,版本控制格外重要。
建议保持这样一个习惯:每个切片验收通过后,立刻提交一个 commit,提交信息写清楚改动内容。不要等 Agent 完成一大块功能再统一提交,否则遇到问题时,你很难定位到底是哪一步引入了 bug。
同时,尽量使用git diff审查每次改动。Agent 可能同时修改了多个文件,但某些改动不是你要求的。通过 diff 可以快速发现多余变更,并及时纠正。
8. 总结与学习路线
如果用 Agent 写代码半年这件事只能留下三个记忆点,我认为是这些:
第一,编码瓶颈从“打字速度”变成了“定义问题的能力”。你能不能把模糊想法变成清晰规格,直接决定了 Agent 产出的质量。第二,规格文档和自动测试是整套工作流的锚,靠对话约束 Agent 是不可靠的,靠文件和测试约束才是可行的。第三,人依然是质量责任人。Agent 能加速,但不会自动替你保证正确性和安全性。
如果你想尝试这种工作流,不建议直接接手一个大型老项目。可以按这个顺序练习:
- 先手动写一个 50 行的小脚本,比如批量重命名文件,理解基础语法和报错信息。
- 尝试用代码补全或聊天助手辅助自己写代码,建立“AI 会犯错”的基本认知。
- 安装一个终端 Agent,让它在单个小函数上帮你做重构,比如把两个函数合并。
- 写一份 20 行以内的规格文档,准备简单的测试脚本,让 Agent 完成一个完整小功能。
- 尝试连续一周、连续一个月只使用 Agent 完成新功能,过程中不断优化自己的规格写作和验收流程。
如果这篇文章对你有帮助,可以先收藏备用。等你用 Agent 跑完第一个小项目,欢迎回来分享你的踩坑记录,尤其是那些测试没过、上下文丢失、命令误执行的经验,它们才是鲜活的实战教材。