
1. 为什么PRD2CODE离不开“Schema 样板间”双引擎1.1 PRD2CODE链路中最容易翻车的一环先说个我自己的真实经历。去年我们在做一个面向运营后台的AI生码工具流程很简单产品经理写好PRD丢给大模型大模型直接生成前端页面代码。最开始的两周demo效果很惊艳领导看了都觉得“这玩意儿快能上线了”。但一放到真实项目里就露馅了字段命名一会儿user_name一会儿userName表单校验规则靠猜接口字段跟后端给的对不上最离谱的一次AI把“用户状态”的枚举值active直接写成了字符串常量active导致整个状态判断全废。问题出在哪出在PRD到代码这条链路里最模糊的一环把自然语言的产品描述映射成结构化的组件和数据模型。大模型很擅长生成“看起来对”的代码但当你要求它做到“字段名全对、类型全对、枚举值全对、交互行为全对”时纯靠prompt是远远不够的。它本质上是概率生成而代码是确定性系统这中间的鸿沟必须靠一层约束来填平。我后来总结了一句话PRD2CODE能不能用不是看AI多聪明而是看我们给AI画的边界有多清楚。而这套边界就是标题里说的“Schema 样板间”双引擎。1.2 Schema管“结构约束”样板间管“风格基准”那这俩分别解决什么问题我用一个装修的类比来解释。Schema就像施工图上的尺寸和材质标注。墙面多高、门窗多宽、插座在哪个位置、用几平方的电线这些都必须在图上写死。对应到代码生成里Schema定义了页面有哪些字段、字段是什么类型、哪些必填、数据从哪里来、交互触发后干什么。它约束的是结构层是AI不能乱来的底线。样板间源码则是装修效果图和施工工艺标准。同样是做了一个卧室用什么样的踢脚线、柜门用什么铰链、插座面板选哪个品牌系列这些风格和工艺层面的细节施工图上不会画但做出来好不好用、好不好看全看这些细节。对应到代码生成里样板间定义了团队用什么组件库、按什么目录结构组织代码、状态管理怎么分层、API请求怎么封装。它约束的是风格与工程规范层是让AI生成的代码“像我们团队自己人写的”的关键。这两个东西必须对齐否则就是施工图标注的是A效果图画的是BAI拿到之后左右为难最终输出的代码大概率是“结构对了一半风格全不对”。所以“对齐规范”这件事本质上是把这两套约束统一成一套翻译规则让Schema里的每一个字段、每一个类型都能在样板间源码里找到明确的落点。2. 从PRD到Schema先让机器读懂需求2.1 把PRD结构化从自然语言到表单模型要让机器读懂PRD第一步不是让AI去“读”而是让产品经理按固定的结构去“填”。我们当时做了一个PRD结构化模板把这个模板嵌到内部的文档系统里产品经理创建需求时必须按模板走。模板核心分五块页面信息页面名称、页面路径、所属模块、访问权限。区块列表页面上按从上到下排列的功能区块每个区块标注类型查询区、表格区、表单区、详情展示区、统计卡片区等。字段定义每个区块里的字段清单包括字段名、中文标签、类型、是否必填、枚举值、默认值。数据源每个字段或区块对应的接口路径、请求方法、参数映射。交互行为按钮点击后的动作跳转、提交、弹窗、刷新以及对应的接口。这个模板看起来简单但它解决了一个关键问题把AI从“猜需求”中解放出来。本来大模型要从一大段自然语言里抽提出字段、类型、关系现在产品经理已经把这些内容表格化了大模型只做一个翻译和组装的工作准确率直接上了一个台阶。我测试过对比数据用纯自然语言PRD让AI生成页面字段名命中率大概在70%左右用结构化PRD后字段名命中率提升到95%以上。原因很简单自然语言里的歧义太多而结构化模板从一开始就消灭了歧义。2.2 选型JSON Schema 2020-12的几个关键理由字段模型有了用什么来描述它我们对比过Protocol Buffers、TypeScript接口定义、JSON Schema最终选了JSON Schema而且直接上了2020-12版本。选它的核心原因有三个第一动态性足够强。PRD是经常变的产品经理今天加一个字段、明天改一个枚举值是家常便饭。JSON Schema是纯数据描述改起来不需要重新编译部署配合后端的Schema配置中心能做到分钟级生效。这一点写死在TypeScript类型里的方案比不了。第二生态工具链成熟。校验用ajv生成mock数据有json-schema-faker转TypeScript类型有json-schema-to-typescript前端的表单渲染可以直接用schema-driven的方案。整套工具链都是围绕JSON Schema建的我们不需要自己造轮子。第三2020-12版本解决了不少历史遗留问题。我们在实际使用中感受最深的有几个改进$defs替代了原来的definitions配合$ref做结构复用更清晰了。我们把页面、区块、字段定义成公共的$defs不同的页面Schema直接引用减少重复。prefixItems取代了数组元组形式的items。在描述表单字段列表、表格列配置这类“固定顺序的异构数组”时prefixItems能精确描述每一项的schema比旧版的元组写法直观很多。unevaluatedProperties配合additionalProperties能对“哪些属性是允许的”做更严格的收口。这对AI生成场景非常关键——AI经常自己发明一些Schema里没定义的字段unevaluatedProperties: false直接堵住这条路。下面是我们定义“区块”的一小段Schema感受一下写法{ $schema: https://json-schema.org/draft/2020-12/schema, $defs: { fieldSchema: { type: object, properties: { fieldName: { type: string }, label: { type: string }, valueType: { type: string, enum: [string, number, boolean, date, datetime, enum] }, required: { type: boolean, default: false }, enumValues: { type: array, items: { type: string } } }, required: [fieldName, label, valueType], unevaluatedProperties: false }, blockSchema: { type: object, properties: { blockType: { type: string, enum: [queryArea, tableArea, formArea, detailArea, statCardArea] }, fields: { type: array, items: { $ref: #/$defs/fieldSchema } } }, required: [blockType, fields], unevaluatedProperties: false } }, properties: { pageName: { type: string }, pagePath: { type: string }, blocks: { type: array, items: { $ref: #/$defs/blockSchema } } }, required: [pageName, pagePath, blocks], unevaluatedProperties: false }这段Schema干了什么事它规定了“一个页面由页面名、路径和区块列表组成每个区块又由区块类型和字段列表组成每个字段有名字、标签、类型等属性”。AI生成的页面结构必须严格落在这些约束里不允许自定义任何额外字段。这就是前面说的“边界”。2.3 Schema校验AI输出前的最后一道闸门Schema定好了怎么让它真正卡住AI答案是在AI生成的每一个关键节点都做校验而不是生成完再回头看。我们的生成链路里有三道校验PRD结构化结果校验大模型从PRD模板里提取出页面模型后先拿Schema校验一遍提取的结果不合法就直接打回让模型重新提取。中间表示校验在生成过程中我们会让AI先输出一份“页面中间表示”相当于页面模型和代码之间的胶水层这个中间表示同样有对应的Schema校验不通过就不允许进入代码生成阶段。生成代码的静态检查代码生成后用eslint、tsc这些工具做静态扫描同时我们再额外写了一个脚本去扫描生成的代码里是否出现了Schema中未定义的字段名或枚举值。校验用的库是ajv的2020-12版本Node.js里集成很简单import Ajv2020 from ajv/dist/2020.js; const ajv new Ajv2020({ strict: true, allErrors: true }); const validate ajv.compile(pageSchema); const result validate(aiOutput); if (!result) { console.error(validate.errors); // 这里可以加上自动重试把错误信息塞回prompt让AI修正 }重点说一下这个“自动重试”机制。我们的经验是大模型第一次输出经常有各种小毛病但你把ajv的报错信息原文塞回去告诉它“你违反了第几条约束”它修正的成功率非常高。实测下来经过两轮校验重试约85%的错误能被AI自己修复。这个闭环是整个PRD2CODE链路里性价比最高的一步。3. 样板间源码的拆解与对齐3.1 样板间不是“示例代码”而是“规范载体”很多团队做AI生码失败不是因为AI不行而是因为样板间没有做好。我见过不少团队从GitHub上clone一个管理后台模板就当样板间这完全跑偏了。样板间的本质是把团队两三年沉淀的代码规范、工程实践、组件封装浓缩成几套可被AI参考、被静态工具扫描的“活文档”。我们的样板间不是按业务页面划分的而是按页面类型划分的。每个页面类型一套完整可运行的源码包含路由、页面容器、区块组件、API请求层、类型定义、样式文件。比如标准列表页查询区 表格区 分页 新增/编辑弹窗复杂表单页分步表单 动态增减字段 联动校验统计看板页统计卡片 图表区 时间筛选详情展示页描述列表 操作按钮区 关联列表每个样板间都有明确的“设计意图”说明文档告诉AI和人类开发者什么场景应该选这套样板间哪些部分可以按Schema扩展哪些部分不允许改动。比如列表页样板间里分页逻辑、查询参数拼接、表单校验这些属于“不允许改动”部分AI只能在其基础上做业务字段的替换和增减。3.2 对齐表把Schema的每个字段映射到源码位置这一步是“Schema与样板间源码对齐规范”的核心我们内部叫对齐表Alignment Map。它是一张把Schema元素映射到样板间代码位置的规则表AI生成代码时按照这张表去找代码模板、填字段而不是自由发挥。我截取了我们对齐表的一部分给个直观感受Schema元素Schema节点样板间位置生成方式页面属性pageName路由配置里的name、面包屑标题直接替换页面路径pagePath路由配置里的path直接替换查询区区块blockType: queryArea列表页样板间/components/QueryPanel.tsx根据fields动态渲染表单项表格区块blockType: tableArea列表页样板间/components/DataTable.tsx根据fields生成列配置字段-枚举类型field.valueType: enum对应列或表单控件的options将enumValues映射为选项数组数据源field.apiPathAPI层/api/xxx.ts的请求函数生成请求函数并替换交互行为block.actions页面容器的事件处理器绑定到按钮的onClick有了对齐表之后AI的角色从“架构师”降级成了“施工队长”——它要做的不是设计怎么实现而是按照既定规则搬运和组装。这个降级是好事因为大模型在架构层面经常给出“看起来新颖但不符合团队规范”的方案而在有明确规则指引的组装场景它出错率极低。3.3 常见的对齐断点与解决方案在对齐表落地的过程中我们踩过不少坑有三个比较有代表性值得单独说说。第一个坑字段命名风格不一致。Schema里定义的是user_namesnake_case但样板间里TS接口和组件Props全是userNamecamelCase。AI在生成时经常把这两者混着用导致前端代码里一半用下划线一半用驼峰。后来我们定了一个铁律Schema里只允许camelCase。这样从PRD到Schema到代码风格彻底统一对齐表上也少了一条需要转换的规则。虽然这个改动让部分后端同事不太舒服但配合一个字段名转换的工具整体收益是正向的。第二个坑样板间的目录结构没被遵守。AI经常把代码“拍平”——所有组件全塞到components/下面一个子组件文件动不动四五百行。这跟我们样板间里“一个组件一个文件、按功能分目录”的约定相去甚远。我们最后解决的办法比较笨但有效在样板间里放了一个虚拟的“目录约束文件”用README注释把目录规则写死然后在生成prompt里强制要求AI“先读完样板间里的README再开始写代码”。同时通过静态脚本扫描生成的代码里import路径的深度超过约定层级直接报错。第三个坑样式的token被写死。样板间里用了设计系统的色板变量var(--color-primary)但AI经常“自作聪明”地直接写死颜色值比如#3b82f6。这倒不是致命的错误但它破坏了整个设计体系的统一性。解决方式是在Schema里增加一个styleToken字段在生成Prompt里明确要求“颜色、间距、字号一律使用样式变量禁止出现数字字面量”并在静态检查阶段用正则扫描颜色值锁定不合格的代码。4. 落地实操我的对齐规范长什么样4.1 目录结构与关键文件理论说完了看看实际落地的形态。我们在项目仓库里建了一套独立的codegen/目录专门存放AI生码相关的东西结构大概这样codegen/ ├── schemas/ │ ├── page.schema.json # 页面级Schema │ ├── block.schema.json # 区块级Schema │ └── field.schema.json # 字段级Schema ├── templates/ │ ├── list-page/ # 标准列表页样板间 │ │ ├── README.md # 设计意图说明AI必须读 │ │ ├── src/ │ │ │ ├── index.tsx │ │ │ ├── components/ │ │ │ ├── api.ts │ │ │ └── types.ts │ │ └── route.config.ts │ ├── form-page/ │ └── dashboard-page/ ├── alignment/ │ └── alignment-map.yaml # Schema到样板间的对齐表 ├── scripts/ │ ├── validate-schema.ts # 校验Schema本身是否合法 │ ├── validate-output.ts # 校验AI输出的中间表示 │ └── scan-generated-code.ts # 静态扫描生成代码 └── prompts/ ├── system-prompt.md # 系统级Prompt声明所有规则 ├── extract-model.prompt.md # PRD - Schema 的Prompt模板 └── generate-code.prompt.md # Schema 样板间 - 代码的Prompt模板这套目录有几个设计上的讲究。schemas/和templates/分开放是为了让Schema的维护者和样板间的维护者能独立更新。但alignment/把它们串起来任何一方的改动都必须同步更新对齐表否则CI直接报错。prompts/单独存放是因为prompt的迭代频率远高于Schema和样板间它需要跟着实际效果不断调整独立成目录后方便做版本管理。4.2 五步走流程一次完整的页面生成整理一下我们一次完整的页面生成流程共五个步骤每一步都有明确的输入输出和校验点第一步PRD结构化。产品经理在线文档里按模板填写需求。这一步的产出是一份结构化PRD文档包含了区块、字段、数据源、交互的完整定义。第二步Schema校验与实例化。从结构化PRD里提取出页面模型然后根据页面类型选用对应的Schema并生成一份“页面Schema实例”。这份实例是后续所有步骤的唯一数据源AI不会直接读取PRD原文。第三步样板间选型。根据页面模型里的pageType列表页、表单页、看板页等自动匹配样板间。匹配规则写在对齐表里必要时允许产品经理手动指定。第四步AI生成代码。系统把“页面Schema实例 样板间README 样板间源码片段 对齐表关键规则”组装成prompt交给大模型生成代码。生成结果先过我们自己的静态扫描工具再过eslint和tsc双重把关。第五步人工Review与反馈回流。生成的代码由前端工程师做最终review任何修改会作为新的“对齐规则”被记录。如果AI在一个问题上反复出错我们会把这部分规则单独强化到prompt和静态扫描里。这套流程走下来一个标准列表页从PRD到可运行代码的时间从原来的人工2天压缩到AI生成人工检查1小时以内。当然这小时里包含了不少等待时间真正的操作时间大约20分钟。4.3 实例拆解一个用户信息编辑页的完整过程光讲流程太空了我拿一个实际的“用户信息编辑页”来走一遍。PRD里的字段定义大概是用户名必填字符串、手机号必填字符串11位、邮箱选填邮箱格式、角色必填枚举管理员/运营/访客、状态必填枚举启用/禁用、备注选填多行文本。交互上有一个“保存”按钮提交后调编辑接口成功后跳回列表页。第一步生成页面Schema实例关键部分长这样{ pageName: 用户信息编辑页, pagePath: /user/:id/edit, pageType: formPage, api: { submitUrl: /api/user/update, method: POST }, blocks: [ { blockType: formArea, fields: [ { fieldName: userName, label: 用户名, valueType: string, required: true }, { fieldName: phone, label: 手机号, valueType: string, required: true, pattern: ^1[3-9]\\d{9}$ }, { fieldName: email, label: 邮箱, valueType: string, required: false, format: email }, { fieldName: role, label: 角色, valueType: enum, required: true, enumValues: [admin, operator, visitor] }, { fieldName: status, label: 状态, valueType: enum, required: true, enumValues: [enabled, disabled] }, { fieldName: remark, label: 备注, valueType: string, required: false, uiControl: textarea } ] } ], actions: [ { actionName: submit, handler: submitForm, target: submitUrl, onSuccess: navigateBackToList } ] }第二步匹配到form-page样板间AI生成的代码会自动套用样板间的风格。比如表单控件会统一走我们封装的FormItem组件手机号的校验规则会写到统一的validator里而不是内联在JSX里。这块儿是样板间的功劳AI只是在做填表。第三步静态扫描和校验。我们会重点检查角色枚举值是不是admin/operator/visitor而不是中文提交成功后是不是按样板间约定调了message.success并跳转校验规则是不是放在/utils/validators.ts里而不是写在组件内。这些检查都是对齐表规则的自动翻译。5. 常见问题与排查技巧实录5.1 我踩过的几个代表性深坑做这套对齐规范的半年里坑没少踩有几个特别有代表性写出来希望你能绕开。坑一Schema本身先失控了。有一段时间产品经理为了追求灵活在PRD模板里加了大量“其他需求”的自由文本这导致结构化的字段定义和自由文本之间频繁冲突。AI在生成时不知道该信哪边表现出来就是代码里一会儿用Schema里的字段名一会儿用自由文本里的说法。最后我们痛下决心把“其他需求”自由文本从生成流程里完全移除它只能作为注释附带到PRD里AI的输入数据源只有结构化部分。坑二$ref循环引用导致AI死循环。我们一度在Schema里定义了page - blocks - fields - block的循环引用想让AI在字段级也能引用区块的信息。结果AI在生成时陷入了深度递归输出内容既冗余又混乱。后来我们把循环引用彻底拆掉改成“扁平结构ID关联”字段不直接嵌套引用区块而是通过blockId外键关联。这个调整大大降低了AI的推理负担生成速度和准确率都明显提升。坑三过度相信静态检查漏掉了运行时错误。静态检查只能保证“语法和约定上没问题”发现不了“逻辑上不对”。比如有一次生成的代码里保存按钮的onClick绑错了方法把查询列表的接口当成编辑接口提交了测试环境没爆错但逻辑完全不对。后来我们加了运行时快照测试——生成代码后自动构建并在浏览器里跑一遍关键路径的冒烟测试才逐渐把这类问题兜住。5.2 排查技巧速查表根据我们的经验整理了一个问题排查速查表遇到问题时按这张表逐项排查能省不少时间症状可能原因排查方法生成的字段名全是下划线和代码风格不一致Schema里混用了snake_case检查Schema实例里的fieldName是否符合camelCase不合规直接报错AI生成的枚举值剩下一部分或直接写死字符串枚举约束没有传进prompt检查提取模型阶段是否把enumValues完整传给了生成阶段代码里多出了Schema里没定义的字段Schema校验的unevaluatedProperties未开启确认页面级Schema里设置了unevaluatedProperties: false组件路径和样板间不一致出现大量相对路径跳转AI没有读样板间README在prompt和静态扫描脚本里双重约束路径规则生成代码能编译但运行时白屏状态管理代码被AI省略或写错查看生成的store/context文件确认和样板间结构一致表单校验和PRD要求不一致校验规则没有映射到样板间的validator层检查对齐表中是否配置了pattern和format的映射规则5.3 一个关于“对齐规范”的长期主义建议最后说点大实话。这套对齐规范不是一次性能做完的它是个不断打磨的活。我们目前大概每个迭代都会根据review和线上问题更新一次对齐表每次更新的内容都不多可能就加一两条规则但半年下来AI生成代码的可接受率从最初的50%左右提升到了85%以上。这个过程中我个人的体会是AI生码的核心难点不在模型而在“组织知识”。把散落在团队各处的编码规范、组件封装、接口约定整理成Schema、样板间、对齐表这些结构化、可校验的东西本身就是一个团队的工程能力升级。哪怕暂时不做AI生码这套东西用来做新人培训、代码审查手册价值也是非常大的。如果你也在做类似的PRD2CODE方向建议先别急着上模型沉下心把Schema和样板间这两个基础件打磨扎实。基础打好了AI只是锦上添花基础不牢再强的模型也翻车。