Day 29:零基础做产品的真实教训
盘盘猫做到第 29 天收尾,攒下了 1,134 次 commit、109 个 PR、9 个产品线。而我到现在想查天干是什么,还得翻资料。
盘盘猫坐在代码与产品堆成的山顶,望着夕阳沉思
系列总结就不另写了,前六篇日记把过程都记完了。这篇只聊最后两天的基础设施工作,那几块拖到最后才动手啃的硬骨头。
开发环境看不出、一上线就炸的 Portal 主题 bug(PR #67)
每个应用有自己的 CSS 主题:.theme-hub 是深蓝,.theme-tarot 是神秘紫,.theme-bazi 是传统红,9 个应用 9 种视觉风格。这套设计被 React portals 全搞坏了。
createPortal(content, document.body) 会把 DOM 节点渲染到 document 根节点上,完全跑到应用主题 wrapper 的外面。挂在 .theme-hub 上的 CSS 变量,modal 和 dropdown 里根本看不见,因为 CSS 继承顺着 DOM 树走,React 组件树管不着。
第一次修(PR #64)修得很丑:每个 modal 组件自己用 closest('.theme-hub') 检测身处哪个主题,再硬编码 .theme-hub-overlay 这样的类。9 个主题、13 个用 portal 的组件,只能逐个打补丁,完全没法扩展。PR #67 才换上正确的解法:
-
ThemeProvider:React context,由应用布局声明当前的主题类 -
useThemeClass():从 context 读取当前主题的 hook -
ThemedPortal:替代createPortal(content, document.body),用正确的主题类包裹 portal 内容,再加一个 MutationObserver 监听暗色模式切换
改完之后,9 个应用布局全部更新,13 个 portal 组件完成迁移,之前逐组件的 hack 全部移除。这种 bug 在开发环境看不出来,因为一次只测一个应用。到了生产环境,用户一打开 modal,颜色全错。
弹性 AI 流式传输,用户断网后结果不能丢(PR #105)
这是整个项目最大的基础设施 PR,动了 56 个文件。问题很直接:用户在流式传输中途断网(手机上常见,国内网络环境下尤其常见),整个测算结果就丢了,积分还照扣。等于花了钱,什么都没拿到。方案是服务端流式缓冲:19 条 AI 流式路由全部接入 StreamBufferManager,把数据块缓冲到 stream_buffers 数据库表里:
AI Provider ──stream──▶ Server (ReadableStream)
│
┌──────┴──────┐
│ │
SSE to Client Periodic flush
(real-time) to Supabase DB
│ │
▼ ▼
User sees Buffer saved
live stream for recovery
客户端断开后,通过 GET /api/stream/{id} 就能拿回完整响应。消费侧也一并统一了:原来 14 个组件里散着 3 种互不一致的 SSE 消费写法,全部换成一个 useAIStream hook。过期的缓冲由 /api/cron/cleanup-streams 定时任务每 15 分钟清理一次。
反直觉的是,加了这一整套弹性基础设施,代码反而净减 186 行。一个统一的 hook,比维护 3 种写法省得多。加功能代码反而变少,说明架构方向做对了。
内容过滤器改到第三版才算对(PR #104)
内容过滤器前后改了三版,每一版都是被用户的真实痛点逼出来的。第一版觉得"够用",第二版用起来"好像有问题",到第三版才"终于对了"。
-
V1:DFA 关键词过滤,消息里含敏感词就整条封掉,简单粗暴。
-
V2(PR #78):流感知过滤,遇到敏感词就截断整个流,用户积分扣了,只拿到残缺的测算结果,体验极差。
-
V3(PR #104):行内遮罩,把敏感词替换成
**检测到违禁词**,流不中断。
V3 的架构是双重过滤。输入过滤严格,AI 处理之前就拦截广告、政治、色情、违法内容。输出过滤宽松,分类相同但去掉了"广告"这一类,因为算命 AI 自然会生成兼职、招聘、桑拿这类词,粗暴的关键词过滤会误杀正常内容。
流式过滤器还做了边界安全遮罩:先把缓冲区完整累积起来做遮罩处理,再分割成已释放和待判定两部分。这样一来,跨数据块边界的敏感词也能捕获到。这一版配了 6 个测试文件、158 个测试。
i18n 集中化(PR #102)
标签定义原来散落在 3 个地方:历史记录弹窗、积分历史映射、历史配置。同样的 180 行行内标签映射,以略有不同的格式重复出现。PR #102 把它们全部收进 packages/i18n/src/labels/ 下的两个集中文件:features.ts 管应用名称、短标签、子类型标签、图标、时间过滤器和历史标题;actions.ts 管约 60 个积分操作标签。消费方减了 223 行,集中源增了 607 行,以后新增标签只改一处。
中国 AI 法规合规(PR #83)
法律页面拆成了中英文两版,带语言切换 UI 和浏览器语言自动检测。这次新增了《生成式人工智能服务管理暂行办法》《深度合成管理规定》《算法推荐管理规定》三部法规对应的内容。另外补上了 PIPL 跨境传输机制、敏感个人信息同意、自动化决策透明度,以及含 72 小时内监管通报的 5 步数据泄露应急程序。面向中国用户做 AI 产品,这些合规一条都躲不开,而且每一条做起来都不简单。
这 29 天验证的假设
这个项目从立项起,重点就不在算命上。它要验证的是一个假设:一个工程师借助 AI,能不能在完全不懂的领域里,从零做出一个完整的多产品平台。29 天下来可以确认,领域知识的鸿沟跨得过去,开发速度也确实快。把一个产品完整地做出来,把工程之外的部分也亲手过一遍,是这几年做过最有意思的事。