我用 DeepSeek 搭建了一个 AI 驱动的 React 代码生成引擎,全流程踩坑记录
在前端开发中,重复性工作占据了大量时间。从接需求、画原型、写组件、调样式到改布局,真正用于创造性思考的时间寥寥无几。因此,我一直在探索:能否将诸如“暗黑风格数据看板,包含 KPI 卡片和趋势图”这类自然语言描述直接交给 AI,让它生成一份可直接运行的 React 代码?

上周末终于付诸实践。花了两天时间,基于 DeepSeek Pro API 构建了一条三阶段流水线,实现了从自然语言到浏览器渲染的完整链路。现将整个过程记录下来,作为一次踩坑参考。
架构设计
整个系统代号为 Athena,拆分为三个独立引擎,各司其职:
用户自然语言↓[引擎1: 意图补全] → TaskJSON (结构化页面描述)↓[引擎2: 代码生成] → React TSX 代码↓[引擎3: 沙箱预览] → 浏览器渲染
三个引擎分别调用独立的 AI 会话,通过 JSON 协议解耦。引擎1 和引擎2 均使用 DeepSeek Pro,引擎3 预留了千问视觉用于设计反馈,但该部分尚未实现。
技术栈
- 运行时: Node.js + TypeScript
- 服务端: Fastify
- AI SDK: OpenAI 兼容协议(调用 DeepSeek)
- 包管理: pnpm monorepo(Turborepo)
- 前端框架: React 18 + Next.js 14(控制台部分)
- 图表: Recharts(后因 CDN 问题切换为 SVG mock)
- 沙箱: Babel standalone 浏览器端编译
引擎1:意图补全 — 自然语言 → 结构化 JSON
引擎1 是整个系统的“翻译层”。用户输入一句“暗黑风格数据看板:2 个 KPI 和 1 个折线图”,DeepSeek 需要输出一个严格符合 Schema 的 TaskJSON。听起来简单,实际坑却不少。
核心挑战:让 LLM 输出严格格式的 JSON
最初以为添加 response_format: { type: 'json_object' } 即可解决。踩坑之后发现,事情远没那么简单。
第一个坑:DeepSeek 会自由发挥字段名。Prompt 中写的是 components,它可能输出 comps 或 widgets。解决方案很直接:在 System Prompt 中给出完整的 JSON 示例结构,每个字段都标注得清晰明了。
第二个坑:Props 格式最容易出错。我需要的是 "props": [{ "name": "title", "value": "销售额" }] 这种数组格式,但 DeepSeek 经常输出 "props": { "title": "销售额" } 这种对象。为了纠正这一习惯,Prompt 中加入了 3 个反例才奏效:
错误 ❌:{ "title": "xxx" }正确 ✅:[{ "name": "title", "value": "xxx" }]
第三个坑:中文乱码。这是最隐蔽的问题。originalIntent 字段要求复制用户原始输入,但 API 返回的总是 ???????。尝试了多种方法后,最终发现问题的根源不是 DeepSeek,而是 PowerShell 的 Invoke-RestMethod 默认使用 ASCII 编码发送 body。改为 UTF-8 字节数组后解决:
$bytes = [System.Text.Encoding]::UTF8.GetBytes($jsonBody)Invoke-WebRequest -Body $bytes -ContentType "application/json; charset=utf-8"
仅此一个问题就耗费了我近 1 小时。
架构要点
- 使用 Zod 对 DeepSeek 的输出进行运行时校验,校验失败时向前端返回详细错误信息
originalIntent不依赖 LLM 输出,后端直接填入request.intentsummary交由 LLM 自行总结,效果优于人工硬写
引擎2:代码生成 — JSON → React 组件
引擎2 接收引擎1 生成的 TaskJSON,负责生成完整的 React 代码。
Prompt 设计
Prompt Builder 是整个引擎2 的核心。它需要拼接的内容包括:完整的 TaskJSON(组件列表、布局、主题)、组件库的元数据(包含哪些组件、每个组件的 Props 是什么),以及生成约束(框架、样式方案、禁止项)。
首次生成的代码质量很差。排查后发现问题出在 Prompt 中的 TaskJSON 中文被“吃掉”——根源是模板字符串拼接。改用 .join("n") 拼接字符串数组后解决。
生成的代码长什么样
style={{ backgroundColor: theme.backgroundColor, minHeight: '100vh', padding: 24 }}> style={{ display: 'grid', gridTemplateColumns: 'repeat(12, 1fr)', gap: 16 }}> style={{ gridColumn: 'span 6' }}>function Dashboard() {var theme = { mode: "dark", primaryColor: "#6366f1", ... };return (
踩坑:TypeScript 类型注解残留
DeepSeek 生成的代码经常包含 : React.FC 这类 TypeScript 注解。浏览器中的 Babel standalone 无法识别,直接报错。解决方案是在 sandbox controller 中添加一个 cleanCode() 函数,通过正则表达式删除所有类型注解和 import 语句。
引擎3:沙箱预览 — 代码 → 浏览器渲染
这是最令人头疼的部分。目标是让生成的代码能够在浏览器中真正运行。
踩坑1:Recharts CDN 死活加载不了
最初使用 unpkg.com/recharts 的 UMD 包,浏览器报 Cannot read properties of undefined (reading 'oneOfType')。换了 3 个 CDN 版本都无效,最终发现是 Recharts 内部依赖 react-smooth 在 UMD 构建时存在 bug。
最终方案:放弃 Recharts CDN,改用纯 SVG 来 mock 图表。虽然是占位图表,但渲染效果出乎意料地好——折线图包含真实的数据点、填充区域和网格线,柱状图带有渐变色的柱子。这部分代码不足 200 行。
踩坑2:file:// 协议无法加载 CDN
保存的 HTML 文件直接双击打开时,CDN 脚本全部报 CORS 错误。必须通过 HTTP 服务器来 serve。最终使用 npx serve 启动本地服务器解决。
踩坑3:Babel standalone 的 JSX 作用域
中定义的变量(如 mock 组件)无法在用户代码中访问,因为 Babel 编译 JSX 时会进行作用域提升。解决方法是将全局组件挂载到 window 对象上(例如 window.KPICard),这样 Babel 编译后就能正确引用了。
最终成果
经过三天开发和两天调试,最终实现了如下效果:
输入:"暗黑风格:2 个 KPI(总销售额、订单数),1 个折线图"↓引擎1 (3-6秒) → TaskJSON↓引擎2 (5-8秒) → React 代码↓浏览器渲染 → 暗黑背景 + KPI 卡片 + SVG 折线图
未完成的部分
- 引擎3(视觉反馈):计划使用 Puppeteer 截图配合千问 VL 分析,形成设计审查闭环——这部分尚未实现
- 真实数据绑定:目前 KPI 值仍为 0,需要接入 API 或 mock 数据源
- 组件库完善:目前仅有 KPICard 加 4 种图表,目标扩展到 20 个以上组件
最深的感悟
Prompt 工程不在于写提示词,而在于设计协议。需要把 Prompt 当作 API 文档来写——明确的输入输出格式、边界情况处理、错误示例,缺一不可。
AI 编码的本质是“翻译”而非“创造”。LLM 擅长将结构化数据转换为代码,但生产级质量需要无数细节打磨。它更像一个速度极快的初级工程师,而非架构师。
前后端全栈的 AI 应用,真正的瓶颈在基础设施。沙箱执行、类型校验、路由注册、CORS、编码问题——这些“杂活”占据了 70% 的开发时间。
一个人做 AI 工程,最大的挑战不是技术,而是保持动力。技术链路跑通后的兴奋感会迅速消退,因为接下来是无穷无尽的细节优化。
代码仓库
github.com/VaneBlien/A…
