AI Signal Board:24 小时 AI 资讯雷达优化版部署方案
821920046/ai-news-radar-enhanced
AI Signal Board 在 ai-news-radar-enhanced (v3) 基础上完成工程优化,新增 config/score_weights.yaml 外置评分权重。
AI Signal Board — 24 小时 AI 资讯雷达(优化版)
多源聚合 · 智能信号评分(S/A/B/C)· AI 摘要 · 构建期静态预渲染(SEO 友好)· 每小时自动更新
本仓库在原 ai-news-radar-enhanced (v3) 基础上做了一批工程优化,本文档同时给出完整的部署方案。
目录
- 一、功能概览
- 二、本次优化内容
- 三、目录结构
- 四、环境要求
- 五、本地开发部署
- 六、数据生成与静态预渲染
- 七、前端静态站点部署
- 八、API 服务部署
- 九、GitHub Actions 自动化
- 十、环境变量清单
- 十一、配置评分引擎
- 十二、健康检查与监控
- 十三、常见问题排查
- 十四、安全须知
一、功能概览
| 模块 | 能力 |
|---|---|
| 数据采集 | 50+ 内置 AI/科技信源(OpenAI、DeepMind、Anthropic、HuggingFace、arXiv、量子位、机器之心…)+ 私有 OPML 订阅 |
| 处理管线 | 采集 → 归一化 → 去重合并 → AI 相关性过滤 → 信号评分 → AI 增强 → 输出快照 |
| 信号评分 | Signal Score 2.0:信源权威/技术深度/新颖度/传播速度/社区信号 5 维加权,S/A/B/C 分层 |
| AI 增强 | TLDR 一句话摘要、标题中文翻译(带缓存)、推荐理由 |
| API | /health /daily-report /daily-report/markdown /trends /items /stats |
| 前端 | AI 强相关/全量切换、时间序/热度榜、高级筛选、源站点检索、采集源健康面板、WaytoAGI 更新日志 |
| 自动化 | GitHub Actions 每小时定时、数据质量校验、Webhook 告警、历史归档 |
二、本次优化内容
| 文件 | 类型 | 说明 |
|---|---|---|
config/score_weights.yaml |
新增 | 评分权重/阈值/信源权威度全部外置,改配置即可调参,无需改代码 |
core/signal_score/scorer.py |
重写 | 配置驱动;权重自动归一化;新增 percentile 动态分级;批量评分输出分布日志 |
api/app.py |
重写 | JSON 读取内存缓存(mtime 失效);CORS 收紧;修复 datetime.utcnow();/health 增加数据新鲜度;/trends 支持趋势引擎产出优先 |
scripts/prerender.py |
新增 | 构建期静态预渲染(SSG),解决纯客户端 SPA 的首屏白屏 + SEO 不可收录问题;幂等可重复运行 |
tests/test_signal_score.py |
新增 | 评分/分级/归一化/percentile 单元测试 |
.github/workflows/update-news.yml |
更新 | 在采集后新增预渲染步骤并提交 index.html;启用失败 Webhook 告警 |
examples/hot-ticker/hot-ticker.js |
新增 | 顶部「24h 最热新闻 + 最热开源」实时滚动榜组件(零依赖、点击查看详情、防 XSS) |
examples/hot-ticker/demo.html |
新增 | 热榜组件独立演示页(内置示例数据) |
examples/hot-ticker/INTEGRATION.md |
新增 | 顶部热榜区集成说明 |
api/app.py /hot |
新增接口 | 返回 news / opensource 两个 24h 热榜(供前端滚动榜调用) |
三、目录结构
ai-news-radar-enhanced/
├── api/
│ └── app.py # FastAPI 应用入口
├── core/
│ ├── models.py # 信源清单与全局常量
│ ├── utils.py # compute_hotness / strip_html_tags 等
│ ├── pipeline/ # 七阶段处理管线
│ └── signal_score/
│ ├── scorer.py # 评分引擎(本次重写)
│ └── features.py # 新颖度/传播速度/社区信号特征
├── config/ # 全部配置集中在这里
│ ├── sources.yaml # 信源配置
│ ├── score_weights.yaml # 评分权重 / 阈值 / 信源权威度
│ ├── model_config.yaml # OpenRouter 模型配置
│ └── topic_rules.json # 主题分类关键词规则
├── scripts/ # 只放可执行入口
│ ├── update_news.py # 数据更新入口(CI 调用)
│ ├── prerender.py # 静态预渲染
│ └── probe_aihot.py # 信源探测工具
├── tools/ci/ # CI 运维脚本
│ ├── cleanup_artifacts.sh # 定时清理 Actions artifact
│ └── retry_transient_failure.sh # 瞬时故障自动重跑判定
├── data/ # 只放前端会 fetch 的 5 个 JSON
│ ├── latest-24h.json
│ ├── latest-24h-all.json
│ ├── source-status.json
│ ├── waytoagi-7d.json
│ └── trends.json
│ # archive.json / trend_history.json / title-zh-cache.json 为管道状态,
│ # 由 update-news.yml 在 pipeline-state 分支上维护,不进主干
├── docs/ # history/ 历史记录,maintenance/ 运维诊断
├── examples/hot-ticker/ # 独立示例组件(主站不引用)
├── tests/ # 业务单测;tests/ci/ 为 CI 脚本测试替身
├── index.html # 前端入口(GitHub Pages 发布,预渲染目标)
├── requirements.txt
└── .github/workflows/update-news.yml
⚠️
index.html的实际位置取决于你的 GitHub Pages 发布设置(仓库根 /docs//gh-pages分支)。预渲染脚本的--html参数要指向这个真实文件。
四、环境要求
- Python 3.11+(CI 使用 3.11,本地 3.11–3.13 均可)
- pip / 虚拟环境(venv 推荐)
- 可选:Docker 20+、Node(仅当你的前端有构建步骤时)
- 可选:OpenRouter API Key(启用 AI TLDR 摘要时需要)
五、本地开发部署
# 1) 克隆 git clone https://github.com/821920046/ai-news-radar-enhanced.git cd ai-news-radar-enhanced # 2) 创建虚拟环境 python3 -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate # 3) 安装依赖 pip install --upgrade pip pip install -r requirements.txt # 4) 跑测试(确认环境正常) python -m pytest tests/ -v # 5) 首次生成数据快照 python scripts/update_news.py --output-dir data --window-hours 24 --archive-days 3 # 6) 启动 API(开发模式,热重载) uvicorn api.app:app --host 0.0.0.0 --port 8000 --reload
打开 http://localhost:8000/health 确认服务正常,http://localhost:8000/docs 查看自动生成的 OpenAPI 文档。
六、数据生成与静态预渲染
# 生成 / 刷新 24 小时快照 python scripts/update_news.py --output-dir data --window-hours 24 --archive-days 3 # 启用私有 OPML 订阅(可选) python scripts/update_news.py --output-dir data --window-hours 24 --archive-days 3 \ --rss-opml feeds/follow.opml # 构建期静态预渲染:把首屏新闻 + SEO 元信息写进 index.html python scripts/prerender.py --data data/latest-24h.json --html index.html --top 30 # 可选:指定 Open Graph 配图 / 输出到其他文件 python scripts/prerender.py --data data/latest-24h.json --html index.html \ --out dist/index.html --top 30 --og-image https://news.my-tv.eu.cc/og.png
预渲染做了什么:
- 注入真实
<title>/description/ Open Graph / Twitter Card / JSON-LDItemList(搜索引擎与分享卡片可读)。 - 内联首屏数据为
<script id="__PRERENDER_DATA__" type="application/json">,前端可直接读取,省去首屏一次 fetch。 - 在
#prerender-root容器输出服务端渲染的新闻列表,首屏无需等待 JS。
脚本幂等:通过 HTML 注释标记定位注入区,可被每小时 CI 重复调用而不会累积重复内容。首次运行若模板里没有标记,会自动在 </head> 前与 <body> 后插入。
前端配合(可选但推荐),在你的 app.js 初始化处加入:
// 优先使用预渲染内联数据,避免首屏再发一次 fetch function getInitialData() { const el = document.getElementById("__PRERENDER_DATA__"); if (el) { try { return JSON.parse(el.textContent); } catch (_) {} } return null; } // 客户端渲染完成后,移除服务端占位列表 function clearPrerender() { const root = document.getElementById("prerender-root"); if (root) root.remove(); }
七、前端静态站点部署
前端是纯静态资源(index.html + JS/CSS + data/*.json),任意静态托管均可。
7.1 GitHub Pages
- 仓库 Settings → Pages,Source 选择
Deploy from a branch。 - 选择分支与目录(仓库根
/或/docs)。 - 每小时 Actions 会自动更新
data/*.json与预渲染后的index.html并 push,Pages 自动重新发布。 - 自定义域名:在仓库根放
CNAME文件(内容为你的域名,如news.my-tv.eu.cc),并在 DNS 配置 CNAME 记录指向<user>.github.io。
当前部署:https://news.my-tv.eu.cc/ 。务必确认 Workflow 的
--html路径与 Pages 发布目录一致。
7.2 Vercel / Netlify / Cloudflare Pages
- Build command:留空(无需构建)或
python scripts/prerender.py --data data/latest-24h.json --html index.html。 - Output directory:仓库根(或
dist/,与--out对应)。 - 数据刷新仍由 GitHub Actions 负责 push,平台监听到 push 后自动重新部署。
7.3 自有服务器 + Nginx
server { listen 80; server_name news.example.com; root /var/www/ai-news-radar; # 放置 index.html 与 data/ 的目录 index index.html; # 数据 JSON:缩短缓存,保证新鲜 location /data/ { add_header Cache-Control "public, max-age=300"; } # 反向代理 API(如同机部署) location /api/ { proxy_pass http://127.0.0.1:8000/; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location / { try_files $uri $uri/ /index.html; } }
用 cron 定时拉取最新代码 / 数据:
# crontab -e ,每小时第 5 分钟同步 5 * * * * cd /var/www/ai-news-radar && git pull --quiet
八、API 服务部署
API 是可选的:纯前端站点直接读取
data/*.json即可运行。需要/items过滤、/trends、/stats等动态接口时再部署 API。
8.1 直接运行 / systemd
pip install uvicorn[standard] uvicorn api.app:app --host 0.0.0.0 --port 8000 --workers 2
/etc/systemd/system/ai-news-api.service:
[Unit] Description=AI News Radar API After=network.target [Service] WorkingDirectory=/opt/ai-news-radar-enhanced Environment="ALLOW_ORIGINS=https://news.my-tv.eu.cc" Environment="DATA_STALE_HOURS=6" ExecStart=/opt/ai-news-radar-enhanced/.venv/bin/uvicorn api.app:app --host 0.0.0.0 --port 8000 --workers 2 Restart=always RestartSec=3 [Install] WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now ai-news-api
sudo systemctl status ai-news-api8.2 Docker / Docker Compose
Dockerfile:
FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt uvicorn[standard] COPY . . EXPOSE 8000 CMD ["uvicorn", "api.app:app", "--host", "0.0.0.0", "--port", "8000"]
docker-compose.yml:
services: api: build: . ports: - "8000:8000" environment: ALLOW_ORIGINS: "https://news.my-tv.eu.cc" DATA_STALE_HOURS: "6" volumes: - ./data:/app/data:ro # 挂载数据目录(只读) restart: unless-stopped
docker compose up -d --build curl http://localhost:8000/health
九、GitHub Actions 自动化
工作流 .github/workflows/update-news.yml 每小时执行:
- Checkout → 安装依赖 → 跑
pytest - (可选)从
FOLLOW_OPML_B64解码私有 OPML - 从
pipeline-state分支恢复去重归档(archive.json/title-zh-cache.json) scripts/update_news.py采集并生成data/*.json- 数据质量校验:条目数 < 3、数据超 6 小时未更新、信源成功率 < 30%、
AI 处理层覆盖率 < 5% 均直接失败(
scripts/validate_data.py) scripts/prerender.py预渲染index.html- 语义级变化检测(
scripts/snapshot_fingerprint.py):剔除generated_at后内容与 HEAD 完全一致则跳过提交 - Commit & push
data/*.json+index.html - 把归档写回
pipeline-state(不进入main历史) - 数据陈旧看门狗:本次运行成功但数据仍过期 → 企微告警
- 失败告警:任意步骤失败 / 取消 / 超时 → 企微告警
启用步骤:
- 仓库 Settings → Secrets and variables → Actions 配置所需 secret(见下表)。
- Settings → Actions → General → Workflow permissions 选 Read and write permissions(CI 需要 push)。
- 手动触发一次:Actions → Update AI News Snapshot → Run workflow,确认全绿。
9.1 长期无人值守运行要点
本项目按「长期自动跑、无需人工干预」设计,关键约定如下:
| 机制 | 位置 | 作用 |
|---|---|---|
cancel-in-progress: false |
workflow concurrency |
单趟流水线实测约 20 分钟,而 cron 每小时触发。必须为 false,否则下一小时的调度会取消正在运行的那一趟,数据可能永远更新不完。 |
timeout-minutes: 40 |
workflow jobs.update |
高于实测 20 分钟,留足余量避免被强杀。 |
| 无实质变化跳过提交 | scripts/snapshot_fingerprint.py |
data/latest-24h*.json 合计约 6.8 MB。若不抑制,generated_at 每次变化都会产生一次提交,约 1200 次后 .git 会膨胀到 GB 级。 |
| AI 层看门狗 | scripts/validate_data.py |
抓取成功 ≠ 处理成功。OpenRouter 配额耗尽时管道不会抛异常,只会安静地退化为未加工的原始条目。看门狗使该故障显式失败并触发告警。 |
| rebase / push 失败即失败 | workflow 提交步骤 | 不吞错误。工作区有未暂存改动时 pull --rebase 会失败,必须先 git stash 清理。 |
| 归档走独立分支 | pipeline-state |
archive.json 约 14 MB 且只用于去重,绝不能进 main(会永久膨胀仓库历史)。现在由 CI 每轮从该分支恢复、处理后再写回。 |
9.2 通知与告警(企业微信)
告警由 scripts/notify_ci.py 统一实现,通过现有 WEBHOOK_URL 发送,
支持企微 / 钉钉(markdown)、飞书、Slack 三种载荷格式。
覆盖三类「无人值守时必须知道」的情况:
| 场景 | 触发条件 | 说明 |
|---|---|---|
| Actions 异常 | 任意步骤失败、被取消、或超时(if: !success(),注意不是 failure()) |
之前用 failure(),会漏掉 cancelled / timed_out —— 而这正是最常见的长任务中断形态。 |
| 数据陈旧 | 流水线成功但 generated_at 超过 6 小时 |
最隐蔽的失败模式:所有信源抓取失败或上游 API 静默降级时,管道仍以 exit 0 结束,站点持续展示过期新闻,而 Actions 一片绿。 |
| 调度停摆 | 独立工作流 staleness-watchdog.yml 每 6 小时检查一次,超 12 小时即告警 |
若 cron 被 GitHub 因仓库不活跃而停用、或工作流被误禁用,更新流程根本不会启动 —— 写在它内部的检查也就永远不会触发。该看门狗不依赖更新流程,两者失败模式相互独立。 |
未配置 WEBHOOK_URL 时:不发送消息,但看门狗仍会让运行失败。
这样「没配告警」不会连带把「能发现故障」的能力一起关掉。
必须做的一件事:配置
WEBHOOK_URL(企微群机器人 webhook 地址), 并把WEBHOOK_TYPE设为wecom。这是目前唯一的故障出口, 不配置则上述三类告警全部无处可去。另建议为
OPENROUTER_KEYS配置多个 key(逗号分隔),配额耗尽是最常见的长期故障源。
9.3 数据保留策略
| 数据 | 保留期 | 说明 |
|---|---|---|
latest-24h.json / latest-24h-all.json |
24 小时 | 前台展示窗口,由 --window-hours 24 控制。 |
archive.json(去重归档) |
3 天 | 由 --archive-days 3 控制。时间戳全部不可解析的记录会被直接剔除 —— 它们无法证明自己新鲜,且永远匹配不到新抓取的条目。 |
trend_history.json |
7 天 / 最多 240 条 | 由 max_history_days 与 max_history_entries 双重限制(core/trend_engine)。日期不可解析的条目同样会剔除,避免损坏条目占满配额、挤掉真实历史。 |
title-zh-cache.json |
跟随 archive | 只保留仍存在于归档中的标题。 |
十、环境变量清单
| 变量 | 作用 | 默认 / 示例 |
|---|---|---|
ALLOW_ORIGINS |
API 允许的跨域来源(逗号分隔) | *;建议生产填 https://news.my-tv.eu.cc |
DATA_STALE_HOURS |
/health 判定数据过期的小时阈值 |
6 |
OPENROUTER_KEYS |
OpenRouter API Key(可多个,逗号分隔) | secret |
OPENROUTER_MODEL |
TLDR 使用的模型 | 如 google/gemini-flash-1.5 |
AI_TLDR_ENABLED |
是否启用 AI 摘要 | true / false |
AI_TLDR_TOP_N |
仅对前 N 条生成摘要 | 30 |
AI_TLDR_MAX_WORKERS |
摘要并发数 | 4 |
WEBHOOK_URL |
失败/热点通知地址 | secret |
WEBHOOK_TYPE |
webhook 类型 | feishu / slack / generic |
WEBHOOK_MODE |
通知模式 | 视实现而定 |
WEBHOOK_HOTNESS_THRESHOLD |
热点推送阈值 | 数值 |
FOLLOW_OPML_B64 |
私有 OPML(base64) | secret |
本地可在项目根放
.env并配合python-dotenv读取;CI 中通过 Actions Secrets 注入。切勿把 Key 提交进仓库。
十一、配置评分引擎
编辑 config/score_weights.yaml,无需改代码即可调参:
weights: # 5 维权重(引擎自动归一化) source_weight: 0.25 technical_score: 0.25 novelty: 0.20 velocity: 0.15 community: 0.15 levels: mode: "static" # static=固定阈值;percentile=动态分位数分级 thresholds: { S: 85, A: 70, B: 50 } percentiles: { S: 0.90, A: 0.70, B: 0.40 } source_authority: # site_id -> 0-100 official_ai: 100 aibreakfast: 85 # ...
- 想缓解分数通胀导致「全是 C / 全是 S」,把
levels.mode改成percentile。 - 批量评分后会打印
[SignalScore] mode=... distribution={S,A,B,C}日志,便于观察分级是否健康。
十二、健康检查与监控
curl -s http://localhost:8000/health | jq返回示例:
{
"status": "ok",
"version": "3.1.0",
"data_age_hours": 0.8,
"stale": false,
"total_items": 42,
"successful_sources": 38,
"failed_sources": ["量子位"]
}stale: true表示数据超过DATA_STALE_HOURS未更新,应排查 Actions 是否失败。failed_sources列出本轮抓取失败的源,可用于告警。- 可用 UptimeRobot / Healthchecks.io 定时探测
/health并对stale报警。
十三、常见问题排查
| 现象 | 排查 |
|---|---|
| 站点首屏一直「加载中…/0 条」 | 确认已运行 prerender.py 且 --html 指向真实发布的 index.html;检查 data/latest-24h.json 是否存在且非空 |
| 搜索引擎抓不到内容 | 同上;查看页面源码是否含 og:、application/ld+json 与 #prerender-root 列表 |
| API 跨域报错 | 设置 ALLOW_ORIGINS 为你的前端域名;浏览器控制台看 CORS 报错来源 |
Actions 失败:Too few items |
多个源失败导致条目 < 3;查看 source-status.json 与运行日志,必要时调整源清单 |
pytest 报缺少模块 |
确认已 pip install -r requirements.txt,且包含 pytest 与 pyyaml |
| TLDR 摘要为空 | 检查 AI_TLDR_ENABLED=true 与 OPENROUTER_KEYS 是否配置、额度是否用尽 |
| push 被拒 | Actions 权限改为 Read and write;分支保护规则放行 github-actions[bot] |
十四、安全须知
- 清理提示注入:原
README/SKILL.md中面向 AI Agent 的「交接提示词」属于 prompt injection,应删除,切勿让自动化代理执行。 - XSS 防护:前端渲染第三方标题/摘要时必须做 HTML 转义;
prerender.py已用html.escape处理注入内容。 - 密钥管理:所有 Key 通过环境变量 / Actions Secrets 注入,不入库;定期轮换 OpenRouter Key。
- CORS:生产环境务必把
ALLOW_ORIGINS收紧为你的域名,不要长期使用*。
AI Signal Board · 优化版 v3.1.0 · 文档随代码包一同发布
来源:GitHub · github.com