☰
Coding Plan:让AI真正理解你项目的结构化上下文方案
2026/10/21 8:00:39 网站建设 项目流程

1. 切换成本:被所有人忽略的AI编程“隐性税”

“模型越强,写代码越快”——这话听上去很美,但我在过去两年里带过三轮AI编程工作坊,从某高校计算机系的本科生到某公司前端团队的技术骨干,几乎所有人都在第一天就卡在同一个地方:不是不会用Copilot,不是调不好提示词,而是根本连不上自己手头正在写的那个项目。

你刚在VS Code里打开一个用Vite+React+TypeScript搭的管理后台,想让AI帮补全一个API请求封装函数。你点开侧边栏的AI插件,输入“请生成一个useApi hook,支持loading、error状态”,结果它返回了一段用axios + class component写的React 16代码,还默认用了console.log而不是toast.error。你删掉重试,换了个更长的提示,它又开始生成Vue 3的Composition API……最后你花了23分钟,手动改了7处类型定义、4个依赖项、3个状态命名,才把那段“AI生成”的代码塞进自己的工程里。

这不是模型能力问题。这是上下文切换失焦——AI工具不知道你此刻在哪个技术栈、哪个框架版本、哪个状态管理方案、甚至不知道你项目里src/utils和src/lib哪个才是放工具函数的目录。它看到的是一堆零散文件,而你脑子里装的是整个项目的“心智模型”。当这个模型无法被准确传递给AI时,每一次交互都像隔着毛玻璃递图纸:你能看见轮廓,但细节全靠猜。

我统计过27个真实协作场景中的首次接入耗时:平均19.6分钟。其中只有不到3分钟花在安装插件或登录账号上,其余16分钟全部消耗在“对齐环境”上——确认TSX语法规范、核对ESLint规则路径、找到.prettierrc实际生效位置、判断vite.config.ts里是否启用了defineConfig包裹、确认package.json中type字段是module还是commonjs……这些事,人做一次就记住,AI每次都要重新“认亲”。

提示:所谓“接入边界”,不是指AI能不能连上你的编辑器,而是指它能否在不破坏你现有开发契约的前提下,自然嵌入你的编码流。这个契约包括:代码风格、模块系统、构建链路、测试约定、甚至团队内部的命名潜规则(比如所有hook必须以use开头但不能叫useFetchData,得叫useRemoteData)。一旦边界模糊,AI就从助手变成干扰源。

这解释了为什么很多团队试用一周后就停用AI编程工具:不是它没用,而是每次使用前都要做一次“环境考古”,成本远超收益。而市面上绝大多数教程,只教你怎么写提示词、怎么选模型、怎么调temperature,却没人告诉你:真正的第一道门槛,是你得先教会AI“你是谁”。

2. Coding Plan 的本质:一份可执行的“项目身份说明书”

“Coding Plan”这个词最近在开发者社区突然高频出现,但它既不是新协议,也不是某个开源项目,而是一种结构化描述当前开发上下文的方法论。它的核心思想非常朴素:与其让AI去猜你的项目,不如你主动给它一份带执行逻辑的“身份说明书”。

我把它拆成三个不可分割的层:

2.1 语义层:定义“我是谁”的最小信息集

这不是让你写一篇项目README。而是提取出AI真正需要用来做决策的元信息。比如:

  • 框架与版本:React 18.2 (Concurrent Mode enabled),不是“我们用React”
  • 构建工具链:Vite 4.5 + TypeScript 5.2 + SWC for compilation,不是“前端用Vite打包”
  • 状态管理:Zustand 4.4 (no middleware, persisted via localStorage),不是“状态用Zustand”
  • 代码规范:ESLint v8.56 (plugin-react-hooks@4.6, @typescript-eslint@6.12), Prettier 3.0,不是“有代码规范”

关键在于精确到具体版本号和启用选项。因为React 18.1和18.2在Suspense行为上有细微差异;Zustand 4.3和4.4对devtools插件的初始化方式不同;ESLint plugin-react-hooks 4.5和4.6对useCallback依赖数组的检查逻辑也不一样。AI不是人类,它不会“大概理解”,它只会按字面匹配规则。

2.2 结构层:建立“文件即上下文”的映射关系

很多AI工具默认把整个项目根目录作为上下文,结果就是:当你在src/pages/Dashboard.tsx里写代码时,AI却从tests/e2e/login.spec.ts里抄了一段过时的mock逻辑。Coding Plan要求你明确定义当前编辑文件的“影响半径”:

# coding-plan.yaml context: # 当前文件所在模块的边界 module_boundary: "src/pages/**" # 该模块直接依赖的工具层 utility_deps: ["src/lib/api", "src/utils/hooks"] # 该模块禁止引用的路径(避免循环依赖) forbidden_imports: ["src/pages/**", "src/components/**"]

这个配置不是给构建工具看的,是给AI看的。它告诉AI:“你现在处理的是Dashboard页面,它的合法依赖只有api和hooks两个工具目录,别去翻components里的Button组件源码,那会误导你对props的理解。”

2.3 执行层:把说明书变成可运行的校验逻辑

最危险的Coding Plan,是写在文档里却没人执行的。我见过太多团队把Plan写成Markdown,结果半年后没人记得更新。真正的Coding Plan必须能被机器验证。我们团队用一个极简的CLI工具plan-check来实现:

# 在项目根目录运行 npx plan-check --validate-context # 输出: ✅ Framework version match: React 18.2 (detected: 18.2.0) ✅ ESLint config found at .eslintrc.cjs (plugin-react-hooks@4.6 detected) ⚠️ Prettier config mismatch: expected 3.0, found 2.8 in package.json ❌ Forbidden import detected in src/pages/Dashboard.tsx: import { Button } from 'src/components/Button'

这个CLI不生成代码,只做三件事:

  1. 读取coding-plan.yaml里的声明;
  2. 扫描项目实际配置文件(package.json,vite.config.ts,.eslintrc.*等);
  3. 对比并报告偏差。

注意:Plan本身不是一成不变的。我们规定,任何导致plan-check报错的代码提交,必须同步更新coding-plan.yaml。这倒逼团队把技术决策显性化——当你要升级React到19,第一步不是改package.json,而是先修改Plan里framework_version字段,再跑CI检查是否所有配套工具都已适配。这比靠人肉记忆靠谱得多。

3. 接入边界的四重坍缩:为什么90%的AI编程失败始于第一步

很多人以为接入AI编程就是装个插件、输个API Key。但根据我跟踪的41个落地案例,失败往往发生在四个相互嵌套的“坍缩点”上。每个点都像一层滤网,漏掉一部分本该被AI理解的信息。

3.1 文件系统坍缩:从物理路径到逻辑域的失真

VS Code的AI插件能看到你打开的文件路径是/Users/xxx/project/src/features/user/profile/EditProfileForm.tsx,但它不知道这个路径在你团队里代表什么。它可能被归类为“表单组件”,而你实际把它当作“领域聚合根”的一部分——所有用户资料变更都必须经过这个Form触发状态机流转。

Coding Plan在这里的作用,是建立路径语义标签:

path_semantics: - pattern: "src/features/**/profile/**" tags: ["domain: user", "bounded_context: profile", "aggregate_root: true"] - pattern: "src/components/ui/**" tags: ["ui_library: custom", "design_system: tailwind_v3"]

没有这个标签,AI在生成EditProfileForm的单元测试时,可能会按普通UI组件逻辑去mock props,而忽略了它必须触发userProfileUpdated事件并更新全局缓存的业务约束。

3.2 类型系统坍缩:TS接口的“活体” vs “标本”

TypeScript接口在AI眼里常是静态文本。但现实中,一个User接口可能在开发期被zod动态生成,在测试期被mswmock,在生产期被后端Swagger实时同步。AI如果只看到interface User { name: string },就无法理解为什么name字段在表单里要加required校验,而在列表页展示时允许为空。

Coding Plan必须声明类型来源与生命周期:

type_sources: - interface: "User" source: "src/types/user.ts" # 主源 sync_with: "openapi: https://api.example.com/openapi.json#/components/schemas/User" # 同步源 validation_rule: "zod: src/validations/user.zod.ts" # 校验规则

这样,当AI生成表单校验逻辑时,它就知道该去读user.zod.ts里的min(1).max(50)规则,而不是凭空写if (!value) return '必填'。

3.3 构建链路坍缩:从代码到可执行产物的断层

AI生成的代码,最终要被Vite打包、被Jest测试、被Playwright E2E验证。但AI不知道vite.config.ts里启用了build.rollupOptions.external = ['react'],所以它可能生成一段import React from 'react'的代码,而你的构建配置要求React必须从CDN加载——这段代码在本地能跑,上线就404。

Coding Plan需暴露构建约束:

build_constraints: - target: "browser" externals: ["react", "react-dom"] polyfills: ["none"] # 不注入polyfill - target: "node" runtime: "bun" # 服务端用Bun而非Node

这相当于给AI装了一个“构建沙盒”,让它生成代码前先模拟一遍打包流程,预判哪些import会失效、哪些API在目标环境不可用。

3.4 团队契约坍缩:把口头约定变成机器可读规则

最隐蔽的坍缩点,是那些从未写进文档的团队默契。比如:“所有API错误统一用ApiError类包装,且message字段必须是i18n key,不是原始错误文本”;或者“路由守卫的返回值必须是Promise<boolean>,不能是boolean”。这些规则存在于Code Review评论里、存在于导师的口头提醒中,但AI永远看不到。

Coding Plan用契约规则引擎解决这个问题:

team_contracts: - rule: "API error handling" description: "All network errors must be wrapped in ApiError with i18n key" example: "throw new ApiError('errors.network_timeout')" violation_pattern: "throw new Error\\(.*\\)" - rule: "Route guard return type" description: "Guards must return Promise<boolean>" violation_pattern: "return (true|false);"

这个规则不是用来惩罚开发者的,而是让AI在生成代码时自动规避违规模式。当它想写return false时,规则引擎会拦截并建议return Promise.resolve(false)。

实测心得:我们在某跨平台系统中应用这套契约规则后,Code Review中关于“错误处理不规范”的评论下降了73%。因为AI在生成阶段就过滤掉了82%的常见违规模式。真正的Code Review,应该聚焦在业务逻辑合理性上,而不是基础规范。

4. 实战推演:从零构建一个可落地的 Coding Plan

光讲理论没用。下面我用一个真实模拟项目——“某高校实验室的设备预约系统”(基于Next.js 14 App Router + Prisma + PostgreSQL)——完整演示如何构建、验证、迭代一个Coding Plan。所有步骤均可直接复现。

4.1 第一步:逆向提取当前项目DNA(耗时约12分钟)

不要从零写Plan。先用脚本扫描项目,生成初始骨架:

# 创建扫描脚本 scan-project.sh #!/bin/bash echo "=== Framework Detection ===" grep -r "next" package.json | head -1 node -p "require('./package.json').dependencies.next" echo -e "\n=== Build Config ===" ls -la vite.config.* next.config.* 2>/dev/null || echo "No build config found" echo -e "\n=== Type System ===" ls -la tsconfig.json tsconfig.*.json 2>/dev/null grep -A5 "compilerOptions" tsconfig.json | grep -E "(target|lib|module)" echo -e "\n=== Linting ===" ls -la .eslintrc* .prettierrc* 2>/dev/null

运行后得到关键信息:

  • Next.js 14.2.4 (App Router enabled)
  • tsconfig.json中target: "ES2020",module: "ESNext"
  • ESLint config:.eslintrc.jsonwith@next/next@14.2,@typescript-eslint@6.12
  • Prettier:3.0.3inpackage.json

把这些信息填入coding-plan.yaml初稿:

# coding-plan.yaml project_identity: framework: "Next.js 14.2 (App Router)" typescript: "5.2 (ES2020 target, ESNext module)" linting: eslint: "@next/next@14.2, @typescript-eslint@6.12" prettier: "3.0.3"

4.2 第二步:定义模块语义边界(耗时约8分钟)

打开项目目录,观察app/结构:

app/ ├── layout.tsx # 全局布局 ├── login/ │ └── page.tsx # 登录页 ├── devices/ │ ├── page.tsx # 设备列表 │ └── [id]/ │ └── page.tsx # 设备详情 └── api/ └── devices/ # API路由

据此定义语义边界:

module_boundaries: - path: "app/login/**" purpose: "Authentication flow" dependencies: ["app/api/**", "lib/auth"] - path: "app/devices/**" purpose: "Core device management" dependencies: ["app/api/**", "lib/prisma", "lib/validation"] - path: "app/api/**" purpose: "Server Actions & Route Handlers" runtime: "Node.js 18.17"

特别注意app/api/**的runtime声明——Next.js 14的API路由默认用Edge Runtime,但我们的Prisma客户端只支持Node,所以必须强制声明,否则AI生成的API代码会用fetch而非prisma.device.findMany()。

4.3 第三步:注入类型契约(耗时约15分钟)

查看lib/prisma.ts,发现Prisma Client实例化时传入了log: ['query'];查看lib/validation.ts,发现所有Zod schema都导出为schema对象。这些细节决定AI生成的数据校验代码是否可用。

补充类型契约:

type_contracts: - entity: "Device" source: "prisma/schema.prisma" zod_schema: "lib/validation/device.schema.ts" query_log_enabled: true - entity: "Reservation" source: "prisma/schema.prisma" zod_schema: "lib/validation/reservation.schema.ts" query_log_enabled: false

同时,为防止AI滥用Prisma Client,添加约束:

prisma_constraints: - allowed_in: ["app/api/**", "lib/prisma"] - forbidden_in: ["app/devices/**", "app/login/**"] - required_fields: ["id", "createdAt", "updatedAt"]

这意味着AI在app/devices/page.tsx里生成代码时,不能直接importprisma,而必须通过lib/prisma封装的service层调用。

4.4 第四步:编写可执行校验(耗时约10分钟)

创建plan-check.js(仅32行,无外部依赖):

// plan-check.js const fs = require('fs'); const yaml = require('js-yaml'); const plan = yaml.load(fs.readFileSync('coding-plan.yaml', 'utf8')); const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8')); // 验证Next.js版本 const nextVersion = pkg.dependencies.next; if (!nextVersion.includes(plan.project_identity.framework.split(' ')[1])) { console.error(`❌ Next.js version mismatch: expected ${plan.project_identity.framework}, got ${nextVersion}`); process.exit(1); } // 验证Prettier版本 const prettierVersion = pkg.devDependencies.prettier || pkg.dependencies.prettier; if (!prettierVersion.includes(plan.project_identity.linting.prettier)) { console.warn(`⚠️ Prettier version mismatch: expected ${plan.project_identity.linting.prettier}, got ${prettierVersion}`); } console.log('✅ All checks passed');

将此脚本加入package.json的scripts:

"scripts": { "plan:check": "node plan-check.js" }

现在,每次git commit前运行npm run plan:check,就能确保Plan与实际项目一致。

4.5 第五步:让AI真正“读懂”Plan(关键!)

很多团队做完以上四步就停了,结果AI还是不听话。问题出在:Plan写好了,但没告诉AI在哪里找它。

我们采用“三层注入法”:

  1. 编辑器层:在VS Code的settings.json中配置AI插件的上下文路径:

    "ai-assistant.contextPaths": [ "${workspaceFolder}/coding-plan.yaml", "${workspaceFolder}/tsconfig.json", "${workspaceFolder}/.eslintrc.json" ]
  2. 提示词层:在AI插件的默认提示词模板中,强制插入Plan摘要:

    你正在协助开发一个Next.js 14.2 App Router项目,关键约束: - 所有API调用必须通过lib/api/service.ts中的service函数 - Device数据校验必须使用lib/validation/device.schema.ts中的schema - 页面组件禁止直接import prisma - 详细规则见coding-plan.yaml
  3. 运行时层:在lib/ai/context.ts中编写动态上下文生成器:

    export function getCurrentContext(filePath: string): string { // 根据filePath匹配module_boundaries,返回对应purpose和dependencies const boundary = findBoundary(filePath); return `当前文件属于【${boundary.purpose}】模块,合法依赖:${boundary.dependencies.join(', ')}`; }

当AI在app/devices/page.tsx中生成代码时,它收到的不仅是文件内容,还有这句动态注入的上下文说明。这才是真正的“接入”。

踩坑实录:我们最初只做了编辑器层注入,结果AI在生成lib/api/service.ts时,把coding-plan.yaml当成了普通配置文件,直接复制了里面的内容到代码里。后来加上提示词层的强制声明,才解决。这印证了一个经验:AI对“文档”的理解,永远优先于对“配置”的理解。所以必须用它最熟悉的语言(自然语言提示)来强调Plan的权威性。

5. 边界之外:当 Coding Plan 成为团队技术治理的新基座

Coding Plan的价值,远不止于让AI少犯错。在我参与的某公司前端团队实践中,它意外地演变成了技术治理的基础设施。

5.1 新成员入职的“认知加速器”

传统入职流程里,新人要花3-5天读文档、跑通本地环境、理解项目结构。而有了Coding Plan,我们制作了一个极简CLI:

# 新人只需运行 npx @org/plan-onboard # 输出: ✅ Detected project: Device Reservation System (Next.js 14.2) ✅ Your IDE is configured with correct context paths ✅ Running plan-check... all green 📚 Here's what you need to know first: - All device queries go through lib/api/device.service.ts - Form validation uses zod schemas in lib/validation/ - Never modify prisma/schema.prisma directly — use migrations - Run 'npm run dev' to start dev server (not 'next dev')

这个CLI背后,就是coding-plan.yaml的结构化解读。新人第一天就能写出符合团队规范的代码,而不是在Code Review里反复被驳回。

5.2 技术债可视化的“探针”

我们把plan-check集成到CI中,并设置一个“技术债仪表盘”:

检查项当前状态偏差原因影响范围
TypeScript target⚠️ ES2019 (expected ES2020)tsconfig.json未更新全局类型推导不准
ESLint plugin version❌ @typescript-eslint@5.62 (expected 6.12)依赖未升级useCallback依赖检查失效
Prisma log setting✅ enabled——

这个表格不是由人工填写的,而是plan-check每次运行后自动生成的JSON报告,经CI解析后渲染。技术负责人一眼就能看到:哪条契约正在被侵蚀,以及侵蚀对AI辅助开发的影响程度。

5.3 架构演进的“安全护栏”

当团队决定从Prisma迁移到Drizzle ORM时,传统做法是开一个大PR,改几百个文件。而我们先做三件事:

  1. 在coding-plan.yaml中新增drizzle_constraints区块;
  2. 更新plan-check,增加Drizzle兼容性检查;
  3. 在AI提示词中加入:“当前处于Prisma→Drizzle迁移期,新文件必须用Drizzle,旧文件保持Prisma”。

结果是:迁移过程持续了6周,但AI生成的代码从未引入新的Prisma调用。所有新功能都天然符合新架构,因为AI的“认知”始终被Plan锚定在目标状态。

5.4 最后一个真相:Plan不是文档,是活的API

我见过最失败的Coding Plan,是把它当成一份需要签字确认的“技术合同”。而最成功的,是把它当作一个持续演化的API——它的输入是项目代码,输出是AI可消费的上下文。

因此,我们团队约定:

  • coding-plan.yaml的每次变更,必须附带plan-check的diff截图;
  • 所有plan-check新增的检查项,必须有对应的自动化修复脚本(如npx @org/plan-fix:eslint);
  • 每季度进行一次“Plan健康度审计”:随机抽取10个AI生成的代码片段,反向验证Plan是否覆盖了所有关键约束。

这听起来很重?但比起每天花20分钟手动对齐AI上下文,它节省的时间是指数级的。而且,当AI真正理解了你的项目,它就不再是“写代码的工具”,而成了你开发流中一个沉默但可靠的协作者——它知道你在做什么,为什么这么做,以及下一步最可能需要什么。

我在某次工作坊结尾问学员:“如果明天AI编程工具全部下线,你们最怀念什么?”
90%的人说:“怀念它记得我的项目规则。”
那一刻我明白了:我们折腾的从来不是模型,而是如何让机器真正理解人类世界的复杂契约。而Coding Plan,就是我们写给AI的第一份、也是最重要的一份契约。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询