From 0c032196d228bce59cc1b37cc0b25cf2e185c862 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 6 Aug 2026 08:17:14 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=97=A5=E6=8A=A5=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=80=BB=E8=A7=88=E7=BB=9F=E8=AE=A1=E4=BF=AE=E5=A4=8D=E4=B8=8E?= =?UTF-8?q?=E5=8F=A3=E5=BE=84=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _collect_pipeline_stats 24h 统计改用 fetched_at(抓取时间), 原实现从 URL 猜日期对多数源失效(实测 804 篇文章仅 3 条计入) - 删除废弃的 _extract_date_from_url - docs/db_schema.md 新增 3.1 口径说明: raw_total=当天抓取文章(sum=raw_by_source), raw_total_24h=24h 内抓取, news.total=30h 窗口 LLM 事件数(≠raw_total), importances/sentiments 之和=news.total --- docs/db_schema.md | 14 ++++++++++++++ scheduler/reporter.py | 35 +++++++++++------------------------ 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/docs/db_schema.md b/docs/db_schema.md index 45bc28b..56aa701 100644 --- a/docs/db_schema.md +++ b/docs/db_schema.md @@ -71,6 +71,20 @@ > 历史文件与新生成日报的 stats 结构存在差异(历史为 HTML 解析快照,新生成为结构化组装),前端建议按 key 防御性读取。 +### 3.1 口径说明(重要,避免误解) + +`stats` 内各数字口径不同,请勿直接互相比较: + +| 字段 | 口径 | +| --- | --- | +| `pipeline.raw_total` | **日报日期当天**抓取的文章数(`data/raw/{src}/{date}/index.jsonl` 中 `stage=article 且 success` 的条目)。`raw_by_source` 是各源明细,**其和 = raw_total**;当天未抓取/无文章的源显示 0 | +| `pipeline.raw_total_24h` | 最近 24 小时内**抓取**(按 `fetched_at`)的文章数;`raw_by_source_24h` 为各源明细,和 = raw_total_24h。当天 07:00 抓取的数据其值 ≈ raw_total(并非"24h 内发布的新闻",raw 层无发布时间的可靠字段) | +| `pipeline.proc / deduped / dups / emb_count / qdrant_count` | 抽取 / 去重后 / 重复 / 向量化 / Qdrant 总量(`qdrant_count` 为全量累计,非当天) | +| `news.total` | **过去 30 小时窗口内**经 LLM 抽取的新闻事件数。**≠ raw_total**:raw 是抓取的文章数,news 是抽取后的事件数(会有过滤/合并),两者不可互相验证 | +| `news.importances` | `{重要度等级(1-5): 事件数}`,**各等级之和 = news.total** | +| `news.sentiments` | `{情绪: 事件数}`(positive/negative/neutral),和 = news.total | +| `news.event_types` | `{事件类型: 事件数}`(TOP 10) | + --- ## 4. 常用查询示例(API 实现参考) diff --git a/scheduler/reporter.py b/scheduler/reporter.py index 86351bf..7c1583e 100644 --- a/scheduler/reporter.py +++ b/scheduler/reporter.py @@ -414,27 +414,8 @@ def _load_article_urls_from_index(index_path: Path) -> list[dict]: return articles -def _extract_date_from_url(url: str) -> datetime | None: - """从 URL 中提取发布日期(用于估算 24h 新鲜度)。""" - import re as _re2 - patterns = [ - _re2.compile(r'/(\d{4})[-/](\d{2})[-/](\d{2})/'), - _re2.compile(r'/(\d{4})(\d{2})(\d{2})/'), - _re2.compile(r'(\d{4})(\d{2})(\d{2})\.(?:s?html|pdf)'), - _re2.compile(r'/t(\d{4})(\d{2})(\d{2})_'), - ] - for pat in patterns: - m = pat.search(url) - if m: - try: - return datetime(int(m.group(1)), int(m.group(2)), int(m.group(3))) - except ValueError: - pass - return None - - def _collect_pipeline_stats(day_str: str) -> dict[str, Any]: - """收集管道统计数据(仅计文章级条目 + 24h 新鲜度)。""" + """收集管道统计数据(仅计文章级条目 + 24h 抓取新鲜度, 用 fetched_at)。""" now = datetime.now() cutoff_24h = now - timedelta(hours=24) @@ -451,12 +432,18 @@ def _collect_pipeline_stats(day_str: str) -> dict[str, Any]: raw_by_source[name] = n raw_total += n - # 统计 24h 内文章 + # 统计 24h 内抓取的文章(用 fetched_at;原实现从 URL 猜日期, 对多数源失效导致全 0) n_24h = 0 for art in articles: - dt = _extract_date_from_url(art.get("url", "")) - if dt and dt >= cutoff_24h: - n_24h += 1 + fa = art.get("fetched_at") + if fa: + try: + if datetime.fromisoformat(fa) >= cutoff_24h: + n_24h += 1 + except ValueError: + n_24h += 1 # 时间格式异常时保守计入 + else: + n_24h += 1 # 时间缺失时保守计入 raw_by_source_24h[name] = n_24h raw_total_24h += n_24h