Files
ephron-ren-prd/prd-ai-daily-full-chain-optimization-audit.md

17 KiB
Raw Permalink Blame History

AI 日报定时任务全链路优化审计

0. 文档信息

  • 审计对象Hermes cron job 76297415d88d,名称 AI日报单任务09:30
  • 实际调度:0 10 * * *,每天北京时间 10:00
  • 审计范围:定时触发、采集、生成、去重、异常、日志、发布、告警、稳定性、近期内容质量
  • 主要证据:本地运行产物 ~/.hermes/scripts/ai_morning_out/2026-06-042026-06-10cron 输出 ~/.hermes/cron/output/76297415d88d/,代码仓库 ~/.hermes/scripts/ai-daily-report

1. 总结

当前 AI 日报链路已经完成了从多源采集、LLM 改写分类、导览生成到博客发布的自动化闭环,但近期运行暴露出三个 P0 级问题:

  1. 语义去重召回失效Stage 3 语义去重完全依赖 Stage 2 的标题相似候选。6/10 的 stage2.possible_duplicates=[],导致 stage3.candidate_group_count=0,实际没有进行语义审查。
  2. 源失败仍静默发布6/9、6/10 橘鸦源连续失败,状态为 error、数量为 0但任务仍发布成功回执没有突出“关键补充源缺失”。
  3. 发布前质量门禁不足:近期日报中多天出现同事件重复,如 6/4 OpenClaw/Suno、6/5 Magenta、6/6 Open Code Review、6/8 OpenAI 芯片、6/9 高德 ABot、6/10 Claude Fable/Mythos。

整体判断:链路不是不可用,而是处于“能发布,但缺少质量防线和可观测性”的状态。优先改造方向应是:保存中间产物、增强候选召回、增加发布前质量门禁、补齐重试/告警/幂等。

2. 当前链路现状

2.1 定时触发

  • Cron job ID76297415d88d
  • 名称:AI日报单任务09:30
  • 实际 schedule0 10 * * *
  • 模式:no_agent: true
  • 脚本:ai_daily_report_cron.py
  • 投递:origin
  • 工具集:terminal
  • 最近状态6/10 10:02:46 运行成功,下一次为 6/11 10:00

问题:任务名仍写 09:30但实际为 10:00这会误导运维判断尤其橘鸦源约 09:34 更新后,调度时间曾多次调整。

2.2 Wrapper 执行流程

Wrapper 文件:~/.hermes/scripts/ai_daily_report_cron.py

流程:

  1. 进入仓库 ~/.hermes/scripts/ai-daily-report
  2. 执行 git pull --ff-only
  3. 设置 PYTHONPATH
  4. 调用 run_daily_report(mode='publish', source_mode='live', llm_mode='live')
  5. 打印采集数量、去重数量、发布 URL

问题:git pull 失败只打印 stderr不阻断最终可能继续用旧代码发布。

2.3 数据源配置

配置文件:config/sources.json

当前源:

类型 角色 timeout retries 状态
AI HOT aihot primary 25s 2 正常
InfoQ AI rss supplement 25s 1 正常3 天年龄过滤
MIT 科技评论 AI rss supplement 25s 1 正常5 天年龄过滤
量子位 rss supplement 25s 1 正常
橘鸦AI早报 juya_rss supplement 45s 2 6/9 起 404/错误

注意:retries 字段存在于配置,但当前采集代码没有真正按配置重试。

2.4 生成发布管线

核心文件:ai_daily_report/pipeline.py

当前阶段:

Stage 模块 说明
0 collect.py 并发采集
1 normalize.py 清洗、canonical URL、质量 flags
2 dedupe.py URL/标题硬去重 + 标题相似候选
2.5 dedupe.py 跨天 URL 去重
3 semantic_dedupe.py LLM 语义去重,仅审 Stage 2 候选
4 rewrite.py LLM 改写 + 分类
5 classify.py 分类校验/兜底
6 guide.py LLM 导览
7 assemble.py Markdown 组装和基础校验
8 publish.py 创建、发布、更新 URL 历史

2.5 近期运行数据

本地完整 run_report.json 覆盖 2026-06-04 至 2026-06-10。

日期 原始条数 最终条数 橘鸦 Stage2 候选 Stage3 删除 发布
6/4 90 89 23 1 0 okslug 为 ai-2026-06-04-2
6/5 88 82 24 0 0 ok
6/6 86 82 21 1 1 ok
6/7 53 52 5 0 0 ok
6/8 53 52 5 0 0 ok
6/9 51 51 0/error 0 0 ok
6/10 50 41 0/error 0 0 ok

结论Stage 3 多数日期没有候选,语义去重实际经常没有工作。

3. 发现的问题

3.1 P0语义去重候选召回失效

证据:

  • 6/5、6/7、6/8、6/9、6/10 的 stage3.candidate_group_count=0
  • 6/10 明显存在 Claude Fable/Mythos 同事件多条,但 stage2.possible_duplicates=[]
  • semantic_dedupe.py 在 candidates 为空时直接返回,不会主动审查所有 items

涉及代码:

  • ai_daily_report/pipeline.pyStage 3 candidates 只来自 stage2.possible_duplicates
  • ai_daily_report/dedupe.py:候选召回只基于标题相似度

影响:重复新闻会绕过去重直接发布;用户看到的是同一事件拆成多条,日报质量下降。

3.2 P0发布前质量门禁过弱

当前 validate.py 只检查:空 items、Markdown 太短、无章节、JSON 碎片、无 URL、非法 section。

缺失检查:

  • 同实体高频出现
  • 同标题高相似度
  • 同一源失败导致覆盖缺口
  • Stage 3 候选为 0 的异常状态
  • 最终 Markdown 中重复标题/同事件重复

近期扫描样本:

日期 可疑重复
6/4 OpenClaw 两条、Suno 融资两条
6/5 Google Magenta RealTime 2 两条
6/6 Open Code Review 两条
6/8 OpenAI 芯片核心成员跳槽 Anthropic 两条
6/9 高德 ABot-Earth0.5 两条
6/10 Claude Fable/Mythos 多条

3.3 P0源失败仍发布告警不充分

证据:

  • 6/9、6/10 橘鸦源状态为 error,条数为 0
  • Stage 8 仍 status=ok
  • cron 回执只展示采集数量,不突出源异常原因

影响:用户不知道日报缺失一个重要补充源,也无法判断当天内容覆盖是否可靠。

3.4 P1配置项未全部生效

config/pipeline.json 中存在:

"rewrite_batch_size": 10

pipeline.py 调用 rewrite_items() 时未传入该配置,实际使用 rewrite.py 默认 batch_size=30

影响:配置与真实行为不一致;后续调参容易误判。

3.5 P1HTTP fetch 无统一重试/backoff

clients.py::fetch_text() 只做单次 urllib.request.urlopen。虽然 sources.jsonretries 字段,但 collect.py 没有按配置重试。

影响:临时网络波动会直接变成源失败;错误类型也未细分为 404、timeout、parse_error、empty。

3.6 P1发布流程缺少幂等保护

publish.py 直接 create_postpublish_post。如果同日重复运行,平台可能生成 ai-YYYY-MM-DD-2 这类 slug。

证据6/4 发布 URL 为 https://blog.ephron.ren/posts/ai-2026-06-04-2

影响:同一天可能出现多个日报版本;历史 URL 与预期 slug 不一致。

3.7 P1中间产物不足回放困难

当前输出只有:

  • blog_markdown.md
  • run_report.json

缺少:

  • stage1_items.json
  • stage2_items.json
  • stage2_5_items.json
  • stage3_items.json
  • stage4_items.json
  • LLM prompt/response 摘要或 hash

影响:一旦用户反馈重复/幻觉/分类错误很难还原某条新闻在每个阶段的标题、摘要、URL、候选关系。

3.8 P2cron wrapper 与包内配置割裂

Wrapper 固定传:

  • mode='publish'
  • source_mode='live'
  • llm_mode='live'
  • base_url='https://blog.ephron.ren'

同时 pipeline.json 又有 default_mode='dry-run'。配置来源不统一。

影响:调试和生产行为容易混淆。

3.9 P2Skill 文档与代码阈值曾出现不一致

技能文档曾记录 title Jaccard 阈值为 0.25,但代码实际为 0.40。该问题说明“运维知识库”和代码配置缺少自动校验。

影响:排障时容易根据过期文档做错误判断。

4. 问题影响

问题 直接影响 长期影响
语义去重候选为 0 重复新闻发布 用户对日报质量信任下降
源失败静默发布 内容覆盖缺口不可见 误以为日报完整
发布前门禁弱 明显质量问题进入公开博客 后续需要人工返工或删除重发
配置不生效 运维调参无效 形成错误排障经验
无重试/backoff 临时波动放大成源失败 稳定性依赖运气
发布不幂等 同日多 slug 历史链接、引用和 SEO 混乱
中间产物缺失 排查成本高 无法建立可靠回归测试集

5. 优先级排序

优先级 事项 原因
P0 新增 Stage 2.8 语义候选召回 当前 Stage 3 经常没有候选,语义去重实际失效
P0 发布前质量门禁 阻止明显重复、源失败、空候选直接发布
P0 保存中间 item 快照 支撑回放、调试、回归测试
P1 统一重试/backoff 与错误分类 提升源稳定性,减少静默失败
P1 发布幂等保护 避免同日多版本和 slug 漂移
P1 让 pipeline 配置真实生效 消除配置与行为不一致
P2 统一 wrapper 与包内 CLI 配置 降低维护成本
P2 技能文档自动校验 防止知识库漂移

6. 具体优化建议与改造方案

6.1 新增 Stage 2.8:语义候选召回

新增文件:ai_daily_report/candidate_recall.py

目标:不新增 LLM 调用,先用规则扩大 Stage 3 的候选池。

建议召回特征:

  • 标题字符相似度
  • 标题 token Jaccard
  • 摘要 token Jaccard
  • 公司/产品/模型实体重叠
  • URL 域名与来源类型
  • 事件阶段词:发布、即将发布、爆料、上线、开源、融资、收购

输出示例:

{
  "item_ids": ["item_a", "item_b"],
  "reason": "entity_overlap",
  "score": 0.78,
  "shared_entities": ["Claude Mythos", "Claude Fable"]
}

接入位置:pipeline.py 中 Stage 2.5 之后、Stage 3 之前。

6.2 增强 Stage 3支持同事件合并

当前 Stage 3 只有 duplicate_groups。建议增加:

{
  "duplicate_groups": [],
  "merge_groups": [],
  "not_duplicates": [],
  "uncertain": []
}

策略:

  • duplicate_groups:同一新闻,删除重复项
  • merge_groups:同一事件不同角度,保留主条目,把其他来源作为补充来源
  • uncertain:默认保留,避免误删

6.3 新增发布前质量门禁

建议新增模块:ai_daily_report/quality_gate.py

检查项:

  • Stage 3 候选为 0 且 item 数大于 30warning
  • 任一强实体出现 3 次以上warning 或 block
  • 最终标题相似度高于 0.55warning
  • 任一 enabled source 状态为 errorwarning
  • required source 失败block
  • publish 前 blocking_errors 非空:阻断发布

6.4 保存中间产物

runner.py 中输出:

stage0_sources.json
stage1_items.json
stage2_items.json
stage2_5_items.json
stage2_8_candidates.json
stage3_items.json
stage4_items.json
quality_gate.json

每条 item 至少保留id、title_raw、title、summary_raw、summary、url、canonical_url、source_group、source_label、section、quality_flags、entities。

6.5 实现统一重试/backoff

改造位置:

  • clients.py::fetch_text
  • collect.py::_collect_one

建议:

  • config.retries 重试
  • 对 429/500/502/503/504/timeout 使用指数退避
  • 对 404 直接失败,不重试
  • report 记录 attemptserror_typehttp_status

6.6 发布幂等保护

改造位置:publish.py

建议:

  • 发布前查询目标 slug 是否已存在
  • 如果存在且内容 hash 相同:返回 status=already_published
  • 如果存在但内容不同:默认阻断,除非显式 allow_republish=true
  • 避免自动生成 -2 slug 后误以为正常

6.7 让 pipeline 配置生效

改造位置:runner.pypipeline.py

建议把以下配置传入实际函数:

  • rewrite_batch_size
  • semantic_dedup_max_deletion_ratio
  • quality_gate 配置
  • semantic_candidate_recall 配置

6.8 改进 cron 回执与失败告警

Wrapper 回执应包含:

  • 每个源状态、条数、错误类型
  • Stage 2/2.5/2.8/3 删除与候选数量
  • 质量门禁结果
  • 发布结果与实际 slug
  • 如果源失败或质量 warning必须在回执中显式显示

对于 no_agent cron脚本 stdout 就是告警内容,应避免只输出“发布成功”。

7. 可执行改造计划

Phase 1可观测性与安全网

  1. runner.py 保存各阶段 item 快照。
  2. validate.py 或新增 quality_gate.py 增加重复/源失败 warning。
  3. 修改 wrapper 回执,展示源失败和质量门禁结果。
  4. 修正 cron job 名称为与实际时间一致。

交付标准:不改变内容生成结果,但能解释每条新闻的处理路径。

Phase 2去重召回升级

  1. 新增 candidate_recall.py
  2. pipeline.py 增加 Stage 2.8。
  3. Stage 3 prompt 增加 merge_groups
  4. 建立 6/10 回归 fixtures。

交付标准Claude Fable/Mythos 三条不再全部独立出现Gemini/Gemma 仍保留。

Phase 3稳定性与发布治理

  1. 实现 fetch retry/backoff。
  2. 发布前做 slug 幂等检查。
  3. pipeline.json 配置真实生效。
  4. 对 required source 失败设置阻断或 draft 降级策略。

交付标准:临时源波动不再轻易导致内容缺失;同日重复运行不会生成 -2 版本。

Phase 4长期维护

  1. 建立 tests/fixtures/recent_daily_reports/
  2. 每次调整去重逻辑跑历史回放。
  3. 将技能文档中的阈值、阶段说明改为从代码/配置生成或定期校验。
  4. 增加每周自动审计报告统计源失败率、重复候选、最终条数、fallback ratio。

8. 涉及文件和代码位置

文件 作用 建议动作
~/.hermes/scripts/ai_daily_report_cron.py cron wrapper 阻断 git pull 失败、增强回执、展示 warning
config/sources.json 源配置 明确 required/min_items/retries 语义
config/pipeline.json 管线配置 增加 quality_gate、candidate_recall、publish_idempotency
ai_daily_report/clients.py HTTP/LLM/Blog client 增加 retry/backoff、HTTP 错误分类
ai_daily_report/collect.py Stage 0 使用 config.retries记录 attempts/error_type
ai_daily_report/dedupe.py Stage 2/2.5 保留硬去重,减少标题候选职责
ai_daily_report/candidate_recall.py 新增 Stage 2.8 实体/摘要/标题综合召回
ai_daily_report/semantic_dedupe.py Stage 3 支持 merge_groups 和候选审计
ai_daily_report/rewrite.py Stage 4 配置 batch_size 生效,避免伪独立标题
ai_daily_report/validate.py Stage 7 校验 扩展质量门禁或迁移到 quality_gate
ai_daily_report/publish.py Stage 8 增加 slug 幂等检查
ai_daily_report/runner.py orchestration 保存中间产物,传递配置
tests/ 测试 增加历史回放与重复回归测试

9. 验收标准

9.1 功能验收

  • 6/10 回放中Stage 3 candidate_group_count 不再为 0。
  • Claude Fable/Mythos 三条不会全部独立发布。
  • 6/4 OpenClaw/Suno、6/5 Magenta、6/6 Open Code Review、6/8 OpenAI 芯片、6/9 高德 ABot 样本能进入候选或被合并。
  • Gemini/Gemma、Cursor Evals/Cursor 欧洲总部这类同公司不同事件不被误删。

9.2 稳定性验收

  • 源 timeout/5xx 会按配置重试。
  • 404 不重复重试,并在 report 中记录为 http_404
  • enabled source 失败会在 cron 回执中显著显示。
  • required source 失败会阻断发布或降级为 draft。

9.3 发布验收

  • 同日重复运行不会默认生成 ai-YYYY-MM-DD-2
  • publish result 明确区分 okblockedfailedalready_published
  • run_report 中包含实际 slug 和公开 URL。

9.4 可维护性验收

  • 每次运行保存完整阶段快照。
  • 新增测试覆盖 6/10 语义重复事故。
  • pipeline.json 中配置项都有测试证明实际生效。
  • 文档与代码阈值不再长期漂移。

10. 建议配置草案

{
  "semantic_candidate_recall": {
    "enabled": true,
    "max_pairs": 80,
    "max_pairs_per_item": 5,
    "title_similarity_threshold": 0.45,
    "title_jaccard_threshold": 0.25,
    "summary_jaccard_threshold": 0.18,
    "strong_entity_overlap_threshold": 2
  },
  "quality_gate": {
    "block_on_required_source_failure": true,
    "warn_on_enabled_source_failure": true,
    "warn_when_stage3_candidates_zero_min_items": 30,
    "warn_on_final_title_similarity": 0.55,
    "warn_on_entity_frequency": 3
  },
  "publish_idempotency": {
    "enabled": true,
    "allow_republish": false
  }
}

11. 结论

当前 AI 日报定时任务的主链路已经能稳定产出,但质量治理还停留在“发布是否成功”的层面,没有充分检查“内容是否应该发布”。

最优先的改造不是继续调 LLM prompt而是补齐工程链路

  1. 让每个阶段可回放;
  2. 让语义去重有足够候选;
  3. 让明显质量问题不能直接发布;
  4. 让源失败、重复发布、配置不生效这类运维问题能被及时发现。