1250 lines
54 KiB
Markdown
1250 lines
54 KiB
Markdown
# 新闻智能分析系统开发计划 v3.0
|
||
|
||
> 基于大模型的时政/军事/科技新闻聚类、评价与整合系统
|
||
>
|
||
> **迭代说明**:参考 AI Daily 项目实践与 military-digest-v3 工程落地经验,融合细粒度聚类、SQLite 增量缓存、翻译管线、网摘生成与工程化部署经验
|
||
|
||
---
|
||
|
||
## 一、项目概述
|
||
|
||
### 1.1 项目目标
|
||
构建一个自动化新闻分析系统,实现:
|
||
- **细粒度聚类**:保留多角度报道,避免简单去重丢失热点
|
||
- **动态热点识别**:基于当日新闻分布统计特征自适应判定热点
|
||
- **三层整合**:摘要级速览(全量)+ 洞察级深度分析(Top 10)+ 参考消息风格网摘(Top 3)
|
||
- **混合处理**:实时热点感知 + 每日批量汇总生成早报
|
||
- **跨日关联**:追踪同一事件的多日发展脉络
|
||
- **增量缓存**:SQLite 三级缓存(文章/网摘/分类摘要),支持断点续跑,再次运行几乎零 token 消耗
|
||
- **外文翻译**:中英分离 + 并发翻译,中文文章零 API 调用
|
||
|
||
### 1.2 核心约束
|
||
| 维度 | 约束 |
|
||
|------|------|
|
||
| 数据规模 | 200-500篇/日 |
|
||
| 数据源 | RSS Feed(公众号订阅源,含外文源) |
|
||
| 输出格式 | Markdown日报 / 网摘PNG长图 / 网摘TXT文本 / 飞书推送 |
|
||
| 成本预算 | 混合模型调度,日成本约1.3-1.6元 |
|
||
| 处理时效 | 批处理15-20分钟完成(冷启动基准:5源156篇→153s) |
|
||
| 缓存命中 | 二次运行 ~5s,API 调用 0 次 |
|
||
| 部署方式 | 支持systemd服务化部署 |
|
||
|
||
### 1.3 双循环架构(参考AI Daily优化)
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Fetch 循环(实时) │
|
||
│ 每30分钟运行一次 │
|
||
│ RSS抓取 → 中英分离 → 外文并发翻译 → LLM评分 → 重要性判断 │
|
||
│ ↓ ↓ ↓ │
|
||
│ 存入SQLite缓存 存入JSON文件(fetch-yyyy-mm-dd.json) 飞书推送│
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
↓
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Push 循环(定时) │
|
||
│ 每日定时运行(如早8点) │
|
||
│ 读取碎片化信息 → 细粒度聚类 → 多角度关联 → 综合评分 │
|
||
│ → 摘要级整合(全量) → 洞察级整合(Top10) → 参考消息网摘(Top3) │
|
||
│ → 生成日报+网摘图片 → 飞书推送 │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 二、与现有项目的对比分析
|
||
|
||
### 2.1 与 AI Daily 的相似性
|
||
|
||
| 维度 | AI Daily | 本系统 | 相似度 |
|
||
|------|----------|--------|--------|
|
||
| **数据源** | RSS订阅源 | RSS订阅源(公众号+外文) | ⭐⭐⭐⭐⭐ |
|
||
| **核心流程** | RSS→LLM评分→推送/汇总 | RSS→翻译→聚类→评分→整合→推送/汇总 | ⭐⭐⭐⭐ |
|
||
| **双循环架构** | Fetch循环+Push循环 | 实时检测+批处理 | ⭐⭐⭐⭐⭐ |
|
||
| **即时推送** | 飞书/Discord webhook | 飞书 webhook | ⭐⭐⭐⭐ |
|
||
| **定时汇总** | 每日定时推送 | 每日早报生成 | ⭐⭐⭐⭐⭐ |
|
||
| **成本预算** | 每天6毛钱 | 每天1.3-1.6元 | ⭐⭐⭐⭐ |
|
||
|
||
### 2.2 与 military-digest-v3 的对比
|
||
|
||
| 维度 | military-digest-v3(现有系统) | 本系统(新架构) | 差异 |
|
||
|------|------|------|:---:|
|
||
| **架构** | 单管线(每日一次全量运行) | 双循环(Fetch 30min + Push 每日) | 🔄 架构升级 |
|
||
| **去重** | 同源同标题简单去重 | 细粒度语义聚类 + 多角度保留 | 🔄 策略升级 |
|
||
| **评分** | 四维度固定加权 | 双层筛选:硬约束 + 动态阈值融合 | 🔄 质量升级 |
|
||
| **翻译** | ✅ 中英分离 + 并发翻译 | ✅ 继承 + 统一为 Fetch 循环环节 | ✅ 复用 |
|
||
| **缓存** | ✅ SQLite 三级缓存(文章/网摘/分类摘要) | ✅ 继承 + 扩展为跨日关联数据源 | ✅ 复用 |
|
||
| **监控** | ✅ 11阶段耗时 + API统计 + 源成功率 | ✅ 继承 + 增加聚类/热点识别指标 | ✅ 复用 |
|
||
| **网摘** | ✅ 参考消息风格 + 字数校验重试 | ✅ 继承 + 作为第三层整合输出 | ✅ 复用 |
|
||
| **推送** | 企业微信 + 飞书 | **聚焦飞书 webhook** | 🔄 收束 |
|
||
| **跨日关联** | ❌ 无 | ✅ Sentinel标记 + 事件时间线追踪 | 🆕 新增 |
|
||
| **防退化** | ❌ 无 | ✅ 禁止套话 + 素材支撑要求 | 🆕 新增 |
|
||
|
||
### 2.3 本系统的差异化优势
|
||
|
||
| 特性 | AI Daily | military-digest-v3 | 本系统 | 价值 |
|
||
|------|----------|------|------|------|
|
||
| **热点识别** | 单篇文章评分 | 四维度固定加权 | **双层筛选:硬约束+动态阈值融合** | 质量控制+热点发现协同 |
|
||
| **去重策略** | 简单去重 | 同源标题去重 | 细粒度聚类+多角度保留 | 不丢失多角度报道 |
|
||
| **内容整合** | 单篇摘要 | 摘要+网摘 | 三层整合(摘要+洞察+网摘) | 覆盖速览/深度/传播三种需求 |
|
||
| **翻译能力** | 无 | ✅ 中英分离并发 | ✅ 继承 | 外文源零额外成本 |
|
||
| **增量缓存** | JSON文件 | ✅ SQLite三级缓存 | ✅ 继承+扩展 | 断点续跑,二次运行~5s |
|
||
| **网摘生成** | 无 | ✅ 参考消息风格+字数校验 | ✅ 继承 | 适合直接传播的成品内容 |
|
||
| **领域聚焦** | AI领域 | 军事/科技 | 时政/军事/科技 | 专业领域实体识别 |
|
||
| **角度分类** | 无 | 无 | 6种报道角度分类 | 支持多角度整合 |
|
||
| **跨日关联** | 无 | 无 | 事件时间线追踪+Sentinel标记 | 追踪事件发展脉络 |
|
||
|
||
### 2.4 从 AI Daily 借鉴的核心设计
|
||
|
||
1. **双循环架构**:Fetch循环(实时)+ Push循环(定时)
|
||
2. **systemd服务化**:使用systemd timer替代Python内部定时,提升稳定性
|
||
3. **文件存储规范**:fetch/notify/push三种文件类型,明确保留策略
|
||
4. **评分硬约束**:非目标领域上限、KOL转述上限,有效控制信息质量
|
||
5. **Prompt防退化**:禁止套话、要求从素材出发,避免LLM输出风格趋同
|
||
6. **双视角洞察**:metadata(事实压缩)+ 正文(趋势判断)解耦
|
||
7. **Sentinel分段标记**:单文件多板块精确管理,支持跨日关联
|
||
|
||
### 2.5 从 military-digest-v3 继承的工程资产
|
||
|
||
1. **SQLite 三级缓存**:文章缓存 + 网摘缓存 + 分类摘要缓存,含自动过期清理和数据库迁移机制
|
||
2. **翻译管线**:中英分离(汉字占比 > 20% 判定)+ 外文并发翻译(ThreadPoolExecutor),中文文章零 API 调用
|
||
3. **监控统计体系**:11 阶段耗时 + API 调用按用途/模型分组 + 源抓取成功/失败率 + 缓存命中率
|
||
4. **网摘生成**:参考消息风格 + 公众号源智能识别前缀 + 字数校验重试(280-320字,最多3次)
|
||
5. **异步流式管线**:生产者-消费者模型,RSS 抓取与 AI 处理流水线并行
|
||
6. **图片生成**:Pillow 命令模式渲染,支持单篇 + TOP3 合并长图
|
||
|
||
---
|
||
|
||
## 三、开发阶段规划
|
||
|
||
### 阶段一:基础架构搭建(Week 1-2)
|
||
**目标**:建立数据流和存储基础,确保稳定采集
|
||
|
||
| 序号 | 任务 | 描述 | 输出 | 依赖 |
|
||
|------|------|------|------|------|
|
||
| 1.1 | RSS采集模块 | 解析公众号RSS Feed,提取标题、正文、来源、发布时间 | 原始文章数据库 | - |
|
||
| 1.2 | 数据标准化 | 统一文章格式,清洗HTML标签,提取纯文本 | 标准化文章表 | 1.1 |
|
||
| 1.3 | 文件存储层 | JSON文件存储(fetch-yyyy-mm-dd.json) | 存储规范 | 1.2 |
|
||
| 1.4 | **SQLite缓存系统** | 三级缓存设计(文章/网摘/分类摘要),含过期策略与迁移机制 | 缓存模块 | 1.2 |
|
||
| 1.5 | **翻译管线** | 中英分离 + 外文并发翻译 | 翻译模块 | 1.2 |
|
||
| 1.6 | **监控统计** | 阶段耗时 + API消耗 + 成功率 + 缓存命中率 | 监控模块 | 1.1 |
|
||
| 1.7 | 日志系统 | 有效日志记录,便于问题定位 | 日志模块 | 1.1 |
|
||
|
||
#### 1.4 SQLite 缓存系统设计(继承 military-digest-v3)
|
||
|
||
**数据库位置**:`data/article_cache.db`
|
||
|
||
**表1:article_cache(文章处理结果缓存)**
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `id` | TEXT (PK) | 文章唯一标识(RSS entry id/link) |
|
||
| `source` | TEXT | 来源名称 |
|
||
| `title` | TEXT | 原始标题 |
|
||
| `link` | TEXT | 原文链接 |
|
||
| `translated_title` | TEXT | 翻译后的中文标题 |
|
||
| `content` | TEXT | 原始正文 |
|
||
| `translated_content` | TEXT | 翻译后的中文正文(前300字) |
|
||
| `ai_score` | REAL | AI 加权总分 |
|
||
| `ai_summary` | TEXT | AI 生成的中文摘要 |
|
||
| `ai_category` | TEXT | 分类(装备动态/地区冲突/战略政策) |
|
||
| `ai_scores_json` | TEXT | 四维度评分 JSON |
|
||
| `webzine_text` | TEXT | 网摘文本(迁移新增字段) |
|
||
| `published_time` | DATETIME | 文章发布时间 |
|
||
| `processed_time` | DATETIME | 处理时间(索引,用于过期判断) |
|
||
| `status` | INTEGER | 1=成功 2=失败 |
|
||
| `error_msg` | TEXT | 失败时的错误信息 |
|
||
|
||
**索引**:`idx_processed_time`、`idx_source`
|
||
|
||
**表2:category_summary_cache(分类摘要缓存)**
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `date` | TEXT (联合PK) | 日期字符串(YYYY-MM-DD) |
|
||
| `category` | TEXT (联合PK) | 分类名称 |
|
||
| `summary_text` | TEXT | 分类介绍文本 |
|
||
| `generated_time` | DATETIME | 生成时间 |
|
||
|
||
**缓存策略**:
|
||
|
||
| 策略项 | 设计 |
|
||
|------|------|
|
||
| 过期时间 | 24 小时(与时间窗口一致,可配置) |
|
||
| 自动清理 | 每次运行时清理 7 天前数据(`clear_expired(keep_days=7)`) |
|
||
| 写入方式 | `REPLACE INTO`(幂等,支持覆盖更新) |
|
||
| 网摘分离更新 | `save_article_webzine()` 单独更新 `webzine_text` 字段,不影响其他缓存 |
|
||
| 连接降级 | 数据库连接失败时静默降级,不影响主流程 |
|
||
| 全局单例 | `get_cache()` 全局单例模式,避免重复创建连接 |
|
||
|
||
**数据库迁移机制**:
|
||
|
||
```python
|
||
def _migrate_add_webzine_text(self):
|
||
"""自动检测并添加 webzine_text 列"""
|
||
cursor.execute("PRAGMA table_info(article_cache)")
|
||
columns = [col[1] for col in cursor.fetchall()]
|
||
if 'webzine_text' not in columns:
|
||
cursor.execute("ALTER TABLE article_cache ADD COLUMN webzine_text TEXT")
|
||
```
|
||
|
||
**缓存命中流程**:
|
||
|
||
```
|
||
文章被抓取 → 以 article.id 查询缓存(expire_hours=24)
|
||
├─ 命中 + status=1 → 直接使用缓存数据(from_cache=True),跳过翻译和AI分析
|
||
└─ 未命中/过期 → 完整翻译+分析管线,结果 REPLACE INTO 写入缓存
|
||
```
|
||
|
||
**实测效果**(military-digest-v3 验证数据):
|
||
|
||
| 指标 | 第一次(冷启动) | 第二次(含缓存) |
|
||
|------|:---:|:---:|
|
||
| 文章缓存命中 | 0 / 15 | **15 / 15** |
|
||
| API 调用次数 | 37 次 | **0 次** |
|
||
| 总耗时 | 153s | **~5s** |
|
||
| 退出码 | 0 | 0 |
|
||
|
||
#### 1.5 翻译管线设计(继承 military-digest-v3)
|
||
|
||
**核心策略**:中英分离,中文文章零 API 调用,外文文章并发翻译。
|
||
|
||
**中文检测**:汉字占比 > 20% 判定为中文(`is_chinese()` 函数)。
|
||
|
||
**翻译流程**:
|
||
|
||
```
|
||
所有文章
|
||
├─ 中文文章 → translated_title = title(零API调用)
|
||
│ translated_content = content[:300](截取即可)
|
||
└─ 外文文章 → 提交到 ThreadPoolExecutor(max_workers=AI_CONCURRENCY)
|
||
├─ translate_title():标题翻译(temperature=0.2, max_tokens=200)
|
||
└─ translate_content():正文前500词翻译 → 截取300字(max_tokens=800)
|
||
```
|
||
|
||
**并发控制**:
|
||
|
||
| 参数 | 值 | 说明 |
|
||
|------|-----|------|
|
||
| `AI_CONCURRENCY` | 8 | 默认并发数,可通过配置调整 |
|
||
| `temperature` | 0.2 | 翻译低温度,确保准确性 |
|
||
| 失败降级 | 原文填充 | 翻译失败时 `translated_title = title`,不中断流程 |
|
||
|
||
**关键实现**:
|
||
|
||
```python
|
||
def batch_translate_titles(articles, api_key, base_url, model, max_concurrent=None):
|
||
"""中英分离:中文直接填充,外文并发翻译标题"""
|
||
chinese_articles = []
|
||
foreign_articles = []
|
||
|
||
for a in articles:
|
||
if is_chinese(a['title']):
|
||
a['translated_title'] = a['title']
|
||
chinese_articles.append(a)
|
||
else:
|
||
foreign_articles.append(a)
|
||
|
||
# 外文并发翻译
|
||
with ThreadPoolExecutor(max_workers=max_concurrent) as executor:
|
||
futures = {executor.submit(translate_title, a, ...): a for a in foreign_articles}
|
||
for future in as_completed(futures):
|
||
chinese_articles.append(future.result())
|
||
|
||
return chinese_articles
|
||
```
|
||
|
||
**设计优势**:
|
||
- 中文文章零 API 调用,节省 40%+ 翻译 token
|
||
- 标题和正文分离翻译,先翻译标题再决定是否需要翻译正文(可配合关键词筛选)
|
||
- 外文并发翻译,翻译耗时 = 单篇耗时(而非 N × 单篇耗时)
|
||
|
||
#### 1.6 监控统计设计(继承 military-digest-v3)
|
||
|
||
**阶段耗时统计(11 阶段)**:
|
||
|
||
| 阶段 key | 中文标签 | 统计内容 |
|
||
|------|------|------|
|
||
| `rss_fetch` | RSS抓取 | 所有源并行抓取耗时 |
|
||
| `time_filter` | 时间过滤 | 按时间窗口过滤耗时 |
|
||
| `deduplicate` | 去重 | 去重处理耗时 |
|
||
| `translate_titles` | 标题翻译 | 外文标题并发翻译耗时 |
|
||
| `keyword_filter` | 关键词筛选 | 关键词命中筛选耗时 |
|
||
| `translate_contents` | 正文翻译 | 外文正文并发翻译耗时 |
|
||
| `ai_analyze` | AI评分分类 | 批量AI评分分类耗时 |
|
||
| `webzine_generate` | 网摘生成 | TOP3网摘并发生成耗时 |
|
||
| `category_overview` | 分类介绍 | 4类分类介绍并发生成耗时 |
|
||
| `report_generate` | 报告生成 | Markdown报告拼接写入耗时 |
|
||
| `image_generate` | 图片生成 | 网摘长图渲染耗时 |
|
||
|
||
**API 调用统计**:按用途(标题翻译/正文翻译/AI评分分类/网摘生成/分类介绍)和模型分组,记录调用次数、token 估算、重试次数、失败次数。
|
||
|
||
**源抓取统计**:成功/失败源计数,失败源名称列表。
|
||
|
||
**文章统计**:总数、缓存命中数、本次处理数、缓存命中率。
|
||
|
||
**报告示例**(运行结束自动打印):
|
||
|
||
```
|
||
================================================================
|
||
📊 运行监控报告
|
||
================================================================
|
||
|
||
── ⏱ 阶段耗时 ──
|
||
RSS抓取 7.5s
|
||
时间过滤 0.0s
|
||
去重 0.0s
|
||
标题翻译 17.9s
|
||
关键词筛选 0.0s
|
||
正文翻译 9.9s
|
||
AI评分分类 30.7s
|
||
网摘生成 53.3s
|
||
分类介绍 33.5s
|
||
报告生成 0.0s
|
||
图片生成 0.3s
|
||
────────────────────
|
||
合计 153.1s / 总运行 153s
|
||
|
||
── 📡 文章统计 ──
|
||
总计: 15 缓存命中: 0 本次处理: 15
|
||
缓存命中率: 0%
|
||
|
||
── 🤖 API调用统计 ──
|
||
[按用途]
|
||
标题翻译: 7次 / tokens≈1400 / 重试0
|
||
正文翻译: 6次 / tokens≈4800 / 重试0
|
||
AI评分分类: 15次 / tokens≈12000 / 重试0
|
||
网摘生成: 5次 / tokens≈4000 / 重试2
|
||
分类介绍: 4次 / tokens≈600 / 重试0
|
||
[按模型]
|
||
deepseek-v3: 37次 / tokens≈22800
|
||
|
||
── 📰 源抓取统计 ──
|
||
源总数: 5 成功: 5 失败: 0
|
||
|
||
================================================================
|
||
```
|
||
|
||
**数据字段规范**(扩展版):
|
||
|
||
```json
|
||
{
|
||
"title": "内容标题",
|
||
"link": "原始链接",
|
||
"published": "发布时间",
|
||
"source": "来源(公众号名称)",
|
||
"content": "Markdown格式的正文内容",
|
||
"translated_title": "翻译后的中文标题",
|
||
"translated_content": "翻译后的中文正文",
|
||
"tags": "LLM识别的标签",
|
||
"score": "LLM评分(0-100)",
|
||
"summary": "LLM生成的中文摘要",
|
||
"fetched_at": "抓取时间",
|
||
"entities": ["提取的实体列表"],
|
||
"reporting_angle": "报道角度",
|
||
"webzine_text": "参考消息风格网摘文本(Top N 专属)",
|
||
"from_cache": "是否来自缓存"
|
||
}
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] 稳定采集50+ RSS源
|
||
- [ ] 数据入库成功率 > 95%
|
||
- [ ] SQLite 缓存三级覆盖(文章/网摘/分类摘要)
|
||
- [ ] 缓存命中时 API 调用为 0
|
||
- [ ] 中英分离正确,中文文章零翻译 API 调用
|
||
- [ ] 监控报告包含阶段耗时、API统计、源成功率、缓存命中率
|
||
- [ ] 日志系统可定位问题
|
||
|
||
---
|
||
|
||
### 阶段二:Fetch循环与双层评分系统(Week 3-4)
|
||
**目标**:实现实时采集、双层评分(硬约束+动态阈值融合)、即时推送
|
||
|
||
| 序号 | 任务 | 描述 | 输出 | 依赖 |
|
||
|------|------|------|------|------|
|
||
| 2.1 | Fetch循环引擎 | 每30分钟循环一次,抓取RSS→中英分离→并发翻译→评分 | fetch引擎 | 1.7 |
|
||
| 2.2 | **第一层:单篇评分Prompt** | 单篇文章重要性评分(0-100),**含硬约束** | `prompts/article_score.txt` | 2.1 |
|
||
| 2.3 | **硬约束过滤器** | 非时政军事上限、KOL转述上限、时效衰减 | 硬约束模块 | 2.2 |
|
||
| 2.4 | 即时推送判断 | 单篇≥90分触发即时推送候选 | 推送判断模块 | 2.3 |
|
||
| 2.5 | 飞书Webhook推送 | 飞书群机器人推送(Fetch 循环即时推送 + Push 循环日报推送) | 飞书推送适配器 | 2.4 |
|
||
| 2.6 | systemd服务化 | 使用systemd timer管理服务 | systemd配置 | 2.1 |
|
||
|
||
#### 双层评分架构设计
|
||
|
||
```
|
||
第一层:单篇文章评分(硬约束) 第二层:实体簇热度(动态阈值)
|
||
├── 输入:单篇RSS文章(已翻译) ├── 输入:同一实体的多篇报道(已硬约束评分)
|
||
├── 处理:LLM评分 + 硬约束修正 ├── 处理:统计特征 + 动态阈值计算
|
||
└── 输出:0-100分(已约束) └── 输出:热点判定
|
||
├── ≤79:非目标领域,过滤 ├── 低于阈值:普通关注
|
||
├── 80-89:一般关注,进入聚类 └── 高于阈值:热点,优先整合
|
||
└── 90+:高优先级,即时推送候选
|
||
```
|
||
|
||
#### 第一层:评分硬约束设计
|
||
|
||
**Prompt核心约束**:
|
||
```markdown
|
||
## 评分规则(硬性约束)
|
||
|
||
1. **非时政/军事/科技主题上限 79 分**
|
||
- 如果内容不属于目标领域(如纯娱乐八卦、生活琐事),最高只能给 79 分
|
||
|
||
2. **KOL 转述上限 89 分**
|
||
- 如果只是 KOL/大V 对已有信息的转述/评论,而非原创信息,最高只能给 89 分
|
||
|
||
3. **时效性衰减**
|
||
- 发布超过24小时:分数×0.9
|
||
- 发布超过48小时:分数×0.8
|
||
|
||
4. **90+ 分必须同时满足**:
|
||
- 重大事件/突破性进展/政策发布
|
||
- 一手信息源(官方发布、权威媒体首发)
|
||
- 对目标领域有实质性影响
|
||
|
||
5. **标签禁止空泛**
|
||
- 禁止:["军事", "新闻", "热点"]
|
||
- 要求:["歼-35A", "舰载战斗机", "隐身性能", "海军航空兵"]
|
||
```
|
||
|
||
**代码实现**:
|
||
```python
|
||
def apply_score_constraints(entry: dict, raw_score: int) -> tuple[int, list[str]]:
|
||
"""
|
||
应用评分硬约束
|
||
返回:修正后的分数,应用的约束标签列表
|
||
"""
|
||
constraints_applied = []
|
||
|
||
# 约束1:非目标领域上限79
|
||
if not is_target_domain(entry['content']):
|
||
raw_score = min(raw_score, 79)
|
||
constraints_applied.append("非目标领域")
|
||
|
||
# 约束2:KOL转述上限89
|
||
if is_kol_repost(entry):
|
||
raw_score = min(raw_score, 89)
|
||
constraints_applied.append("KOL转述")
|
||
|
||
# 约束3:时效性衰减
|
||
hours_old = get_hours_since_published(entry)
|
||
if hours_old > 24:
|
||
decay_factor = 0.9 ** (hours_old // 24)
|
||
raw_score = int(raw_score * decay_factor)
|
||
constraints_applied.append(f"时效衰减({hours_old}h)")
|
||
|
||
return raw_score, constraints_applied
|
||
```
|
||
|
||
**第一层输出分级**:
|
||
| 分数段 | 处理策略 | 说明 |
|
||
|--------|---------|------|
|
||
| ≤79 | 过滤,不入库 | 非目标领域内容 |
|
||
| 80-89 | 入库,进入聚类 | 一般关注,参与动态阈值计算 |
|
||
| 90+ | 入库,即时推送候选 | 高优先级,同时触发即时推送判断 |
|
||
|
||
**验收标准**:
|
||
- [ ] Fetch循环每30分钟稳定运行
|
||
- [ ] 翻译管线集成正确(中英分离+并发翻译)
|
||
- [ ] 硬约束生效(非目标领域≤79,KOL转述≤89,时效衰减)
|
||
- [ ] 90+文章触发即时推送候选
|
||
- [ ] SQLite缓存正常工作(二次运行零API调用)
|
||
- [ ] 监控统计准确记录各阶段数据
|
||
- [ ] systemd服务可一键启动/停止
|
||
|
||
---
|
||
|
||
### 阶段三:实体提取与细粒度聚类(Week 5-6)
|
||
**目标**:实现细粒度聚类,区分同一实体的不同报道角度
|
||
|
||
| 序号 | 任务 | 描述 | 输出 | 依赖 |
|
||
|------|------|------|------|------|
|
||
| 3.1 | 实体提取Prompt | 设计并优化实体提取提示词 | `prompts/extract_entities.txt` | 2.6 |
|
||
| 3.2 | 角度分类Prompt | 设计报道角度分类提示词 | `prompts/classify_angle.txt` | 2.6 |
|
||
| 3.3 | 批量实体提取 | 实现50篇/批并发调用,小模型处理 | 实体提取服务 | 3.1 |
|
||
| 3.4 | 角度分类服务 | 批量角度分类,与实体提取并行 | 角度分类服务 | 3.2 |
|
||
| 3.5 | 实体归一化 | 大模型消歧,合并别名 | 实体归一化服务 | 3.3 |
|
||
| 3.6 | 聚类算法 | 基于实体+角度的细粒度聚类 | 聚类引擎 | 3.4, 3.5 |
|
||
| 3.7 | 记忆系统 | 避免同一信息反复推送 | 去重模块 | 3.6 |
|
||
|
||
**验收标准**:
|
||
- [ ] 实体提取准确率 > 85%
|
||
- [ ] 角度分类准确率 > 80%
|
||
- [ ] 聚类后实体簇数量合理(200篇→30-50个簇)
|
||
- [ ] 同一信息不重复推送
|
||
|
||
---
|
||
|
||
### 阶段四:热度评价与动态阈值(Week 7-8)
|
||
**目标**:实现第二层筛选——基于硬约束后分数的动态热点识别
|
||
|
||
| 序号 | 任务 | 描述 | 输出 | 依赖 |
|
||
|------|------|------|------|------|
|
||
| 4.1 | **实体簇热度计算** | 融合硬约束分数的多维度热度计算 | 热度指标服务 | 3.6 |
|
||
| 4.2 | **第二层:动态阈值算法** | 基于硬约束后分数分布的自适应阈值 | 阈值计算模块 | 4.1 |
|
||
| 4.3 | 热点识别引擎 | 硬约束过滤 + 动态阈值筛选的双层判定 | 热点识别服务 | 4.2 |
|
||
| 4.4 | 热点精评Prompt | 大模型深度评分(Top 30热点簇) | `prompts/precise_heat.txt` | 4.3 |
|
||
|
||
#### 第二层:动态阈值设计(融合硬约束分数)
|
||
|
||
**实体簇热度计算**:
|
||
```python
|
||
class Cluster:
|
||
def __init__(self, entity_name: str):
|
||
self.entity_name = entity_name
|
||
self.articles = [] # 已硬约束评分的文章列表
|
||
self.hot_score = 0 # 综合热度分
|
||
|
||
def calculate_hot_score(self) -> int:
|
||
"""
|
||
计算实体簇热度,融合硬约束后的单篇分数
|
||
"""
|
||
if not self.articles:
|
||
return 0
|
||
|
||
# 基础分数:最高分文章的分数(已硬约束)
|
||
max_score = max(a['score'] for a in self.articles)
|
||
|
||
# 传播热度:报道数量 × log(来源多样性)
|
||
report_count = len(self.articles)
|
||
source_diversity = len(set(a['source'] for a in self.articles))
|
||
propagation_heat = report_count * math.log(source_diversity + 1)
|
||
|
||
# 角度覆盖度:不同报道角度数 / 6
|
||
angles = set(a.get('reporting_angle', 'unknown') for a in self.articles)
|
||
angle_coverage = len(angles) / 6
|
||
|
||
# 高优先级文章加成(90+文章额外加权)
|
||
high_priority_count = sum(1 for a in self.articles if a['score'] >= 90)
|
||
priority_bonus = high_priority_count * 5 # 每篇90+加5分
|
||
|
||
# 综合计算
|
||
self.hot_score = min(100, int(
|
||
max_score * 0.4 + # 单篇最高分权重40%
|
||
min(propagation_heat * 3, 30) + # 传播热度权重30%
|
||
angle_coverage * 20 + # 角度覆盖权重20%
|
||
priority_bonus # 高优先级加成10%
|
||
))
|
||
|
||
return self.hot_score
|
||
```
|
||
|
||
**动态阈值算法(融合版)**:
|
||
```python
|
||
def calculate_hotspot_threshold(clusters: list[Cluster], date: str) -> float:
|
||
"""
|
||
基于硬约束后分数分布计算动态阈值
|
||
同时考虑统计特征和高优先级文章数量
|
||
"""
|
||
# 获取当日所有簇的热度分数(已融合硬约束)
|
||
scores = [c.hot_score for c in clusters]
|
||
|
||
if len(scores) < 5:
|
||
return 75 # 数据不足时使用保底阈值
|
||
|
||
# 统计特征
|
||
mean_score = mean(scores)
|
||
std_score = std(scores)
|
||
median_score = median(scores)
|
||
|
||
# 方法1:统计阈值(均值+1.5倍标准差)
|
||
threshold_stat = mean_score + 1.5 * std_score
|
||
|
||
# 方法2:保底阈值(至少3条或前10%)
|
||
min_count = max(3, len(scores) * 0.1)
|
||
sorted_scores = sorted(scores, reverse=True)
|
||
threshold_adaptive = sorted_scores[min_count - 1]
|
||
|
||
# 方法3:硬约束保底(考虑90+高优先级文章数量)
|
||
high_priority_count = sum(
|
||
1 for c in clusters
|
||
if any(a['score'] >= 90 for a in c.articles)
|
||
)
|
||
if high_priority_count >= 3:
|
||
threshold_backup = median_score
|
||
else:
|
||
threshold_backup = mean_score + 0.5 * std_score
|
||
|
||
# 取三者中较低值,确保热点不被遗漏
|
||
final_threshold = min(threshold_stat, threshold_adaptive, threshold_backup)
|
||
|
||
logger.info(f"动态阈值计算: 统计={threshold_stat:.1f}, "
|
||
f"保底={threshold_adaptive:.1f}, 硬约束保底={threshold_backup:.1f}, "
|
||
f"最终={final_threshold:.1f}")
|
||
|
||
return final_threshold
|
||
```
|
||
|
||
**双层热点判定流程**:
|
||
```python
|
||
def identify_hotspots(clusters: list[Cluster], date: str) -> tuple[list[Cluster], list[Cluster]]:
|
||
"""
|
||
双层热点识别:硬约束过滤 + 动态阈值筛选
|
||
"""
|
||
# 第一层:硬约束过滤(单篇层面已处理,此处检查)
|
||
valid_clusters = []
|
||
for c in clusters:
|
||
if all(a['score'] <= 79 for a in c.articles):
|
||
continue
|
||
valid_clusters.append(c)
|
||
|
||
# 计算各簇热度(融合硬约束分数)
|
||
for c in valid_clusters:
|
||
c.calculate_hot_score()
|
||
|
||
# 第二层:计算动态阈值
|
||
threshold = calculate_hotspot_threshold(valid_clusters, date)
|
||
|
||
# 阈值筛选 + 额外条件
|
||
hotspots = []
|
||
normal = []
|
||
for c in valid_clusters:
|
||
if c.hot_score >= threshold:
|
||
has_high_priority = any(a['score'] >= 90 for a in c.articles)
|
||
significantly_above = c.hot_score >= threshold + 10
|
||
|
||
if has_high_priority or significantly_above:
|
||
hotspots.append(c)
|
||
else:
|
||
normal.append(c)
|
||
else:
|
||
normal.append(c)
|
||
|
||
hotspots.sort(key=lambda x: (x.hot_score, x.max_article_score), reverse=True)
|
||
|
||
return hotspots, normal
|
||
```
|
||
|
||
**融合设计优势**:
|
||
| 层级 | 作用 | 输入 | 输出 |
|
||
|------|------|------|------|
|
||
| 第一层(硬约束) | 质量控制 | 单篇文章 | 过滤非目标领域,标记高优先级 |
|
||
| 第二层(动态阈值) | 热点发现 | 实体簇(多篇聚合) | 识别突发热点,自适应当日分布 |
|
||
|
||
**验收标准**:
|
||
- [ ] 实体簇热度计算正确(融合硬约束分数)
|
||
- [ ] 动态阈值自适应当日分布
|
||
- [ ] 双层筛选协同工作(硬约束过滤→动态阈值筛选)
|
||
- [ ] 热点识别召回率 > 90%,误报率 < 20%
|
||
|
||
---
|
||
|
||
### 阶段五:Push循环与三层整合(Week 9-10)
|
||
**目标**:实现定时汇总、三层整合(摘要+洞察+网摘)、日报生成
|
||
|
||
| 序号 | 任务 | 描述 | 输出 | 依赖 |
|
||
|------|------|------|------|------|
|
||
| 5.1 | Push循环引擎 | 每日定时运行,读取碎片化信息→整合→推送 | push引擎 | 4.4 |
|
||
| 5.2 | 摘要级整合Prompt | 多源报道合并为50-80字精炼摘要 | `prompts/summary_integrate.txt` | 5.1 |
|
||
| 5.3 | 洞察级整合Prompt | Top 10生成结构化深度分析(**双视角设计**) | `prompts/insight_integrate.txt` | 5.1 |
|
||
| 5.4 | **Prompt防退化模块** | 禁止套话、要求从素材出发 | 防退化检查器 | 5.2, 5.3 |
|
||
| 5.5 | 摘要整合服务 | 小模型批量处理全量簇 | 摘要整合服务 | 5.2, 5.4 |
|
||
| 5.6 | 洞察整合服务 | 大模型逐个处理Top 10(**双视角输出**) | 洞察整合服务 | 5.3, 5.4 |
|
||
| 5.7 | **网摘生成服务** | Top 3 参考消息风格网摘(继承 military-digest-v3) | 网摘生成服务 | 5.1 |
|
||
| 5.8 | 时间线提取 | 从多源报道中提取事件时间线 | 时间线提取模块 | 5.5 |
|
||
| 5.9 | 多角度整合 | 同一事件不同角度报道的整合 | 多角度整合模块 | 5.6 |
|
||
|
||
#### 双视角洞察设计(借鉴AI Daily)
|
||
|
||
**设计理念**:metadata(新闻编辑视角,事实压缩)+ 正文(情报分析师视角,趋势判断)解耦
|
||
|
||
**Prompt设计**:
|
||
```markdown
|
||
## Part 1: Metadata(新闻编辑视角)
|
||
|
||
请生成以下结构化信息:
|
||
- title: 10字以内的标题(事实陈述,无修饰)
|
||
- lead: 20字以内的导语(核心事实)
|
||
- highlights: 3-5个关键要点(bullet points,纯事实)
|
||
|
||
约束:只陈述事实,不做趋势判断,不用形容词。
|
||
|
||
## Part 2: 洞察正文(情报分析师视角)
|
||
|
||
基于多源报道,识别:
|
||
1. 事件脉络:关键时间节点和进展
|
||
2. 多角度分析:技术维度、战略维度、舆论维度
|
||
3. 影响与展望:短期影响、中长期趋势、值得关注信号
|
||
|
||
约束:
|
||
- 禁止空泛评论(如"意义重大""影响深远")
|
||
- 每个观点必须有素材支撑
|
||
- 使用具体名称(装备型号/政策名称/组织名称),不用"某些""部分"等模糊指代
|
||
```
|
||
|
||
**输出示例**:
|
||
```markdown
|
||
---
|
||
title: "歼-35A列装部队"
|
||
lead: "空军官方确认歼-35A已列装首批作战部队"
|
||
highlights:
|
||
- 首次公开确认进入实战化部署阶段
|
||
- 隐身性能对标F-35C,可能在舰载领域形成优势
|
||
- 多国媒体关注其对西太平洋军事平衡的影响
|
||
---
|
||
|
||
## 事件脉络
|
||
- 2024-11:珠海航展首次公开亮相
|
||
- 2025-03:完成舰载适配测试
|
||
- 2025-05:空军确认列装首批作战部队【新进展】
|
||
|
||
## 多角度分析
|
||
- **技术维度**:隐身涂层、航电系统、舰载适配均有突破
|
||
- **战略维度**:提升海军航空兵远海作战能力
|
||
- **舆论维度**:外媒关注中美舰载机代差缩小
|
||
|
||
## 影响与展望
|
||
短期:提升海军航母编队作战能力
|
||
中期:可能在西太平洋形成局部优势
|
||
值得关注:后续舰载版测试进展、出口动向
|
||
```
|
||
|
||
#### 网摘生成设计(继承 military-digest-v3)
|
||
|
||
**定位**:在三层整合中,网摘是面向直接传播的成品内容。不同于摘要(速览用)和洞察(分析用),网摘采用《参考消息》官方新闻报道格式,适合直接推送到飞书群或作为独立内容分发。
|
||
|
||
**核心特性**:
|
||
|
||
| 特性 | 说明 |
|
||
|------|------|
|
||
| 风格 | 《参考消息》官方新闻报道格式:标题+原标题+发布日期+正文+价值点 |
|
||
| 来源识别 | 自动区分微信公众号源("据公众号 XXX 报道")和普通源("据 XXX 报道") |
|
||
| 字数校验 | 正文严格 280-320 字(含标点),价值点 35-45 字 |
|
||
| 重试机制 | 不达标自动重试,最多 3 次,每次带具体反馈("正文只有 XX 字,太少!") |
|
||
| 生成范围 | Top 3 文章,与摘要(全量)和洞察(Top 10)互补 |
|
||
| 输出格式 | 文本文件(.txt)+ Pillow 渲染长图(.png) |
|
||
|
||
**网摘 Prompt 核心约束**:
|
||
|
||
```markdown
|
||
标题要求
|
||
简洁、客观、中性、信息密度高
|
||
结构:主体 + 事件 + 核心态势
|
||
不抒情、不夸张、不用网络用语
|
||
|
||
正文格式要求
|
||
开头第一句必须加:据[来源]报道
|
||
正文风格:客观、平实、严谨、书面化,类似外电编译稿
|
||
必须充分展开:补充背景、说明意义、分析影响、展望前景
|
||
全文一段到底,不分段
|
||
|
||
【字数强制要求 - 必须严格执行】
|
||
正文字数严格控制在 280~320 字(含标点),一个字都不能少,一个字都不能多!
|
||
如果内容不够,请合理补充相关背景、行业态势、同类项目对比等专业内容
|
||
|
||
价值点要求
|
||
正文结束后空一行,再写价值点
|
||
价值点严格 35~45 字(含标点)
|
||
句式结构:事件 - 影响 / 后果 - 值得关注
|
||
```
|
||
|
||
**字数校验重试机制**:
|
||
|
||
```python
|
||
def generate_webzine_for_article(article, api_key, base_url, model, max_retries=3):
|
||
"""为单篇文章生成参考消息风格网摘,支持字数校验重试"""
|
||
for attempt in range(max_retries):
|
||
result = call_ai([...], purpose="网摘生成")
|
||
|
||
# 提取正文部分
|
||
body_text = extract_body(result)
|
||
body_len = len(body_text)
|
||
|
||
if 280 <= body_len <= 320:
|
||
return result # 达标,直接返回
|
||
|
||
# 不达标,在 prompt 末尾追加反馈
|
||
if body_len < 280:
|
||
prompt += f"\n\n【上次反馈:正文只有{body_len}字,太少!请大幅增加内容!】"
|
||
else:
|
||
prompt += f"\n\n【上次反馈:正文有{body_len}字,太多!请精简内容!】"
|
||
|
||
return last_result # 重试耗尽,返回最后结果
|
||
```
|
||
|
||
**输出示例**:
|
||
|
||
```
|
||
标题:歼-35A隐身舰载战斗机正式列装海军航空兵部队
|
||
原标题:China's J-35A stealth fighter enters service with naval aviation
|
||
发布日期:2025年05月24日
|
||
正文:据解放军报报道,中国海军航空兵部队已于近日正式列装歼-35A隐身舰载战斗机,标志着中国成为继美国之后第二个具备隐身舰载机作战能力的国家。歼-35A采用双发中型设计,配备国产涡扇-19发动机,最大起飞重量约30吨,雷达反射截面积据分析优于F-35C。该机配备有源相控阵雷达、分布式光电系统以及先进的电子战套件,可携带霹雳-15中远程空空导弹和鹰击-12超音速反舰导弹等多型武器。军事专家指出,歼-35A的列装将大幅提升中国海军航母编队的制空作战和远程打击能力,特别是在西太平洋方向形成对F-35C的局部数量优势。美国海军战争学院报告认为,中国正加速缩小与美国在舰载航空领域的技术差距,预计到2030年前后将形成至少3个歼-35A舰载机联队的规模。日本防卫省已表示将密切关注相关动向。
|
||
价值点:歼-35A列装标志着中国成为全球第二个拥有隐身舰载机的国家,将显著改变西太平洋海上力量对比,后续量产规模和舰载适配进展值得持续关注。
|
||
```
|
||
|
||
**三层整合对比**:
|
||
|
||
| 层次 | 产品 | 覆盖范围 | 定位 | 输出格式 |
|
||
|------|------|:---:|------|------|
|
||
| 第一层 | 摘要级速览 | 全量(所有簇) | 快速浏览,50-80字精炼 | Markdown表格 |
|
||
| 第二层 | 洞察级分析 | Top 10 热点 | 深度分析,双视角(metadata+正文) | Markdown段落 |
|
||
| 第三层 | 参考消息网摘 | Top 3 热点 | 成品内容,直接传播 | TXT文本 + PNG长图 |
|
||
|
||
#### Prompt防退化设计(借鉴AI Daily)
|
||
|
||
**禁止套话列表**:
|
||
```python
|
||
FORBIDDEN_PHRASES = [
|
||
"意义重大", "影响深远", "引发关注", "备受瞩目",
|
||
"深水区", "拐点", "白热化", "新纪元", "里程碑",
|
||
"不容忽视", "值得注意", "值得关注", # 除非后接具体内容
|
||
"某些", "部分", "一些", "相关", # 模糊指代
|
||
]
|
||
|
||
def check_degeneration(content: str) -> list[str]:
|
||
"""检查内容是否包含禁止套话"""
|
||
violations = []
|
||
for phrase in FORBIDDEN_PHRASES:
|
||
if phrase in content:
|
||
violations.append(phrase)
|
||
return violations
|
||
```
|
||
|
||
**Prompt中的防退化指令**:
|
||
```markdown
|
||
## 输出约束(防退化)
|
||
|
||
1. 禁止使用以下套话:
|
||
- "意义重大""影响深远""引发关注""备受瞩目"
|
||
- "深水区""拐点""白热化""新纪元""里程碑"
|
||
|
||
2. 每个观点必须有素材支撑:
|
||
- 错误:该装备性能先进
|
||
- 正确:该装备隐身系数0.01,优于F-35的0.02
|
||
|
||
3. 使用具体名称,禁止模糊指代:
|
||
- 错误:某些国家表示关注
|
||
- 正确:日本防卫省发布关注声明
|
||
|
||
4. 从素材出发,每次措辞应不同,避免模板化输出
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] 摘要信息完整度 > 90%
|
||
- [ ] 洞察分析覆盖双视角(metadata + 正文)
|
||
- [ ] 网摘正文字数达标率 > 90%(280-320字)
|
||
- [ ] 网摘价值点字数达标率 > 90%(35-45字)
|
||
- [ ] 无禁止套话出现
|
||
- [ ] 整合内容无明显事实错误
|
||
|
||
---
|
||
|
||
### 阶段六:日报生成与跨日关联(Week 11-12)
|
||
**目标**:生成结构化日报(含Sentinel标记),实现跨日关联
|
||
|
||
| 序号 | 任务 | 描述 | 输出 | 依赖 |
|
||
|------|------|------|------|------|
|
||
| 6.1 | 日报模板设计 | Markdown模板,包含热点、速览、数据概览、网摘 | `templates/daily_report.md` | 5.9 |
|
||
| 6.2 | **Sentinel分段标记** | 单文件多板块精确管理 | `src/utils/sentinel.py` | 6.1 |
|
||
| 6.3 | **跨日关联模块** | 从历史日报提取上下文,追踪事件脉络 | `src/processors/cross_day.py` | 6.2 |
|
||
| 6.4 | 排序算法 | 综合热度、时效、重要性的排序逻辑 | 排序模块 | 4.4 |
|
||
| 6.5 | 日报渲染服务 | 按模板组装数据,生成Markdown(含Sentinel) | 日报渲染服务 | 6.1, 6.2, 6.4 |
|
||
| 6.6 | 网摘图片渲染 | Pillow渲染TOP3合并长图,含Banner标题 | 图片渲染服务 | 5.7 |
|
||
| 6.7 | Web归档站点 | 部署在线阅览站点 | Web站点 | 6.5 |
|
||
| 6.8 | 数据保留策略 | fetch保留2天,notify保留2天,push保留5天 | 清理策略 | 6.7 |
|
||
|
||
#### 简化版Sentinel分段标记设计
|
||
|
||
**标记格式**:
|
||
```markdown
|
||
---
|
||
title: "每日情报早报"
|
||
date: "2026-05-24"
|
||
stats:
|
||
total_entities: 87
|
||
hotspot_count: 10
|
||
---
|
||
|
||
<!-- SECTION:summary BEGIN -->
|
||
## 📋 全量新闻速览
|
||
|
||
| # | 实体 | 摘要 | 来源数 | 热度 |
|
||
|---|------|------|--------|------|
|
||
| 1 | 歼-35A | 空军确认列装\|隐身性能对标F-35C | 8 | ★★★★★ |
|
||
<!-- SECTION:summary END -->
|
||
|
||
<!-- SECTION:insights BEGIN -->
|
||
## 🔥 热点深度洞察
|
||
|
||
### 1. 歼-35A列装进展 [持续跟踪]
|
||
> 速览:空军官方确认歼-35A已列装首批作战部队 [8源报道]
|
||
|
||
**事件脉络**
|
||
- 5月20日:首次公开亮相
|
||
- 5月24日:【新进展】确认列装部队
|
||
...
|
||
<!-- SECTION:insights END -->
|
||
|
||
<!-- SECTION:webzine BEGIN -->
|
||
## 📰 参考消息网摘
|
||
|
||
(网摘文本内容,含标题/原标题/发布日期/正文/价值点)
|
||
<!-- SECTION:webzine END -->
|
||
```
|
||
|
||
**核心代码**:
|
||
```python
|
||
import re
|
||
from datetime import datetime, timedelta
|
||
|
||
def extract_section(content: str, section_name: str) -> str:
|
||
"""从日报内容中提取指定section"""
|
||
pattern = rf'<!-- SECTION:{re.escape(section_name)} BEGIN -->(.*?)<!-- SECTION:{re.escape(section_name)} END -->'
|
||
match = re.search(pattern, content, re.DOTALL | re.IGNORECASE)
|
||
return match.group(1).strip() if match else ""
|
||
|
||
def get_historical_context(entity_name: str, days: int = 7) -> dict:
|
||
"""获取某实体近N天的历史报道上下文"""
|
||
context = {
|
||
"first_seen": None,
|
||
"previous_summaries": [],
|
||
"previous_insights": [],
|
||
"mention_count": 0
|
||
}
|
||
|
||
for i in range(days):
|
||
date = (datetime.now() - timedelta(days=i)).strftime("%Y-%m-%d")
|
||
file_path = f"data/reports/daily-{date}.md"
|
||
|
||
try:
|
||
with open(file_path, 'r', encoding='utf-8') as f:
|
||
content = f.read()
|
||
|
||
if entity_name in content:
|
||
context["mention_count"] += 1
|
||
if context["first_seen"] is None:
|
||
context["first_seen"] = date
|
||
|
||
summary_section = extract_section(content, "summary")
|
||
for line in summary_section.split('\n'):
|
||
if entity_name in line and line.strip().startswith('|'):
|
||
context["previous_summaries"].append({
|
||
"date": date,
|
||
"content": line.strip()
|
||
})
|
||
|
||
insights_section = extract_section(content, "insights")
|
||
if entity_name in insights_section:
|
||
insight_blocks = re.split(r'### \d+\.', insights_section)
|
||
for block in insight_blocks:
|
||
if entity_name in block:
|
||
context["previous_insights"].append({
|
||
"date": date,
|
||
"content": block.strip()[:500]
|
||
})
|
||
break
|
||
except FileNotFoundError:
|
||
continue
|
||
|
||
return context
|
||
|
||
def generate_cross_day_marker(entity_name: str) -> str:
|
||
"""生成跨日标记"""
|
||
history = get_historical_context(entity_name, days=7)
|
||
|
||
if history["mention_count"] == 0:
|
||
return "首次报道"
|
||
elif history["mention_count"] == 1:
|
||
return "持续跟踪"
|
||
else:
|
||
return f"持续跟踪({history['mention_count']}次)"
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] 日报生成时间 < 20分钟
|
||
- [ ] Sentinel标记正确,可精确提取summary/insights/webzine
|
||
- [ ] 跨日关联可追踪实体历史报道
|
||
- [ ] 网摘长图渲染正常(合并多篇 + Banner标题)
|
||
- [ ] Web站点可正常访问
|
||
|
||
---
|
||
|
||
### 阶段七:优化与迭代(Week 13-14)
|
||
**目标**:持续优化,功能扩展
|
||
|
||
| 序号 | 任务 | 描述 | 优先级 |
|
||
|------|------|------|--------|
|
||
| 7.1 | Prompt优化 | 基于实际效果迭代优化提示词 | 高 |
|
||
| 7.2 | 反馈闭环 | 收集用户反馈,标注数据,优化模型 | 高 |
|
||
| 7.3 | 趋势分析 | 7日热点趋势图,周期性报告 | 中 |
|
||
| 7.4 | 个性化推荐 | 基于用户阅读历史的个性化排序 | 低 |
|
||
|
||
---
|
||
|
||
## 四、技术栈建议
|
||
|
||
### 4.1 核心组件
|
||
| 组件 | 推荐方案 | 说明 |
|
||
|------|----------|------|
|
||
| 数据采集 | Python + feedparser | RSS解析 |
|
||
| 数据存储 | SQLite + JSON文件 | 数据库+文件双存储 |
|
||
| LLM调用 | OpenAI SDK / 国产模型SDK | 统一接口封装 |
|
||
| 翻译 | 中英分离 + ThreadPoolExecutor并发 | 中文文章零API调用 |
|
||
| 缓存 | SQLite 三级缓存 + 自动迁移 | 断点续跑,二次运行 ~5s |
|
||
| 图片渲染 | Pillow + 中文字体fallback | 网摘长图生成 |
|
||
| 任务调度 | systemd timer | 系统级服务管理 |
|
||
| Web展示 | Hugo / Flask | 静态/动态站点 |
|
||
| 推送 | 飞书 Webhook | 群机器人消息推送 |
|
||
| 日志 | Python logging + systemd journal | 集中日志管理 |
|
||
| 监控 | 11阶段耗时 + API统计 + 源成功率 | 运行结束自动打印报表 |
|
||
|
||
### 4.2 模型选择
|
||
| 任务类型 | 推荐模型 | 预估成本 |
|
||
|----------|----------|----------|
|
||
| 标题翻译 | DeepSeek-V3 / Qwen-Plus | ~0.001元/次 |
|
||
| 正文翻译 | DeepSeek-V3 / Qwen-Plus | ~0.002元/次 |
|
||
| 实体提取 | DeepSeek-V3 / Qwen-Plus | ~0.001元/次 |
|
||
| 角度分类 | DeepSeek-V3 / Qwen-Plus | ~0.001元/次 |
|
||
| 单篇评分 | DeepSeek-V3 / Qwen-Plus | ~0.001元/次 |
|
||
| 摘要整合 | DeepSeek-V3 / Qwen-Plus | ~0.002元/次 |
|
||
| 网摘生成 | DeepSeek-V3 / Qwen-Plus | ~0.005元/次(含重试) |
|
||
| 实体消歧 | GPT-4o / Claude-3.5-Sonnet | ~0.01元/次 |
|
||
| 热点精评 | GPT-4o / Claude-3.5-Sonnet | ~0.02元/次 |
|
||
| 洞察整合 | GPT-4o / Claude-3.5-Sonnet | ~0.1元/次 |
|
||
|
||
**日成本估算**:
|
||
- 200-500篇/日 × 0.001元(小模型)= 0.2-0.5元
|
||
- 外文翻译(按30%外文率):60-150篇 × 0.003元 = 0.18-0.45元
|
||
- 100个簇 × 0.001元(小模型)= 0.1元
|
||
- 3篇网摘 × 0.005元 = 0.015元
|
||
- 10个洞察 × 0.1元(大模型)= 1元
|
||
- **总计:约1.5-2.1元/日**
|
||
|
||
---
|
||
|
||
## 五、项目结构
|
||
|
||
```
|
||
news-intelligence-system/
|
||
├── config/
|
||
│ ├── rss_sources.json # RSS源配置
|
||
│ ├── models.yaml # 模型配置
|
||
│ └── systemd/ # systemd服务配置
|
||
├── src/
|
||
│ ├── collectors/ # 数据采集
|
||
│ │ └── rss_collector.py
|
||
│ ├── processors/ # 处理管道
|
||
│ │ ├── translator.py # 翻译管线(中英分离+并发翻译)
|
||
│ │ ├── entity_extractor.py
|
||
│ │ ├── angle_classifier.py
|
||
│ │ ├── cluster_engine.py
|
||
│ │ ├── heat_calculator.py
|
||
│ │ └── cross_day.py # 跨日关联
|
||
│ ├── integrators/ # 内容整合
|
||
│ │ ├── summary_integrator.py
|
||
│ │ ├── insight_integrator.py
|
||
│ │ └── webzine_generator.py # 网摘生成(参考消息风格+字数校验)
|
||
│ ├── generators/ # 报告生成
|
||
│ │ ├── daily_report_generator.py
|
||
│ │ ├── image_renderer.py # 网摘图片渲染(Pillow)
|
||
│ │ └── web_renderer.py
|
||
│ ├── push/ # 推送平台
|
||
│ │ ├── base.py
|
||
│ │ └── feishu.py # 飞书 webhook(即时+日报双模式)
|
||
│ ├── storage/ # 存储层
|
||
│ │ ├── cache.py # SQLite三级缓存(文章/网摘/分类摘要)
|
||
│ │ └── file_storage.py # JSON文件读写
|
||
│ ├── monitor/ # 监控统计
|
||
│ │ └── monitor.py # 阶段耗时+API消耗+成功率+缓存命中率
|
||
│ └── utils/ # 工具函数
|
||
│ ├── sentinel.py # Sentinel分段标记
|
||
│ ├── prompt_checker.py # Prompt防退化检查
|
||
│ └── language.py # 中文检测(is_chinese)
|
||
├── prompts/ # 提示词文件
|
||
│ ├── article_score.txt # 单篇评分(含硬约束)
|
||
│ ├── extract_entities.txt
|
||
│ ├── classify_angle.txt
|
||
│ ├── quick_heat.txt
|
||
│ ├── precise_heat.txt
|
||
│ ├── summary_integrate.txt
|
||
│ ├── insight_integrate.txt # 双视角洞察
|
||
│ └── webzine_generate.txt # 参考消息网摘(含字数约束)
|
||
├── templates/
|
||
│ └── daily_report.md # 含Sentinel标记(summary/insights/webzine)
|
||
├── data/
|
||
│ ├── cache.db # SQLite缓存数据库
|
||
│ ├── fetch/ # fetch-yyyy-mm-dd.json
|
||
│ ├── notify/ # notify-yyyy-mm-dd.json
|
||
│ └── reports/ # daily-yyyy-mm-dd.md
|
||
├── output/ # 输出文件
|
||
│ ├── report_YYYYMMDD.md # Markdown日报
|
||
│ ├── webzine_YYYYMMDD.txt # 网摘文本
|
||
│ └── webzine_YYYYMMDD.png # 网摘合并长图
|
||
├── web/ # Web归档站点
|
||
├── tests/
|
||
├── scripts/
|
||
│ ├── fetch_loop.py
|
||
│ ├── push_loop.py
|
||
│ └── setup_systemd.sh
|
||
├── requirements.txt
|
||
└── README.md
|
||
```
|
||
|
||
---
|
||
|
||
## 六、关键风险与应对
|
||
|
||
| 风险 | 影响 | 应对措施 |
|
||
|------|------|----------|
|
||
| RSS源失效 | 数据缺失 | 多源备份,监控告警 |
|
||
| LLM API限流 | 处理延迟 | 批量处理,重试机制,降级策略 |
|
||
| 翻译质量不稳定 | 后续分析偏差 | 低温度(0.2)翻译,失败时原文填充 |
|
||
| 缓存数据库损坏 | 缓存失效 | 连接降级不影响主流程,自动重建 |
|
||
| 网摘字数不达标 | 输出质量下降 | 3次重试校验,每次带具体反馈 |
|
||
| 实体识别错误 | 聚类偏差 | 人工标注反馈,Prompt迭代 |
|
||
| 热点漏识别 | 信息缺失 | 动态阈值保底机制,人工复核 |
|
||
| 成本超支 | 预算超支 | Token监控,模型降级,配额控制 |
|
||
| 进程崩溃 | 服务中断 | systemd自动重启 |
|
||
| LLM输出退化 | 质量下降 | **Prompt防退化检查** |
|
||
|
||
---
|
||
|
||
## 七、里程碑与交付物
|
||
|
||
| 里程碑 | 时间 | 交付物 |
|
||
|--------|------|--------|
|
||
| M1 | Week 2 | 稳定运行的数据采集系统(含翻译管线、SQLite缓存、监控统计) |
|
||
| M2 | Week 4 | Fetch循环上线,**评分硬约束生效**,飞书即时推送可用 |
|
||
| M3 | Week 6 | 细粒度聚类引擎,实体提取准确率>85% |
|
||
| M4 | Week 8 | 动态热点识别系统,召回率>90% |
|
||
| M5 | Week 10 | Push循环上线,**三层整合(摘要+洞察+网摘)+Prompt防退化** |
|
||
| M6 | Week 12 | **Sentinel跨日关联上线**,网摘长图渲染,Web归档站点 |
|
||
| M7 | Week 14 | 持续优化迭代 |
|
||
|
||
---
|
||
|
||
## 八、借鉴与继承总结
|
||
|
||
### 8.1 架构层面(借鉴 AI Daily)
|
||
1. **双循环架构**:Fetch循环(实时)+ Push循环(定时)
|
||
2. **systemd服务化**:替代Python内部定时,提升稳定性
|
||
3. **文件存储规范**:fetch/notify/reports三种文件类型,明确保留策略
|
||
|
||
### 8.2 LLM应用层面(借鉴 AI Daily)
|
||
1. **评分硬约束**:非目标领域≤79、KOL转述≤89,有效控制信息质量
|
||
2. **Prompt防退化**:禁止套话、要求从素材出发,避免LLM输出风格趋同
|
||
3. **双视角洞察**:metadata(事实压缩)+ 正文(趋势判断)解耦
|
||
|
||
### 8.3 数据管理层面(借鉴 AI Daily)
|
||
1. **Sentinel分段标记**:单文件多板块精确管理,支持跨日关联
|
||
2. **跨日关联**:从历史日报提取上下文,追踪事件发展脉络
|
||
3. **数据保留策略**:自动清理过期文件
|
||
|
||
### 8.4 工程资产层面(继承 military-digest-v3)
|
||
1. **SQLite三级缓存**:文章+网摘+分类摘要,含自动迁移和过期清理,二次运行 ~5s
|
||
2. **翻译管线**:中英分离(汉字占比>20%判定)+ 外文并发翻译,中文零API调用
|
||
3. **监控统计**:11阶段耗时 + API按用途/模型分组 + 源抓取成功率 + 缓存命中率
|
||
4. **网摘生成**:参考消息风格 + 公众号源智能识别 + 280-320字校验重试(3次)
|
||
5. **图片渲染**:Pillow命令模式 + 中文字体多级fallback
|
||
6. **飞书推送**:webhook文本推送 + 5级异常分层捕获 + 失败优雅降级
|
||
|
||
### 8.5 工程层面
|
||
1. **日志集中管理**:systemd journal便于问题定位
|
||
2. **推送聚焦飞书**:webhook消息推送,覆盖即时快讯和日报两种场景
|
||
3. **成本可控**:每天约1.5-2.1元(含翻译),缓存命中时几乎零成本
|
||
|
||
---
|
||
|
||
## 附录A:military-digest-v3 实际运行验证数据
|
||
|
||
> 以下数据来自 military-digest-v3 系统 2026-05-14 的实际运行验证,为新系统的设计和成本估算提供基准参考。
|
||
|
||
### A.1 全链路冷启动验证
|
||
|
||
清除所有缓存和输出文件后运行,完整链路通过:
|
||
|
||
| 阶段 | 耗时 | 状态 |
|
||
|------|:---:|:---:|
|
||
| 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 ✅ 数据完整无异常
|
||
```
|
||
|
||
### A.2 SQLite 缓存数据完整性验证
|
||
|
||
冷启动全链路运行后,直接查询 `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 | ✅ 合理 |
|
||
|
||
### A.3 缓存命中验证(连续运行2次)
|
||
|
||
| 指标 | 第一次(冷启动) | 第二次(含缓存) |
|
||
|------|:---:|:---:|
|
||
| 文章缓存命中 | 0 / 15 | **15 / 15** ✅ |
|
||
| API 调用次数 | 37 次 | **0 次** ✅ |
|
||
| 总耗时 | 153s | **~5s** ✅ |
|
||
| 退出码 | 0 | 0 |
|
||
|
||
> 第二次运行时文章缓存、网摘缓存、分类介绍缓存全部命中,跳过所有 AI 调用,仅做 RSS 抓取 + 文件排版,几乎零 token 消耗。
|
||
|
||
### A.4 飞书推送验证
|
||
|
||
| 场景 | 配置 | 结果 | 主流程 |
|
||
|------|------|------|:---:|
|
||
| 推送关闭 | `enable_feishu_push: false` | "未启用任何推送渠道,跳过" | exit 0 ✅ |
|
||
| **飞书(真实webhook)** | `enable_feishu_push: true` | **飞书推送成功** 🎉 | exit 0 ✅ |
|
||
|
||
**错误处理矩阵(5级分层):**
|
||
|
||
| 异常类型 | 处理方式 | 主流程影响 |
|
||
|------|------|:---:|
|
||
| `ConnectionError` | warning 日志 + 返回 False | 无 |
|
||
| `Timeout` (连接5s/读取15s) | warning 日志 + 返回 False | 无 |
|
||
| `HTTPError` (4xx/5xx) | warning 日志 + 返回 False | 无 |
|
||
| `JSONDecodeError` | warning 日志 + 返回 False | 无 |
|
||
| 其他 `Exception` | warning 日志 + 返回 False | 无 |
|
||
|
||
---
|
||
|
||
*文档版本:v3.0*
|
||
*最后更新:2026-05-29*
|
||
*状态:融合 AI Daily 四大核心设计 + military-digest-v3 六大工程资产*
|