DEV LOG · v4.3.0
踩过的坑,比做成的功能更值得写下来。
这不是变更日志——功能清单在「建设现状」页。这里记的是过程中真正卡住过的地方: 现象是什么、根因在哪、最后怎么处置的。多数坑的共同点是它们都不显眼。
建设脉络
- 起点
通读现有实现与需求文档,产出十项问题清单与一条迁移策略: 不集成、不复刻上游生产链路,先把修正数据接回来。
- 第一轮
埋点接收端与工作台。HTTP 接收 + 三层防线、登录/录入/审核/看板四个界面, 连带修掉六个存量问题。
- 补丁
处理完成埋点。补上率的分母——在此之前只能算「修正次数」, 算不出「修正率」,两条验收指标都无从判定。
- 上线
公网接入、常驻守护、联调自检脚本九条。 数据目录归零,等待真实埋点。
- 第二轮
规则回流可执行化。评估「集成或复刻上游」后改判: 规则从自由文本变成四类封闭词表的类型化配置,上游加一个加载器即可消费。
- 发布
v4.1.0。发布说明里已知限制单独成节。
- 加固
用真实模型验证提取路径,当场发现结构合法却会破坏数据的规则, 补上语义自测与调用降级。128 个测试全绿。
- 第三轮
降低理解门槛。此前要看懂这套机制,得同时具备上游接入、模型密钥、 公网可达三个条件。做了一条零依赖的演示工作流:启动服务、点五个按钮, 每步显示真实产物。发布 v4.2.0。
- 可读性
把术语翻译成人话。跑得通不等于看得懂——每一页补上「这一页回答什么问题」, 看板改成结论先行,修正录入改成对着 AI 的产出值逐格改。 新增只读的运行状态页。180 个测试全绿。
- 量化
把提取质量变成可测量的。评测脚本把失败分成四类, 顺手量出真实模型 15/15——原以为要修的提示词其实没坏, 倒是发现了一条 schema 拦不住的无效规则。199 个测试全绿。
- 真实数据
闭环第一次跑在真实数据上。一批真实的双版本表格转成埋点格式导入, 产出 9 条规则草案——分布与演示样本完全不同,真实瓶颈是「没抓到」 而不是「抓错了」。同时量出 65% 的修正无法倒推原因。268 个测试全绿。
- 供数链
编目智能体上线。只读、按编号取一部剧的编目记录,两阶段协议直连; 两端接缝用金样文件逐字锁定后并行开发。接真表时踩到一个被截断的报错, 修好授权后下游端到端跑通并留证。
- 发布
v4.3.0。收录 17 个 PR:真实数据首次验证、编目智能体、工作台可读性、 提取质量测量、四份对外文档。284 个测试全绿。
踩过的坑
配置文件从来没被读过
- 现象
- 示例配置文件一直在,服务启动失败时还提示「请在 .env 中设置」。
- 原因
- 没有任何代码读取它。写进去的配置全部不生效,而错误提示反过来让人以为写了就有用。
- 处置
- 补上加载逻辑,规则是真实环境变量优先、配置文件只补空缺—— 否则线上用环境变量设的值会被仓库里的文件悄悄覆盖。
隧道地址拿到了,请求全部失败
- 现象
- 公网隧道建立成功、地址也拿到了,但所有请求返回 530。
- 原因
- 出网环境封了 UDP 7844,而隧道客户端默认走 QUIC,握手一直超时。 它的启动预检其实已经打印了「UDP 不通、TCP 通」,只是淹没在日志里。
- 处置
- 强制走 HTTP/2 回退,立即注册成功。这条写进了部署文档—— 换台机器大概率还会遇到。
边缘防护悄悄吃掉埋点
- 现象
- 换到正式域名后,自检九条全部 403, 但用 curl 手工发同一个请求却是 200。
- 原因
- CDN 的机器人防护按 User-Agent 拦截。实测 12 种主流 HTTP 客户端, 只有一种被拦——恰好是自检脚本用的那种。
- 处置
- 脚本改发具名 UA,并在全 403 时直接输出「这是边缘拦截」的判断。 真正危险的是:被边缘拦掉的请求根本到不了服务端, 兜底队列救不了、日志里也看不见——表现为「上游以为发了,我们这边什么都没有」, 正是数据不可追补的那种丢法。
图上的箭头全丢了,截图看不出来
- 现象
- 一张流程图渲染「正常」,截图检查也没发现问题。
- 原因
- 共享样式表里有一条针对图形元素的通用规则, CSS 会覆盖 SVG 的表现属性——箭头标记被换成了一个不存在的引用, 警示用的语义色也被刷成了普通灰。图的核心信息(哪两段还没通)视觉上完全消失。
- 处置
- 改用不设子元素样式的独立容器,并把验证方式从「看截图」换成 读取计算后样式逐项核对。后来画大幅架构图时, 又用脚本量了全部 68 个文字元素的包围盒做重叠检测——零碰撞。
「接通了一天」,其实从未成功读过一次
- 现象
- 编目智能体接上真表的当天,日志里唯一一条查询记录写着
读表工具: {。看不出是什么错,也没人追。 - 原因
- 两层叠在一起。读表工具在权限不足时把错误打成多行 JSON, 代码只取首行,于是只剩一个左花括号;而权限确实不足——读表的身份没有申请对应权限。 再往下还有第三层:文档里的自检样例编号是接入文档里编的示例,真表里根本没有, 就算权限通了,照文档自检也永远「未找到」。
- 处置
- 错误是 JSON 就原样透传,把
message与修复提示一并带给操作员; 用户身份登录并授予读表权限;自检样例换成真表里存在的一部剧。 修完之后第一次真实读表成功,两阶段协议全绿,下游端到端随即跑通。 没有报错不等于成功——要看到产物才算。
演示样本结构上不可能暴露的缺陷,真实数据一跑就冒出来
- 现象
- 换到真实数据后,一条「写法归一」规则的映射里出现了 「空值 → 某个具体人名」这样的条目。作为全局规则毫无意义, 但它通过了全部结构校验。
- 原因
- 规则的证据用的是该字段的全部样本,不区分修正原因。 真实数据里同一个字段常常并存多种原因——某个字段既有写法差异, 也有大量「AI 压根没抓到」——于是把后者的空值也当成了写法样本。 演示样本每个字段只有一种原因,结构上碰不到这个组合。 语义自测同样被拖累:拿永远修不好的样本去测一条格式规则,覆盖率被无谓压低。
- 处置
- 规则的证据按类型同源筛选,并补上测试。 同一轮里导入脚本自己也犯了同类错误:格式判定分支有单元测试、逻辑也正确, 但整条链路在调用它之前就把相关格子跳过了——单测绿不等于分支走得到。
成功率是凭印象记住的,量完发现待办已经过期
- 现象
- 待办上写着「提取成功率大概三分之一,需要打磨提示词」, 躺了一段时间,每次盘点都照抄一遍。
- 原因
- 那个数字来自一次手工试跑的印象。失败的原因(反向引用写成了 另一种语言的语法)后来已经修掉,但从没复测过,印象就留了下来。 真去测量时成功率是 15/15——照着待办调提示词,改的是一个没坏的东西。
- 处置
- 把评测做成脚本,并把失败分成四类——调用失败、空返回、 形状不合 schema、语义自测不过。四类的改法完全不同,混成一个「成功率」 就看不出该动哪里。真正的收获也来自这次测量:15 次输出里有一条 schema 全过却不可能有效的规则,补了对应的校验。
图表「渲染成功」,其实什么都没画
- 现象
- 工作台看板新做了一个纯 CSS 条形图,单元测试全绿, 页面上文字与数值都在,看着也不缺东西。
- 原因
- 模板里引用的五个 CSS 变量从来没有被定义过。
var()取不到值时不会报错,也不会留下任何痕迹,只是静默失效—— 条形图填充块的背景色因此是透明的。文字照常显示,唯独那根条不存在。 截图里色块本来就窄,肉眼扫过去也发现不了。 - 处置
- 补上变量定义,并加一条结构断言钉死这一类问题: 模板里用到的每个 CSS 变量,都必须在基础模板里有定义。 修复后实测三根条的填充宽度为 398 / 361 / 157 像素, 对应 10 / 9 / 4 次修正——比例与数据吻合,这才算验过。
差点为一个假想的消费者做抽象
- 现象
- 注意到姊妹项目与本项目「结构同构」,一度把「抽出共用模块」 列为当前最该做的技术投入。
- 原因
- 那句「结构同构」来自代码注释,没有人去量实际重叠。 实测后发现两边根本没有重复代码:一边 106 行含类型化契约与兜底队列, 另一边 33 行写自由字典;规则相关的代码,姊妹项目一行都还没写。
- 处置
- 否决自己的建议。为一个真实消费者加一个还没动工的假想消费者做抽象, 接口大概率要推翻重来。结论是「现在不做」,并把证据写下来。
规则通过了校验,却会破坏数据
- 现象
- 类型化提取的模型调用路径此前从未被真实验证过——测试里它是被替身覆盖的。 实测后发现:某条归一化规则通过了全部结构校验,参数键合法、正则能编译。
- 原因
- 它用了另一种语言的反向引用语法,在本运行时会被当成字面量插入—— 把「2023年」变成「$1年」。拿它自己的六条证据样本回算,0/6 成立。 结构合法不等于语义正确:schema 只能挡住形状不对的参数。 更危险的是这种规则会一路走到人工审核,而审核人看到「像模像样的正则」很可能就批了。
- 处置
- 用设计里已有的东西:规则强制附带的证据样本,就是它自己的测试用例。 入库前把规则应用到每条样本上回算。判据分三态——改对、没改(正则未匹配,原样返回,无害)、 改坏(一票否决)——再加覆盖率下限。 真实模型复测:三轮中一轮放行,改对 4、未覆盖 2、改坏 0。 未覆盖的样本会继续产生修正记录,攒够了自然提出配套规则,一次解决一部分。
几条被反复验证的做法
测试先红后绿,而且要验证它真的会红
每个功能都先写会失败的测试。更进一步:写完防回归测试后, 把旧写法塞回去确认它确实变红——否则你可能只是加了一条永远为真的断言。
把约定变成会失败的测试
文档里写「改了要同步」,没人会记得。有一条测试盯着文档里写死的测试数量, 七次改动触发了七次拦截——包括一次跨分支合并时自动发现文档过期。 约定靠自觉会漂移,靠测试不会。
验证要量化,不要靠眼睛
截图「看着没问题」骗过一次之后,后续的图形验证全部改成读计算样式、 量包围盒、算重叠。同理,部署完不只看「进程在跑」—— 直接杀掉进程,确认它会自己回来,那才叫守护。
结构化的东西可以校验,自由文本不能
规则从自然语言改成带 schema 的参数之后,模型编造出一个不存在的数据源名, 入库时当场被拒。写文档时作者本人就编过一个这样的源名,是自查脚本抓出来的。 能被机器拒绝的错误,就不该留给人去发现。