修改说明

This commit is contained in:
poiuy
2026-07-12 20:11:56 +08:00
parent 54ca4b1b6a
commit c1a7ee3351
2 changed files with 766 additions and 249 deletions
+480 -249
View File
@@ -1,286 +1,517 @@
# 🪖 军事科技每日摘要系统 V3
# 军事科技每日资讯推送系统
完整的军事科技 RSS 抓取 + AI 智能分析 + 增量缓存 + 运行监控系统
基于大模型的军事/科技新闻采集、评分、整合与推送系统。自动抓取 RSS 订阅源,经 LLM 评分筛选与实体聚类后,生成日报并推送到飞书群。
## ✨ 完整功能
## 系统架构
### 📥 数据抓取
- 🔗 可配置 RSS 源直抓(rss_feeds.txt 管理,支持 100+ 源)
- ⏰ 24 小时时间窗口过滤
- 🔄 同源标题去重(保留跨源对同一事件的不同报道)
- ⚡ ThreadPoolExecutor 并发抓取,内置失败重试
```
┌───────────────────────────────────────────────────────────────┐
│ Fetch 循环(每 30 分钟) │
│ RSS 抓取 → 中英翻译 → LLM 评分 → 硬约束过滤 → 动态阈值 │
│ → 实体提取 + 角度分类 → 实体归一化 → 实体聚类 │
│ → 广告过滤 → 缓存 → 簇热度计算 → 即时推送(≥90 分) │
└──────────────────┬────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────┐
│ Push 循环(每日 08:00
│ 推送 1: 🔥 热点速览(簇视图) │
│ 今日速览 + 热点洞察(含[持续跟踪])+ 跨日追踪 + 趋势分析 │
│ 推送 2: 📰 军事科技每日精选(单篇视图) │
│ 网摘 Top3 + 分类概览(5 类 × 5 条) │
│ 推送 3: 🖼️ 网摘图片(PNG 合并长图,图床上传后推送) │
└───────────────────────────────────────────────────────────────┘
```
### 🗄️ 增量缓存
- 💾 SQLite 本地缓存:文章翻译/AI分析结果持久化
- 📰 网摘文本缓存:再次运行零 token 消耗
- 🏷️ 分类介绍缓存:按日期+分类独立存储
- 🧹 自动过期清理(7天)+ 表结构自动迁移
| 核心功能 | 说明 |
|---------|------|
| 订阅源管理 | OPML 文件管理 137 个 RSS 源(英文军事 + 中文微信公众号) |
| 双语翻译 | 中英自动分离(汉字占比 > 20%),中文零 API 调用,外文并发翻译 |
| 三层评分 | 硬约束(非目标 ≤79 / KOL ≤89)+ 时间衰减 + 动态阈值 |
| 实体提取 | 5 类实体(人物/机构/装备/事件/地域)+ 6 类角度分类,双层归一化(规则 200+ 别名 + LLM 消歧) |
| 实体聚类 | 加权实体重叠 + 并查集,细粒度聚类保留多角度报道,自动命名簇 |
| 簇热度 | 四维评分(最高分 + 传播 + 角度 + 优先)+ LLM 精评融合,热度簇提升为热点 |
| 簇记忆 | SQLite 持久化已推送簇 ID,24h 内同簇不重复推送 |
| 广告过滤 | 四层防御纵深(tag 黑名单 → 动态阈值 → 空角度 → 空实体) |
| 增量缓存 | SQLite 三级缓存(文章/网摘/分类摘要),二次运行几乎零 token 消耗 |
| 飞书推送 | 即时热点推送(≥90 分)+ 每日早报三波(热点速览 + 每日精选 + 网摘图片) |
| 双视角洞察 | LLM 一次调用输出精炼摘要 + 结构化洞察(事件脉络/多角度分析/影响与展望) |
| 分类概览 | 5 类军事新闻分类(装备技术/地区安全/军事改革/产业观察/其他热点),每类 top 5 |
| 网摘图片 | PIL 渲染网摘为 PNG 合并长图,自适应高度 + 动态文本换行,图床 API 上传后推送飞书 |
| 反馈闭环 | 用户评分修正记录 + 规则自动生成,修正后的评分自动应用到后续批次 |
| 运行监控 | 12 阶段耗时 + API 调用统计 + 源抓取成功率 + 缓存命中率 |
### 🌐 智能翻译
- 🔍 自动识别外文文章
- 📝 分步翻译:先标题 → 再正文(仅通过筛选的文章)
- ⚡ 并发翻译(ThreadPoolExecutor
## 快速开始
### 🔑 关键词筛选
- 🎯 44 个军事科技专业关键词库
- 📊 命中数排序,文章 >15 篇时自动过滤
### 环境要求
### 🤖 AI 智能分析
- ⭐ 多维度评分(技术35% + 战略30% + 信息20% + 时效15%
- 📋 自动分类:今日必看 / 装备动态 / 地区冲突 / 战略政策
- ✍️ 每篇文章 AI 智能摘要(80-120字)
- 🔮 分类深度洞察分析(每类50字综述)
- 🏆 TOP3 必看深度分析
- ⚡ 批量并发评分 + 分类
- Python 3.10+
- 能访问 OpenAI 兼容 APIDeepSeek / 火山引擎 / OpenAI 等
### 🖼️ 图片生成
- 📄 3800px 超长网摘图片
- 🎨 军事主题视觉设计
- 📊 包含TOP3完整内容
- 💾 自动保存PNG文件
### 📊 运行监控
- ⏱ 各阶段耗时统计,自动输出结构化报表
- 🤖 API 调用量/token 按用途和模型分组统计
- 📰 源抓取成功/失败率一目了然
- 📡 文章统计 + 缓存命中率
## 🚀 快速开始
### 方式一:本地直接运行(推荐)
### 安装
```bash
# 1. 安装依赖
# 1. 进入项目目录
cd my-daily
# 2. 安装依赖
pip install -r requirements.txt
# 2. 配置环境
cp config/.env.example config/.env
vim config/.env
# 编辑 OPENAI_API_KEY
# 3. 配置环境变量
cp .env.example .env
# 编辑 .env,填入 API Key 和 Webhook 地址
# 3. 运行
make local
# 4. 配置系统参数(可选,已有默认值)
cp config.json.example config.json
# config.json 已存在时跳过,按需修改调度/评分/源过滤参数
```
### 方式二:Docker 部署
### 环境变量(.env
```env
# OpenAI 兼容接口(必填)
OPENAI_API_KEY=your_api_key
OPENAI_API_BASE=https://api.deepseek.com
OPENAI_MODEL=deepseek-v4-flash
# LLM 消歧 + 网摘专用模型(可选,不填则复用 OPENAI_MODEL
WEBZINE_MODEL=deepseek-v4-pro
# 飞书推送(可选,不填则跳过推送)
FEISHU_WEBHOOK=your_webhook_url
# 图床上传配置(可选,网摘图片通过图床 API 上传)
IMAGE_HOSTING_PASSWORD=your_image_hosting_password_here
# ── 定时计划配置(可选,覆盖 config.json)──
# FETCH_INTERVAL_MINUTES=30 # RSS 拉取间隔(分钟)
# FETCH_LOOKBACK_MINUTES=60 # 回溯时间窗口(分钟)
# PUSH_CRON="0 8 * * *" # 推送定时计划(cron 表达式,多个用逗号分隔)
# TIMEZONE_HOURS=8 # 时区
# ── 评价管线参数覆盖(可选,最高优先级)──
# HEAT_THRESHOLD=30 # 热度阈值固定值
# HOT_THRESHOLD=90 # 即时推送分数线
# CLUSTER_MERGE_THRESHOLD=4 # 聚类合并阈值
# PRECISE_BLEND_RATIO=0.4 # LLM 精评融合权重
# MEMORY_WINDOW_HOURS=24 # 簇记忆去重时间窗口
# CROSS_DAY_MIN_OVERLAP=0.05 # 跨日关联实体重叠阈值
```
> **优先级规则**`.env` 环境变量 > `config.json` > 源码默认值。详见 [CONFIG.md](CONFIG.md)。
### 配置项速查
所有参数及调优指南见 **[CONFIG.md](CONFIG.md)**。
| 配置节 | 控制环节 | 关键参数 |
|--------|----------|----------|
| **`.env`** | 接口/推送/定时/图床/评价覆盖 | `OPENAI_API_KEY`, `FEISHU_WEBHOOK`, `IMAGE_HOSTING_PASSWORD`, `FETCH_INTERVAL_MINUTES`, `PUSH_CRON` |
| `schedule` | 定时计划 | `fetch_interval_minutes`(30), `push_cron`(0 8 * * *) |
| `filter` | 过滤推送 | `hot_threshold`(90), `cluster_promotion_offset`(15) |
| `scoring` | 单篇评分 | `non_target_max_score`(79), `half_life_hours`(12), `min_age_hours`(6) |
| `cluster` | 聚类成簇 | `merge_threshold`(4), `entity_weights`, `generic_entities` |
| `heat` | 簇热度 | `score_weight`(0.4), `propagation_cap`(30), `precise_blend_ratio`(0.4) |
| `memory` | 去重记忆 | `window_hours`(24), `cleanup_days`(7) |
## 命令用法
### 一键启动(推荐)
```bash
# 1. 克隆或解压
cd military-digest-v3
# 2. 配置环境
cp config/.env.example config/.env
vim config/.env
# 编辑 OPENAI_API_KEY
# 3. 构建镜像
make build
# 4. 测试运行
make run-once
# 5. 安装定时任务
make install-cron
python start.py
```
## 📁 目录结构
同时启动数据管线后端(Fetch + Push 循环)和 Web 归档站点(http://0.0.0.0:8080)。所有终端输出实时写入 `logs/YYYYMMDD-HHmmSS.log`,按 Ctrl+C 安全停止所有服务。
```
military-digest-v3/
├── scripts/
│ ├── military_daily_report_v3.py # 主入口 — 流程编排
│ └── modules/ # 功能模块包
│ ├── __init__.py # 包初始化 + 统一导出
│ ├── config.py # 配置模块:常量 + AI配置加载
│ ├── utils.py # 工具函数:语言检测/HTML清洗/AI调用/换行
│ ├── rss_fetcher.py # RSS抓取:12源 × 6线程并发
│ ├── translator.py # 翻译处理:中英分离 + 并发翻译
│ ├── keyword_filter.py # 关键词筛选:49词命中排序
│ │ ├── ai_analyzer.py # AI分析:评分/分类/摘要/洞察/网摘生成
│ │ ├── cache.py # SQLite缓存:文章/网摘/分类介绍增量缓存
│ │ ├── monitor.py # 运行监控:阶段耗时/API/AI token调用统计
│ │ ├── pusher.py # 推送模块:企业微信/飞书 webhook 推送
│ │ ├── image_generator.py # 图片生成:TOP3合并3800px长图
│ └── get_chinese_font.py # 字体模块:跨系统中文字体适配
├── config/
│ ├── config.json # 系统配置
│ └── .env.example # 环境变量示例
├── output/ # 输出目录
│ ├── military_report_YYYYMMDD.md # 完整报告(Markdown格式,支持链接、样式)
│ ├── military_webzine_YYYYMMDD.txt # TOP3网摘文本
│ └── military_webzine_YYYYMMDD.png # TOP3网摘合并长图
├── logs/ # 日志目录
├── Dockerfile
├── docker-compose.yml
├── Makefile
└── README.md
```
## 🧩 模块架构
```
military_daily_report_v3.py ← 主入口(流程编排)
├── config.py ← 配置常量,零依赖
├── logger.py ← 统一日志模块
├── cache.py ← SQLite增量缓存,自动迁移
├── monitor.py ← 运行监控统计
├── pusher.py ← 企业微信/飞书推送
├── rss_fetcher.py ← 依赖 config + utils + monitor
├── translator.py ← 依赖 utils
├── keyword_filter.py ← 依赖 config
├── ai_analyzer.py ← 依赖 config + utils
├── image_generator.py ← 依赖 utils + get_chinese_font
└── get_chinese_font.py ← 零依赖
```
### 各模块职责
| 模块 | 行数 | 职责 | 对外导出 |
|------|------|------|----------|
| `config.py` | 90 | RSS源、关键词库、评分维度、分类定义、AI配置加载 | `RSS_FEEDS`, `WECHAT_SOURCES`, `KEYWORDS`, `SCORE_DIMENSIONS`, `CATEGORIES`, `load_ai_config()` |
| `utils.py` | 130+ | 语言检测、HTML清理、时间解析、AI API封装(含监控钩子)、文本换行 | `is_chinese()`, `clean_html()`, `extract_content()`, `parse_pub_time()`, `call_ai()`, `wrap_text()` |
| `rss_fetcher.py` | 100+ | 多线程并行抓取RSS源(MAX_THREADS并发,含2次重试+监控上报) | `fetch_all_feeds()` |
| `translator.py` | 150+ | 中英文分离 + 标题/正文分步AI并发翻译 | `batch_translate_titles()`, `batch_translate_contents()` |
| `keyword_filter.py` | 23 | 44关键词命中计数 → 排序 → Top15筛选 | `keyword_filter()` |
| `ai_analyzer.py` | 240+ | 四维度评分、智能分类、中文摘要、分类洞察、《参考消息》风格网摘生成 | `score_and_classify_article()`, `calculate_weighted_score()`, `generate_category_overview()`, `generate_webzine_for_article()`, `batch_score_and_classify_articles()` |
| `cache.py` | 310+ | SQLite增量缓存:文章处理结果+网摘+分类介绍,支持自动迁移+过期清理+断点续跑 | `get_cache()`, `get_article()`, `save_article()`, `save_article_webzine()`, `get_category_summary()`, `save_category_summary()` |
| `monitor.py` | 160+ | 运行时监控:阶段耗时/AI API调用量+token/源成功率/缓存命中率,结束自动输出报表 | `get_monitor()`, `monitor.stage()`, `monitor.report()` |
| `pusher.py` | 130+ | 企业微信文本/图片/Markdown推送 + 飞书文本推送,主流程末自动触发,失败优雅降级 | `send_daily_push()`, `push_wechat_text()`, `push_wechat_image()`, `push_feishu_text()` |
| `image_generator.py` | 220 | 网摘文本渲染为PNG图片(单篇 + TOP3合并长图) | `create_webzine_image()`, `create_combined_webzine_image()` |
| `get_chinese_font.py` | 50 | 跨系统中文字体自动匹配,支持Windows/Linux/macOS,内置开源字体可选 | `get_chinese_font(size, bold)` |
## ⚙️ 配置说明
### 环境变量 (.env)
| 配置项 | 说明 | 默认值 |
|--------|------|--------|
| OPENAI_API_KEY | OpenAI API 密钥 | - |
| OPENAI_API_BASE | API 地址 | https://api.openai.com/v1 |
| OPENAI_MODEL | AI 分析模型 | gpt-4o-mini |
| WEBZINE_MODEL | 网摘专用模型 | deepseek-v3.2-250518 |
### config.json 配置项
| 配置项 | 说明 | 默认值 |
|--------|------|--------|
| time_window_hours | 时间窗口(小时) | 12 |
| max_articles | 最多保留文章数 | 15 |
| top3_count | TOP3 必看文章数 | 3 |
| wechat_target | 微信接收者ID | - |
| wechat_account | 微信机器人账号 | - |
| enable_wechat_push | 开启微信推送 | true |
| enable_feishu_push | 开启飞书推送 | false |
| enable_image_gen | 开启图片生成 | true |
| enable_txt_gen | 开启TXT文件生成 | true |
### rss_feeds.txt 订阅源配置
所有RSS源都在`config/rss_feeds.txt`文件中管理,不需要修改代码即可增删订阅源:
- 格式:每行 `源名称|RSS地址`
-`#`开头的行是注释,会自动忽略
- 空行会自动跳过
- 修改后直接运行程序,无需重启或改代码
- ✅ 容错保障:文件不存在、为空或格式错误时,自动回退使用内置的12个默认源,不会影响程序运行
- 支持所有标准RSS/Atom格式,中英文源无需区分,系统自动识别翻译
## ⏰ 定时任务
### 方式一:宿主机 crontab
### main.py 命令
```bash
make install-cron
# 或手动添加
crontab -e
# 加入:0 8 * * * cd /path/to/military-digest-v3 && python3 scripts/military_daily_report_v3.py >> logs/cron.log 2>&1
python main.py <command> [options]
```
### 方式二:Docker Cron
| 命令 | 说明 | 示例 |
|------|------|------|
| `check` | 校验 LLM 接口可达性 | `python main.py check` |
| `fetch` | 单次抓取 → 翻译 → 评分 → 缓存 | `python main.py fetch --lookback 120` |
| `test-fetch` | 测试抓取(限定源数量) | `python main.py test-fetch --max 5 --lookback 300` |
| `push` | 单次推送(含早报/默认两种模式) | `python main.py push` |
| `rss` | 仅生成 RSS Digest(不推送,打印到终端) | `python main.py rss` |
| `loop` | 长跑模式(Fetch + Push 双循环并行) | `python main.py loop` |
| `feedback` | 提交评分修正反馈 | `python main.py feedback --link URL --score 85 --reason "理由"` |
### test-fetch / fetch 参数
| 参数 | 默认值 | 说明 |
|------|:---:|------|
| `--max N` | 5(仅 test-fetch | 仅拉取前 N 个源 |
| `--lookback N` | 来自 config | 时间回溯窗口(分钟) |
### feedback 参数
| 参数 | 说明 |
|------|------|
| `--link URL` | 需要修正评分的文章链接 |
| `--score N` | 修正后的分数(0-100 |
| `--reason "..."` | 修正理由 |
| `--list` | 查看所有已记录的修正 |
| `--rules` | 查看已生成的修正规则 |
| `--stats` | 查看修正统计数据 |
## 部署
### Linuxsystemd
```bash
docker-compose --profile cron up -d
sudo bash scripts/setup_systemd.sh # 一键安装
bash scripts/status.sh # 查看运行状态
sudo journalctl -u my-daily-fetch -f # 实时日志
sudo bash scripts/uninstall_systemd.sh # 卸载
```
## 🔍 RSS 源列表
### Windows(计划任务)
1. Defense One - 美国防务新闻
2. NEWUAS - 无人机系统新闻
3. Seapower - 海军力量杂志
4. The War Zone - 战区军事分析
5. 国防科技要闻
6. 战略前沿技术
7. 无人机邦
8. 浮空飞行器
9. 海鹰资讯
10. 渊亭防务
11. 电波之矛
12. 龙牙的一座山
## 📊 输出示例
```
🪖 军事科技每日摘要
📅 2026年05月12日 | 共收录 12 篇
====================
🔥 今日必看 TOP3
====================
1.【美军MQ-9无人机测试发射激光制导火箭弹】
⭐ 综合评分:9.2/10
📰 来源:The War Zone
📝 摘要:美国通用原子公司联合美空军在内华达测试场完成...
🔮 深度洞察:低成本改装令察打无人机获得反无人机能力...
🔗 阅读原文:...
...
```batch
scripts\setup_service.bat install # 安装为计划任务
scripts\setup_service.bat start # 手动运行一次
scripts\setup_service.bat status # 查看任务状态
scripts\setup_service.bat loop # 前台调试
scripts\setup_service.bat uninstall # 卸载计划任务
```
## 🔄 数据流程
### Docker(推荐,适用于绿联云 NAS / 任何 Linux 主机)
```
main()
├── Step 1: RSS 并行抓取(ThreadPoolExecutor × MAX_THREADS 并发,内置重试)
├── Step 2: 24小时时间窗口过滤
├── Step 3: 同源标题去重
├── Step 4: SQLite 缓存查询(分离已处理/待处理文章)
├── Step 5: 外文标题并发翻译(ThreadPoolExecutor × AI_CONCURRENCY
├── Step 6: 关键词筛选(仅当 >15篇)
├── Step 7: 外文正文并发翻译(仅筛选通过的文章,大幅省 token)
├── Step 8: 批量 AI 评分 + 分类 + 摘要(并发)
├── Step 9: 新处理文章写入 SQLite 缓存
├── Step 10: 合并缓存+新文章,按评分降序
├── Step 11: TOP3 网摘生成(优先缓存命中,支持字数重试,TXT+PNG)
├── Step 12: 四分类整理 + 深度洞察生成(优先缓存命中)
├── Step 13: Markdown 报告输出(全缓存+文件已存在则跳过)
├── Step 14: 多渠道推送(企业微信/飞书 webhook,失败优雅降级)
└── 监控报告输出(阶段耗时/API+token统计/源成功率/缓存命中率)
```
## ✅ 系统要求
- Python 3.8+ 或 Docker
- OpenAI API Key(或兼容API,如火山引擎、DeepSeek等)
- 内存 ≥ 512MB
- 磁盘 ≥ 1GB
## ❓ 常见问题
### 生成的图片中文字体乱码怎么办?
无需额外配置,代码已自动适配所有主流系统的自带中文字体:
- Windows: 自动匹配宋体/黑体/微软雅黑
- Linux: 自动匹配NotoSansCJK/文泉驿微米黑
- macOS: 自动匹配PingFang/黑体
如果需要完全不依赖系统字体,可以下载开源无版权的NotoSansCJK字体放到`scripts/modules/assets/fonts/`目录下:
- 粗体:`NotoSansCJK-Bold.ttc`
- 常规:`NotoSansCJK-Regular.ttc`
### 日志输出在哪里?
日志默认输出到标准错误流,包含INFO/WARNING/ERROR三个级别,运行时会实时打印到控制台,也可以重定向到日志文件:
```bash
python scripts/military_daily_report_v3.py >> logs/runtime.log 2>&1
# 1. 准备目录和配置
mkdir -p docker/data docker/cache
cp .env docker/.env
# 2. 构建并启动
docker compose up -d --build
# ── 运维 ──────────────────────────────────────
docker compose logs -f # 查看实时日志
docker compose restart # 重启
docker compose down # 停止并删除容器
docker compose up -d --build # 更新代码后重建并启动
```
#### 目录结构(Docker 部署)
```
my-daily/
├── docker/ # Docker 持久化目录
│ ├── .env # 配置文件(API Key, Webhook 等)
│ ├── data/ # 运行时数据(fetch/push/image 文件)
│ └── cache/ # SQLite 缓存(避免重复消费 API)
├── Dockerfile # 镜像构建文件
├── docker-compose.yml # Docker Compose 配置
└── .dockerignore # 构建忽略文件
```
#### 在绿联云 NAS 上的操作
```bash
# 方案 ASSH 直接操作(NAS 开启 SSH 后)
# 将 my-daily 目录上传到 NAS,进入目录执行:
docker compose up -d --build
# 方案 B:绿联云 Docker 管理器(图形界面)
# 1. 在本机构建镜像并导出:
docker save -o military-digest.tar military-digest:latest
# 2. 将 tar 文件上传到 NAS
# 3. 在绿联云 Docker > 镜像管理 > 导入镜像
# 4. 创建容器时挂载卷:
# - /path/to/docker/.env → /app/.env(只读)
# - /path/to/docker/data → /app/data
# - /path/to/docker/cache → /app/cache
# 5. 设置重启策略:always
# 6. 启动容器
```
## 运维工具
| 脚本 | 平台 | 功能 |
|------|:---:|------|
| [scripts/check_sources.py](scripts/check_sources.py) | 通用 | 源连通性检测:HTTP 可达性 + RSS 解析 + 条目时效分布 |
| [scripts/test_entity_extract.py](scripts/test_entity_extract.py) | 通用 | 实体提取 + 角度分类独立测试 |
| [scripts/test_normalizer.py](scripts/test_normalizer.py) | 通用 | 实体归一化效果验证 |
| [scripts/test_llm_disambiguate.py](scripts/test_llm_disambiguate.py) | 通用 | LLM 消歧层独立测试 |
| [scripts/status.sh](scripts/status.sh) | Linux | 项目状态查看 |
| [scripts/install.sh](scripts/install.sh) | Linux | 依赖安装 + systemd 服务文件生成 |
| [scripts/uninstall.sh](scripts/uninstall.sh) | Linux | systemd 服务卸载 |
### 源连通性检测
```bash
python scripts/check_sources.py # 全部源
python scripts/check_sources.py --max 50 # 前 50 个源
python scripts/check_sources.py --failed-only # 仅显示失败源
python scripts/check_sources.py --show-urls # 显示 URL
python scripts/check_sources.py --concurrency 20 # 自定义并发数
```
## 数据与输出
| 目录/文件 | 说明 | 保留策略 |
|-----------|------|:---:|
| `data/fetch-YYYY-MM-DD.json` | 当日 Fetch 结果(含评分、摘要) | 7 天 |
| `data/hotalert-YYYYMMDD-HHmmSS.md` | 军事科技快讯(即时推送) | 7 天 |
| `data/cluster-YYYYMMDD-HHmmSS.md` | 热点速览(簇视图推送) | 7 天 |
| `data/digest-YYYYMMDD-HHmmSS.md` | 军事科技每日精选(单篇视图推送) | 7 天 |
| `data/images/YYYYMMDD/` | 网摘 PNG 图片 | 7 天 |
| `data/article_cache.db` | SQLite 三级缓存(文章/网摘/分类摘要) | 7 天自动清理 |
| `logs/YYYYMMDD-HHmmSS.log` | 运行日志(终端输出完整记录) | 手动清理 |
## 评价管线详解
从 RSS 抓取到即时推送,文章经历 **12 个步骤**,分属两个层面:单篇评分层和簇级评价层。
### 第一层:单篇评分
#### 步骤 1 — LLM 批量评分
调用 `score_batch()` 对每篇文章打分(0-100),同时产出 `tags``summary`
| 配置项 | 位置 | 默认值 | 说明 |
|--------|------|:---:|------|
| `max_prompt_chars` | `config.json → llm` | 10000 | 单批次 Prompt 上限,超限自动拆分 |
| `max_concurrent_batches` | `config.json → llm` | 3 | LLM 并发批次数 |
| Prompt | `prompts/score_batch.md` | — | 含核心约束:非军事≤79、KOL≤89、90+需官方首发 |
#### 步骤 2 — 硬约束过滤
`apply_hard_constraints()` 对 LLM 评分施加上限:
| 约束 | 阈值 | 作用 |
|------|:---:|------|
| 非目标域名(YouTube/Twitter/B站/微博等) | **≤79** | 社交媒体来源不可信 |
| KOL 域名(同上) | **≤89** | 个人账号非官方首发 |
> 配置:`config.json → scoring.non_target_max_score` / `kol_max_score`
#### 步骤 3 — 时间衰减
`apply_time_decay()` 指数衰减旧文章分数:
```
有效年龄 = max(0, 当前时间 - 发布时间 - min_age_hours)
衰减因子 = 0.5 ^ (有效年龄 / half_life_hours)
最终分数 = 原分数 × 衰减因子
```
| 参数 | 默认值 | 说明 |
|------|:---:|------|
| `half_life_hours` | 12 | 半衰期 |
| `min_age_hours` | 6 | 6 小时内不衰减 |
#### 步骤 4 — 动态阈值
`calculate_dynamic_threshold()` 筛选合格文章:
```
阈值 = 均值 + 1.5 × 标准差
clamp[40, 90]
```
只有 `score ≥ 阈值` 的文章进入下一步实体提取。
---
*V3 版本 - 全流程重构 + 模块化拆分 + 跨系统字体适配 + 日志标准化 + Markdown报告输出,2026年5月发布*
### 第二层:簇级评价
#### 步骤 5 — 实体提取 + 角度分类
`batch_extract_all()` 对合格文章提取实体和角度,两个 LLM 调用并发执行。
| 类别 | Prompt | 输出 |
|------|--------|------|
| 实体(5 类) | `prompts/extract_entities.md` | `entities: {person, org, equipment, event, location}` |
| 角度(6 类) | `prompts/classify_angle.md` | `reporting_angle: 政策发布/技术突破/战略分析/舆论反应/冲突事件/人物动态` |
#### 步骤 6 — 实体归一化
`normalize_entries_async()` 双层归一化:
| 层 | 方式 | 成本 | 示例 |
|----|------|:---:|------|
| 规则映射 | `entity_aliases.json`200+ 别名) | 零 | `PL-21 → 霹雳-21``DARPA → 美国国防高级研究计划局` |
| LLM 消歧 | `WEBZINE_MODEL` 批量判断未知别名 | API | `Strait of Hormuz → 霍尔木兹海峡` |
#### 步骤 7 — 聚类成簇
`cluster_articles()` 加权实体重叠 + 并查集:
| 实体类别 | 权重 | 合并条件 |
|----------|:---:|------|
| equipment / event | **3**(核心) | 共享即直接合并(泛化实体黑名单内的除外) |
| org / person | **2** | 需累计权重 ≥ 4 |
| location | **1** | 需累计权重 ≥ 4 |
> 泛化黑名单(不触发直接合并):`无人机`、`导弹`、`雷达`、`坦克`、`战机`、`军舰` 等 23 个类别词
#### 步骤 8 — 启发式热度计算
`calculate_cluster_heat()` 四维评分(0-100):
```
热度 = min(100,
最高分 × 0.4 × 时间衰减
+ min(报道数 × log(来源多样性) × 3, 30)
+ 角度数/6 × 20
+ min(90+文章数 × 5, 10)
)
```
#### 步骤 9 — 热度阈值
`calculate_hotspot_threshold()` 动态阈值:
```
热度阈值 = 均值 + multiplier × σ
clamp[clamp_min, clamp_max]
```
| 配置 | 位置 | 默认值 | 效果 |
|------|------|:---:|------|
| `threshold_clamp_min` | `config.json → heat` | 35 | 阈值下限 |
| `threshold_clamp_max` | `config.json → heat` | 80 | 阈值上限 |
| `threshold_multiplier` | `config.json → heat` | 1.5 | σ 乘数 |
| `HEAT_THRESHOLD` | `.env` | — | **固定覆盖**(最高优先级) |
#### 步骤 10 — LLM 精评融合
`precise_score_clusters()` 对 Top 30 簇调用 LLM 重新精评(军事价值40% + 时效25% + 信息密度20% + 传播15%),与启发式 6:4 融合:
```
融合热度 = 启发式 × 0.6 + 精评分 × 0.4
```
> Prompt`prompts/precise_heat.md`,失败时回退纯启发式热度。
#### 步骤 11 — 热点识别
热点文章 = **三类并集**
| 来源 | 条件 |
|------|------|
| 直接热点 | 单篇 score ≥ `hot_threshold`90 |
| 簇提升 | 所在簇热度 ≥ 阈值,且单篇 ≥ `hot_threshold - 15`75 |
#### 步骤 12 — 簇记忆去重
`cluster_memory.is_recently_pushed()` 检查簇是否在 24h 内已推送,已推送则跳过。
> 存储:SQLite 表 `cluster_memory`7 天自动清理。
---
### 配置速查
| 参数 | 位置 | 默认值 | 作用环节 |
|------|------|:---:|------|
| `hot_threshold` | `config.json → filter` | 90 | 步骤 11:直接热点线 |
| `non_target_max_score` | `config.json → scoring` | 79 | 步骤 2:非目标封顶 |
| `kol_max_score` | `config.json → scoring` | 89 | 步骤 2KOL 封顶 |
| `half_life_hours` | `config.json → scoring` | 12 | 步骤 3:衰减半衰期 |
| `dynamic_threshold_multiplier` | `config.json → scoring` | 1.5 | 步骤 4:σ 乘数 |
| `threshold_clamp_min` | `config.json → heat` | 35 | 步骤 9:热度下限 |
| `threshold_multiplier` | `config.json → heat` | 1.5 | 步骤 9:热度 σ 乘数 |
| `HEAT_THRESHOLD` | `.env` | — | 步骤 9:固定覆盖 |
## 项目文件说明
```text
my-daily/
├── main.py # 入口 + 双循环架构
├── start.py # 一键启动(含日志记录)
├── config.json # 用户配置(不入库)
├── .env # API 密钥(不入库)
├── config.json.example # 配置模板
├── .env.example # 环境变量模板
├── requirements.txt # Python 依赖
├── CONFIG.md # 配置参数完整说明
├── Dockerfile # Docker 镜像构建
├── docker-compose.yml # Docker Compose 配置
├── .dockerignore # Docker 构建忽略
├── logs/ # 运行日志(YYYYMMDD-HHmmSS.log
├── prompts/ # LLM 提示词(10 个)
│ ├── score_batch.md # 批量评分
│ ├── digest.md # RSS Digest 生成
│ ├── immediate_push.md # 即时推送快讯
│ ├── webzine.md # 参考消息风格网摘
│ ├── category_overview.md # 分类概览
│ ├── extract_entities.md # 实体提取(5 类)
│ ├── classify_angle.md # 角度分类(6 类)
│ ├── precise_heat.md # 热点精评(四维评分)
│ ├── cluster_insight.md # 双视角洞察
│ └── cluster_summary.md # 轻量簇摘要
├── resources/
│ └── rss_feeds.opml # RSS 订阅源(137 个源)
├── templates/ # 推送模板
│ ├── cluster_view.md # 簇视图模板
│ ├── article_view.md # 单篇视图模板
│ └── immediate_push.md # 即时推送模板
├── scripts/ # 运维 + 测试脚本
│ ├── check_sources.py # 源连通性检测
│ ├── setup_systemd.sh # Linux systemd 安装
│ ├── setup_service.bat # Windows 计划任务管理
│ ├── install.sh / uninstall.sh
│ └── status.sh
├── systemd/ # systemd 模板
├── src/ # 源代码
│ ├── config.py # 配置加载 + OPML 解析 + 环境变量覆盖
│ ├── fetcher.py # RSS 异步抓取
│ ├── translator.py # 中英分离翻译管线
│ ├── llm.py # LLM 调用(评分/摘要/洞察/精评)
│ ├── scoring.py # 评分后处理(约束/衰减/阈值)
│ ├── cache.py # SQLite 三级缓存
│ ├── storage.py # 文件存储 + Sentinel 分段
│ ├── monitor.py # 运行监控统计
│ ├── logger.py # 日志系统
│ ├── processor.py # HTML → Markdown
│ ├── utils.py # 通用工具函数
│ ├── markdown_utils.py # Markdown 解析工具
│ ├── renderer.py # 模板渲染
│ ├── generators/ # 图片生成模块
│ │ ├── image_renderer.py # 网摘 PNG 图片渲染
│ │ └── get_chinese_font.py # 中文字体管理
│ ├── processors/ # 处理管线
│ │ ├── entity_extractor.py # 实体提取 + 角度分类
│ │ ├── entity_normalizer.py # 实体归一化(双层)
│ │ ├── entity_aliases.json # 别名映射表
│ │ ├── cluster_engine.py # 实体聚类引擎
│ │ ├── cluster_memory.py # 簇记忆系统
│ │ ├── heat_calculator.py # 簇热度计算
│ │ ├── rank_engine.py # 排序引擎
│ │ ├── trend_analyzer.py # 趋势分析
│ │ ├── feedback.py # 反馈闭环
│ │ └── cross_day.py # 跨日关联
│ ├── integrators/
│ │ └── summary_integrator.py # 摘要整合编排
│ ├── utils/
│ │ └── prompt_checker.py # Prompt 防退化检查器
│ ├── push/ # 推送模块
│ │ ├── feishu.py # 飞书 Webhook 推送
│ │ └── image_hosting.py # 图床上传模块
│ └── sections/ # 内容板块
│ ├── rss/section.py # RSS Digest 板块
│ └── webzine/section.py # 网摘生成板块
├── CHANGELOG.md # 更新日志
├── PROGRESS.md # 开发进展追踪
└── README.md # 本文件
```
## 常见问题
**Q: 为什么英文源抓不到文章?**
A: 英文军事源(如 The War Zone、Defense One)更新频率低,可能 2-3 天无新文章。中文微信公众号源更新频繁,扩大 `--lookback` 可覆盖更多时间窗口。
**Q: 如何添加/屏蔽订阅源?**
A: OPML 文件管理为主。临时追加用 `config.json``sources.add`,屏蔽用 `sources.block``sources.block_domains`。支持 `*.domain.com` 通配。
**Q: 如何修改定时计划?**
A: 在 `.env` 中设置 `FETCH_INTERVAL_MINUTES`(拉取间隔)和 `PUSH_CRON`(推送定时,cron 表达式),重启服务即可生效。
**Q: 内存/CPU 占用如何?**
A: 异步抓取 + 并发控制(默认 10 并发),翻译/评分均分批处理。137 源全量抓取峰值内存约 200MB,日常运行稳定在 100MB 以内。