17 KiB
军事科技每日摘报系统优化方案指南
🎯 优化目标
适配100+RSS订阅源、每日200+文章的处理需求,降低运行成本,提升稳定性,缩短整体处理时间到5分钟以内。
📋 优化优先级划分
🔴 P0级(高优先级,投入极小,收益极大,已完成)
| 优化项 | 预期效果 | 实施难度 | 状态 |
|---|---|---|---|
| 抓取模块线程池改造+失败重试 | 抓取速度提升60%,成功率提升15% | ⭐ | ✅ 已完成 |
| AI全链路并发化改造 | AI相关环节耗时降低70%,整体流程缩短一半 | ⭐ | ✅ 已完成 |
| 处理顺序前置优化 | 翻译token成本降低40%,翻译时间缩短40% | ⭐ | ✅ 已完成 |
🟠 P1级(中优先级,投入小,收益大,优先开展)
| 优化项 | 预期效果 | 实施难度 | 状态 |
|---|---|---|---|
| 增量处理+SQLite本地缓存 | ⭐⭐ | ✅ 已完成 | |
| 失败降级机制 | ⭐ | ✅ 已完成 | |
| 基础监控统计 | ⭐⭐ | ✅ 已完成 |
🟡 P2级(中低优先级,投入中等,长期收益明显)
| 优化项 | 预期效果 | 实施难度 | 状态 |
|---|---|---|---|
| main()函数拆分重构 | ⭐⭐ | ✅ 已完成 | |
| 推送模块完善 | ⭐⭐ | ✅ 已完成 | |
| 图片生成模块去重重构 🔥 | create_webzine_image与create_combined_webzine_image存在 |
⭐ | ⏳ 待开展 |
| 差异化抓取调度 | 高频新闻源缩短抓取间隔,低频源延长间隔,降低无效抓取请求 | ⭐⭐ | ⏳ 待开展 |
| 异步流式处理队列 | 抓取和后续处理并行执行,整体耗时再缩短30% | ⭐⭐⭐ | ⏳ 待开展 |
🟢 P3级(低优先级,投入大,规模扩大后再考虑)
| 优化项 | 适用场景 | 实施难度 | 状态 |
|---|---|---|---|
| SimHash相似内容去重 | 源>500、大量转载内容场景,减少重复文章 | ⭐⭐⭐⭐ | 💤 待规划 |
| 微服务拆分+分布式消息队列 | 源>1000、单机器性能不足时,支持水平扩展 | ⭐⭐⭐⭐ | 💤 待规划 |
| 管理后台界面 | 可视化配置源、查看历史报告、统计分析 | ⭐⭐⭐⭐ | 💤 待规划 |
| 本地大模型部署 | 降低API调用成本,实现私有化部署 | ⭐⭐⭐⭐ | 💤 待规划 |
📊 已完成进度总览
| 阶段 | 完成度 | 核心成果 |
|---|---|---|
| 架构重构 | 100% | 单文件拆分为模块化结构,可维护性大幅提升 |
| P0级优化 | 100% | 抓取+AI环节速度提升2-3倍,成本降低40% |
| P1级优化 | 100% | SQLite缓存+全链路降级+基础监控统计,再次运行几乎零token消耗 |
| 功能完善 | 100% | 推送模块完成,企业微信文本+图片推送+飞书文本推送 |
| 稳定性优化 | 100% | 抓取重试+全链路降级+监控统计 |
🚀 下一阶段优化实施计划
第一阶段 (当前优先开展,预计1-2天完成) ✅ 已完成
核心目标:完善可观测性,补齐P1最后一块拼图
统计监控功能(P1收尾)✅ 已完成:- 阶段耗时自动统计,运行结束输出结构化报表
- API调用量/token消耗按用途和模型分组统计
- 源抓取成功/失败率一目了然
SQLite增量缓存系统实现✅ 已完成全链路失败降级机制✅ 已完成
第二阶段(当前开展,预计2-3天完成)
核心目标:提升代码质量,完善功能
推荐顺序(按投入产出比排列):
main()函数拆分重构(建议最先做)✅ 已完成推送模块完善✅ 已完成:modules/pusher.py封装企业微信(文本/图片/Markdown)+ 飞书(文本)webhook 推送config.py新增load_push_config()统一加载推送配置(环境变量 > .env > config.json)- 主流程末自动调用,推送失败优雅降级(warning 日志,不影响 exit 0)
- 支持关闭推送(
enable_wechat_push: false/enable_feishu_push: false)
- 异步流式处理改造:
- 需变更架构(引入队列),建议在函数拆分完成、代码结构清晰后再做
- 差异化抓取调度:
- 为 rss_feeds.txt 增加频率字段,需配合定时调度器改造
第三阶段(按需扩展,规模扩大后开展)
核心目标:支持更大规模源和更高性能要求 根据实际业务需求,优先开展最急需的优化点。
📌 实施建议
优先完成第一阶段优化✅ P0+P1 共 6 项全部到位第二阶段 main()拆分 + 推送模块✅ 已完成- 当前优先级:图片生成模块去重重构 → 差异化抓取调度 → 异步流式处理(源扩展后再做)
- P3 级优化建议在单机器无法满足性能要求,或者有明确功能需求时再实施
🧪 验证阶段
4.1 main() 函数拆分验证
2026-05-14 完成拆分重构,按 主函数过长问题.md 方案落地:
| # | 提取函数 | 职责 | 行数 | 可测试性 |
|---|---|---|---|---|
| ① | merge_from_cache |
合并缓存+新文章,按评分排序 | 9 | ⭐⭐⭐ |
| ② | process_new_articles |
完整管线:排序→标题翻译→筛选→正文翻译→AI分析→写缓存 | 32 | ⭐⭐ |
| ③ | deduplicate_articles |
同源标题去重 | 12 | ⭐⭐⭐ |
| ④ | categorize_articles |
三类文章分组 | 11 | ⭐⭐⭐ |
| ⑤ | write_markdown_report |
Markdown报告拼接+写入(含_append_category消除三分类重复) |
40 | ⭐⭐ |
| ⑥ | build_webzine_content |
网摘文本生成(优先缓存+写回) | 33 | ⭐⭐ |
| ⑦ | get_or_generate_overview |
分类介绍查缓存/生成(消除原有4处×8行重复) | 9 | ⭐⭐ |
拆分效果: main() 从 ~310 行缩减至 ~115 行(-63%),消除代码重复 60+ 行。拆分后 7 次独立运行全部 exit 0。
附带修复的 Bug: 发现 keyword_filter.py 在正文翻译前硬读 a['translated_content'] 导致 KeyError,已修复为安全 fallback(a.get('translated_content') or a.get('content') or '')。
4.2 SQLite 缓存数据完整性验证
2026-05-14 冷启动全链路运行后,直接查询 article_cache.db 验证:
基本信息:
| 表 | 行数 | 说明 |
|---|---|---|
article_cache |
15 | 与关键词筛选后文章数完全一致 |
category_summary_cache |
4 | 今日必看/装备动态/地区冲突/战略政策全覆盖 |
逐项检查:
| 检查项 | 结果 | 判定 |
|---|---|---|
| id 重复 | 0 条 | ✅ |
| 失败条目 (status≠1) | 0 条 | ✅ |
| translated_title 缺失 | 0/15 | ✅ |
| translated_content 缺失 | 0/15 | ✅ |
| ai_summary 缺失 | 0/15 | ✅ |
| ai_category 缺失 | 0/15 | ✅ |
| published_time 缺失 | 0/15 | ✅ |
| webzine_text 覆盖率 | 3/15 | ✅ (仅TOP3需要) |
| ai_score 范围 | 3.3 ~ 6.5 (avg 5.3) | ✅ 正态分布 |
| 分类分布 | 装备7 / 战略5 / 冲突3 | ✅ 合理 |
缓存命中验证(连续运行2次):
| 指标 | 第一次(冷启动) | 第二次(含缓存) |
|---|---|---|
| 文章缓存命中 | 0 / 15 | 15 / 15 ✅ |
| API 调用次数 | 37 次 | 0 次 ✅ |
| 总耗时 | 153s | ~5s ✅ |
| 退出码 | 0 | 0 |
第二次运行时文章缓存、网摘缓存、分类介绍缓存全部命中,跳过所有 AI 调用,仅做 RSS 抓取 + 文件排版,几乎零 token 消耗。
4.3 全链路冷启动验证
2026-05-14 清除所有缓存和输出文件后运行,完整链路通过:
| 阶段 | 耗时 | 状态 |
|---|---|---|
| RSS 抓取(5源) | 7.5s | ✅ |
| 时间过滤 (156→22篇) | 0.0s | ✅ |
| 去重 | 0.0s | ✅ |
| 标题翻译(7篇外文) | 17.9s | ✅ |
| 关键词筛选 (22→15篇) | 0.0s | ✅ |
| 正文翻译(6篇外文) | 9.9s | ✅ |
| AI评分分类(15篇) | 30.7s | ✅ |
| 网摘生成(3篇,含2次重试) | 53.3s | ✅ |
| 分类介绍生成(4类) | 33.5s | ✅ |
| 图片生成 | 0.3s | ✅ |
| 报告生成 | 0.0s | ✅ |
| 总耗时 | 153s | ✅ |
API 消耗统计:
| 用途 | 次数 | tokens 估算 |
|---|---|---|
| 标题翻译 | 7 | 1,400 |
| 正文翻译 | 6 | 4,800 |
| AI评分分类 | 15 | 12,000 |
| 网摘生成 | 5 (含2次重试) | 4,000 |
| 分类介绍 | 4 | 600 |
| 合计 | 37 | ~22,800 |
输出文件验证:
military_report_20260514.md 10.6 KB ✅ Markdown 格式完整
military_webzine_20260514.txt 4.9 KB ✅ TOP3 网摘文本完整
military_webzine_20260514.png 868.5 KB ✅ 合并长图渲染正常
article_cache.db 60.0 KB ✅ 数据完整无异常
4.4 当前优化完成总览
| 优先级 | 完成度 | 已实现项目 |
|---|---|---|
| P0 | 100% | 线程池抓取、AI全链路并发、处理顺序前置 |
| P1 | 100% | SQLite缓存、全链路降级、基础监控统计 |
| P2 | 60% | main()函数拆分 ✅ / 推送模块 ✅ / 图片去重 ⏳ / 差异化调度 ⏳ / 异步流式 ⏳ |
| P3 | 0% | 待规划 |
| 安全 | 100% | .gitignore + 配置分离 + 全项目扫描无硬编码凭据 |
| 配置 | 100% | RSS_PROXY_BASE 提取 + .env.example 结构对齐 |
4.5 推送模块验证
2026-05-21 完成推送模块开发并实际调用验证:
架构:
| 文件 | 职责 |
|---|---|
modules/pusher.py |
企业微信文本/图片/Markdown + 飞书文本推送,含失败兜底(5级异常分层捕获) |
modules/config.py:load_push_config() |
三层配置加载(环境变量 > .env > config.json) |
military_daily_report_v3.py:L334-338 |
主流程末自动触发 |
配置职责分离:
| 配置来源 | 内容 | 说明 |
|---|---|---|
config.json |
enable_wechat_push, enable_feishu_push |
仅开关标志,可 git 追踪 |
config/.env |
WECHAT_ACCOUNT, WECHAT_TARGET, FEISHU_WEBHOOK |
敏感凭据,.gitignore 排除 |
错误处理矩阵(5级分层):
| 异常类型 | 处理方式 | 主流程影响 |
|---|---|---|
ConnectionError |
warning 日志 + 返回 False | 无 |
Timeout (连接5s/读取15s) |
warning 日志 + 返回 False | 无 |
HTTPError (4xx/5xx) |
warning 日志 + 返回 False | 无 |
JSONDecodeError |
warning 日志 + 返回 False | 无 |
其他 Exception |
warning 日志 + 返回 False | 无 |
实际运行验证(2026-05-21):
| 场景 | 配置 | 结果 | 主流程 |
|---|---|---|---|
| 双推送关闭 | enable_wechat_push: false, enable_feishu_push: false |
"未启用任何推送渠道,跳过" | exit 0 ✅ |
| 企业微信(无效key) | enable_wechat_push: true |
服务器返回 errcode=93000,warning 降级 |
exit 0 ✅ |
| 飞书(真实webhook) | enable_feishu_push: true |
飞书推送成功 🎉 | exit 0 ✅ |
关键验证点:
- ✅ 飞书 webhook 真实连接成功,消息送达飞书群
- ✅ 企业微信请求正确送达服务器(无效 key 返回标准 errcode,非网络异常)
- ✅ 所有异常均优雅降级为 warning 日志,不抛异常,不影响主流程
- ✅ 推送代码可通过 config.json 开关一键启用/关闭,webhook 凭据统一在 .env 管理
4.6 配置安全加固
2026-05-21 完成全项目敏感信息扫描和加固:
发现的问题:
| 问题 | 严重程度 | 修复 |
|---|---|---|
项目缺少 .gitignore,config/.env 可能泄露到 Git |
🔴 严重 | 新建 .gitignore,首行即排除 config/.env |
config.json 含 wechat_target/wechat_account 敏感字段 |
🟡 中等 | 删除,统一迁移到 .env(WECHAT_ACCOUNT/WECHAT_TARGET) |
docker-compose.yml 缺少推送环境变量 |
🟡 中等 | 两个服务均补充 WECHAT_ACCOUNT/WECHAT_TARGET/FEISHU_WEBHOOK |
.env 字段名与代码不一致 |
🟡 中等 | WECHAT_WEBHOOK_KEY → WECHAT_ACCOUNT,FEISHU_WEBHOOK_URL → FEISHU_WEBHOOK |
加固后架构:
config.json ← 仅开关标志,可 git 追踪
config/.env ← 所有敏感凭据,.gitignore 排除
环境变量 ← Docker/CI 场景的最终覆盖
扫描结论: 所有 .py 代码、Makefile、Dockerfile、config.json 中均无硬编码凭据残留。.env.example 与 .env 结构完全一致(11 个字段全部对齐)。
4.7 RSS_PROXY_BASE 配置提取
2026-05-21 将 default_feeds 中 8 个硬编码的 werss.yynnice.top URL 统一提取为可配置项:
配置架构:
| 层级 | 来源 | 效果 |
|---|---|---|
| 默认值 | 代码内置 | https://werss.yynnice.top |
| 覆盖 | config/.env 的 RSS_PROXY_BASE |
更换代理服务只需改一行 |
| 最终覆盖 | 环境变量 | Docker/CI 注入 |
涉及文件:
| 文件 | 改动 |
|---|---|
config.py |
_load_rss_proxy_base() 三层加载 + 8 个 default_feeds 改用 f-string |
.env / .env.example |
新增 RSS_PROXY_BASE 配置项 |
docker-compose.yml |
两个服务均传递 RSS_PROXY_BASE |
第三方 RSS 代理服务更换时只需改
.env一行,无需修改 Python 代码或rss_feeds.txt。
4.8 图片生成模块去重重构
按 01.md 方案提取 4 个公共函数:
| # | 提取函数 | 职责 | 消除重复行 |
|---|---|---|---|
| ① | _parse_webzine_line |
统一文本前缀解析(标题:/正文:/价值点:等) | ~30行 × 2 |
| ② | _load_webzine_fonts |
集中字体加载和配置(banner/article_title/bold/content) | ~4行 × 2 |
| ③ | _build_render_commands |
将网摘文本转为渲染命令列表 | ~40行 × 2 |
| ④ | _execute_render_commands |
执行渲染命令,在画布上绘制内容 | ~30行 × 2 |
重构效果预期:
| 指标 | 重构前 | 重构后 |
|---|---|---|
create_webzine_image |
115行 | ~50行 |
create_combined_webzine_image |
106行 | ~45行 |
| 新增公共函数 | 0 | 4个(合计~110行) |
| 净减少代码 | — | ~100行(-45%) |
| 可单独测试的单元 | 0 | 4个公共函数 |
回归验证方式: 运行一次完整冷启动,对比重构前后输出的 3 个文件(md/txt/png)的字节级一致性。
🎯 下一步推荐
P2 剩余 3 项待开展,建议按以下顺序推进:
🥇 图片生成模块去重重构(投入 ⭐,收益大,建议最先做)
当前问题: image_generator.py 中 create_webzine_image(131行)与 create_combined_webzine_image(106行)存在三层重复:
- 文本解析层:前缀识别(标题:/正文:/价值点:等)逻辑完全一致
- 字体配置层:4种字体的加载代码完全相同(banner/article_title/bold/content)
- 渲染绘制层:prefix_sameline / article_title / normal 等命令的绘制逻辑高度相似
方案: 提取 4 个公共函数(_parse_webzine_line / _load_webzine_fonts / _build_render_commands / _execute_render_commands),建立清晰的抽象层次。详见 01.md。
涉及改动: 仅 modules/image_generator.py 一个文件,纯内部重构,无外部依赖变化。
推荐理由:
- 投入最低(⭐),仅涉及同一文件内的函数提取
- 风险极低,两个函数的现有行为完全不变
- 延续 main() 拆分的重构方法论,代码风格一致
- 可作为热身任务,在差异化调度和异步流式之前快速完成
🥈 差异化抓取调度(投入 ⭐⭐,收益大)
当前问题: 所有源统一 24h 抓一次,高频源(无人机类、战区类)可能错过半天内的重要新闻。
方案: 为 rss_feeds.txt 增加频率字段,格式从 名称|URL 扩展为 名称|URL|频率(小时)。兼容现有格式(无频率默认 24h)。
涉及改动: config.py 解析逻辑 + rss_fetcher.py 读取上次抓取时间 + 外部 crontab 调高频率。
🥉 异步流式处理队列(投入 ⭐⭐⭐,规模化时收益大)
当前架构: 阶段间串行(全部抓取完 → 全部过滤完 → ...)。5 源 153s 完全够用。
何时做: 源扩展到 50+、冷启动耗时明显增加时再考虑。当前 5 源 RSS 抓取仅占 7-15s,瓶颈在 AI 调用而非抓取。