Files

240 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 📰 新闻智能跟踪系统(news-tracker
一款面向 AI 领域的**自动采集 → 智能分析 → 邮件推送**的新闻跟踪系统。后台定时从数据源采集资讯,先规则打分、再 LLM 深度分析,把与你兴趣画像相关且重要的资讯**实时发邮件提醒**,并每天 10:00 发一份**AI 资讯日报**。带完整网页管理台,兴趣画像/数据源/通知参数全部可配。
> 当前版本 **v1.4.3**:大模型接口 token/次数统计 + 调用优先级自动切换 + 通知日志查看正文/增强分页/批量删除。
---
## 系统运转流程(一张图看懂)
```
┌──────────┐ ┌──────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ 数据源 │ │ 后台调度器 │ │ 智能分析引擎 │ │ 邮件通知 │
│ sources │───▶│ scheduler │───▶│ analysis.py │───▶│ notifier.py │
│ (7个默认) │ │ (后台线程) │ │ (规则+LLM两级分析) │ │ (实时+日报) │
└──────────┘ └──────────────┘ └──────────────────┘ └─────────────────┘
真实URL→爬虫抓取 每30分钟采集一次 规则打分→候选 LLM完成→重要度判定
crawler.py (scan_interval_min) →后台LLM深度分析 →发实时邮件
正文清洗+全文入库 每天10:00日报 →实时/日报邮件
example.com→模拟 大模型可一键切换
```
**完整链路:**
1. **采集**(每 `scan_interval_min` 分钟,默认 30):`scheduler.collect_once()``crawler.fetch_all()`——
- 真实 URL 源:用 `requests` 抓页面 → **清洗出干净可读正文**readability 风格,去除导航/广告/脚本)→ 提取候选资讯链接(按文章相似度排序、过滤导航词)→ 对前 5 条抓全文,**连同页面可读正文一起入库**(`articles.full_text`,详情页可查看);
- `example.com` 占位源:用 `simulate.items_for_source()` 仿真数据填充,保证链路不空;
- 真实源抓取失败:跳过并标记该源 `status=error`(数据源页可见),**不塞模拟数据冒充**。
2. **规则打分**(立即):`analysis.analyze_article()` 对每条新资讯算**兴趣相关度**(关键词命中权重 + 领域匹配 + 关注公司命中)和**重要度启发式**(发布/融资/禁令等强信号词、金额量级、时效性、数据源权重、公司影响),得到**综合分 total_score (0-100)**。
3. **LLM 深度分析**(后台异步):规则分 ≥ `llm_threshold`(默认 60)的资讯进入 `llm_analyze()`,**使用网页激活的大模型接口**(默认 SiliconFlow,可一键切换;调用失败自动切换下一个可用接口),输出重要度 1-10、相关度、分类、一句话结论,并与规则分融合更新。
4. **实时通知**:扫描「重要 + 已分析完成 + 未通知」的资讯,综合分 ≥ `realtime_threshold`(默认 80)→ 发 **🔥 重要AI资讯实时提醒** 邮件,标记 `notified=1`
5. **每日日报**(新闻机制,默认每天 10:00):汇总往前 `summary_window_hours`(默认 24h)内普通源综合分 ≥50 的资讯,按分排序取前 `max_summary_items`(默认 15)条发 **📰 AI资讯日报**,含领域分布统计。
6. **定制监控汇总**(定制机制,默认每天 18:00,独立时间单独配置):汇总窗口内定制源**命中推送标准**(is_important=1)的资讯发 **🎯 定制监控汇总** 邮件。
7. **历史采样**:每次采集(无论成功/失败)写入 `source_snapshots`,数据源页可查看,也可用 `/api/sources/history``/api/sources/articles` 提取给自动流程。
---
## 功能特性
- 📥 **真实网页采集**:真实 URL 源自动抓取 → readability 风格清洗出干净可读正文 → **全文入库**(详情页可看),提取候选资讯链接按文章相似度排序;`example.com` 占位源走仿真数据;失败源标记 error 不造假
- 🌐 **web-capture-api 网页提取**:数据源真实网页**默认通过 web-capture-api 项目接口抓取**(反爬强、可滚动态/选 agent-browser|playwright 后端),接口地址在设置页可改;每个数据源可**单独选择获取方式**(自动 / 仅 web-capture-api / 直接抓取)并**编辑抓取参数**(抓取动作 html/text、等待时间、滚动次数、后端、高级 JSON)
- 📋 **通知日志分页 + 筛选**:通知日志支持分页查看,可按**类型**(实时/新闻汇总/定制汇总/系统错误)、**状态**(成功/失败)、**关键词**搜索
- ⚠️ **系统错误邮件通知**:采集/分析/通知/汇总/系统异常自动记录并邮件通知,可设置**通知频率(冷却分钟)** 与 **静默时段**(多段/跨午夜,静默期不发、结束后自动补发)
- 🧠 **两级智能分析**:规则打分秒级响应 + LLM 深度分析(重要度/相关度/分类/结论)
- 🤖 **大模型接口可配置**:设置页可增删改/测试大模型接口,预置 SiliconFlow(默认)/ DeepSeek 官方 / Autodl / Local Qwen 四个,**一键切换即时生效**,激活接口失败自动切换下一个可用接口
- 🔥 **实时重要资讯邮件**:综合分达到阈值自动推送,单批最多 10 条
- 🎯 **定制监控数据源**:数据源可选「定制监控」方式——**不设权重**,填写「推送标准」后由大模型逐条判断是否达到标准,达到即实时邮件推送(页面可直接「🧪 测试推送标准」);命中推送的资讯在邮件/列表中带「🎯 定制监控命中」标识
- 🔀 **两套独立机制**:新闻监控与定制监控**完全分开**——各自的采集间隔、汇总时间/窗口/条数单独配置(设置页「定制监控机制」区块);定制源命中会单独发「🎯 定制监控汇总」邮件,不混入新闻日报
-**每源独立采集周期**:全局有统一采集间隔(新闻30分/定制15分),每个数据源可用本源「采集周期」字段自定义覆盖(0=跟随全局)
- 📜 **历史采样留档**:每次采集记录时间/条数/成功失败到 `source_snapshots`,数据源页「📜 历史采样」可查看每次快照及本源采集到的资讯;`/api/sources/history``/api/sources/articles` 供自动流程提取历史数据
- 📰 **每日 AI 资讯日报**:默认每天 10:00,按领域统计 + 重点资讯卡片
- 🎯 **兴趣画像管理**:关键词(带权重)、领域、关注公司,全部可在网页维护,改完一键「重新打分」
- 🔗 **数据源管理**:7 个内置数据源模板,**完整编辑**(模态框一次性改名称/类型/URL/权重/描述/监控方式/推送标准)、启停、删除;普通源带权重,定制监控源填「推送标准」
- 📊 **网页管理台**:仪表盘(总数/重要/待分析/趋势/领域分布)、资讯列表(筛选/搜索/分页)、详情页(含全文)、通知日志
- 📧 **邮件通知**:支持 plain / starttls / ssl 三种 SMTP 模式,可测试发送
- 🛠 **手动操作台**:一键采集 / 一键 LLM 分析 / 一键发日报 / 一键造数据 / 重新打分 / 测试邮件
---
## 技术栈
- **后端**Python 3 + Flaskthreaded
- **网页采集**:优先 **web-capture-api**agent-browser / Playwright 双后端,反爬强);直接抓取回退用 requests + BeautifulSoup/lxmlreadability 风格正文清洗)
- **数据库**SQLite(原生 sqlite3WAL 模式,无 ORM
- **大模型**OpenAI 兼容接口,默认 SiliconFlow `deepseek-ai/DeepSeek-V4-Flash`(可一键切换,`response_format=json_object`
- **前端**:原生 HTML/CSS/JS + Jinja2 模板(无前端框架)
---
## 目录结构
```
news-tracker/
├── app.py # Flask 主应用:页面路由 + API(含 /api/llm 大模型接口管理)
├── config.py # 全局配置(端口/LLM/预置大模型接口/采集参数/自动化/画像/数据源模板)
├── db.py # SQLite 数据访问层(原生 sqlite3;含 llm_providers / full_text 迁移)
├── crawler.py # 真实网页采集:抓取 + readability 清洗 + 链接提取 + 全文入库
├── scheduler.py # 后台调度器:定时采集 + 每日日报 + 实时通知扫描
├── analysis.py # 智能分析引擎:规则打分 + LLM 深度分析(多接口自动切换)
├── simulate.py # 模拟数据源(example.com 占位源 / 真实源失败时回退)
├── notifier.py # 邮件通知(实时 / 日报 / 系统错误,含通知频率+静默时段控制)
├── start.sh # 启动/停止/状态脚本
├── templates/ # Jinja2 页面模板
├── static/ # 静态资源
├── data/ # news_tracker.db(运行时生成)
└── logs/ # 运行日志 + app.pid
```
---
## 快速开始
```bash
cd works/news-tracker
./start.sh # 启动(默认端口 16100
./start.sh status # 查看状态
./start.sh stop # 停止
./start.sh restart # 重启
```
访问:`http://<服务器IP>:16100/`
> ⚠️ 端口 16100 属于外网安全组白名单区间(16001-16100),可直接公网访问。
> 首次启动自动初始化:写入默认数据源/兴趣画像/设置,并 `simulate.seed_all()` 造一批模拟资讯 + 后台跑一轮 LLM 分析。
---
## 配置说明
所有默认值在 `config.py`,运行时可在网页「设置」页修改并持久化到数据库(网页优先级更高):
### 自动化参数(设置页)
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `auto_collect` | 1 | 是否自动定时采集 |
| `scan_interval_min` | 30 | 采集扫描间隔(分钟) |
| `realtime_threshold` | 80 | 综合分 ≥ 该值 → 实时邮件通知 |
| `llm_threshold` | 60 | 规则分 ≥ 该值 → 进入 LLM 深度分析 |
| `realtime_enabled` | 1 | 是否启用实时重要资讯邮件 |
| `summary_enabled` | 1 | 是否启用每日汇总 |
| `summary_time` | 10:00 | 每日汇总时间 |
| `summary_window_hours` | 24 | 汇总窗口(往前 N 小时) |
| `max_summary_items` | 15 | 汇总邮件最多条目数 |
### 邮件配置(设置页)
`smtp_host` / `smtp_port` / `smtp_user` / `smtp_pass` / `smtp_mode`(plain|starttls|ssl) / `email_to` / `sender_name`。默认 `mail.tphai.com:587` plain,收件人 `wlq@tphai.com`
### 大模型接口(设置页,一键切换)
预置 4 个接口(`config.py``LLM_PROVIDERS_DEFAULT`,首启写入 `llm_providers` 表):
| 名称 | Base URL | 模型 |
|------|----------|------|
| **SiliconFlow(默认激活)** | `https://api.siliconflow.cn/v1` | `deepseek-ai/DeepSeek-V4-Flash` |
| DeepSeek 官方 | `https://api.deepseek.com` | `deepseek-v4-flash` |
| Autodl(火山方舟中转) | `https://www.autodl.art/api/v1` | `qwen3.6-plus` |
| Local Qwen(内网) | `http://121.40.164.32:18003/v1` | `unsloth/Qwen3.8-27B-Q6_K` |
设置页可**增删改/测试**任意接口,点「⚡ 切换为当前」一键切换即时生效;分析时当前接口失败会自动切换下一个可用接口。`config.py` 里的 `LLM_BASE_URL/KEY/MODEL` 仅作无激活记录时的回退。
### 采集参数(config.py
`CRAWL_DEFAULTS``per_source_links`(每源最多采集条目,默认 8)、`full_fetch_links`(其中抓全文条数,默认 5)、`crawl_timeout`(单页超时,默认 20s)、`user_agent`
### web-capture-api(设置页)
`WEBCAPTURE_DEFAULTS``enabled`(是否启用)、`api_url`(默认 `http://121.40.164.32:16025`,可改)、`timeout`(单次抓取超时秒)。数据源页可对每个源选**获取方式**(`auto`=优先 web-capture-api、失败回退直接抓取;`webcapture`=仅走网页提取服务;`direct`=直接抓取)并编辑**抓取参数**`capture_params` JSON`action` html|text、`wait_time` 等待加载 ms、`scroll_times` 滚动次数、`backend` auto|agent-browser|playwright,以及任意高级参数如 `scroll_delay`/`full_page`/`viewport`)。
### 系统错误邮件通知(设置页)
`ERRNOTIFY_DEFAULTS``enabled`(是否启用)、`mode``immediate` 立即 / `cooldown` 按冷却聚合 / `off` 关闭)、`cooldown_min`(**通知频率**:两次错误邮件最小间隔分钟,默认 60)、`quiet_enabled` + `quiet_periods`**静默时段**,每段 `HH:MM-HH:MM`,支持多段/跨午夜,静默期不发、结束后自动补发)、`max_items`(单封最多错误条数)。错误记录在 `system_error_log` 表(同类合并计数),发送后写 `notification_log(type=error)`
---
## 网页页面
| 路由 | 说明 |
|------|------|
| `/dashboard` | 仪表盘:总资讯/重要资讯/待分析/已通知 + 近7天趋势 + 领域分布(重要资讯 TOP / 最新收录各显示最多 10 条,含「更多」链接) |
| `/news` | 资讯列表:按领域/重要/关键词搜索,分页 |
| `/news/<id>` | 资讯详情:规则分/LLM分/实体/分析结论 + **页面可读全文** |
| `/sources` | 数据源管理:**完整编辑**(模态框,含获取方式+抓取参数)/启停/删除,支持普通(权重)与定制监控(推送标准),每源独立采集周期,「📜 历史采样」可查看每次采样快照与本源资讯 |
| `/profile` | 兴趣画像:关键词/领域/公司维护 |
| `/logs` | 通知日志:**分页 + 类型/状态/关键词筛选**,支持测试邮件/测试错误通知 |
| `/settings` | 设置:自动化参数(新闻机制)+ **定制监控机制(独立配置)** + **网页提取服务(web-capture-api** + **系统错误邮件通知(频率/静默)** + 邮件配置 + **大模型接口管理(一键切换/测试)** |
---
## API
| 接口 | 方法 | 说明 |
|------|------|------|
| `/api/stats` | GET | 统计数据(总数/重要/趋势/领域分布) |
| `/api/sources` | POST | `action=add\|update\|delete\|toggle\|test_standard` 数据源管理(update 可改全部配置含采集周期;test_standard=用大模型试测推送标准) |
| `/api/sources/history` | GET | `?source_id=&limit=` 数据源历史采样记录(每次采集时间/条数/状态/备注,自动流程提取用) |
| `/api/sources/articles` | GET | `?source_id=&page=&page_size=&q=` 数据源历史采集到的资讯(分页/搜索,自动流程提取用) |
| `/api/profile` | POST | `action=add\|delete``kind=keyword\|domain\|company` 画像维护 |
| `/api/settings` | POST | 更新 `auto` / `mail` / `custom` / `webcapture` / `errnotify` 设置 |
| `/api/llm` | POST | 大模型接口:`add\|update\|delete\|switch\|toggle\|test`switch=一键切换) |
| `/api/actions` | POST | `action=collect`(采集) `llm`(LLM分析) `summary`(发日报) `seed`(造数据) `reanalyze`(重新打分) `test_mail`(测试邮件) `test_error_mail`(测试错误通知) `test_webcapture`(测试网页提取服务) |
| `/api/logs` | GET | 通知日志分页:`?page=&page_size=&type=&status=&q=` |
| `/api/errors` | GET | 系统错误日志:`?limit=`(最近 N 条,含累计次数/首末次时间/通知状态) |
请求体为 JSON,如:`{"action": "collect"}``{"action": "llm", "limit": 10}`
---
## 数据模型
- **sources**:数据源(name/type/url/weight/**kind**/**monitor_standard**/**scan_interval_min**/**fetch_method**/**capture_params**/enabled/status/last_fetch/last_count`kind`=normal 普通按权重打分 / custom 定制监控按推送标准 LLM 判断;`monitor_standard`=定制监控的推送标准说明;`scan_interval_min`=本源采集间隔分钟数,0=跟随所属机制全局值;`fetch_method`=auto 优先 web-capture-api 失败回退直接抓取 / webcapture 仅走网页提取服务 / direct 直接抓取;`capture_params`=web-capture-api 抓取参数 JSONstatus 标记真实源采集 ok/error
- **source_snapshots**:历史采样记录(source_id/fetched_at/count/status/detail,每次采集留档)
- **articles**:资讯(title/url/content/summary/**full_text(页面可读全文)**/domain/entities/importance/relevance/total_score/is_important/analysis/llm_status/notified/status/published_at/collected_at
- **keywords** / **domains** / **companies**:兴趣画像
- **llm_providers**:大模型接口(name/base_url/api_key/model/active/enabledactive=当前激活)
- **notification_log**:邮件通知日志(typerealtime / summary / custom_summary / error
- **system_error_log**:系统错误日志(source/message/detail/count/pending/first_seen/last_seen/last_notified/notified_count,同类错误合并计数,pending=待通知)
- **settings**KV 配置(`auto` / `mail` / `custom` / `webcapture` / `errnotify` 为 JSON
`llm_status``pending`(待分析) → `done`(完成) / `skipped`(未达标跳过) / `error`(失败)
**采集调度**`scheduler.collect_once()` 按**每源独立周期**采集(本源 `scan_interval_min` > 0 用本源值,否则普通源用 `scan_interval_min`、定制源用 `custom_scan_interval_min`);调度循环 30s 轮询到期源。新闻源受 `auto_collect` 控制、定制源受 `custom_enabled` 控制,互不影响。手动「立即采集」忽略周期全量采集。
**定制监控(kind=custom**:无权重(规则打分时权重项按 0 处理,仅作展示);采集后**无条件进入 LLM 分析**(不受 `llm_threshold` 限制),LLM 按该源的 `monitor_standard`(推送标准)判断 `meets_standard`,达到 → `is_important=1` → 实时邮件推送;未达到 → 不推送。命中推送的资讯也会纳入每日日报。
**综合分公式**`total_score = 0.4 × 相关度 + 0.6 × 重要度`LLM 分析后按 `0.6×规则分 + 0.4×LLM分量` 融合)
---
## 接入真实数据源
系统已内置真实网页采集(`crawler.py`),接入真实源**无需写代码**
1. 在数据源页「编辑」已有源或「新增」源,填真实 URL(如媒体频道/官网新闻/RSS 页);
2. 保存后下次采集会自动通过 **web-capture-api** 抓取该页 → 清洗出可读正文 → 提取资讯链接 → 对前几条抓全文入库(每源可在「获取方式/抓取参数」里调:抓取动作、等待时间、滚动次数、后端等);
3. 抓取失败的源会在数据源页标记 **error** 状态(网络被拒/反爬),并触发**系统错误邮件通知**(受频率/静默控制);
4. `example.com` 开头的占位地址继续走模拟数据,方便调试。
> 提示:web-capture-api 接口地址在设置页「网页提取服务」可改、可「测试接口」;个别重 JS 渲染/强反爬站点可改 `action=text` 整页取正文,或换 `backend=playwright`。
---
## 常见问题
- **收不到邮件?** 在设置页点「测试邮件」;确认 `smtp_mode` 与端口匹配(plain 一般不加密、starttls 用 587、ssl 用 465)。
- **LLM 分析失败?** 设置页大模型接口点「测试」;切一个更稳的接口(如 SiliconFlow);系统会自动切备用接口,仍失败则该条标 `error` 不阻塞。
- **真实源采集不到?** 数据源页看该源状态是否 **error**(网络被拒/反爬);可换成可达的站点或 RSS 地址。
- **想立即看效果?** 数据管理页(`/api/actions` 手动)点「立即采集 / LLM 分析 / 发送日报」。
- **改了兴趣画像想重新算分?** 点「重新打分」(reanalyze)即可全量重跑规则打分。
- **日志在哪?** `logs/app.log`(服务日志)+ 网页 `/logs`(通知日志,可分页筛选)。
- **系统出错没收到邮件?** 设置页「系统错误邮件通知」确认已启用、通知方式非「关闭」、冷却分钟不宜过大,且当前不在静默时段(静默时段不发送,结束后自动补发)。
---
## Git
- 仓库:`hz4th_coder/news-tracker`
- 版本:`v1.0.0`(初版)→ `v1.0.1`(补 README)→ `v1.1.0`(真实网页采集+全文入库 / 大模型接口多预置一键切换 / 数据源可编辑)→ `v1.2.0`(数据源完整编辑模态框 / 定制监控类型:无权重+推送标准+LLM判断推送+测试按钮)→ **`v1.3.0`**(每源独立采集周期 / 定制监控与新闻监控分离独立配置+独立汇总 / 历史采样留档+查看+提取API)→ **`v1.4.0`**(通知日志分页+筛选 / 仪表盘 TOP10+更多链接 / 数据源走 web-capture-api 抓取+获取方式与参数可编辑 / 系统错误邮件通知:频率+静默时段)→ **`v1.4.1`**(修复邮件退信:发信补 `Date` 头,mail.tphai.com amavisd 强制要求否则 554 退信 / 数据源状态列新增一键启动/停用开关,点击即生效无需刷新)→ **`v1.4.2`****修复日报/定制汇总刷屏**`_maybe_daily` 日期判断元组嵌套 bug 导致每天只发一次的逻辑失效、到点后每 30s 重复发;改为纯字符串 + 发送日期持久化到 settings,重启也不重复 / **一键全部启用/停用**:数据源页顶部两个按钮 + `/api/sources` 新 action enable_all/disable_all / **快速筛选**:关键词搜索 + 监控方式 + 启用状态 三条件过滤,重置一键还原)→ **`v1.4.3`**(**大模型调用统计**:新增 llm_stats 表按天/接口记录调用次数、Prompt/Completion/总 Token、失败次数,设置页新增「LLM 调用统计」区块(各接口累计 + 最近14天) / **大模型调用优先级**llm_providers 新增 priority 字段,设置页 ↑↓ 调整优先级,调用按优先级顺序(高优先失败→自动切换下一个,全部失败才算异常并邮件通知),每次调用自动记录统计 / **通知日志增强**:发信时存正文(body),网页点击主题/👁查看弹窗渲染邮件正文(旧记录回退到关联资讯链接);分页增强(每页条数选择/首页/末页/跳转);支持勾选批量删除与「删除当前筛选结果全部」)