概述

约 1000 元/月的 AI 订阅服务,1 天完成了过去需要数天的全栈重构——不仅迁移了技术栈,还从零新增了管理后台,同时自动产出了 2700 行设计文档。12 个 OpenSpec 提案,2700 行设计文档,1400 行业务代码——一次关于 AI Coding 流程控制的真实记录。个人网站地址:http://dreamyouxi.com/

先说结果

核心收益

重构前后对比:

重构前(C++ LiteHttp)重构后(FastAPI)
更新一张卡片手改 HTML 源码 → 重新编译 C++ → 部署打开后台管理界面 → 填表提交,完成
管理后台没有,所有操作靠改代码完整的 Web 管理面板:增删改查、拖拽排序、图片裁剪上传、卡片置顶
首页加载5.2 MB0.55 MB(降幅 89%)
维护门槛只有作者本人能维护(C++ 手写 HTTP 服务器)Python + FastAPI,任何后端开发者都能接手
内容管理硬编码在 HTML 中,改一个字要碰源码JSON 数据驱动 + 模板渲染,内容和代码完全分离
移动端未适配三断点响应式(手机/平板/桌面)

其中管理后台是从 0 到 1 的新增——原来的 LiteHttp 没有任何管理界面,每次更新内容都要直接改 HTML 文件再重新部署。现在有了一个 598 行的完整管理面板(admin.html),支持卡片增删改查、SortableJS 拖拽排序、Cropper.js 图片裁剪、一键置顶、分类筛选。维护成本从"找到源码 → 编辑 → 编译 → 部署"变成了"打开浏览器 → 点几下"。

产出明细

指标数据
总耗时1 天(含 Claude Code 环境搭建 + 全部前后端重构)
AI 协作时间约 5 小时高密度 Coding
提案数12 个完整 OpenSpec 提案
后端代码server.py 414 行(路由 + API + 中间件,一个文件搞定)
前端模板4 个 Jinja2 模板共 933 行(index 213 + admin 598 + about 66 + detail 56)
样式index.css 94 行(替代原来 298KB 的合并 CSS)
数据cards.json 800 行(73 张卡片 + 16 个详情页,从硬编码 HTML 迁移而来)
测试72 行 pytest 测试用例
设计文档71 个 OpenSpec artifacts,共 2700 行(proposal + design + specs + tasks)
月费Max 5 计划约 1000 元人民币,当天消耗约 60% 额度,未触发限流

一句话总结:1000 元/月的 AI 伙伴,1 天完成了过去需要数周的全栈重构——不仅迁移了技术栈,还从零新增了管理后台,同时自动产出了 2700 行设计文档。

一、背景:一个学生时代的技术追求,和它 11 年后的技术债

dreamyouxi.com 是我的个人作品展示网站,2015 年上线,至今运行 11 年。它的底层是一套完全自研的 C++ + C# + Lua 服务器——LiteHttp。这是我学生时代的作品,出于对底层技术的好奇和自我挑战,从 TCP socket 开始手写了整个 HTTP 服务器:协议解析、路由分发、静态文件服务、CGI 接口——每一层都是自己造的轮子。当时的目标很纯粹:想知道一个 Web 服务器到底是怎么工作的,所以从零写一个。这套东西确实扛住了时间的考验,运行了 11 年。但学生时代的代码终究是学生时代的代码,问题也积累了 11 年:

  • 维护成本极高:改一个小功能要重新编译 C++ 再部署,toolchain 都快找不到了
  • 技术栈完全孤立:C++ Web 服务器没有生态,所有中间件都是手写的,没有文档,只有我自己能维护
  • 数据全部硬编码:73 张卡片、16 个详情页的数据直接写死在 HTML 里,加一张卡片要手改两个 HTML 文件
  • 前端停留在 2015 年:298KB 的合并 CSS、17 个 JS 文件、Revolution Slider、fontello 527 个图标字体——大部分从未被使用

LiteHttp 完成了它的历史使命——它让我理解了 Web 的底层原理,也支撑了网站 11 年。但 2026 年,继续修补一个学生时代的 C++ 服务器已经不再合理。我决定彻底重构。

二、为什么选 Claude Code

市面上 AI Coding 工具不少,我选 Claude Code 的原因很具体:

1. 它是 CLI 工具,不是聊天框

Claude Code 运行在终端里,直接操作文件系统。它能读代码、写代码、跑命令、看输出,交互循环和人类开发者一样。不需要手动复制粘贴代码片段到聊天窗口,也不需要把 AI 的输出再手动粘回编辑器。

2. Opus 4.6 + Thinking 的推理能力

这次重构不是"写一个 TODO 应用"——它需要理解 11 年前的 C++ 代码结构、提取硬编码在 HTML 里的 73 张卡片数据、保持旧页面的路由兼容性、处理无扩展名文件的中间件逻辑。这些都要求模型有足够的上下文理解能力和推理链条。Opus 4.6 在这方面非常可靠。

3. CLAUDE.md 的项目感知

Claude Code 会读取项目根目录的 CLAUDE.md 文件作为持久化的项目指令。我在里面写清了目录结构、路由规则、安全约束、模板规范,之后每次对话 AI 都不需要重新了解项目背景。这一点在 12 个提案迭代过程中价值巨大——AI 始终知道"不要引入数据库""不要修改无扩展名文件""卡片 id 不得复用"。

三、OpenSpec:让 AI Coding 变得可控

问题:AI 写代码太快,人跟不上

直接让 AI "帮我重构网站"的结果通常是:它一口气改了 20 个文件,你看不过来,确认不了对错,出了问题也不知道从哪里回退。AI 的速度优势反而变成了风险。

解法:提案驱动的四阶段工作流

我采用 OpenSpec 工作流来约束 AI 的开发流程。它把每次变更拆成四个阶段:

Explore(分析) → Propose(提案) → [人工确认] → Apply(实施) → Archive(归档)

每个阶段都是一个 Skill 命令,AI 必须按顺序执行,不能跳过:

阶段做什么不能做什么
/opsx:explore读代码、分析问题、讨论方案不能写业务代码
/openspec-propose生成提案 + 设计 + 规格 + 任务清单不能写业务代码
/opsx:apply按任务清单逐项实施不能跳过任务、不能擅改方案
/opsx:archive归档完成的提案

关键约束:Propose 完成后必须等人确认,未经确认不能进入 Apply。这不是形式主义。12 次提案里,至少有 3 次我在 review 阶段调整了方案——比如 AI 提议用 Nginx 做 Gzip 压缩,但我们的部署没有 Nginx 层,改成了 FastAPI 内置的 GZipMiddleware;又比如 AI 想给所有图片做 <picture> 标签兼容,但 WebP 兼容率已经 97%,我砍掉了这个额外复杂度。

一个提案长什么样

以「流量优化」提案为例,AI 生成的 artifacts 包括:

proposal.md — 概述目标、范围和影响:目标是启用 Gzip 压缩、设置 Cache-Control、清理重复文件,预期效果是传输量减少 60–80%。

design.md — 技术决策和权衡:

决策方案理由否决的替代方案
压缩方式Starlette GZipMiddlewareFastAPI 内置,零依赖Nginx 反向代理(部署没有 Nginx)
压缩阈值500 字节太小的文件压缩后反而更大1KB(过于保守)
缓存策略静态资源 30 天,HTML no-cache资源文件名不含 hash,30 天合理永久缓存+hash(改动太大)
API 缓存no-store管理接口不能缓存

specs/ — 每个子功能的技术规格(验收标准):

  • Gzip:所有 text/html、text/css、application/javascript 响应体在 >500 字节时自动压缩
  • Cache-Control:/cdn//css//js//fonts//res/ 前缀的资源返回 public, max-age=2592000
  • 清理:jquery.min.js 在 3 个目录存在副本,保留 cdn/ 下的,删除 js/ 和 style/js/ 的

tasks.md — 实施任务清单:

- [x] 1.1 添加 GZipMiddleware(minimum_size=500)
- [x] 1.2 验证 gzip Content-Encoding 头
- [x] 1.3 验证小文件不被压缩
- [x] 2.1 添加 Cache-Control 中间件
- [x] 2.2 静态资源 → public, max-age=2592000
- [x] 2.3 HTML 页面 → no-cache
- [x] 2.4 Admin API → no-store
- [x] 3.1 识别重复文件
- [x] 3.2 grep 搜索所有引用
- [x] 3.3 更新引用路径
- [x] 3.4 删除重复文件(www/js/ 388KB)
- [x] 3.5 grep 验证零残留

这套 artifacts 就是 AI 和人之间的「合约」——人确认了提案,AI 就严格按任务清单实施,不多做也不少做。

四、12 个提案的完整重构历程

整个重构拆成 12 个 OpenSpec 提案,自然形成了四个阶段。以下按实际执行顺序展开。

阶段一:基础架构迁移(2 个提案)

提案 1:FastAPI 服务化 + 后台管理

这是整个重构的地基。AI 需要完成以下工作:

  • 数据提取:从 index.html 和 index2.html 两个硬编码页面中提取 73 张卡片数据,从 16 个独立 HTML 详情页提取结构化内容,全部写入 cards.json
  • 路由设计//page/{n} 走模板分页,/{slug}.html 先查 details 数据再回退静态文件,blog/login/archive 等无扩展名文件通过中间件直接返回
  • 后台系统:完整的管理面板(598 行 admin.html),支持卡片 CRUD、拖拽排序、图片裁剪上传
  • 安全机制:token 认证、每日 5 次密码错误限制、禁用 FastAPI 文档路由

AI 在 design.md 中记录了关键的数据模型设计:

{
  "page_size": 20,
  "cards": [
    {"id": 1, "title": "标题", "link": "URL", "image": "路径", "category": "web|graphic"}
  ],
  "details": {
    "slug": {
      "title": "详情标题",
      "descriptions": ["段落", "## 小标题", "段落"],
      "links": [{"label": "按钮文字", "url": "URL"}]
    }
  }
}

三个顶层字段(page_size / cards / details),之后所有提案都严格向后兼容这个结构。最终 server.py 只有 415 行,涵盖了:页面路由、公共 API、管理 API(登录/CRUD/排序/置顶/上传)、中间件(无扩展名 HTML、Server 头、Cache-Control)。

提案 2:Admin 增强

第一个提案部署后暴露了几个实际问题:密码硬编码在源码里、卡片日期嵌在标题中("激斗火柴人-2018"这种格式需要前端运行时解析)、排序和图片处理需要外部工具。AI 在 Explore 阶段分析了这些问题,然后在 Propose 阶段一次性给出了 7 项增强:

  1. 密码外置secret.txt 文件(不用环境变量,因为 Windows 下文件更方便)
  2. 日期字段独立 → 迁移脚本将 "标题-年份" 拆分为 title + date 两个字段
  3. 新卡片置顶 → insert(0) 而非 append,默认分类改为 "web"(职业生涯)
  4. 卡片置顶pinned 布尔字段 + 红色边框视觉标记
  5. 拖拽排序 → SortableJS,只在"全部"视图下启用(筛选视图下禁用,避免部分排序混乱)
  6. 图片裁剪 → Cropper.js,支持自由/16:9/4:3/1:1 四种比例,客户端裁剪不加后端负担
  7. 守护进程部署 → pythonw 后台运行,--debug 参数控制热重载

这 7 项增强里有一个有意思的设计决策:为什么拖拽排序在筛选视图下要禁用?因为如果用户在"学生时代"分类下拖拽排序,只会看到部分卡片,排序结果应用到全局列表时会产生不可预期的位置变化。AI 在 design.md 里记录了这个决策及原因,这样半年后回来维护不会疑惑"为什么排序有时候不能用"。

阶段二:前端现代化(4 个提案)

提案 3:前端重写——750KB 到 10KB

这是整个重构中最"暴力"的一步。AI 在 Explore 阶段分析了现有前端:298KB 合并 CSS(index_all_style2.css)只用了其中约 44 个类;17 个外部 JS 文件中,fontello 加载了 527 个图标但实际用了 0 个,Revolution Slider 100+KB 只为了两张图片的轮播,fancybox/owl carousel/prettify 从未被调用。AI 提出的方案很直接:全部删掉,从零手写。design.md 中记录了每个决策:

组件旧方案新方案理由
样式298KB 合并 CSS~200 行手写 CSS只有 44 个类,150-200 行足够
轮播Revolution Slider (100+KB JS)纯 CSS @keyframes只有 2 张图片,CSS 动画完全够用
导航栏Bootstrap navbarflexbox 5 个链接没有下拉菜单,不需要框架
布局isotope.jsCSS Grid已不需要筛选动画
图标字体fontello (527 icons)删除实际使用 0 个

最终创建了一个 www/cdn/index.css,用 flexbox + CSS Grid + @keyframes 三件套替代了整个前端依赖。但 AI 也在 design.md 里标注了"不删除旧文件"——因为 www/ 下还有其他独立维护的静态 HTML 页面引用了 index_all_style2.css,清理需要作为独立提案处理。这种边界感知是 AI 在 OpenSpec 约束下做得很好的地方。

提案 4-6:视觉迭代

前端重写后经历了三轮视觉迭代:

  • 卡片换肤(提案 4):从暗色覆盖层改为白底 + 底部信息栏,标题常驻可见(不再 hover 才显示),加入分类标签徽章,分页从伪卡片改为独立分页栏
  • 主页重设计(提案 5):CSS Grid 4 列布局,AJAX 分页(新增 GET /api/cards?page=N 接口避免全页刷新),访问计数器(stats.json 持久化,初始值 111727 来自旧站统计),简化导航和页脚
  • 移动端适配(提案 6):纯 CSS media query,三个断点:
/* 手机 <768px */  → 1 列,容器宽度 95%
/* 平板 768-1024px */ → 2 列,容器宽度 90%
/* 桌面 >1024px */   → 4 列,容器宽度 85%

这三个提案的特点是纯 CSS 变更,不动后端。AI 在每个提案的 Non-Goals 里都明确标注了不改 server.py、不改 cards.json 结构。这种自我约束让每个提案的影响范围非常可控——出问题只需要看 CSS。

阶段三:流量优化(4 个提案)

提案 7:CDN 本地化

网站原来所有静态资源都从外部 CDN(http://cdn.dreamyouxi.com/)加载。CDN 不再维护后需要迁移到本地。AI 的方案极简:纯字符串替换。在 cards.json 中将 "http://cdn.dreamyouxi.com/ 替换为 "/cdn/,在模板和 HTML 中做同样的替换,然后 grep 验证零残留。154 个文件已经缓存在 www/cdn/ 目录下,所以这个提案的风险极低——只是改引用路径,文件本身不需要动。AI 在 tasks.md 里逐文件列出了需要替换的文件和验证步骤。

提案 8:Gzip + 缓存头 + 文件去重

这是前面详细展示过的流量优化提案。值得补充的是 AI 在实施过程中发现的一个问题:AI 原计划删除 www/js/ 下所有重复的 JS 文件,但在 grep 搜索引用时发现 www/ 下的独立 HTML 页面(full_stack_engine.html)仍在引用 style/ 目录下的文件。于是 AI 暂停实施,回写 tasks.md,将 style/ 目录标记为"保留,有外部引用",只删除了确认无引用的 www/js/ 目录(388KB)。这就是 OpenSpec 的 artifact 回写规则在实际中的价值——实施中发现的问题不是留在对话里然后忘掉,而是写回到文档中持久化

提案 9-10:WebP 图片优化

两个提案分别处理存量和增量:

存量转换(提案 9):

  • 用 Pillow 批量转换 www/cdn/ 下 99 张图片为 WebP(quality=80)
  • 大于 760px 宽的图片自动缩放(卡片显示宽度 380px,760px = 2x 视网膜屏足够)
  • favicon.ico 从 128×128 的 66KB ICO 换成 32×32 的 2KB PNG(favicon 的 WebP 兼容性不如 PNG)
  • 保留 timer.png(1×10 像素占位图,1 字节,无需转换)
  • 更新 cards.json 所有图片路径为 .webp,grep 验证零残留,删除原始文件

AI 在 design.md 里记录了 quality 参数的测试数据:78.jpg: 172KB → 56KB (quality=80),slider-bg2: 116KB → 37KB。quality=85 体积提升不值得,quality=75 有可感知的画质下降。

增量一致性(提案 10):

  • 修改 api_upload 接口:上传的 JPG/PNG 自动通过 Pillow 转换为 WebP
  • 添加 Pillow==12.2.0 到 requirements.txt
  • 顺带清理了前端重写后遗留的废弃文件:www/res/js/(20 个 JS)、www/style/(旧主题副本)、www/fonts/(fontawesome)、17 个 CDN JS 文件、8 个 CSS/sourcemap——共约 3.6MB

最终效果:

优化措施传输大小累计降幅
初始状态5.2 MB
压缩图片尺寸3.3 MB37%
Gzip + 去重2.7 MB48%
精简 CSS/JS2.4 MB54%
WebP 格式0.55 MB89%

阶段四:收尾打磨(2 个提案)

提案 11-12:UI 统一 + 数据清理

最后两个提案处理视觉一致性和数据完整性:

  • 卡片统一为 2px #d0d0d0 边框、hover 蓝色边框 + scale(1.12) 放大
  • 固定 16:9 宽高比,无图片的卡片显示灰色背景 + JS 动态缩放标题字体
  • 详情页从 <center> 布局改为 max-width 800px 的白色卡片(左对齐)
  • 页脚统一为三栏结构(名称、日期标签橙色、分类标签)
  • cards.json 数据清理:删除 3 张重复卡片(ID 5/6/12),ID 按新到旧重排,修正错误日期(PLANESHOOTER 2026→2016)
  • 最终全部 26 个 pytest 测试通过

五、AI Coding 的实际协作模式

人做什么

回顾整个过程,人的工作集中在三件事:

1. 定方向

每个提案开始前,我会用一两句话描述需求("前端太重了优化一下""图片换成 WebP"),AI 进入 Explore 分析现状,然后 Propose 生成完整方案。我的工作是在方案中做判断:用不用 Nginx?要不要 <picture> 兼容?拖拽排序在筛选视图下怎么处理?

2. 审提案

这是最关键的环节。AI 生成的 design.md 里每个决策都有"方案 / 理由 / 否决的替代方案"三栏。我只需要逐条看"否决理由"是否合理。大部分时候 AI 的判断是准确的,但偶尔需要纠正——比如它倾向于做更多兼容性工作(WebP 兼容、旧浏览器适配),而我知道这个站的访客几乎全是开发者和游戏玩家,不需要兼容 IE。

3. 跑验收

每个提案完成后,AI 会运行 pytest 测试(最终 26 个测试用例),我手动在浏览器里检查页面渲染。两层验收,一个自动一个人工。

AI 做什么

AI 的工作量远超"写代码"这一步:

  • 分析现状:读旧代码、统计文件大小、识别重复文件、计算覆盖率
  • 写提案文档:proposal + design + specs + tasks 四层完整 artifacts
  • 实施编码:server.py (415 行)、4 个 Jinja2 模板(index 213 行、admin 598 行、detail 56 行、about 67 行)、CSS、数据迁移脚本
  • 数据迁移:从硬编码 HTML 提取 73 张卡片 + 16 个详情页到 JSON
  • 批量处理:99 张图片格式转换、CDN 路径替换、废弃文件清理
  • 写测试:26 个 pytest 用例覆盖模板渲染、API 认证、数据读写
  • 自我验证:每次变更后 grep 验证零残留、确认无引用遗漏

一个典型的交互循环

以 WebP 图片优化为例,实际的交互大致是:

我:图片太大了,换成 WebP
   ↓
AI:[Explore] 分析 www/cdn/ 下 99 张图片,统计总大小 4.9MB,
    列出 WebP 兼容率 97%,估算 quality=80 可减少 40-60%
   ↓
AI:[Propose] 生成提案:直接替换(不做 <picture> 兼容)、
    quality=80、760px 缩放、favicon 换 PNG、保留 timer.png
   ↓
我:确认。不需要兼容,直接替换。
   ↓
AI:[Apply] 逐项执行 tasks.md:
    → 批量转换 99 张图片
    → 更新 cards.json 路径
    → 更新模板引用
    → 删除原始文件
    → grep 验证零残留
    → pytest 全部通过
   ↓
AI:[Archive] 归档提案到 changes/archive/

整个过程大约 15-20 分钟。我的实际输入只有两句话,但过程完全可追溯。

六、成本分析

项目数据
模型Claude Opus 4.6 + Thinking
工具Claude Code (CLI / VSCode 扩展)
计划Max 5(约 $200/月,约 1000 元人民币)
总耗时约 5 小时高密度 Coding
额度消耗约 60% 每日上限
限流未触发
提案数12 个完整 OpenSpec 提案
代码产出server.py 415 行 + 4 模板 934 行 + CSS + 测试
文档产出12 套 proposal + design + specs + tasks

关键数字:1000 元/月,换来一个能读懂 11 年旧代码、会写提案、能做数据迁移、会批量处理图片、还会写测试的全栈协作伙伴。如果雇一个外包做同样的工作,报价不会低于 5 位数。而且 Max 5 计划的 Opus 4.6 额度比想象中充裕。5 小时高密度使用(不是偶尔问一句,是持续的 Explore → Propose → Apply 全流程)只消耗了 60% 额度,完全不需要省着用。

七、OpenSpec 的核心价值

用了 12 个提案之后,我总结 OpenSpec 工作流的价值在于四点:

1. 它是 AI 的「工作记忆」

Claude Code 的对话上下文是有限的。一个长会话里,AI 可能忘记前面做过什么决策。但 OpenSpec 的 artifacts 是写在文件里的——design.md 里的决策、tasks.md 里的进度、specs/ 里的验收标准。即使对话被截断或开启新会话,AI 读到这些文件就能恢复上下文。

2. 它是变更的「防火墙」

每个提案都有明确的 Goals 和 Non-Goals。AI 在"前端重写"提案里不会去动 server.py,在"流量优化"提案里不会去改卡片样式。这种边界约束让每次变更的爆炸半径可控——出问题只需要在当前提案的范围内排查。

3. 它是决策的「审计日志」

为什么用 GZipMiddleware 而不用 Nginx?为什么 WebP quality 是 80 不是 85?为什么拖拽排序在筛选视图下禁用?这些决策全部记录在 design.md 里,带着"方案 / 理由 / 否决的替代方案"。半年后回来维护,不用猜"当时为什么这么写"。

4. 它是人机协作的「协议层」

人看得懂 proposal.md(自然语言描述需求和影响),AI 看得懂 tasks.md(结构化的实施清单)。两边用同一套文档对齐理解,消除"我以为你要的是那个"的沟通偏差。

八、踩过的坑和建议

坑 1:AI 倾向于过度设计

AI 几乎在每个提案里都会提出一些"锦上添花"的方案——WebP 兼容降级、CSS 预处理器、数据库迁移。如果你不 review 就确认,最终会得到一个比需要的更复杂的系统。提案审核不能走过场。

坑 2:批量替换需要 grep 验证

CDN 路径替换、图片格式替换这种批量操作,AI 做完后必须 grep 验证零残留。我在 CLAUDE.md 里写入了"涉及批量替换的变更必须验证零残留",之后每个相关提案 AI 都会自动在 tasks.md 里加上 grep 验证步骤。