Files
llm-intelligence/docs/plans/2026-05-25-intraday-discovery-verification-implementation-plan.md

16 KiB
Raw Blame History

Intraday Discovery + Verification Implementation Plan

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: 在不污染正式日报语义的前提下,为现有日内链路增加“搜索引擎 + 大模型候选发现层”和“官方来源验证层”,让当天的大模型价格新闻、版本发布、活动窗口能更早进入候选池,并只把已验证事实接入现有 daily_signal_snapshot / 日报语义链路。

Architecture: 保留现有 scripts/run_intraday_price_watch.sh 作为结构化价格事实刷新入口,不改它“只刷新价格/信号、不生成正式日报”的边界。新增一条独立的 run_intraday_discovery_watch.sh 发现链路:先用搜索引擎与 LLM 生成候选事件,再通过官方页面 / 价格页 / docs / 公告页做二次验证。候选与验证结果分别落入新表;只有 official_confirmed 的事件才允许映射进 materialize_daily_signals.gosignalModelEvent,并由现有 generate_daily_report.go 继续消费,不新造第二套日报事实系统。发现层与验证层必须通过仓库内可运行的 provider adapter 落地,不能依赖当前会话专属工具;实现上采用“命令或 HTTP provider 适配层 + fixture 测试”的方式,确保本地 cron 和 CI 环境可执行。已验证 discovery 事件接入现有事件流时必须去重:若同一 provider + model + event_type + date 已由 importer / 原生 loader 给出则以原生事实为准discovery 事件只补缺,不覆盖。

Tech Stack: Go 1.22、PostgreSQL、Bash、可配置搜索/LLM provider adapter、JSONB


Task 1: 为候选发现与验证链路定义持久化结构

Files:

  • Create: db/migrations/017_intraday_news_candidates.sql
  • Modify: docs/CONFIGURATION.md
  • Modify: DEPLOYMENT.md

Step 1: 新增候选表与验证表 migration

创建两张表:

  • intraday_news_candidate
  • intraday_news_verification

候选表至少包含:

  • candidate_date
  • event_type
  • provider_name
  • model_name
  • provider_country
  • title
  • summary
  • candidate_urls JSONB
  • discovery_source
  • discovery_query
  • discovery_evidence JSONB
  • normalized_key
  • status
  • verification_confidence
  • verification_notes

验证表至少包含:

  • candidate_id
  • verifier_source
  • verifier_url
  • verifier_status
  • extracted_facts JSONB
  • notes

约束:

  • intraday_news_candidate.normalized_key 必须唯一,用于防止同日重复发现
  • status 至少支持:candidate / verifying / verified / rejected / stale
  • verification_confidence 至少支持:candidate / secondary_confirmed / official_confirmed

Step 2: 明确与正式事实层的边界文档

docs/CONFIGURATION.mdDEPLOYMENT.md 写明:

  • 候选发现层不会直接写 daily_report
  • 候选发现层不会覆盖 latest_report
  • daily_signal_snapshot 只消费已验证事实,不消费 candidate_only
  • leak_or_rumor 默认只保留在候选层,不进入正式日报事实

Step 3: 运行 migration 验证

Run:

  • bash scripts/apply_migration.sh

Expected:

  • 新表创建成功
  • 重复执行 migration 不报错

Step 4: Commit

git add db/migrations/017_intraday_news_candidates.sql docs/CONFIGURATION.md DEPLOYMENT.md
git commit -m "feat(intraday): add candidate and verification persistence"

Task 2: 实现候选发现层最小闭环

Files:

  • Create: scripts/discover_intraday_news_candidates.go
  • Create: scripts/discover_intraday_news_candidates_test.go
  • Create: scripts/testdata/intraday_discovery_search_sample.json
  • Create: scripts/testdata/intraday_discovery_llm_sample.json
  • Modify: docs/CONFIGURATION.md
  • Create: scripts/intraday_discovery_provider.go

Step 1: 先写失败测试

补 4 组测试:

  • 搜索结果解析测试:验证能从样例结果提取 title / summary / url / provider 线索
  • LLM 输出解析测试:验证能把 LLM JSON 输出转成候选事件
  • 候选归一化测试:验证同一事件经过标题差异改写后仍生成同一 normalized_key
  • URL 过滤测试:验证没有 URL 的候选被丢弃,避免 LLM 空口造线索

Step 2: 运行失败测试

Run:

  • go test -count=1 -tags llm_script ./scripts/discover_intraday_news_candidates.go ./scripts/discover_intraday_news_candidates_test.go

Expected:

  • 新增测试失败
  • 失败原因是缺少解析、归一化或去重逻辑

Step 3: 实现最小候选发现器

discover_intraday_news_candidates.go 中实现:

  • 固定 provider 查询模板集(中英双语)
  • 搜索结果抓取适配层
  • LLM 候选摘要适配层
  • 去重与归一化逻辑
  • 写入 intraday_news_candidate
  • provider adapter 抽象层(搜索 / LLM 均可通过命令或 HTTP provider 接入,默认实现不可依赖当前会话专属工具)

限制:

  • LLM 只允许输出候选,不允许直接标成 verified
  • 无 URL 候选直接丢弃
  • 搜索 / LLM provider 未配置时必须以前置条件错误退出,不能伪装成业务无新闻
  • 默认事件类型至少支持:
    • price_cut
    • price_increase
    • official_release
    • promo_campaign
    • leak_or_rumor
    • unknown

Step 4: 重新运行测试

Run:

  • go test -count=1 -tags llm_script ./scripts/discover_intraday_news_candidates.go ./scripts/discover_intraday_news_candidates_test.go

Expected:

  • 候选解析与归一化测试通过

Step 5: 运行一次 dry-run 验证

Run:

  • go run -tags llm_script ./scripts/discover_intraday_news_candidates.go --date=2026-05-25 --dry-run

Expected:

  • 输出 candidate_total / provider_hit_count / event_type_counts
  • dry-run 不写 daily_report
  • dry-run 不改 latest_report

Step 6: Commit

git add scripts/discover_intraday_news_candidates.go scripts/discover_intraday_news_candidates_test.go scripts/testdata/intraday_discovery_search_sample.json scripts/testdata/intraday_discovery_llm_sample.json docs/CONFIGURATION.md
git commit -m "feat(intraday): add news candidate discovery pipeline"

Task 3: 实现候选验证层并固化“只信官方事实”的规则

Files:

  • Create: scripts/verify_intraday_news_candidates.go
  • Create: scripts/verify_intraday_news_candidates_test.go
  • Create: scripts/testdata/intraday_verification_official_release.html
  • Create: scripts/testdata/intraday_verification_pricing_page.html
  • Create: scripts/testdata/intraday_verification_secondary_media.html
  • Modify: docs/CONFIGURATION.md

Step 1: 先写失败测试

补 5 组测试:

  • 官方发布页验证测试:命中模型名与发布时间时,产出 official_confirmed
  • 官方价格页验证测试:只有拿到真实价格变化时,才允许产出 price_cut / price_increase
  • 活动页验证测试:官方活动页可映射为 promo_campaign
  • 二手媒体降级测试:二手媒体最多得到 secondary_confirmed,不能直接进入正式事实层
  • 泄露类隔离测试:leak_or_rumor 即使有外部讨论,也不会升级为正式日报事实

Step 2: 运行失败测试

Run:

  • go test -count=1 -tags llm_script ./scripts/verify_intraday_news_candidates.go ./scripts/verify_intraday_news_candidates_test.go

Expected:

  • 新增测试失败
  • 失败原因是缺少来源分类与验证状态映射逻辑

Step 3: 实现验证器

verify_intraday_news_candidates.go 中实现:

  • 读取 candidate / verifying 状态候选
  • 拉取 candidate_urls
  • 基于域名与页面内容判定:
    • official_page
    • pricing_page
    • official_docs
    • official_blog
    • secondary_media
  • 把验证轨迹写入 intraday_news_verification
  • 更新 intraday_news_candidate.statusverification_confidence
  • 验证成功后只更新候选层状态,不直接写 daily_signal_snapshot;正式事实仍统一由物化器汇总

规则:

  • 只有官方页面 / 价格页 / docs / 公告页可以产出 official_confirmed
  • 价格新闻若无法拿到真实价格事实,只能维持候选或二级确认,不能伪造价格变化事件
  • leak_or_rumor 默认不升级为正式事实

Step 4: 重新运行测试

Run:

  • go test -count=1 -tags llm_script ./scripts/verify_intraday_news_candidates.go ./scripts/verify_intraday_news_candidates_test.go

Expected:

  • 验证规则测试通过

Step 5: 运行一次 dry-run 验证

Run:

  • go run -tags llm_script ./scripts/verify_intraday_news_candidates.go --date=2026-05-25 --dry-run

Expected:

  • 输出 verified_total / official_confirmed_total / secondary_confirmed_total
  • dry-run 只打印摘要,不写 daily_report

Step 6: Commit

git add scripts/verify_intraday_news_candidates.go scripts/verify_intraday_news_candidates_test.go scripts/testdata/intraday_verification_official_release.html scripts/testdata/intraday_verification_pricing_page.html scripts/testdata/intraday_verification_secondary_media.html docs/CONFIGURATION.md
git commit -m "feat(intraday): add candidate verification pipeline"

Task 4: 把已验证事件接入现有 materialize_daily_signals.go

Files:

  • Modify: scripts/materialize_daily_signals.go
  • Create or Modify: scripts/materialize_daily_signals_test.go
  • Modify: docs/plans/2026-05-27-intraday-price-watch-plan.md
  • Modify: README.md
  • Modify: docs/PRODUCTION_CHECKLIST.md

Step 1: 先写失败测试

补 4 组测试:

  • 已验证官方发布事件会进入 daily_signal_snapshot.top_events
  • 已验证活动事件会进入 daily_signal_snapshot.top_events
  • candidate_onlyleak_or_rumor 不进入正式快照
  • 未拿到真实价格变化数据的“价格新闻”不会被错误映射为 price_cut / price_increase

Step 2: 运行失败测试

Run:

  • go test -count=1 -tags llm_script ./scripts/materialize_daily_signals.go ./scripts/materialize_daily_signals_test.go

Expected:

  • 新增测试失败
  • 失败原因是当前物化器还不会读取已验证候选事件

Step 3: 最小实现 verified event loader

materialize_daily_signals.go 中新增:

  • loadVerifiedIntradayNewsEvents(db, date string)
  • official_confirmed 的:
    • official_release
    • promo_campaign
    • 已确认真实价格变化的 price_cut / price_increase 映射为现有 signalModelEvent
  • 与现有 loadSignalModelEvents 结果做去重合并;同日同模型同事件类型若已由 importer / 原生 loader 给出,则 discovery 事件仅补 SourceURL / 证据缺口,不抢占优先级

约束:

  • 不新造第二套快照表
  • 不改变 daily_signal_snapshot 的正式事实语义
  • secondary_confirmed 默认不进入正式快照

Step 4: 重新运行测试

Run:

  • go test -count=1 -tags llm_script ./scripts/materialize_daily_signals.go ./scripts/materialize_daily_signals_test.go

Expected:

  • verified event 相关测试通过

Step 5: 联合验证日内边界

Run:

  • REPORT_TRIGGER_SOURCE=intraday_discovery go run -tags llm_script ./scripts/materialize_daily_signals.go --date=2026-05-25 --dry-run

Expected:

  • 输出含 page_mode / event_count
  • 不写 daily_report
  • 不覆盖 latest_report

Step 6: Commit

git add scripts/materialize_daily_signals.go scripts/materialize_daily_signals_test.go README.md docs/PRODUCTION_CHECKLIST.md docs/plans/2026-05-27-intraday-price-watch-plan.md
git commit -m "feat(intraday): materialize verified discovery events"

Task 5: 组装新的日内发现入口并补部署说明

Files:

  • Create: scripts/run_intraday_discovery_watch.sh
  • Modify: README.md
  • Modify: docs/CONFIGURATION.md
  • Modify: DEPLOYMENT.md
  • Modify: docs/PRODUCTION_CHECKLIST.md

Step 1: 实现独立入口脚本

脚本顺序固定为:

  1. discover_intraday_news_candidates.go
  2. verify_intraday_news_candidates.go
  3. materialize_daily_signals.go(仅消费 verified 事件)

要求:

  • 明确要求 DATABASE_URL
  • 搜索 / LLM 所需 key 缺失时,输出前置条件错误,不伪装成代码失败
  • 不执行 generate_daily_report.go
  • 不写 daily_report
  • 不覆盖 latest_report

Step 2: 更新调度文档

文档里明确两条 cron

  • 结构化价格刷新:run_intraday_price_watch.sh
  • 新闻发现与验证:run_intraday_discovery_watch.sh

推荐起步频率:

  • run_intraday_discovery_watch.sh:每 2 小时一次
  • run_intraday_price_watch.sh:每 4 小时一次

Step 3: 运行脚本级 dry-run

Run:

  • bash scripts/run_intraday_discovery_watch.sh --dry-run

Expected:

  • 输出候选发现摘要 + 验证摘要 + 信号物化摘要
  • 不生成正式日报产物

Step 4: Commit

git add scripts/run_intraday_discovery_watch.sh README.md docs/CONFIGURATION.md DEPLOYMENT.md docs/PRODUCTION_CHECKLIST.md
git commit -m "feat(intraday): add discovery watch runner"

Task 6: 运行最终联合验收并准备本地提交

Files:

  • Modify: README.md(仅在最终说明缺失时)
  • Modify: docs/CONFIGURATION.md(仅在最终说明缺失时)
  • Modify: DEPLOYMENT.md(仅在最终说明缺失时)

Step 1: 运行 focused Go tests

Run:

  • go test -count=1 -tags llm_script ./scripts/discover_intraday_news_candidates.go ./scripts/discover_intraday_news_candidates_test.go
  • go test -count=1 -tags llm_script ./scripts/verify_intraday_news_candidates.go ./scripts/verify_intraday_news_candidates_test.go
  • go test -count=1 -tags llm_script ./scripts/materialize_daily_signals.go ./scripts/materialize_daily_signals_test.go

Expected:

  • 发现层、验证层、信号物化层 focused tests 全通过

Step 2: 运行现有日报/前端回归边界

Run:

  • go test -count=1 -tags llm_script ./scripts/generate_daily_report.go ./scripts/generate_daily_report_test.go ./scripts/official_import_signature_audit_query_lib.go
  • bash scripts/secret_gate_test.sh
  • bash scripts/test_importers.sh
  • cd frontend && npm test -- --run
  • cd frontend && npm run build

Expected:

  • 原有日报与前端链路不回归
  • discovery 新增能力不污染正式日报边界

Step 3: 运行脚本级联合 dry-run

Run:

  • bash scripts/run_intraday_discovery_watch.sh --dry-run
  • REPORT_TRIGGER_SOURCE=intraday go run -tags llm_script ./scripts/materialize_daily_signals.go --date=2026-05-25 --dry-run

Expected:

  • 不写 daily_report
  • 不覆盖 latest_report
  • 能稳定输出候选数、验证数、事件数、page_mode、source_audit

Step 4: 本地提交

git add db/migrations/017_intraday_news_candidates.sql scripts/discover_intraday_news_candidates.go scripts/discover_intraday_news_candidates_test.go scripts/verify_intraday_news_candidates.go scripts/verify_intraday_news_candidates_test.go scripts/materialize_daily_signals.go scripts/materialize_daily_signals_test.go scripts/run_intraday_discovery_watch.sh README.md docs/CONFIGURATION.md DEPLOYMENT.md docs/PRODUCTION_CHECKLIST.md docs/plans/2026-05-25-intraday-discovery-verification-implementation-plan.md docs/plans/2026-05-27-intraday-price-watch-plan.md
git commit -m "feat(intraday): add discovery and verification watch pipeline"

验收标准

实现完成后,必须同时满足:

  • 搜索 + LLM 只能产生候选事件,不能直接写成正式日报事实
  • 只有 official_confirmed 的事件才能进入正式 daily_signal_snapshot 语义链路
  • leak_or_rumor 不进入正式日报事实层
  • run_intraday_discovery_watch.shrun_intraday_price_watch.sh 职责分离
  • 正式日报仍只由 run_daily.sh 负责
  • 新增链路不会写 daily_report、不会覆盖 latest_report
  • discovery provider adapter 在无配置时会明确报前置条件错误;有 fixture / dry-run 模式可本地验证
  • 新增 focused tests、现有日报测试、前端构建全部通过

非目标

本计划刻意不做:

  • 不新增第二套正式日报系统
  • 不让 LLM 直接替代价格 importer 或官方发布 importer
  • 不把二手媒体新闻直接映射为 price_cut / price_increase
  • 不在第一阶段引入新的前端“候选情报面板”复杂交互;若后续需要,单独立计划