# 我用AI从0开发一个网站，实际过程记录

URL: https://caijiao.org/posts/129
Source: docs/posts/129.md
Description: 从脚手架到上线，AI 帮我把代码写完只用了几天，但第一天它给的方案就把处理放在了服务端。ffmpeg.wasm 需要 COOP 和 COEP 两个响应头，写 SQL 时它还漏了 WAL 和索引，这两处最后都得自己补上。

这次做的还是个工具站。JetBrains 的调研说 90% 的专业开发者每周至少用一次 AI 编码 agent，所以这次我决定从头用到尾，把过程记下来。

结论先放：代码部分它确实完成了，但第一天就给了个方向错误的方案。

## 第一天：它把处理放在了服务端

我的第一句提示是"写一个把图片转成 WebP 的在线工具"。

它给回来的东西结构很完整：一个上传接口、一个处理函数、一个下载链接。能跑。问题在于，这条路径意味着每个用户都要把文件上传到我的服务器，处理完再传回去——成本随用户量线性上涨，还要自己写文件清理。

我改了需求，重新描述：处理要在浏览器里完成，服务器只发静态文件。它立刻给出了第二版，用 canvas 加 `OffscreenCanvas` 编码，代码质量不错。

这一次让我确认了一件事：**AI 的默认答案是服务端方案，因为它训练语料里这类例子最多。但工具站的成本结构恰恰要求另一个答案。**

10015.io 的原则是几乎所有工具在客户端运行、极少请求服务端，50 多个工具靠这条把开销压住。这个原则得由我先说出来，AI 不会主动想到。

![10015.io 官网首页截图](/images/2026-09/10015.io.png)

*10015.io 首页放着 Chrome 和 Firefox 两枚扩展入口——工具集的分发不只靠网页。*

## 脚手架和页面：指定"页面必须在构建时就有内容"

第二步是搭骨架。我用的是 Next.js，命令就是 `npx create-next-app@latest`，这部分没什么好说的。

关键是我在提示里加了一句约束：每个工具一个独立路由，页面的 HTML 必须在服务端或构建时生成，不能是前端路由渲染的空壳。

这句约束必须显式写出来。不给的话，它会把工具做成单页应用里的 Tab 切换——代码更简洁，但每个工具都不再是独立的搜索入口，页面矩阵直接没了。

页面生成之后我抽查了几个：关掉 JS 看源码里有没有真实内容。这一步 AI 不会替你做。

## 客户端计算那一块，它错了两次

重计算的部分我用了 [WebAssembly](/webassembly/)，ffmpeg 编译成 wasm 在浏览器里跑视频转码。

第一次报错：SharedArrayBuffer is not defined。

原因很具体——多线程的 wasm 依赖 SharedArrayBuffer，浏览器只有在跨源隔离开启时才暴露它，需要服务器返回两个响应头：

```
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
```

AI 给的代码里没有这一层。我把报错贴回去，它给了加响应头的方案，但第二次又错了——它建议我在应用层的中间件里加。这两个头必须由真正处理请求的那一层发出，放在应用框架里发给 HTML 是可以的，但静态资源那条路径要单独确认。我最后是直接在 [Nginx](/nginx/) 里加的，一次到位。

两次修正花的时间和一个熟练的人差不多。AI 在这儿的价值是"能很快给出第二个猜测"，不是"一次给对"。

## 第三天：它写的 SQL 少了两样东西

需要存一点用户配置和历史记录，我用了 SQLite，让 AI 写了建表和查询。

代码能跑，但少了两样。**一是没有开 WAL**——默认是回滚日志模式，写操作会挡住所有读，并发一上来就是 `SQLITE_BUSY`。得自己补上 `PRAGMA journal_mode = WAL` 和 `PRAGMA busy_timeout = 5000`。

**二是没有建索引。** 表建好了、查询写对了，但常查的那两个字段上没有索引。早期数据量小完全看不出来，等数据上万行就会突然变慢。

这两样它都不会主动加，因为从"功能正确"的角度看，缺了它们也一样能跑。缺的是运行时的行为，不是语法。

## 部署：Dockerfile 加一段 nginx

部署这一步它做得最省事。Dockerfile 的多阶段构建、compose 里三个服务的依赖关系，一次就跑通了——这类配置它见得太多了。

nginx 那段我改了两处：给静态资源设长缓存头，开启压缩并确认 wasm 在压缩类型里。默认配置经常漏掉 `application/wasm`，漏了的话每个用户都要多下几百 KB。

用 [Docker](/docker/) 打包时记得把数据目录挂成持久卷，这条和 AI 无关，是每次部署都容易忘的地方。

## 上线之后：它帮不上忙的部分

代码写完、站跑起来，剩下的事 AI 参与度急剧下降。

写 sitemap、接入 Search Console、检查结构化数据字段——这些不是"写代码"，是"确认什么必须为真"。页面能不能被抓到、能不能被正确理解，决定了前面所有工作有没有回报。结构化数据的字段该怎么填，去查 JSON-LD 的实现文档比泛泛问 AI 靠谱。

Omni Calculator 的广告负责人 Alexander Utz 说过一句话我记下来了：页面速度对他们没有商量余地，整个增长都依赖 SEO，Core Web Vitals 直接影响排名。他这个站 2026 年 6 月的估算访问量约 1429 万、30 多种语言，把计算器抽象成配置、页面由模板生成。这种"什么必须为真"的清单，AI 不会主动给你列。

## 做完之后对着的两个数字

10015.io：50 多个工具，AdSense，MRR 约 300 美元。

terrific.tools：做了 12 个月才迎来第一个完整变现月，广告 174.41 美元加桌面版 125 美元，同期 30 天 2.6 万用户、3.4 万次会话，每千次会话广告收入 6.89 美元。

同样是工具站，代码量差得不多，结果差在别的地方。AI 把写代码那一段从几周压到几天，这一点我这次是确认了的；但它没有压缩"12 个月才等到第一个变现月"里的任何一天。

下一次我会改的只有一件事：把"什么必须为真"的清单写在第一句提示之前，而不是等代码写完再去补。清单本身很短，但它决定了后面每一句提示的方向。你用 AI 写完一个站之后，清单上有几条是真的去验证过的？
