AI Coding 全流程实战:从需求到上线,我用 AI 开发了一个 NPM 包
本文以一个真实项目 md-wx(微信公众号 Markdown 渲染组件)为例,完整记录从需求文档到任务拆分再到项目规则约束 AI 的全流程。每个阶段都附带真实 AI 提示词和代码片段,你可以直接照着做。
背景:为什么要用 AI 开发一个 NPM 包
微信公众号编辑器不支持直接粘贴 Markdown,开发者写完文章后要手动调格式,代码块没有高亮,引用块没有样式边框。市面上的排版工具要么是 Web 应用(不能集成到自己的工具链),要么样式不可定制。
md-wx 要解决的问题是:输入 Markdown 字符串,输出带内联样式的 HTML,直接粘贴到公众号编辑器就能用。以 NPM 包形式发布,支持自定义主题。
整个项目用 AI Coding 的方式完成。下面按开发顺序,拆成五个阶段来讲。
一、需求文档:让 AI 从模糊到清晰
模糊需求阶段:AI 头脑风暴
我的初始需求只有一句话:"把 Markdown 转成公众号能用的格式"。不确定该支持哪些语法、要不要主题系统、API 怎么设计。这时候需要 AI 帮我拓展思路。
给 AI 的提示词:
我想开发一个 NPM 包 md-wx,把 Markdown 转成微信公众号编辑器能识别的 HTML。
请从以下维度帮我分析需求:
1. 核心功能:应该支持哪些 Markdown 语法?公众号编辑器对 HTML 有哪些限制?
2. 用户界面:作为 NPM 包,需要提供 React 组件还是纯函数 API?
3. 可配置性:是否需要支持自定义主题?主题系统怎么设计?
4. 边界情况:有哪些 Markdown 语法在公众号里会渲染异常?
请列出功能清单和优先级建议。
AI 返回的分析帮我收敛出一份需求清单:
| 优先级 | 功能 | 说明 |
|---|---|---|
| P0 | Markdown 解析 | 支持 md 标准语法,基于 markdown-it |
| P0 | 内联样式输出 | 所有样式写成 inline style,不依赖外部 CSS |
| P1 | 主题系统 | 内置 3 套主题(默认/暗色/优雅),支持自定义 |
| P1 | React 组件 | 提供 <MdwxRenderer /> 组件,支持 props 传参 |
| P2 | CLI 工具 | 命令行直接转文件,npx md-wx input.md |
知识点:PRD(Product Requirements Document) PRD 是产品需求文档,由产品经理编写,描述产品要做什么、为谁做、每个功能的具体规则。一份合格的 PRD 包含:背景与目标、用户画像、功能列表、交互逻辑、验收标准。在 AI Coding 流程中,PRD 是喂给 AI 的第一份"规格说明",直接决定后续生成代码的方向。
知识点:MVP(Minimum Viable Product) MVP 是最小可行产品——用最少的开发成本做出能跑通核心流程的版本。上表中 P0 功能就是 MVP 的边界:能解析 Markdown、能输出带内联样式的 HTML,就算可用。P1 和 P2 是后续迭代。
清晰需求阶段:AI 完善文档
需求方向确定后,让 AI 把散落的想法整理成结构化文档:
根据上面的需求分析,请帮我输出一份完整的 PRD 文档,包含:
- 项目背景与目标
- 功能清单(按 P0/P1/P2 分级)
- 技术约束(公众号编辑器对 HTML 的限制)
- API 设计草案
- 验收标准
项目技术栈:React 18 + TypeScript 5
AI 输出的 PRD 中,有一段关键技术约束我之前没注意到:
微信公众号编辑器会过滤
<style>标签和外部 CSS 文件,只保留元素的style属性。因此所有样式必须以内联方式写入 HTML 标签。此外,公众号不支持<script>,不支持外部图片引用(需要转成 base64 或使用公众号图床)。
这条约束直接影响了后面的技术方案设计。
二、技术架构设计:AI 调研 + 方案选型
用 Tavily 补充实时信息
大模型的训练数据有截止日期,问它"markdown-it 最新版本是多少"可能得到过时答案。这时候用 Tavily 搜索 API 先抓取最新信息,再让 AI 基于这些信息做判断。
知识点:Tavily Tavily 是专为大模型和 AI Agent 打造的搜索 API。和普通搜索引擎不同,它的输出是结构化的网页检索内容,可以直接喂给大模型补充实时网络信息。在技术选型时,用 Tavily 先搜索"markdown-it vs marked 2026 对比"拿到的数据,再让 AI 分析,比直接问 AI 可靠得多。
让 AI 做技术选型
给 AI 的提示词:
我需要为 md-wx 选择一个 Markdown 解析库。请对比以下两个方案:
方案 A:markdown-it
方案 B:marked
对比维度:
1. 解析速度
2. 插件生态(是否支持自定义渲染规则,这关系到内联样式注入)
3. TypeScript 支持
4. 社区活跃度
我的核心需求是:解析 Markdown 后,能在渲染阶段给每个 HTML 标签注入内联 style 属性。
AI 给出的分析结论是 markdown-it 更合适——它的渲染规则是可配置的,可以通过 renderer.rules 自定义每种标签的输出,这正好满足"注入内联样式"的需求。而 marked 的自定义能力相对弱,改渲染输出需要 hack 源码。
架构设计产出
最终确认的架构如下:
md-wx/
├── src/
│ ├── parser/ # Markdown 解析层
│ │ └── index.ts # 初始化 markdown-it,配置渲染规则
│ ├── themes/ # 主题样式层
│ │ ├── default.ts # 默认主题
│ │ ├── dark.ts # 暗色主题
│ │ └── elegant.ts # 优雅主题
│ ├── renderer/ # 渲染层
│ │ └── index.ts # 把 token 转成带内联样式的 HTML
│ ├── components/ # React 组件层
│ │ └── MdwxRenderer.tsx
│ └── index.ts # 包入口
├── cli/
│ └── index.ts # CLI 工具入口
├── AGENTS.md # 项目规则文件
├── tasks.md # 任务清单
└── package.json
数据流很清晰:
Markdown 字符串 → markdown-it 解析 → token 流 → renderer 注入内联样式 → HTML 字符串 → React 组件 / CLI 输出
三、原型设计:AI 生成可交互的 MVP
技术方案确定后,先让 AI 快速产出一个可运行的原型,验证"Markdown → 内联样式 HTML"这条路走得通。
给 AI 的提示词:
基于以下架构设计,生成一个可运行的原型代码。
要求:
1. 用 markdown-it 解析一段示例 Markdown
2. 自定义 renderer.rules,给每个标签注入内联 style
3. 暂时硬编码一套样式,不需要主题系统
4. 输出一个 HTML 文件,双击能在浏览器打开,样式不丢失
示例 Markdown:
# 标题测试
**加粗文本**
> 引用块
- 列表项1
- 列表项2
```js
console.log('hello')
AI 生成的核心代码片段:
```typescript
import MarkdownIt from 'markdown-it'
import { themeDefault } from './themes/default'
const md = new MarkdownIt()
// 自定义渲染规则:给每个标签注入内联样式
const originalRender = md.renderer.rules
const rules = ['heading_open', 'paragraph_open', 'blockquote_open', 'code_block', 'list_item_open']
rules.forEach(rule => {
md.renderer.rules[rule] = (tokens, idx, options, env, self) => {
const token = tokens[idx]
// 根据 token 类型和主题配置,注入对应的 inline style
const style = getStyleForToken(token, themeDefault)
if (style) {
token.attrSet('style', style)
}
return originalRender[rule]?.(tokens, idx, options, env, self) || self.renderToken(tokens, idx)
}
})
export function render(markdown: string): string {
return md.render(markdown)
}
主题配置文件 themes/default.ts:
import type { Theme } from './types'
export const themeDefault: Theme = {
h1: 'font-size: 24px; font-weight: bold; color: #333; margin: 20px 0 16px;',
h2: 'font-size: 20px; font-weight: bold; color: #333; margin: 18px 0 14px;',
p: 'font-size: 16px; line-height: 1.8; color: #555; margin: 10px 0;',
blockquote: 'border-left: 4px solid #ddd; padding: 8px 12px; color: #888; background: #f9f9f9; margin: 12px 0;',
code: 'background: #f6f8fa; padding: 2px 6px; border-radius: 3px; font-size: 14px; font-family: monospace;',
pre: 'background: #1e1e1e; color: #d4d4d4; padding: 16px; border-radius: 6px; overflow-x: auto;',
ul: 'padding-left: 24px; margin: 10px 0;',
li: 'font-size: 16px; line-height: 1.8; color: #555; margin: 4px 0;',
strong: 'font-weight: bold; color: #333;',
}
原型跑通后,确认技术路线没问题:markdown-it 的 renderer.rules 确实能逐个标签注入内联样式,输出结果直接粘贴到公众号编辑器,样式不丢失。
四、任务拆分:像管理者一样拆任务
原型验证通过后,进入正式开发。这一步最容易犯的错误是把整个项目丢给 AI 一次性完成。
为什么不能给 AI 分配大任务
原因一:上下文窗口限制。
知识点:上下文窗口(Context Window) 大模型的上下文窗口指它在一次对话中能"记住"的信息总量,单位是 token(大约 1 个汉字 ≈ 1-2 个 token)。现代模型窗口从早期的 4K 扩展到 128K 甚至更长,但在处理长任务时,模型对早期输入的关注度会下降。学术界把这种现象称为 "Lost in the Middle"——中间位置的信息最容易被遗忘。
具体表现:你要求用函数式组件,写到第三个文件时 AI 可能开始写 class 组件;你要求 TypeScript,写到 utils 时它可能开始用 JavaScript。早期约定的规则在长任务中逐渐被遗忘。
原因二:细节处理能力下降。
一次性处理的内容越多,AI 对每个细节的关注度越低。代码的命名一致性、错误处理完整性、边界条件覆盖度都会打折扣。
tasks.md:任务拆分的产出
把项目拆成 5 个独立任务,每个有明确的输入和输出,写进 tasks.md:
# md-wx 任务清单
## Task 1: 项目初始化
- **状态**: 待办
- **输入**: 技术栈选型(React 18 + TypeScript 5 + markdown-it + Vite)
- **输出**: 可运行的空项目脚手架,包含 package.json、tsconfig、vite.config
- **验收**: `npm run dev` 能启动,`npm run build` 能打包
## Task 2: Markdown 解析模块
- **状态**: 待办
- **输入**: Markdown 字符串
- **输出**: markdown-it 实例,配置好 renderer.rules 钩子
- **验收**: 输入示例 Markdown,能输出标准 HTML(此时不带内联样式)
- **依赖**: Task 1
## Task 3: 主题样式系统
- **状态**: 待办
- **输入**: 主题配置对象(Theme 类型)
- **输出**: getStyleForToken() 函数,根据 token 类型返回内联样式字符串
- **验收**: 切换不同主题,输出的内联样式对应变化
- **依赖**: Task 2
## Task 4: React 渲染组件
- **状态**: 待办
- **输入**: Markdown 字符串 + 主题配置(可选)
- **输出**: <MdwxRenderer markdown="# hello" theme={customTheme} /> 组件
- **验收**: 组件渲染出的 HTML 带内联样式,可直接粘贴到公众号
- **依赖**: Task 2, Task 3
## Task 5: CLI 工具
- **状态**: 待办
- **输入**: Markdown 文件路径
- **输出**: 转换后的 HTML 文件
- **验收**: `npx md-wx input.md` 生成 output.html,样式完整
- **依赖**: Task 2, Task 3
知识点:tasks.md tasks.md 是任务清单文件,记录项目拆分后的所有任务及状态(待办/进行中/已完成)。它同时服务两个角色:给 AI 看——明确当前该做什么、做到什么程度算完成;给人看——追踪项目进度。类似于传统项目管理里的 Jira 看板,但更轻量,直接放在项目根目录。
拆分原则
每个任务遵循三个原则:
- 独立性:完成一个就能看到可见结果,不需要等全部做完
- 可验收:有明确的验收标准,不是"写完就行"
- 小粒度:一个任务对应一个模块,不超过 200 行代码
拆完任务后,先审查一遍依赖链是否合理,再让 AI 逐个执行。每完成一个就检查,发现问题立刻修正,而不是等五个任务全做完再统一检查。
五、项目规则:用 AGENTS.md 管住 AI
任务拆好了,还有一个问题:怎么保证 AI 执行每个任务时不自由发挥,而是严格遵守项目约定?
答案是项目根目录放一个 AGENTS.md 文件,AI 在开始工作前会自动读取。
知识点:AGENTS.md AGENTS.md 是放在项目根目录的规则文件,AI 编程助手在开始工作前会自动读取。类似给新员工的"项目手册"——新人入职先读手册再干活。区别在于,新员工可能读完就忘,AI 每次对话都重新加载规则。在 Cursor 里叫
.cursorrules,在 Windsurf 里叫.windsurfrules,作用相同。
md-wx 的 AGENTS.md
这是实际使用的项目规则文件:
# md-wx 项目规则
## 技术栈
- React 18 + TypeScript 5
- 构建工具: Vite
- Markdown 解析: markdown-it
- 不引入其他 Markdown 解析库,不引入 CSS-in-JS 方案
- 样式方案: 内联 style 属性(公众号编辑器限制,不能用外部 CSS)
## 代码风格
- 使用 2 空格缩进
- 字符串使用单引号
- 组件使用函数式组件 + Hooks,不使用 class 组件
- 命名: 组件用 PascalCase,函数用 camelCase,类型用 PascalCase
- 所有导出函数必须有 TypeScript 类型注解
## 任务执行边界
- 每次只执行 tasks.md 中标记为"待办"的下一个任务
- 不要修改当前任务范围之外的文件
- 完成任务后停下来,在 tasks.md 中更新状态,等待确认再继续
- 遇到不确定的技术决策,列出 2 个备选方案的优缺点,等待选择,不要自行决定
## 目录结构约定
- src/parser/ → Markdown 解析逻辑
- src/themes/ → 主题配置
- src/renderer/ → 渲染逻辑
- src/components/ → React 组件
- cli/ → CLI 工具
- 不要在 src 下创建新的顶级目录
## 测试要求
- 每个模块至少覆盖 3 个测试用例(正常输入、空输入、边界输入)
- 测试文件放在同目录下,命名为 *.test.ts
四个维度的规则解析
1. 任务执行边界 —— 控制 AI 何时开始、何时停止。写明"每次只做一个任务,不要越界改其他模块"。防止 AI 在修一个 bug 时顺手重构了半个项目。
2. 代码风格 —— 确保生成代码统一。缩进、引号、组件写法、命名规范全部明确。没有这条,AI 可能第一个文件用单引号,第二个文件用双引号,第三个文件又混着用。
3. 技术栈遵循 —— 锁死技术选型。"不引入其他 Markdown 解析库,不引入 CSS-in-JS 方案"这条规则,防止 AI 自作主张引入 marked 或者 styled-components。
4. 沟通方式 —— 规定遇到不确定决策时不要自己拍板。列出备选方案让你选,避免 AI 在不知情的情况下做了一个影响深远的架构决策。
实际执行效果
有了 tasks.md 和 AGENTS.md,我逐个执行任务,每个任务的提示词结构都是:
请执行 tasks.md 中的 Task N。
项目规则见 AGENTS.md,请严格遵守。
当前任务的验收标准是:[从 tasks.md 复制]
完成后请:
1. 更新 tasks.md 中该任务状态为"已完成"
2. 不要修改其他任务的文件
3. 等待我的确认后再执行下一个任务
以 Task 3(主题样式系统)为例,AI 生成的代码:
// src/themes/index.ts
import type { Theme } from './types'
import { themeDefault } from './default'
import { themeDark } from './dark'
import { themeElegant } from './elegant'
export const themes: Record<string, Theme> = {
default: themeDefault,
dark: themeDark,
elegant: themeElegant,
}
export function getStyleForToken(
token: { type: string; tag: string },
theme: Theme
): string | null {
// 根据 token 的 tag 名查主题配置
const tag = token.tag
if (tag && theme[tag as keyof Theme]) {
return theme[tag as keyof Theme] as string
}
return null
}
// src/themes/types.ts
export interface Theme {
h1?: string
h2?: string
h3?: string
p?: string
blockquote?: string
code?: string
pre?: string
ul?: string
ol?: string
li?: string
strong?: string
em?: string
[key: string]: string | undefined
}
AI 严格遵守了 AGENTS.md 的约定:用了 TypeScript 类型注解、函数式写法、单引号、2 空格缩进,没有越界修改 parser 或 renderer 目录的代码。完成后自动更新了 tasks.md 的状态,停下来等我确认。
五个任务全部执行完成后,项目结构完整,代码风格统一, npm run build 打包成功,发布到 NPM。
总结:五步工作流
需求文档(PRD)→ 技术架构设计 → 原型设计 → 任务拆分 → 项目规则 → 逐个执行
前三个阶段解决"做什么":需求文档定义功能和交互,技术架构定义技术方案,原型设计验证可行性。
后两个阶段解决"怎么管 AI":任务拆分确保 AI 一次只做一件小事,项目规则确保 AI 做每件事时都守规矩。
核心原则:把 AI 当成一个能力很强但需要明确指令的新员工来管理。 给它清晰的任务、明确的边界、一致的规则,它就能成为可靠的编码助手。反过来,模糊地描述需求然后期待魔法发生,大概率会收获一堆能跑但没法维护的代码。
这套流程不仅适用于 NPM 包开发,任何用 AI 编程助手做项目的场景都可以套用:先写需求,再定架构,验证原型,拆任务,立规矩,最后逐个执行。