Hermes Decision Trace

飞书 Card 收敛式重构 · 执行与修复复盘

飞书 card 渲染完成收敛式重构:原生 table 替代脆弱 column_set、长说明用 collapsible_panel 折叠、路由按内容分类分流。执行中途主力模型因额度不足触发 fallback,降级模型静默改歪一处路由分流维度,经 git stash 加备份双重 diff 审计定位,三处定点修复收口,全量测试 229 passed。唯一未收口项是折叠阈值修复尚未进运行态,需重启 gateway 后确认。

🧭
推荐路径

重启 gateway,用真实 agent 回复验证运行态折叠生效(约 1-2 分钟中断)。

🔎
关键依据

全量测试 test_feishu.py + test_feishu_long_decision_autopublish.py229 passed

🛠️
落地方式

先把已验证方案当成稳定基线:保留当前 schedule / deliver / workdir,不急着继续扩面;新增候选先读源码、看 output、做 run-now 验证,再决定是否转 script-only。

证据摘要

  • 全量测试 test_feishu.py + test_feishu_long_decision_autopublish.py229 passed
  • 运行态脚本验证(真实路由 _build_outbound_payload):结构化内容判定 interactive ✓、原生 table 真实生成 ✓、折叠面板真实触发 ✓。
  • 真实发送飞书 Home 频道 code=0 success。

行动清单

重启 gateway,用真实 agent 回复验证运行态折叠生效(约 1-2 分钟中断)。
运行态确认后,将本次收敛重构固化进 feishu-card skill 的 builder 约定与 schema 2.0 边界清单。

边界 / 风险

折叠阈值修复尚未进运行态

折叠阈值(220→120)是 22:50 重启之后才改的,gateway 进程内存里跑的还是旧阈值。磁盘代码已验证,但 gateway agent 回复链路的真实折叠行为还需一次重启才能确认。这是当前唯一未收口项。

阈值类判据的中文特性

硬编码阈值对中文场景脆弱:中文长段落不换行导致行数判据失效,字符阈值差 1 个字就漏判。后续若调整折叠/分页阈值,必须同时考虑中文不换行特性,优先用字符数而非行数作主判据。

fallback 静默改歪的系统性隐患

本次是飞书 card,但任何长任务执行中途 fallback 都可能发生类似偏差。已确认 fallback notify 功能(fallback.notify_on_activation)可让降级即时可见,建议长改造任务开启,降低静默偏差风险。

完整记录

飞书 Card 收敛式重构 · 执行与修复复盘(2026-06-15)

结论

飞书 card 渲染完成收敛式重构:原生 table 替代脆弱 column_set、长说明用 collapsible_panel 折叠、路由按内容分类分流。执行中途主力模型因额度不足触发 fallback,降级模型静默改歪一处路由分流维度,经 git stash 加备份双重 diff 审计定位,三处定点修复收口,全量测试 229 passed。唯一未收口项是折叠阈值修复尚未进运行态,需重启 gateway 后确认。

背景

涛哥反馈飞书 card 样式长期有瑕疵:显示效果不稳定、内容经常被截断显示不全。历史上经历过约 20 轮 text→post→interactive 的反复横跳,根因不是样式参数,而是架构问题:分类器 kind 过多互相打架、单卡 builder 不分页、没用飞书原生折叠能力。

改了什么

三个文件的收敛式改造(相对 HEAD:feishu.py +7 行,feishu_cards.py 大改 216 行,test_feishu.py +2 行):

  • builder 收敛:report/table 两条 builder 并成一条主链 _compile_card_blocks_to_elements,消除重复逻辑。
  • 原生 table 替代 column_set:markdown 表格改用飞书原生 table 组件(data_type: markdown),移动端竖排友好,替掉脆弱的 column_set 斑马纹。
  • 折叠面板:长纯文字 section 用 collapsible_panel 承载,不再粗暴裁切成"已保留核心段落"。
  • 路由按分类分流:结构化内容默认走 interactive 主链;带格式的简短聊天走 post;不再在 text/post/interactive 间乱跳。

Fallback 插曲(本次最有价值的经验)

执行中途主力模型 MC-4.8 因额度不足触发 HTTP 403,自动 fallback 到降级模型。降级模型在收敛路由时,把 post/card 的分流维度从"内容分类"误改成了"是否单行""\n" not in content),导致多行的带格式短聊天被错误推向卡片渲染路径。

这是一个隐蔽的偏差:代码能跑、能过编译,但设计意图被悄悄改写。教训是——模型中途 fallback 后,绝不能信任"记忆里的完成态",必须对磁盘真实代码做差异审计。

审计方法

用两套基线做交叉验证:

  • git stash 退回基线:把工作区 stash 回动手前状态,跑测试确认那两个失败测试在 HEAD 就是红的,证明不是本次改坏的。
  • 备份 diff 比对:拿 before-convergent-refactor 备份逐文件 diff,确认 builder 层重构干净无污染,fallback 真正改歪的只有 feishu.py 路由一处。

结论清晰:问题仅此一处,三行定点修复即可收口,无需推翻重写。

修复的三个问题

  • 路由分流维度:改回按分类区分。结构化报告(report_structured 等)一律 interactive,chat_structured 才考虑 post。
  • 分类器缺陷chat_structured 把"结论+多列表的状态型回复"和"短富文本闲聊"混成一个 kind,期望出口却相反。新增规则:带 ≥2 个无序列表项且无代码块 → 归入 report_structured → 走 interactive。
  • 折叠阈值脆弱性:原阈值 字符>220 或 行数>=3。那段"根因复盘"正文好 219 字,差 1 字符没触发折叠;更深的问题是中文长段落往不换行(行数=1),行数判据几乎永远失效,等于折叠只剩一根高阈值独木桥。改成 字符>120 或 行数>=4

飞书 Card Schema 2.0 边界(技术沉淀)

live 实测确认的硬约束,供后续避坑:

  • collapsible_panel 不能内嵌 table,表格必须放 body.elements 顶层。
  • column_set + background_color 在 schema 2.0 下直接 HTTP 400,已废弃该路径。
  • 原生 table 单卡最多 5 个,单元格内容超长末尾省略(光标悬浮查看)。
  • collapsible_panel 需飞书客户端 7.20+,schema 2.0 已默认满足。

验证结果

  • 全量测试 test_feishu.py + test_feishu_long_decision_autopublish.py229 passed
  • 运行态脚本验证(真实路由 _build_outbound_payload):结构化内容判定 interactive ✓、原生 table 真实生成 ✓、折叠面板真实触发 ✓。
  • 真实发送飞书 Home 频道 code=0 success。

风险与边界

风险 1:折叠阈值修复尚未进运行态

折叠阈值(220→120)是 22:50 重启之后才改的,gateway 进程内存里跑的还是旧阈值。磁盘代码已验证,但 gateway agent 回复链路的真实折叠行为还需一次重启才能确认。这是当前唯一未收口项。

风险 2:阈值类判据的中文特性

硬编码阈值对中文场景脆弱:中文长段落不换行导致行数判据失效,字符阈值差 1 个字就漏判。后续若调整折叠/分页阈值,必须同时考虑中文不换行特性,优先用字符数而非行数作主判据。

风险 3:fallback 静默改歪的系统性隐患

本次是飞书 card,但任何长任务执行中途 fallback 都可能发生类似偏差。已确认 fallback notify 功能(fallback.notify_on_activation)可让降级即时可见,建议长改造任务开启,降低静默偏差风险。

下一步

  • 重启 gateway,用真实 agent 回复验证运行态折叠生效(约 1-2 分钟中断)。
  • 运行态确认后,将本次收敛重构固化进 feishu-card skill 的 builder 约定与 schema 2.0 边界清单。