Grtsinry43 的前端札记
文章
技术学习

我是怎样使用 AI Agent 写代码的

2026年7月15日 32 分钟阅读 浏览 喜欢 0 评论
AI 摘要

代码如渊,其深难测;思维如翼,借风而行。
AI 编程已从辅助之手,蜕变为协作者,但终究是人的心智延展,而非替代。
文章梳理出一条从 AI 辅助编码Vibe Coding 再到 混合开发 的实践路径:以 Claude Code 为主力,CursorCodex 为辅,让强模型承担架构判断与验收,轻量模型负责搜索与实现。关键不在于工具更迭,而在于上下文管理Skill 蒸馏文档化记忆分层协作——避免上下文过长导致模型涣散,用 CLAUDE.md 固化规则,借 Obsidian 和 Handoff 文档实现跨会话交接。
面对 AI 生成的代码,必须坚守 禁止推测、最小修改、求索根因 的 Debug 原则,并通过 分层测试严格 Review 抵御过度防御性编码、Placeholder 实现和悄然扩散的“LGTM”式失控。成本与安全是落地的基石,技术选型也可向 AI 友好方向倾斜。
真正会使用 AI,并非全盘交付,而是知道何时讨论、何时执行、何时 Review、何时亲手书写;模型放大了执行能力,但架构、取舍与最终责任,只能由人承担。Vibe Coding 止于原型,混合工作流方能行远,这正是人与 AI 共生的理性边界。

从 AI 辅助编码、Vibe Coding,到现在的混合开发工作流

TL;DR?复制下面这段提示词给你的 Agent,让它结合全文帮你优化自己的 AI 编程工作流:

md
这是一篇关于 AI Agent 编程工作流的经验总结:
https://blog.grtsinry43.com/posts/coding-with-ai-agent

请先完整阅读文章,再结合我当前的项目和使用习惯,帮我检查并优化自己的 AI 编程工作流。

重点分析:

1. 我现在使用 Agent 的流程中,哪些步骤缺失、重复或不合理;
2. 怎样根据任务复杂度区分直接执行、简单规划和深度设计;
3. 我的 CLAUDE.md、AGENTS.md、Skills、MCP 和 Hooks 应该怎样组织;
4. 怎样管理上下文、Compact、Handoff、项目文档和长期记忆;
5. 怎样合理使用主 Agent、Subagent 和不同能力、不同成本的模型;
6. 怎样改进编码、Debug、测试、Review 和验收流程;
7. 怎样减少无效 Token 消耗、过度思考和无意义的工具调用;
8. 怎样控制权限、修改范围和第三方 Skill、MCP 带来的安全风险;
9. 哪些工作适合交给 AI,哪些工作应该由我亲自完成或主导;
10. 最终为我整理出一套可以实际执行的分级工作流。

不要直接套用文章中的方案。请先询问我的技术栈、常用工具、项目规模、预算、权限要求和当前痛点,再根据我的实际情况进行调整。

输出时请包含:

- 当前工作流的问题;
- 建议保留和删除的部分;
- 优化后的完整流程;
- 推荐的规则文件结构;
- 推荐的 Skill、MCP 和模型分工;
- 一份适合我直接放进项目中的协作规范草稿。

写在前面

都已经 2026 年了,我的网站里居然还没有几篇真正讲 AI 的文章,这件事多少有点抽象。

别人早就开始 all in AI,用 AI 接手越来越多的工作,我到现在才认真写这个话题。其实我很早就想写一篇 AI 编程入门教程,在学校的时候便建好了草稿,只是一直没有时间完善。那篇文章现在还在路上,之后应该会单独发布。我可能还会再写一篇模型横评,专门讲国内外模型、Agent 工具和各种订阅的体验。

这一篇,我想先讲自己的经验。文章里大概有两条线。

一条比较具体,讲我现在怎样配置模型、管理上下文、使用 Skill、MCP、Subagent 和文档,以及怎样让 AI 编码、Debug、测试和 Review。

另一条是我自己的变化。我从最初的 AI 辅助编码,逐渐进入大量 Vibe Coding,又慢慢形成了现在这种 AI 与手写并行的工作方式。

因为白天还要上班,晚上真正能用来写东西的时间并不算多。最近我也在调整作息,不知道是天气还是病毒的原因,这几天身体稍微有些不舒服,所以大概只能晚上抽出两个小时来写。

文章的大部分内容由我先口述,再让 GPT-5.6 帮我二次整理。我已经明确要求它尽可能保留我的口吻、主体思想和原本的表达方式。它主要负责修正口误、语音识别错误和不太通顺的句子。

大概就是这样,真的精力有限,是我第一个开始用 AI 写的文章,忘本了555。


我是从什么时候开始使用 Agent 的

还是先从自己的经历讲起。

我真正开始频繁使用 AI 编程工具,应该是在实习之后,也就是我受伤的那段时间。那时候我开始使用 Claude Code,不过走的是第三方渠道。现在回头看,我甚至无法完全确定当时接入的 Claude 到底是不是真的。

我主要用它写自己的项目,包括一个 Android 项目,也做过一些其他尝试。大概从九月、十月开始,我便一直在用。到了十月、十一月接触 Codex 之后,后面的使用方式也慢慢延续下来。关于那段时间的经历和感受,我之前在另一篇文章里也讲过一些。

LINK

从想法到实践:在无序的生活里,试图用代码敲出一点秩序

至于我什么时候开始有意识地优化工作流,应该是在今年年初。

当时我使用的是 Claude Code 官方订阅。用了一段时间之后,我发现 Token 消耗得实在太快,于是开始想方设法优化整个流程。后来也算阴差阳错,我看了很多教程,自己又做过一些 AI 方向的项目,慢慢积累下不少经验。

从三四月份回到学校开始,我已经比较熟练地使用这些 Agent 编程工具了。之后的几个月里,我一直在高强度体验各种模型和产品。

Claude Code 官方的 Pro 订阅我用过,也借朋友的 Max 订阅体验过;OpenAI 的 Plus 和 Business 用过;Cursor、OpenCode Go、MiniMax、MiMo、火山引擎 Coding Plan、Poe、OpenRouter 也都陆续试过。DeepSeek 更多按照 API 用量付费,之前通过教育认证拿到的 Gemini 学生订阅现在也还在。

LINK

我患上了 token 的瘾

作为第一批被“纯 Vibe”工作流彻底卷入的开发者,我感到了一种前所未有的失语。

有朋友形容我是:

哪个 AI 都吃一口。

这么说倒也没错,我确实基本都试了一遍。

具体的模型体验和订阅选择,我会留到之后的入门文章和模型横评里。这一篇主要讲这些实践最后怎样变成了我的工作流。


我的主力工具

我目前使用最多的 Agentic Coding 工具还是 Claude Code。

它是一款命令行形态的工具。在我看来,它目前依然属于 Agent 编码工具里非常成熟的一档。当然,它也有一个很明显的问题:太费 Token。

Claude Code 的界面运行在终端里,模型输出速度特别快时,TUI 偶尔会暴露出一些性能问题。不过正常使用时,这不算什么大问题。

真正重要的是,它的工作流程已经比较成熟,我自己也最熟悉这套交互方式。所以直到现在,Claude Code 仍然是我的主力工具。

除了 Claude Code,我用得比较多的还有 Cursor。Cursor 里的一些模型给我的感觉是便宜、快,同时效果也不错,多少有点碰到了“不可能三角”。它们响应很快,能力又足够完成大多数日常任务,所以我也会经常使用。

Codex 当然也在我的工作流里。之前我还尝试过把 GPT 系列模型接入 Claude Code。有人反馈,同一个模型放进 Claude Code 以后会比在其他 Agent 工具里更好用。我自己的体验没有那么明显,最终效果可能还是更取决于模型本身。

不过,Agent 工具的设计当然也很重要。至少从我的使用感受来看,Claude Code 确实好用。

国内外还有很多其他选择,比如 MiMo Code、OpenCode 等。我之前认真使用过一段时间 OpenCode,它的功能比较丰富,TUI 体验也不错,整体定位很明显,就是在对标 Claude Code。

image.png

我的建议是,工具可以多试,但没有必要一直来回切换。找到一两个自己最习惯的主力工具,再围绕它们完善工作流,通常比不停追逐新产品更重要。


不要迷信上下文窗口

我使用这些工具时,最先关注的问题之一是上下文管理。

无论一个模型宣传的 Context Window 有多大,实际使用时,我都不建议一直堆到接近上限。

image.png

比如,一个模型拥有 1M 的上下文窗口,使用到 20%,其实就已经积累了大约 200k Tokens。按照我的经验,到这个阶段,上下文便可能开始涣散。模型仍然能够看到前面的内容,却不一定还能稳定抓住其中最重要的约束。

如果一个模型本身只有 200k 上下文,那么积累到这个量级时,基本已经接近用满。GLM、DeepSeek 等模型也会遇到相似的问题,上下文增加到一定程度以后,效果会逐渐下降。

所以,我通常会在使用到一定比例时(经验是 25% 到 35%)进行 Compact,必要时直接 Clear,重新开启会话。

很多人会觉得,上下文越多,模型知道的东西越多,效果理应越好。实际使用中并非如此。旧日志、无关的工具输出、已经放弃的方案和重复讨论不断积累,一方面会增加成本,另一方面也会干扰模型判断。

上下文不是仓库,不能什么都往里面塞。


让不同模型承担不同角色

另一个重要问题,是不同模型应该怎样配合。

现在有不少模型可以直接担任主模型,但我通常不会让一个模型包办所有工作。Claude Code、Cursor 这些工具里都有类似 Subagent 的能力,我觉得应该充分利用。

我一般会让多个 Subagent 分别阅读、搜索和分析代码,或者把一些已经明确的实现任务交给它们,最后由主模型统一检查和验收。

能力更强的模型适合负责理解任务、判断架构、制定方案和最终验收;速度更快、价格更低的模型可以负责扫描代码、搜索文件,以及完成边界清晰的实现。

如果预算比较充足,也可以一直使用强模型,只是在不同任务里调整 Reasoning Effort。国产模型同样可以按照类似方式组合。我有时会把它们统一接入 Claude Code,再根据能力和成本,映射到 Opus、Sonnet、Haiku 对应的角色上。

具体哪个模型必须映射到哪个名字并不重要。真正重要的是形成分层:关键判断交给更可靠的模型,搜索和执行交给更快、更便宜的模型;简单任务不浪费昂贵模型,重要决策也不交给只适合快速执行的小模型。

不过,模型能力越来越强以后,也不能无脑叠加复杂流程。

像 Superpowers、OpenSpec 这类强调 Brainstorm、Spec 和完整规划的 Skill,本身当然有价值,我身边也有同事在用。但如果模型本身已经具备很强的推理能力,再把 Reasoning Effort 调到 High,同时套上一整套要求它不断质疑和反复确认的 Skill,很容易出现过度思考。

模型会一直分析、一直反问、一直完善方案,就是迟迟不动手。最后未必得到明显更好的结果,时间和 Token 却消耗了很多。

所以,我更习惯按照任务复杂度决定流程。简单任务直接完成,中等任务先做简短分析,真正涉及架构和大范围修改的任务,再进入完整的设计过程。

困难任务当然需要深入思考。问题出现在一套流程被无差别地套到所有任务上。

Skill 应该补充模型和任务的短板,而不是机械地增加步骤。


人和 AI 之间,需要一套固定的协作关系

现在有各种插件、Skills、Spec、Plan 和 Brainstorm 工具。这些东西都可以尝试,但最终有没有用,还是取决于它们是否符合自己的工作方式。

我自己写过一个规范人与 AI 协作的 Skill,之前也开源过。它会按照任务复杂度,选择不同的执行流程。

https://github.com/grtsinry43/agent-skills/blob/main/skills/grt-collaborating/SKILL.md https://github.com/grtsinry43/agent-skills/blob/main/skills/grt-collaborating/SKILL.md

简单修改,比如调整样式、修改文本或者完成一个边界清楚的小功能,不需要大量前期规划,直接动手即可。

涉及较大功能或架构变化时,我通常会先和 AI 讨论。先把需求、影响范围、实现方案和潜在问题理清楚,再进入编码。

如果是大型项目或者关键设计,流程还会继续细分,包含需求梳理、代码探索、方案设计、任务拆分、实现、测试和验收。

如果正好习惯这套方式,可以直接使用我的 Skill;如果不习惯,也可以复制里面的文本,再按照自己的习惯修改。

这里真正重要的是,人和 AI 之间最好形成一套稳定的协作节奏。

AI 默认的工作方式不一定符合我们的习惯。有时候,你只想先讨论一下,它却上来就开始改代码;有时候,你明确让它快速完成一个小改动,它又不停追问一些不需要确认的细节。这两种情况都很痛苦。

所以,最好提前约定什么时候可以直接修改,什么时候必须先讨论,什么时候需要完整规划,写完以后怎样汇报,以及哪些地方必须由人确认。

让 AI 知道应该怎样与你协作,比单独把某一次提示词写得特别长更重要。


Skill:让一个人“自愿蒸馏”自己

既然说到了 Skill,那就专门讲讲它。

之前我在文章里写过一句话:

Skill 可以让一个人自愿蒸馏自己。

过去,当我们拿到一个项目或者一份文档时,通常只能看到最终的设计、代码,以及作者具体做过什么。至于他为什么这样设计、平时怎样思考、积累过哪些经验,往往没有被真正提炼出来。

Skill 有些不同。

它相当于一个人主动把自己的经验、想法和工作方式整理出来,写进一个 Markdown 文档。稍微夸张一点说,当你拿到这份 Markdown 时,就像拿到了这个人的一部分思想。

这个说法确实有些夸张,但 Skill 的价值就在这里。它把原本只存在于个人经验里的东西,变成一套可以被 AI 读取、遵循和复用的工作方式。

现在寻找 Skill 的地方也很多,比如相关的 Marketplace、skills.sh ,以及各种可以通过命令快速安装 Skill 的工具。安装以后,还能把它们软链接到 Claude Code、Codex、OpenCode 等不同 Agent 工具中统一使用。

我比较推荐的组合,是一个负责协作方式的主 Skill,再搭配若干领域 Skill。

主 Skill 规定什么时候讨论、什么时候规划、什么时候编码,以及最后怎样验收。领域 Skill 则负责某个具体技术方向的知识和最佳实践。前端项目可以加载 Web Best Practices,React Router 项目可以加载对应的框架 Skill,后端项目则加载语言和框架相关规范。Debug 时也可以使用专门的排查 Skill。

GitHub 上还有不少开发者公开了非常个人化的 Skill。比如,有些 Skill 会要求 AI 一直向你提问,直到真正把需求了解清楚以后才允许开始写代码。这些东西都挺有意思。

你甚至可以在全局的 CLAUDE.mdAGENTS.md 或类似文件里规定:进入一个尚未初始化 Skill 的项目时,先分析各个目录使用了什么技术,再寻找并配置合适的 Best Practices Skill。

这样,就相当于把别人已经总结好的经验,很方便地引入自己的项目。


CLAUDE.mdAGENTS.md 和项目规则

除了 Skill,项目里最好还有一份稳定的规则文件。

使用 Claude Code,可以写 CLAUDE.md;使用 Codex,可以写 AGENTS.md;其他工具也有类似文件。

这些文件用来保存项目级规则和说明。在你和 AI 协作的过程中,它们通常比某次对话里临时提出的要求更稳定,也更容易成为 AI 长期遵循的约束。

拿到一个新项目,准备让 AI 参与开发时,我非常推荐先写好这样一份文件。

其中可以包括协作和编码规范,比如禁止 Placeholder 实现,使用陌生库之前必须先查文档,调用 API 前确认当前版本的真实用法,明确哪些情况可以直接修改,哪些情况必须先讨论,以及完成后需要执行哪些测试和检查。

还可以介绍项目本身,包括它解决什么问题、整体架构怎样划分、各个目录承担什么职责,以及主要的数据流和模块边界。

开发流程同样可以写进去,例如怎样创建分支、怎样提交、需要经过哪些检查、如何合并和发版。某些项目还会有自己的特殊规则,比如遇到特定报错时优先检查哪个模块,或者在某个目录工作时必须遵循哪些限制。

不过,这类文件最好控制篇幅。

上下文越来越长以后,模型对规则的注意力很容易下降。如果把大量不常使用、彼此重复,甚至互相冲突的内容全部塞进去,真正关键的规范反而可能失效。

因此,这类文件应该尽量简短、明确,只放那些适用于大多数任务、确实需要长期遵守的规则。更具体的内容可以拆到单独文档中,再从主规则文件里引用。

用我的来举个例子:

md
# Repository Guidelines

## Project Structure & Module Organization
- `cmd/api`: application entry point (config load, dependency wiring, Fiber startup).
- `internal/`: core packages (config, database, HTTP handlers/routers, services, domain models, persistence).
- `configs/`: runtime configuration files (e.g., app and auth settings).
- `migrations/`: Goose SQL migrations using `NNNN_description.sql` naming.
- `docs/`: generated OpenAPI artifacts (`swagger.json`).
- `storage/`: runtime data (logs, uploads, HTML snapshots, GeoIP databases).

## Build, Test, and Development Commands
- `go mod tidy`: sync Go module dependencies.
- `APP_PORT=8080 go run ./cmd/api`: run the API locally.
- `make migrate-up|migrate-down|migrate-status|migrate-version`: manage database migrations via Goose.
- `make migrate-create NAME=add_posts_table`: create a new SQL migration.
- `make docs`: regenerate OpenAPI JSON from Swagger annotations.
  - Note: `swag` is sensitive to annotation order; keep `@BasePath /api/v2` in the main comment block (prefer near the end) to ensure it is emitted into `docs/swagger.json`.

## Coding Style & Naming Conventions
- Use standard Go formatting (`gofmt`) and idiomatic Go naming (PascalCase exports, camelCase locals).
- Keep packages cohesive and aligned with the existing layout (e.g., `internal/app/*` for services, `internal/domain/*` for entities/repositories).
- Migrations must follow `NNNN_description.sql` so Goose can order them.

## Additional Agent Requirements
- 0. Guarantee high-quality code; forbid placeholder implementations, fake implementations, or problematic code.
- 1. For major changes, list a modification plan first, including strategy, impacted files, technical approach, and choices with reasons.
- 2. Act as an excellent Go engineer; adhere to Go and Fiber best practices, handle Go legacy issues with modern syntax, and avoid GORM pitfalls using experienced practices.
- 3. Before using any library, check its latest version and documentation; leverage search and network access; do not guess or assume usage.

## Testing Guidelines
- No dedicated test suite is present in this repo yet. When adding tests, place `_test.go` files alongside the code under `internal/` and run `go test ./...`.
- Prefer table-driven tests for handler/service logic where possible.

## Commit & Pull Request Guidelines
- Follow the observed Conventional Commits style (e.g., `feat: ...`, `feat(server): ...`, `fix: ...`).
- PRs should include a clear summary, rationale, and any required config or migration notes.
- If you change API handlers or models, update `docs/swagger.json` via `make docs` and mention it in the PR.

## Database
- The only supported database is **PostgreSQL 17+**. SQLite support was removed; do not write cross-dialect SQL workarounds.
- Raw SQL may use PostgreSQL-specific syntax (e.g., `FILTER (WHERE ...)`, `jsonb` operators) when needed.
- Prefer GORM model-based queries where possible; use raw SQL only for aggregations or features not expressible through GORM's query builder.

## Security & Configuration Tips
- Runtime behavior is controlled via env vars like `APP_PORT`, `DB_DRIVER`, `DB_DSN`, `AUTH_SECRET`, and `AUTH_DEFAULT_ROLES`.


跨上下文:Agent 的记忆问题

很多人在使用 AI 编码时,都会遇到一个很大的限制:Agent 的 Context 太小了。

第一次在完整上下文里开发时,模型效果可能非常好。但只要进行过一次压缩,它便可能忘记这个项目原本是什么样的。之后生成的代码不仅效果变差,还会和前面已经完成的设计无法对应。

多个 Agent 协作时,这个问题更加明显。一个 Agent 已经完成一半工作,你想把任务交给另一个工具继续,却发现前面积累的上下文太长,根本带不过去。不同工具组织上下文的方式也不一样,很难直接完成交接。

还有一种情况是,当前会话已经积累了大量内容。继续工作会越来越贵,尤其缓存过期以后,之前的 Tokens 可能需要重新发送。可一旦 Compact,又会损失不少细节。

所以,Agent 的状态管理,尤其是记忆,一直很难彻底解决。

我目前会把信息分成三个层次。

长期稳定、所有 Agent 都必须遵守的内容,放进项目规则文件。某个需求的分析、设计、进度和交接信息,放进项目文档或者 Obsidian。开发过程中逐渐积累的经验、历史决策和踩坑记录,则交给 Agent Memory。

我之前使用过一个 Agent Memory 项目。它可以部署在本地,通过 Hooks 捕获 Agent 的工具调用、代码读取和修改行为,再从中整理记忆;也可以通过 MCP 接入不同工具,让 Agent 主动搜索、增加、修改或者删除某条 Memory。

https://github.com/rohitg00/agentmemory https://github.com/rohitg00/agentmemory

不过,单纯依赖自动记忆还不够。

对一个稍微复杂的项目来说,最好还是给 Agent 留下明确文档。我自己会使用 Obsidian。比如,当我要让 AI 完成一个新需求时,可以先为它建立单独的目录或文档。

第一次接手任务的 Agent 阅读代码以后,需要把需求背景、当前结构、相关模块、已经确认的事实、仍然存在的问题、准备采用的方案、当前进度和下一步工作都整理进去。

当它的上下文快要用完,或者需要换另一个工具继续时,就先进行一次 Handoff,把当前状态完整写入文档。下一个 Agent 不需要继承前面全部的聊天记录,只要读取这份交接文档和相关代码,就可以继续工作。

还有一些开发中反复出现的提醒,比如某种实现以前失败过、某个模块不适合按照常规方式修改、遇到某类问题应该优先检查某个位置。这些内容不一定适合写成永久规则,但又值得以后重新想起,就可以保存到 Agent Memory。

我现在的思路,是把会话里的状态逐渐转化成规则、文档和记忆,而不是一直试图搬运完整的历史对话。


一些我会安装的插件

插件方面,如果使用 Claude Code,我个人非常推荐 Claude HUD。

image.png

它可以直接显示当前的上下文占用、订阅额度、工具调用、Todo 状态、Token 消耗,以及模型输出速度。这些信息不会直接提高模型能力,却能让整个运行状态变得很直观。

你可以随时知道上下文还剩多少,模型已经消耗了多少 Token,任务执行到了哪一步,以及是否应该 Compact 或者开启一个新会话。

MCP 数量比较多,或者同时使用多个 Agent 工具时,也可以使用专门的管理和同步工具。现在已经有不少开源项目可以统一管理 Skills、MCP 和配置文件,按照自己的习惯选择即可。

不过,插件、Skill 和 MCP 最终都只是工作流的辅助。真正重要的,还是你怎样面对一个实际项目。


以我的博客项目为例

当时我写博客项目的时候,其实已经赶上了这一轮 AI 快速发展的阶段。不过那时我使用 AI 还比较克制,并没有把大量工作直接交出去。

以前端部分为例,拿到项目之后,我首先会确认项目规范,也就是前面提到的规则文件,然后再设计目录结构。

代码结构一般讲究高内聚、低耦合。我的博客本身比较复杂,所以设计目录时,我大致借用了一些领域拆分的思路,把业务模块、公共能力和通用 UI 分开。

当时写给 Agent 的规范,大概包括这些内容:

markdown
页面只负责数据接入和模块编排,不堆积复杂业务逻辑。

业务能力按 feature 划分;跨功能能力收敛到 shared;通用组件放入 ui。

浏览器 API 必须封装,统一处理 cleanup、SSR 安全和重复绑定问题。

页面级数据优先通过 SvelteKit load 提供;跨层共享使用 Context 和 Store。

内容页面优先 SSR/SSG,评论、点赞和交互增强采用客户端 Islands。

禁止在 SSR 阶段访问浏览器对象,禁止随意引入全局状态,禁止修改构建产物和依赖目录。

只基于代码、日志和已知事实得出结论;证据不足时继续验证,不凭空猜测。

规范确定以后,再决定先完成哪些模块。

比如,首先要写文章模块。我会先和 AI 讨论这个模块应该怎样实现,包括数据查询、接口组织和整体结构。然后把自己的设计想法告诉它,让它按照项目规范给出实现方案、执行步骤,以及必要的代码片段。

等方案确认以后,再让 AI 正式实现。实现完成后,我会进行第一次 Review,检查它有没有遵守规范,结构是否合理,代码是否符合预期。Review 基本通过后,再让它补充单元测试,最后进行实际验证。

整个过程可以概括成:先确定模块,再讨论方案;对齐 API、技术栈和设计思路后开始实现;实现完成后由人 Review,再补测试和验证。

如果要使用 AI 辅助开发,提示词至少应该写清楚一些。要告诉它具体完成哪个模块,使用哪些 API,采用什么技术栈,遵循什么设计,哪些地方不能修改,以及最终怎样验收。

什么都不说清楚,只给一个模糊需求,AI 只能自行补全大量假设。最后很容易得到一套看起来完整,却和你的真实想法并不一致的实现。


一个更完整的项目流程

我之前给朋友写过一个 Android 项目。

这类任务,我一般会先自己阅读文档,确认功能到底能不能实现。我会看 Android 官方文档,也会查 Jetpack Compose 等相关资料,先对准备使用的 API 和实现方式形成基本认识。

确认功能可行以后,再让 AI 阅读已有代码。我会开启 Subagent,让它们分别探索项目和相关模块,再按照我的协作 Skill 进入完整流程。

代码读完以后,让主 Agent 分析当前问题,并写出一份实践文档。文档里需要说明当前项目结构、与需求相关的代码、已经确认的事实、可选方案、方案之间的取舍,以及具体执行步骤。

这些内容写进文档以后,当前上下文可能已经消耗了很多 Tokens。这个时候可以 Compact,也可以开启一个新模型,让它重新对照代码和文档,检查方案有没有问题。

如果第二个模型认可,我自己也认可,就正式开始实现。

实现完成后,我会先自己 Review,确认方向和结构没有明显问题,再让 AI 补充测试。测试写完以后,再做第二轮 Review 和实际运行验证。

这就是我现在比较完整的一套工作流程。


怎样让 AI Debug

让 AI Debug 时,我比较重视三个原则。

第一个原则是禁止推测。

AI 应该根据日志、代码、运行现象和各种线索分析问题,解释这些现象为什么会出现。证据不足时,就继续补充日志、设计实验或者提出需要验证的问题,不能直接把一个听起来合理的猜测当成根因。

第二个原则是控制修改面。

修复问题时,修改范围应该尽可能小。不能为了处理一个局部 Bug,顺便重构整个架构,牵连大量无关代码。应该先找到最小修复点,再说明为什么只需要修改这里,以及这次修改可能影响哪些范围。

第三个原则是寻找根因,不要只让报错消失。

几乎所有 AI 在修 Bug 时,都有一个比较明显的倾向:这里抛出异常,就加一个 catch;这里出现错误状态,就补一个默认值;某一步失败,就增加一个兜底分支,把问题绕过去。

报错可能暂时消失,原本的问题却仍然存在,只是变得更难发现。

所以,必须提前规定,AI 的目标是找到问题最初出现在哪里。除非兜底本身就是设计的一部分,否则不能用兜底代替根因修复。


测试不能只为了覆盖率

让 AI 写测试时,也不能只是为了提高覆盖率,机械地生成大量测试代码。

每个测试都应该有明确目的,比如验证正常路径、边界条件、历史 Bug 是否会再次出现、不同平台上的行为是否一致,以及模块之间的集成是否符合预期。

测试也应该分层推进,从单元测试、集成测试,到多端测试、自动化实际运行测试,最后进入真实环境验证。

尤其需要注意,AI 很容易为自己刚刚写出的实现,再写一个“必然通过”的测试。

测试通过,不代表需求真的被满足。


怎样 Review AI 写的代码

Review 非常重要。

我曾经有一段时间没有认真 Review AI 生成的代码,但现在基本都会检查。

在我自己的协作 Skill 里,有一个任务完成后的汇报机制。AI 写完以后,不能只告诉我“任务已经完成”,还要先汇报这次修改。

我会要求它自己判断,哪些内容最需要我关注。比如最重要的文件在哪里,核心功能怎样实现,做出了哪些设计选择,为什么选择这种方案,放弃了哪些方案,里面存在哪些取舍,以及哪些代码风险较高。

我会先检查这些重要部分,再去扫剩下的修改。

异常路径、资源生命周期和修改范围肯定也要看。尤其要确认 Diff 是否过大,有没有修改需求之外的代码,是否引入不必要的抽象,是否增加过多包装和参数,是否真的处理了根因,以及有没有资源没有释放或者状态流被破坏的问题。

如果模型一次写出的内容太多,我会让它把汇报拆开,一部分一部分讲。

修改量特别大时,还可以引入第二个模型进行交叉 Review,比如让 Codex 再检查一次其他模型写出的代码。


AI 是怎样毁掉一个项目的

早期的 AI 很喜欢直接覆盖 Existing Code。

你让它修改一个地方,它可能一下子把原来的实现全部替换掉,甚至只留下一段注释,告诉你这里以后应该实现什么。

现在的模型很少再犯这么明显的问题,但还是有很多其他方式可以把项目搞坏。

它可能不搜索最新文档,直接按照训练数据里记住的内容猜测 API,最后生成大量不存在或者已经过时的接口。

它也可能写出大量 TODO、Placeholder,以及只有注释、没有真实实现的代码。表面上结构已经搭好,实际上核心功能根本没有完成。

还有过度防御性编程的问题。AI 有时很喜欢增加大量参数,在调用链之间一层又一层地包装,再加入各种没有必要的防御逻辑。最后看起来考虑得非常全面,真正的业务逻辑却被淹没了。

另一个常见问题,是需求还没有理解完整,它便已经开始修改。修一个 Bug 时不断扩大范围,最后动到很多无关模块。

面对复杂问题时,不同模型还可能给出完全相反的分析,最后变成一种左右脑互搏。

所以,人必须始终抓住已经确认的事实,不能被一套又一套听起来合理的解释带着走。


让 AI 辅助设计和文档

AI 也可以参与设计工作。

比如,可以先让它根据现有视觉风格生成一套原子化设计规范和 Design Tokens,再由人逐项 Review。确认 Token 以后,再按照它们实现组件。这样比让 AI 每次临时发挥,更容易保持视觉统一。

技术文档也是一样。

完成一次问题排查后,文档至少应该讲清楚最初出现了什么现象,收集了哪些证据,排除了哪些方向,最终根因是什么,做了哪些修改,怎样验证有效,以及还有哪些风险。

任务完成以后,不同信息应该存到不同地方。

可复用的经验写进 Agent Memory;对某个模块形成的新理解,更新到项目文档;这次具体做过哪些修改,写进任务记录或者 Handoff 文档。

如果开发过程中发现原有文档已经不准确,也应该顺便更新,避免后面的 Agent 继续依据错误信息工作。


技术选型也可以考虑 AI

如果是公司项目,技术栈已经确定,那基本没有选择空间。这里主要讨论个人项目,或者自己能够参与技术选型的场景。

当多个方案都能满足需求时,可以考虑哪些技术更方便 AI 编写,同时也方便人 Review。

前端方面,TypeScript 和 React 应该是目前 AI 非常熟悉的一套组合。SWR、TanStack Query、React Scan、Jotai、shadcn/ui、Tailwind CSS、Vite、Next.js 等工具的文档、示例和开源项目都比较多,AI 对常见用法通常也更熟悉。

这并不意味着所有项目都应该选择 React。只是当几个方案都合适时,选择资料丰富、社区规范成熟的技术,确实能降低和 AI 协作的成本。

有些语言在 AI 时代反而变得更有优势。

比如 Go。过去很多人会觉得 Go 表达能力有限,重复代码多,写起来比较累。有了 AI 以后,机械和重复的部分可以交出去,而 Go 清晰、直接、约束强的特点,让人更容易阅读和 Review AI 生成的代码。

Rust 也有相似的变化。过去使用 Rust 的门槛比较高,所有权、生命周期和类型问题会消耗很多时间。AI 降低了这部分成本,一些以前不太敢写的模块,现在也可以尝试完成。

不过,Rust 仍然不能完全交给 AI。自己至少需要具备一定基础,能够判断所有权、并发模型和错误处理是否合理。

代码通过编译,不代表设计一定正确。


让合适的模型做合适的事情

不同模型的能力倾向并不完全一样,最好不要让一个模型包办所有任务。

以前端为例,我通常会把视觉设计、UI 实现、业务逻辑和 Review 分开。

前端视觉设计和原型,我更愿意交给 Gemini。具体的前端组件和页面实现,我会优先使用 Claude。业务逻辑和复杂代码更适合交给 Codex,代码 Review 也通常以 Codex 为主,Claude 可以作为补充。

Gemini 在视觉方向、页面布局和原型探索方面比较合适。Claude 在样式、组件结构和前端代码上的综合表现比较稳定。Codex 更擅长业务逻辑、复杂重构和 Review。

但如果直接让 Codex 从零设计一套 UI,我自己的体验并不算好。即使提供了 Design Tokens,最后的视觉效果有时还是比较普通。

没有一个模型能在所有任务上保持最优。让合适的模型完成它更擅长的工作,通常比始终使用同一个最强模型更加舒服。


成本是无法绕过的问题

使用 Agent 编码,成本始终无法绕过。

首先还是上下文。上下文越长,成本越高。要做好记忆、文档、Handoff 和任务接力,避免重复探索,也不要不断消耗没有价值的 Tokens。

虽然前面推荐了很多 Skills 和 MCP,但数量也一定要控制。它们的定义、说明和工具信息同样会占用上下文。装得太多,不仅增加成本,也可能影响模型注意力。

模型和 Reasoning Effort 也应该按照任务强度分配。简单任务不需要最昂贵的模型,也没有必要开启最高推理强度。真正复杂的任务,再使用更强的模型和更高的 Effort。

至于订阅和 API,我自己的选择比较直接。Claude、Codex 这类价格较高、使用频率也高的工具,优先走订阅。国内模型如果提供合适的 Coding Plan 或 Token Plan,也优先购买套餐。

像 DeepSeek 这种 API 本身非常便宜的模型,可以直接按量付费。其他价格较高、又没有合适套餐的模型,我通常不太推荐长期走 API。


权限和安全

安全问题也非常重要。

我之前发过一条 QQ 动态,说自己开启 --dangerously-skip-permissions 以后,就像打开了潘多拉魔盒,再也回不去了。

但实际上,不可能真的一直这样使用。这种权限实在太危险了。我真正长期完全跳过确认的时间并不多,最近也重新回到了需要手动确认的状态。

很多 Agent 工具都提供 Sandbox 模式,平时应该尽量开启,限制 Agent 能读取和修改的范围。也可以使用 Bubblewrap 一类的隔离工具,限制进程只访问相关目录。容器化也是比较可靠的方案。

对于危险命令,可以通过 Hooks 设定规则,要求必须由人确认,不能默认执行。

第三方 MCP 和 Skills 在安装前同样需要 Review。它们可能包含命令、提示词和外部服务调用,不能因为开源就完全不检查。


并行开发

并行开发方面,我使用 git worktree 的次数其实不算多。

大多数时候,我还是串行开发,先完成一个任务,再继续下一个。即使同时工作,也通常是多个项目并行,很少在同一个项目中同时开启大量 Agent 修改代码。

如果多个任务之间会相互影响,我反而不太建议直接把它们完全隔离,让多个 Subagent 分别修改。这种情况下,可以考虑 Agent Team,让不同 Agent 之间互相发送消息、同步进度。

如果只是让几个完全隔离的 Agent 同时写同一套代码,确实很容易产生冲突。

并行并不是越多越好。任务之间真正能够拆分,而且边界足够清楚时,并行才有价值。


我为什么不再完全 Vibe Coding

我开始大量把代码交给 AI,应该是在今年三四月份,也就是刚回到学校的那段时间。

当时我一直在尝试各种新模型,也开始采用比较纯粹的 Vibe Coding。它的效率确实非常高。只要提示词给得足够清楚,AI 生成的代码质量往往也不会太差。

但这种方式最大的问题,是可维护性太差。

一个项目只要大量使用 AI 编写,后面就可能变得很难维护。尤其是 AI 连续写了好几天,提交了一大堆 Diff,而你 Review 时只想看一眼,说一句:

LGTM。

这其实是非常痛苦的。

项目仍然在快速推进,你对它的理解却可能已经逐渐跟不上。到最后,你甚至会发现,一个项目只要大量使用过 AI,自己便不太敢再手动维护它。

所以,我现在会按照任务类型决定哪些工作可以直接交给 AI。

如果一个功能的实现方式已经完全确定,我也知道它应该怎样写,只是需要真正实现出来,那可以完整交给 AI。

如果我只有一个想法,还不知道具体怎样落地,或者里面涉及技术取舍,那就应该先讨论,不能直接动手。

还有一些事情,比如编译器、渲染系统和整体架构设计,我不会完全交给 AI。AI 可以提供方案、参与讨论、帮助验证,最终设计仍然需要由我完成。

我重新开始手写,也不意味着放弃 AI。不同类型的工作,本来就不应该采用同一种自动化程度。

现在对我来说,手写的意义也不只是练习语法。

手写会迫使我逐步回答:数据从哪里来,状态在哪里改变,这一段依赖什么假设,失败以后系统会处于什么状态,为什么这一层必须存在。

真正需要自己建立完整模型的代码,我仍然会亲手写,或者至少全程参与。

AI 负责扩大我的执行能力,我负责维护对系统的理解。


AI 与学习

AI 确实可以提高学习效率。

过去接触一个新的库,可能需要先记住大量 API。现在很多时候,只要理解这个库解决什么问题、具备哪些能力、有哪些重要注意事项,就已经可以开始尝试。真正需要具体 API 时,再查文档或者让 AI 辅助。

但基础知识不能完全跳过。

AI 可以帮助学习,却无法替代思考。一定要分清楚,什么时候是自己真正理解了,什么时候只是 AI 帮你把东西做出来了。

千万不要因为 AI 能够完成一个任务,就认为自己已经掌握了这个领域。

我敢大量交给 AI 的内容,通常还是自己比较熟悉的技术栈。只有自己具备一定理解,才有能力判断它写出的东西是否正确。

至少目前,我还没有到离开 AI 就完全不会写代码的程度。我仍然保留手写和 AI 并行的习惯。


AI 编程真正改变了什么

AI 编程首先改变的,可能是精力分配。

有了 AI 以后,我们可以把一部分重复、机械的实现工作交出去,节省时间和精力,用来完成更多事情。

但成本没有消失,只是发生了转移。

过去主要面对学习成本、实现成本和时间成本。现在又增加了 Token 成本、Review 成本、验证成本、维护成本,以及理解 AI 生成代码的成本。

而且,一个项目中由 AI 生成的代码越多,它的可读性和可维护性就越容易下降。这很难完全避免。

所以,在 AI 时代,人的价值可能会更多体现在架构、取舍和判断上。至少在 AI 编程这个场景里,判断力和学习能力仍然非常重要。


Vibe Coding 和 AI 辅助编码是两种用法

Vibe Coding 和 AI 辅助编码其实是两个概念。

个人做原型、小项目,或者验证一个想法时,可以使用更接近 Vibe Coding 的方式,快速把东西做出来。

但面对大型、长期维护的项目,我不太建议完全采用这种方式。你当然可以使用 Agent 工具,但不能把整个项目全部交出去,自己不再参与理解和判断。

而且,Agent 越强,反而意味着你越需要了解自己的项目。

这件事看起来有些反直觉。很多人会觉得,模型能力越强,使用门槛就应该越低。

但模型越强,它一次能够完成的修改越多,能够替你做出的决策也越复杂。这个时候,使用者反而更需要具备足够的能力,判断它的方向是否正确。

你对项目理解得越深入,需求表达得越清楚,模型最终完成的效果才会越好。

更强的 Agent 确实降低了部分实现门槛,同时也提高了对架构能力、判断能力和表达能力的要求。


怎样才算会使用 AI 编程

最后,怎样才算真正会使用 AI 编程?

我觉得,AI 编程有点像把工作交给一个小团队。

在传统开发中,你可能是一个组长。你需要理解需求、完成设计、拆分任务,再把不同工作交给不同成员。过程中还要跟进进度、Review 结果,并为最终交付负责。

现在面对 AI,做的其实是类似的事情。

你需要具备需求表达能力、架构设计能力、任务拆分能力、上下文组织能力、风险判断能力,以及 Review 和验收能力。

把需求扔进去,等代码吐出来,还不能算真正会使用 AI。

真正会使用 AI,意味着你能够判断哪些事情应该交给它,应该交给哪个模型,需要提供什么上下文,怎样验证结果,以及什么时候必须由自己接管。

所以,“会使用 AI 编程”在我看来,其实已经是一个比较高的评价。

如果一个人真的能够把 AI 编程这件事做好,那么至少在传统的软件开发环境里,他应该已经具备了带领一个小团队完成工作的部分能力。

半年前,我还在尝试让 AI 多替我写一些代码。

后来,我开始期待它替我完成整个项目。

到现在,我更关心的是,怎样让它参与工作的同时,不让自己失去对工作的理解。

我不再追求所有代码都由自己手写,也不再把“全部交给 AI”当成效率的终点。

真正稳定的状态,是知道什么时候应该讨论,什么时候可以让它执行,什么时候需要 Review,以及什么时候应该停下来,自己亲手实现。


语音输入太多,口干舌燥了,就先写到这里吧

喜欢 0
评论区在赶来的路上...