这篇文章记录“小红书笔记剪藏 / RedNote Clipper”浏览器插件从需求沟通、规格拆解、实现迭代到发布收口的过程。比起展示一个小工具本身,我更想沉淀的是这背后的开发方式:不是让 AI 随便写,而是用 Specs 驱动 AI 协作,把一个想法推进成可以发布的 MVP。
浏览器插件地址:
开场
过去一段时间,我做了一个简单的浏览器扩展,叫“小红书笔记剪藏 / RedNote Clipper”。
它解决的是一个很具体的自用需求:我在小红书上看到一篇有价值的笔记时,希望可以顺手把它保存到本地,而不是只丢进站内收藏夹。站内收藏夹的问题是难以管理、阅读体验差,也不方便继续做 AI 加工。导出的内容最好不是一张截图,也不是一段复制出来的文本,而是一个可以继续整理、搜索、归档的本地资料包。
当前版本支持导出当前打开的单篇笔记,保存标题、正文、作者、发布时间、标签、互动统计、图片或视频媒体,也支持手动选择评论一起导出。导出结果包含 note.json、note.md、note.html 和 media/ 目录。
从结果看,它像是一个小工具。但对我来说,这个项目更值得记录的是开发方式:它不是让 AI 随便写,也不是靠一句 prompt 直接生成一个可发布产品,而是一个 Specs 驱动的 AI 协作开发过程。
为什么不是纯 Vibe Coding
现在很多人会把 AI 辅助开发概括成 Vibe Coding。这个词很容易让人误解,好像开发者只要描述一个大概感觉,剩下的就交给 AI 自由发挥。
这次项目不是这样。需求不是边写边漂移出来的,方向也不是靠实现过程里的灵感随机调整出来的。相反,在正式实现之前,我和 AI 反复沟通,把需求、边界、非目标、数据模型、任务拆解和验收方式都沉淀到了 specs 里。
真正的工作方式更接近:
- 先把模糊想法规格化。
- 再按 specs 推进最小闭环。
- 实现中遇到真实问题后,回到设计、测试和文档里收口。
- 最后把问题复盘成下一个项目可以复用的方法论。
所以,如果一定要给这个过程起一个名字,我更愿意叫它:
Specs 驱动的 AI 协作开发。
AI 参与了需求澄清、方案拆解、代码实现、测试补充、文档维护和发布材料整理,但项目的控制系统不是 AI 的一次性生成能力,而是持续更新的规格、边界和验收标准。
先把需求规格化
项目最早的需求其实很朴素:浏览小红书时,看到一篇有价值的笔记,可以轻量地保存到本地。
这个需求如果不规格化,很容易发散成一个过大的产品:
- 要不要批量导出收藏夹?
- 要不要自动抓取作者主页?
- 要不要做云端同步?
- 要不要做在线管理后台?
- 要不要把评论全量展开?
- 要不要做成知识库系统?
这些方向都可以想象,但它们不是 V1 该做的事。
所以 specs 里先确定了产品目标和非目标。V1 只做“当前打开的单篇笔记”,只在用户明确触发时导出,只保存到本地,不做批量采集、不做无人值守、不做云端同步,也不主动抓取用户没有浏览到的内容。
这个边界非常重要。它让项目从一开始就不是“采集效率最大化”的工具,而是一个“浏览时顺手收藏并导出”的个人整理工具。
同时,导出模型也被提前定义清楚。V1 的导出包包含:
<export-root>/
note.json
note.md
note.html
media/
其中:
note.json负责结构化保真。note.md负责直接阅读、检索和导入知识库。note.html负责离线打开和更好的阅读呈现。media/负责保存图片、视频、封面和评论图片。
这一步的价值是,后续写代码时不需要反复争论“到底要导出什么”。实现可以调整,但目标产物是明确的。
V1 边界比功能清单更重要
这个项目最重要的工程判断之一,是不断把边界写清楚。
V1 支持:
- 当前单篇笔记导出。
- 用户主动点击触发。
- 页面内轻量悬浮入口。
- 手动选择当前页面已出现的评论。
- 本地生成
note.json、note.md、note.html和媒体文件。 - 图片、视频、视频封面、Live 图封面和评论图片的相对路径引用。
V1 不支持:
- 批量导出多个笔记。
- 自动遍历作者主页、搜索页或列表页。
- 自动展开全部评论。
- 后台持续采集。
- 云端同步。
- 绕过平台没有暴露的数据边界。
- 承诺所有媒体都能拿到理论最高画质。
边界不是为了少做功能,而是为了保证每个已做的功能能形成闭环。
比如评论选择。如果目标是“把所有评论都保存下来”,实现会立刻变成自动展开、翻页、加载、排序、去重和风控问题。但 V1 的目标是“用户手动选择当前页面已出现的评论”。这个边界让交互、数据模型和导出逻辑都变得可控。
再比如媒体。项目不承诺突破平台没有稳定暴露的资源,只在页面状态、DOM 和用户真实浏览过程中可获得的范围内做稳定导出。这不是能力不足的托辞,而是产品边界的一部分。
按 Specs 实现主链路
规格明确之后,代码结构也比较自然地收敛成四层:
content:负责页面识别、悬浮面板、评论选择、笔记与评论的 DOM/状态抽取。bridge:负责 content script 和 background 之间的消息协议。background:负责导出任务编排、媒体下载、评论图片缓存命中和导出阶段增强。core:负责纯逻辑,包括导出模型、命名、Markdown/HTML 渲染、媒体 URL 归一化。
核心数据流是:
- 内容脚本识别当前页面是否为单篇笔记页。
- 用户点击导出后,内容脚本读取页面状态和 DOM,生成
ExtractedNotePayload。 - 内容脚本通过消息协议把 payload 发给 background。
- background 补充评论图片缓存命中,再调用导出组装逻辑生成文本文件和媒体任务。
- background 使用浏览器下载能力把
note.json、note.md、note.html和媒体文件写入本地。 - 导出阶段校验实际落盘路径,确保文档引用和本地文件一致。
这里 AI 的价值很明显。它可以快速根据 specs 拆模块、生成候选实现、补齐测试、整理文档。但它不能替代最核心的判断:哪些逻辑应该放在 content,哪些应该放在 background,哪些应该沉到 core 作为纯函数。
如果没有 specs,AI 很容易把功能写成“哪里方便写哪里”。有了 specs,协作方式就变成了:围绕明确边界,让 AI 帮你更快到达可验证的实现。
问题驱动的迭代
这个项目最值得复盘的地方,不是主链路第一次跑通,而是后续真实问题如何推动设计收口。
很多问题一开始看起来只是 bug,但复盘后会发现,它们其实暴露的是某个边界、模型或验收方式还不够清楚。
导出产物本身就是最高优先级的验收对象
当项目进入“能导出”之后,问题重心很快从“按钮能不能点”转向“导出的结果是否可信”。
真实反馈里出现过这样的情况:
media/里实际保存的是image-01.webp。- 但
note.json或note.md里引用的是image-01.jpg。
这不是小显示问题,而是导出包的一致性问题。只要路径不一致,用户就无法把这个资料包稳定放进 Obsidian、静态站点或其他知识库系统里。
这件事推动了一个重要原则:
对导出型产品来说,最终产物本身就是最高优先级的验收对象。
所以后续不再只看“有没有触发下载”,而是要检查 note.json、note.md、note.html 和 media/ 是否互相一致。
对应实现也做了收口:图片类媒体先转成 data: URL 再写入声明路径,普通视频和 Live 动态视频保持直接下载,但必须校验浏览器实际落盘路径,避免文档里写的是一套路径,文件系统里保存的是另一套路径。
评论抽取要优先保证语义干净
评论区的问题也很典型。
真实页面里,评论文本可能混入“展开 / 收起”等交互文案;作者昵称旁可能混入“作者”徽标;楼中楼评论的时间可能和正文、地区、回复提示混在同一个区域;图片里还可能有表情、贴纸和装饰图。
如果只是粗暴读取可见文本和图片,短期看能拿到内容,长期看会污染结构化数据。
这推动了几个实现原则:
- 评论正文要过滤交互文案。
- 作者名要去掉徽标、标签和装饰节点。
- 时间提取不能靠宽松全文扫描,要优先识别明确的时间节点和短时间格式。
- 评论图片要区分真正配图和表情贴纸。
- 用户手选评论的导出顺序要尊重点击顺序,而不是强行恢复 DOM 顺序。
这些问题修完以后,代码只是表层变化,更重要的是方法论变了:评论区不是一个纯文本容器,而是一个视觉结构复杂、语义边界容易污染的区域。抽取逻辑必须优先保证字段语义干净。
正文格式也是内容的一部分
正文提取也经历了类似收口。
一开始如果只关心“文本有没有拿到”,把空白压成单个空格似乎也能接受。但导出结果要直接阅读时,段落和换行本身就是内容的一部分。
真实导出里出现过正文被压成一整段的问题。修复之后,正文提取优先从 DOM 获取,保留块级元素和 <br> 带来的换行结构;结构化状态里的 desc 只作为兜底。
这件事沉淀出的原则是:
用于归档阅读的内容,不只要文本完整,也要保留基本阅读结构。
后续 note.html 的设计也沿着这个原则展开:它不是复刻小红书网页,而是一个静态、离线、适合归档阅读的文档页。
评论图片高清不是无条件承诺
评论图片是另一个典型边界问题。
当前比较稳定的现实是:页面上默认可见的通常是预览图;如果用户打开过评论大图,浏览过程中可以缓存到更高清的资源。于是正式链路采用的是:
- 默认导出页面可见缩略图。
- 用户浏览过评论大图时,通过本地缓存优先导出高清图。
- 导出结果中保留
resolutionSource,说明图片来源。
这里没有去做“根据缩略图 URL 猜原图”的激进方案。因为这类方案可能短期有效,但不稳定,也容易偏离产品边界。
这个问题沉淀出的原则是:
当平台没有稳定暴露某类资源时,产品应该明确边界,而不是用不可靠猜测伪装成能力。
真实环境验证要克制,但不能完全缺席
这类浏览器扩展最自然的实现方式,其实是让 AI 直接打开真实页面:观察 DOM、读取页面状态、注入扩展、触发导出,再根据真实结果不断修正实现。
如果没有平台约束,这会是效率最高的闭环。因为页面结构、懒加载、SPA 路由、媒体字段、评论区 DOM 都不是靠静态想象能完整推出来的,真实页面反馈越快,实现越不容易偏。
但小红书这类站点有风控约束,自动化访问、重复打开、脚本请求、批量验证都可能偏离正常用户行为。项目级规则也明确要求,默认不要让 Agent 自动请求、抓取或访问真实小红书页面进行验证。
所以验证策略被拆成几层:
- 构建验证:确认扩展产物能生成。
- 本地逻辑验证:用离线样本、测试夹具和导出模型验证核心逻辑。
- 真实页注入验证:只在用户明确提供真实链接并主动配合时进行,且优先做诊断,不做批量访问。
这也推动了 verify:live 这类脚本变成工程资产。它不是用来“自动抓页面”的,而是用来诊断扩展是否加载、权限是否正常、content script 是否执行、页面识别和 UI 挂载是否成功。
这个边界对后续项目很有参考价值:真实环境反馈很重要,但不能把所有验证都压到真实站点上。离线样本、导出目录和可复跑测试应该承担更多回归职责;真实页面验证则应该尽量克制、可诊断、由用户明确触发。
缺少自动真实验证时,调研可以帮助破局
真实页面自动验证受限之后,AI 在实现过程中更容易绕圈。
有些卡点并不是代码本身复杂,而是缺少足够真实的外部事实。例如:页面状态里到底有哪些媒体字段,图片 URL 的不同来源有什么差异,Live 图动态资源通常暴露在哪里,评论图片高清资源是否有稳定入口。
如果这时只让 AI 在本地代码里反复猜,很容易出现两种问题:
- 对不存在的字段写过度兼容逻辑。
- 对真实页面已有的模式视而不见,迟迟找不到正确抽取入口。
这个阶段,网络搜索和公开资料调研就变得有价值。它不能替代真实样本,也不能直接变成最终方案,但可以提供参考方向:别人如何理解页面状态、哪些字段更常见、哪些资源链接只是缩略图、哪些所谓“原图方案”并不稳定。
项目里的图片公开方案调研就是这个作用。它没有直接决定实现,但帮助我们把问题分成几类:
- 笔记主图可以优先关注页面状态中的
imageList。 - DOM 中的可见图片更适合作为回退,而不是主来源。
- 评论图片高清资源不能只靠缩略图 URL 猜测。
- Live 图只有在状态字段暴露动态资源时才能稳定导出。
这件事沉淀出的原则是:
当真实自动验证受限时,不要让 AI 在本地闭门造车;应该用公开资料、用户样本、离线夹具和最小真实诊断共同缩小搜索空间。
调研不是为了找一个可以照抄的答案,而是为了减少无效试错,让实现重新回到可验证的问题上。
把问题沉淀成方法论
这个项目里,问题修复不是终点。更重要的是把问题放回项目资产里。
大致有几种沉淀方式:
- 已完成并稳定的能力,写回验收清单。
- 已确认但未彻底解决的问题,进入未解决问题清单。
- 与某次迭代强相关的过程经验,写进 journal。
- 影响产品边界或技术边界的内容,更新 specs 或 architecture。
- 真实出现过的问题,尽量补成离线回归测试。
- 面向上架和传播的表达,进入 release 文档。
比如媒体路径一致性问题,最后不只是修了下载逻辑,还被写进了任务清单和验收要求。
比如 Live 图动态资源,如果页面状态里有 imageList[*].livePhoto + stream.h264,当前链路可以稳定导出;如果只剩 DOM 里的 blob:,就不能假装已经解决,而是进入未解决问题清单,等待新的样本和单独设计。
这种做法的好处是,项目不会只依赖开发者记忆。每一个真实问题都尽量转化为某种可复用资产:测试、文档、设计约束或后续任务。
AI 的实际价值
经过这个项目,我对 AI 协作开发的感受更清楚了一点。
AI 最有价值的地方不是“替我决定做什么”,而是:
- 把模糊需求快速拆成文档结构。
- 根据 specs 生成多个实现候选。
- 在已有代码风格下做局部实现。
- 帮忙检查上下游影响。
- 把真实问题转成测试样例。
- 把过程经验整理成 journal、release 或 specs。
- 在长链路任务里保持上下文连续。
但 AI 不应该替代这些事情:
- 产品边界判断。
- 风险边界判断。
- 哪些能力进入 V1,哪些明确不做。
- 某个问题是抽取层、导出层、缓存层还是验证层的问题。
- 最终产物是否符合用户真实使用场景。
换句话说,AI 可以让实现速度更快,但不能让工程判断变得可有可无。
如果没有 specs,AI 越快,项目越容易发散。如果有 specs,AI 的速度才会变成生产力,而不是噪声。
从功能到发布
另一个容易被低估的阶段,是从“功能能用”到“可以发布”。
这个项目后期不只是继续写代码,还补了:
note.html静态阅读页。- Chrome / Edge 商店文案。
- 隐私政策草稿。
- 审核答复模板。
- 发布说明模板。
- 素材验收清单。
- 小红书宣传笔记策略和正文草稿。
这些工作看起来不如写功能有技术感,但它们是产品化的一部分。
尤其是隐私和能力边界表达,必须和真实实现一致。文案里不能宣称批量导出、自动采集、全量备份收藏夹,也不能暗示绕过限制。能说的就是当前真实支持的能力:用户主动导出当前单篇笔记,结果保存在本地,不上传到开发者服务器。
这也再次说明 specs 的价值。发布材料不是凭空写出来的,而是从产品边界、数据模型和实现能力里自然长出来的。
可复用流程
如果把这次经验抽象成下一个项目可以复用的流程,我会这样总结:
-
需求沟通 先和 AI 反复问清楚真实场景、用户动作、输入输出和非目标。
-
Specs 固化 把需求、设计、任务、验收、风险分别写下来,不让实现阶段承担所有决策。
-
最小闭环 只实现能证明价值的主链路,避免一开始做重型系统。
-
问题分层 遇到 bug 先判断属于抽取、导出、缓存、渲染、下载、验证还是发布表达问题。
-
离线回归 真实出现过的问题,尽量转成离线样本和可复跑测试。
-
文档回写 修复后更新 specs、journal、验收清单或未解决问题清单,避免经验只留在聊天记录里。
-
发布收口 功能稳定后,同步收口隐私、商店文案、使用说明、素材和审核答复。
这个流程的核心不是文档越多越好,而是让每份文档承担明确职责。Specs 控制方向,tests 控制回归,journal 沉淀经验,release 承接发布。
结尾
这次做“小红书笔记剪藏”,我最大的收获不是“AI 帮我写了多少代码”,而是更清楚地看到了 AI 协作开发的控制点。
AI 可以加快沟通、拆解、实现和复盘,但前提是项目有清晰的规格、边界和验收对象。否则,所谓 Vibe Coding 很容易变成“功能不断冒出来,但系统越来越难收口”。
对我来说,更可持续的方式是 Specs 驱动:
先把需求说清楚,再让 AI 加速实现;实现中暴露的问题,继续反向沉淀成设计、测试、文档和方法论。
这不是最炫的 AI 编程方式,但它更接近一个真实项目从想法走到可发布 MVP 的过程。