如何在老项目里用 AI 写代码的一些感悟:从搭系统到落地的一整套方法
AI 编程有个很容易上头的错觉:模型越强,开发就越轻松。新项目确实是这样。但老项目跑了好几年之后,真正难的从来不是写代码,而是搞清楚"这段代码为什么存在""改了会不会炸""AI 给的方案是业务需要还是它在猜"。
我花了一年时间在 Platform 项目(React + Vite 前端,Go + Gin 后端,Cloudflare D1 + R2)上摸索,最后沉淀下来一些经验,分两个部分分享给大家。希望能有所帮助。
第一部分:构建系统
核心思路
Agent 在老项目里表现差,通常不是因为模型能力不够,而是它不知道该读什么、信什么、能做什么。把所有文档塞进 system prompt 只会制造噪声。
解决方式是三层文件结构:
AGENTS.md → Agent 进项目读的第一份文件(规则 + 路由)
ai-doc/ → 稳定知识库(架构、契约、约束),按索引按需加载
.agents/skills/ → 工作流程(开发/调试/测试/知识维护)
三层各司其职:AGENTS.md 说"你是谁、能干什么",ai-doc 说"项目现在是什么样",skills 说"遇到某类任务应该怎么干"。
下面是实际在用的每一层。
第一层:AGENTS.md
AGENTS.md 是 Agent 进入项目读的第一份文件。原则:只写规则,不写知识。 知识放在 ai-doc 里按需加载。
我的 AGENTS.md 长这样(约 30 行):
# Platform Agent Rules
## Scope and sources
- Use `ai-doc/README.md` as the index for stable architecture and domain knowledge.
- Resolve conflicts in this order: current code and runtime behavior, `ai-doc`,
module README files, then `plan/` and `docs/plan/` historical material.
## Skill routing
- Use `$platform-dev` for feature work, refactors, and authorized code fixes.
- Use `$platform-debug` for bug investigation, root-cause analysis, and fixes.
- Use `$platform-test` only when the user asks for testing or verification.
- Use `$platform-knowledge` for locating, explaining, or writing project docs.
- Compose skills: debug → dev for a non-trivial fix, test after implementation,
knowledge after a stable contract change.
## Always-on boundaries
- Do not push, deploy, call production APIs, or SSH to production unless the
user explicitly asks.
- Preserve unrelated user changes in the working tree.
- Push to `master` triggers deployment unless the commit contains `#ignoreDeploy`.
## Tech stack
- Frontend: Vite + React (`frontend/`)
- Backend: Go + Gin, entry `backend/main/main.go`
四个要点:
权威顺序。 代码 > ai-doc > README > plan/。这条规则直接决定了 Agent 在信息冲突时的判断。老项目里计划文档经常描述"准备实现的未来",README 也可能滞后。Agent 如果把所有 Markdown 当同等可信来源,很容易按一份过期设计去改已经演进过的代码。
技能路由。 不是让 Agent 自由发挥,而是明确告诉它"这类任务走这个流程"。开发走 dev,调试走 debug,测试走 test,知识变更走 knowledge。组合使用:一个复杂 bug 先 debug 定位根因,再 dev 修,最后 test 验证 + knowledge 写回。
行为边界。 不能 push、不能部署、不能调生产 API。这几条是硬约束,防止 Agent 把"下一步"当成"应该自动执行"。Lab 环境随便跑,生产操作必须人手动确认。
技术栈。 一行。方便 Agent 快速知道项目是什么技术栈,不需要从 package.json 和 go.mod 里猜。
第二层:ai-doc(知识库)
ai-doc 目录存的是已经稳定的事实——不是开发日志,不是未来计划,是"项目现在是什么样"。
文件结构
ai-doc/
├── README.md ← 索引(只做路由,不存知识)
├── shared/
│ ├── development.md ← 开发共享规范(大小改动定义、开发流程)
│ ├── testing.md ← 测试共享规范(按风险选策略)
│ ├── debugging.md ← 调试共享规范(先复现再改代码)
│ ├── ci-deploy.md ← CI 与部署边界
│ ├── reusable-tools.md ← 可复用工具与流程清单
│ └── coverage-map.md ← 代码到文档的覆盖地图
├── backend/
│ ├── architecture.md ← 后端启动、服务注册
│ ├── gateway-auth.md ← 网关、鉴权、权限传播
│ ├── config-and-ai.md ← 配置、AI 相关
│ ├── observability.md ← 日志、BI、SQL tracing
│ ├── services.md ← 服务目录与命令矩阵
│ ├── testing.md ← 后端测试基线
│ └── account.md, auto.md, cmd.md, todone-core.md, web-storage.md
├── frontend/
│ └── architecture.md ← 前端路由、鉴权、请求、共享组件
├── library/
│ ├── knowledge.md ← Library 领域知识
│ ├── testing.md ← Library 测试矩阵
│ └── ui-locator.md ← UI 元素定位
├── todone/
│ ├── knowledge.md
│ ├── testing.md
│ └── ui-locator.md
└── ...(更多模块)
索引文件 ai-doc/README.md
索引不存业务知识,只做路由。Agent 接到任务后先读这个,然后按需要加载具体文档:
# AI Docs Index
## Authority
1. Current code and verified runtime behavior are authoritative.
2. `ai-doc` stores stable architecture, contracts, recurring constraints.
3. Root and module README files are user-facing orientation.
4. `plan/` and `docs/plan/` contain future direction and history;
verify their claims against code before treating them as current.
## Shared guidance
1. Development decisions: `shared/development.md`
2. Test selection and execution: `shared/testing.md`
3. Bug diagnosis: `shared/debugging.md`
4. CI, deployment, and external-operation boundaries: `shared/ci-deploy.md`
5. Reusable helpers and flows: `shared/reusable-tools.md`
6. Code-to-doc coverage: `shared/coverage-map.md`
## System knowledge
### Frontend
- Shell, routing, auth, requests, shared config: `frontend/architecture.md`
### Backend
- Bootstrap and service registration: `backend/architecture.md`
- Gateway, auth, cookies, permission propagation: `backend/gateway-auth.md`
- Config, AI, and R2: `backend/config-and-ai.md`
- Logs, BI, SQL tracing, profiling: `backend/observability.md`
- Service catalog: `backend/services.md`
- Backend testing baseline: `backend/testing.md`
## Domain knowledge
- Todone: `todone/knowledge.md`, `todone/testing.md`, `todone/ui-locator.md`
- Library: `library/knowledge.md`, `library/testing.md`, `library/ui-locator.md`
## Reading rules
1. Load only documents that answer the current task's questions.
2. For frontend/backend shared behavior, load system doc before domain doc.
3. For testing, load only the relevant shared, backend, or domain testing doc.
4. When a loaded fact looks stale or affects a risky decision, spot-check it
against code before relying on it.
## Writing rules
1. Persist only reusable facts: contracts, ownership, stable behavior, recurring
failure patterns, durable environment constraints.
2. Do not persist one-off command output, patch history, or trial-and-error notes.
3. Use `TODO-verify` for unresolved conflicts; do not overwrite uncertain facts.
4. Merge or rewrite overlapping content instead of appending duplicate bullets.
这套索引机制解决了一个关键问题:Agent 不会把所有文档全塞进上下文。 接到 Library 相关的开发任务,它只加载 shared/development.md + library/knowledge.md + backend/todone-core.md,而不是把几十份文档全灌进去。
知识文档怎么写
以 ai-doc/library/knowledge.md 为例,这份文件大概 200 行。摘录几个关键段落展示写法:
模块归属——先定义边界:
## Module role and loading boundary
1. Library is rendered under `/todone/:group` and activates only when
`group type = 1` (GroupType.Library).
2. Shared todone behavior is defined in `ai-doc/todone/knowledge.md`.
3. Library has no dedicated backend service; core state remains in todone
`Task.Note`, while private round notes use a D1 side table.
数据模型——写死约束,防止 Agent 在设计方案时踩坑:
## Data model
4. `library_notes` owns note content, event time, revision, idempotency
request id, timestamps, and soft-delete state.
5. Note create/edit/delete does not rewrite `Task.Note` or refresh
Library `updatedAt`.
已通过验证的行为——标注验证方式:
9. List page derives runtime meta via `deriveLibraryMeta(extra)` with one
log scan per item.
10. Derived meta is in-memory only and must not add new backend/database fields.
## Default UI state
9. Detail drawer loads D1 notes only while a Library item is open;
note loading failure is isolated from core detail rendering.
常见故障特征——帮助调试时快速定位:
## Common failure signatures
1. `group not exist`
2. `sub group not exist`
3. `task not exist`
写法要点:每条信息回答"现在是什么"而不是"应该怎么设计"。约束直接写死("must not add new backend fields"),不给 Agent 留过度设计的空间。验证过的行为标注验证方式(verified via interaction),未验证的标注 TODO-verify。
共享规范怎么写
ai-doc/shared/development.md(约 35 行)定义了开发流程的核心逻辑:
# Development Guidance
## Principles
1. Fix behavior in its owning layer rather than masking it in a caller.
2. Prefer one minimal coherent change over scattered compatibility patches.
3. Keep shared code generic: pass business policy through explicit inputs.
## Small and large changes
A change is large when it does any of the following:
- adds or removes a service or major module;
- changes a frontend/backend API contract;
- changes schema, migration, persistent data, permissions, or config contracts;
- changes ownership of a route, shared abstraction, or reusable component;
- changes CI or deployment behavior;
- materially spans three or more modules.
Everything else may be treated as a small change.
## Development flow
1. For a small change, inspect the owning code and implement it directly.
2. For a large change, first confirm the architecture boundary and owning
layers with the user, then confirm the user flow, API/data behavior,
and migration approach before editing.
3. Skip repeated confirmation when the user has already made those decisions.
4. Investigate technical facts from code. Ask the user only when an unresolved
choice would materially change product behavior, architecture, or data.
ai-doc/shared/testing.md(约 35 行)定义了测试策略:
# Testing Guidance
## Scope selection
Choose the smallest test that can answer the user's question:
1. Backend logic, transformations, pure utilities: unit or package tests.
2. Frontend pure logic, hooks, component interactions: unit or component
tests with focused mock data.
3. Layout, visual state, routing, browser APIs: local rendered fixture or
browser flow.
4. Frontend/backend contracts: start and integrate both services only when
the user asks for integration testing or the test explicitly requires
the real contract.
5. Shared components: test the changed consumer and one simple adjacent
consumer.
Unit tests do not by themselves prove visual layout or full browser behavior.
## Execution rules
4. The test workflow may add or update test files and fixtures, but must
not silently change production behavior to make a test pass.
6. For local HTTP checks, use the configured localhost development path.
Do not call production or other external state-changing endpoints
without explicit authorization.
第三层:技能(Skills)
技能描述的是"怎么做",不是"做什么"。每个技能 15-30 行,引用共享规范而不是复制它们。
platform-dev
# Platform Development
1. Read `ai-doc/shared/development.md` first, then load only relevant
documents through `ai-doc/README.md`.
2. Classify the work as small or large using the documented triggers.
3. Small change: implement directly.
4. Large change: confirm architecture ownership first, then user/API/data
flow second. Skip confirmation the user already supplied.
5. Discover technical facts from code. Ask only about unresolved choices
that materially change product behavior, architecture, or data.
6. Preserve unrelated working-tree changes and keep shared abstractions generic.
7. If testing is requested, use `$platform-test` after implementation.
Otherwise run only inexpensive static or compile checks and report
what was not tested.
8. If a stable reusable contract changes, use `$platform-knowledge` before
finishing.
platform-debug
两条铁律:
# Platform Debugging
1. Read `ai-doc/shared/debugging.md` first, then load only relevant docs.
2. Distinguish diagnosis-only requests from requests that authorize a fix.
3. Reproduce or inspect the smallest failing path, classify the owning layer,
form hypotheses, and validate with evidence before concluding.
4. Do not ask the user to confirm a technical guess before collecting evidence.
5. For an authorized small and certain fix, change it directly. For a
non-trivial fix, finish root-cause analysis before entering dev gates.
6. Use `$platform-test` only when testing is requested.
7. Do not turn workstation or sandbox symptoms into permanent code workarounds
without verifying the current environment.
platform-test
按风险选策略:
# Platform Testing
1. Read `ai-doc/shared/testing.md` first.
2. Read the requested behavior and current diff, then load only the matched
testing document.
3. Choose the smallest sufficient level: unit/package, frontend component
with mock data, local rendered/browser flow, or full integration.
4. Do not treat unit tests as proof of visual layout or full browser behavior.
5. For shared code, test the changed consumer and one simple adjacent consumer.
6. You may add or update test files and fixtures, but do not silently modify
production logic to make tests pass.
platform-knowledge
# Platform Knowledge
1. Read `ai-doc/README.md` first and follow its authority, reading, and
writing rules.
2. Load only the documents needed for the current question.
3. Verify material facts against current code or runtime before asserting
or refreshing them. Code and verified behavior override plans.
4. Persist stable contracts, ownership, reusable behavior, recurring failure
patterns, and durable constraints. Keep one-off commands and temporary
experiments in the task report instead.
5. Update `Last verified` only when the document's facts were actually checked.
Use `TODO-verify` for unresolved conflicts.
6. Merge duplicate content rather than appending history.
第二部分:使用系统
系统搭好了,怎么用它干活。
开发流程
开发流程的核心判断就一个:改之前先回答几个问题。
跨模块?改 Schema?改 API?影响部署?涉及三个以上模块?
全否 → 小改动流程。直接定位所有权,做最小修改,跑目标测试,列出未覆盖的内容。一次前端文案修正属于这档,三分钟完事。
任一为是 → 大改动流程。分两次对齐:先确认业务归属和架构边界,再确认用户流程、数据行为和迁移方式。两次都确认后,才进入具体实现。不确认不改代码。
举个例子。Library 备注从 JSON 迁移到 D1 独立表,触发了"改 Schema + 跨模块",走大改动流程。Agent 先做归属分析——备注属于 Library 域还是通用笔记系统?确认属于 Library 后,大量分支(接外部服务、建独立服务)自然消失。然后确认 D1 表结构、API 契约、前端加载方式。最后才开始写代码。
关键不是流程长短,是流程强度跟风险匹配。小改动走重流程只会让人跳过流程。
信息流:只加载需要的
Agent 进项目后的实际加载路径:
AGENTS.md(规则 + 路由)
↓
ai-doc/README.md(索引:这个任务涉及哪些文档?)
↓
shared/development.md(开发流程规则)
↓
library/knowledge.md(Library 领域的稳定事实)
↓
backend/todone-core.md(后端涉及的接口和约束)
五份文件,不是整个 ai-doc 目录。这跟把所有文档全塞进 system prompt 有天壤之别——上下文里只有跟当前任务相关的内容。
业务信息:人给约束,Agent 才能不做无用功
Agent 不可能从代码里推导出业务容忍度。在 Library 迁移中,我说了一句"可以停机迁移,本机操作"——整个方案从需要双写、在线兼容、灰度切换,直接简化成"只读计划 → 停服 → 备份 → 迁移 → 校验 → 部署 → 失败回滚"。代码量降到无停机方案的 1/5。
这件事的教训是:人不需要告诉 Agent 怎么做,但必须告诉它边界条件。 可以停机吗?可以接受数据丢失吗?必须兼容旧版本吗?这些信息 Agent 猜不出来,但决定了方案复杂度。
测试:不把"过了"当结论
Agent 报告"测试通过"的时候,必须追问:测了什么、用什么测的、什么没测。
Library 迁移第一轮实现完成后,Agent 说"测试通过"。追问"有没有真实前后端联调过?D1 是真连还是 mock?"——答案是没有。启动真实联调后发现了一个单元测试没暴露的问题:D1 适配器对数据库默认值的反射有限,导致第二次自动迁移错误地判断需要破坏性修改。
所以测试规范里写死了两条硬规则:
- 禁止说"测试通过"。 必须分开说明:跑的什么测试、用的 mock 还是真实服务、哪些场景没覆盖。
- 单元测试不能证明前端行为正确。 浏览器交互和数据迁移必须做真环境验证。
决策包:一次一个
Agent 能一次性生成很多代码和解释的时候,人很容易被动接受总结——"AI 说过了,那应该没问题"。
解决方式不是逼自己看更多,是改了交付方式。架构阶段,Agent 不允许同时抛出数据库模型、接口列表、状态管理和几十个边界情况。必须压缩成一个决策包:
- 当前已知事实
- 两三个真正不同的选择
- 每个选择的主要代价
- 推荐哪个、为什么
一个决策确认后才进下一层。人始终在处理自己擅长的业务判断,而不是被实现细节淹没。
上线:人手控开关
提交、推送、部署、生产迁移——这些不是"下一步",是手动控制点。Agent 只能把代码、测试证据和操作手册准备好;人决定什么时候停服、什么时候执行迁移、什么时候推送触发 CI。
实际流程是这样的:
Agent:代码 + 测试 + 操作手册准备好了。
人:关远端服务。
人:本机连生产 D1,执行只读统计。
人:备份。
人:执行迁移。
人:校验。
人:推送。
CI:部署。
人:浏览器验证。
Agent 在每个步骤只提供命令和检查点,不替人按开关。这跟"Agent 一键部署"的区别是——出问题的时候,人知道每一步发生了什么,回滚路径清晰。
快速搭建
如果你也想给自己的项目搭一套,把这段发给你用的 AI:
我需要为这个项目建立 AI Agent 开发体系。请帮我创建以下文件:
## 1. AGENTS.md(根目录,约 30 行)
包含:技术栈、Agent 行为边界(不能 push/deploy/调生产 API 除非明确授权)、
技能路由(dev/debug/test/knowledge 四个场景)、信息权威顺序
(代码 > 知识库 > README > 历史计划)。
## 2. ai-doc/ 知识库目录
- ai-doc/README.md:索引文件,只做路由。列出共享规范、系统知识、领域知识的位置。
- ai-doc/shared/development.md:开发规范(定义大小改动边界 + 对应流程)
- ai-doc/shared/testing.md:测试策略(按风险选测试层级)
- ai-doc/shared/debugging.md:调试规范(先复现再改代码)
- ai-doc/shared/ci-deploy.md:CI 与部署边界
- 系统知识(backend/、frontend/ 架构和契约)
- 领域知识(每个模块 knowledge.md + testing.md)
规则:知识文件只写"现在是什么",不写"未来要做什么"。
已通过代码或运行时验证的事实标注验证方式,不确定的标 TODO-verify。
## 3. .agents/skills/ 四个技能
- platform-dev:区分大小改动,大改动先对齐架构再对齐流程
- platform-debug:先复现再改代码,不准把猜测写进代码
- platform-test:按风险选测试层级,禁止输出笼统的"测试通过"
- platform-knowledge:稳定契约变更时触发,写回知识库
完成后告诉我每个文件的位置和内容概要,我来 review。
把你的项目名、技术栈、模块名替换进去。跑完之后检查产出的文件是否符合实际情况,不符合的改掉。
这套方法不是什么银弹。但它解决了 Agent 在老项目里最核心的问题:在正确的时刻有正确的信息,人也只在需要做决策的时候才被叫到。 搭系统的成本大概一两个小时,但之后每次开发都在省——省的是 Agent 猜错方向再返工的时间,省的是人重复解释项目背景的精力。