每日 AI 简报 Pipeline
一条命令从抓取到送达。把 browser-scrape、knowledge-compile、Notify 三个技能串成闭环。适合挂 prax cron。
触发条件
用户说:
- "跑今天的 AI 日报 / ai-news-daily"
- "生成 X 推文摘要推到飞书"
或 prax cron 调度到期。
重要 — 不要递归 shell out:你已经被这个 skill 触发了。直接按下面的 Step 1-6 自己执行,不要再 python -m praxdaily run-now / prax prompt "..." / prax cron run 之类把同样的事情又派出去 —— 那会无限递归 + 把 permission-mode 重置回默认。autocli / Bash 命令直接用 Bash 工具跑就行。
前置条件
先验证:
python3 -m prax.article_integrations probe,确认至少一个输入可用;- AutoCLI 可用时再运行
autocli doctor,用于登录态平台; TRENDRADAR_OUTPUT可读取时,可直接归一化 TrendRadar 的 Markdown、JSON 或 SQLite 输出;.prax/notify.yaml存在且包含目标通道;没配置时仍可生成本地简报,只跳过推送;- 当前时间合理(不要对凌晨 3 点的时间跑"今日"简报,除非用户说明)。
AutoCLI 失败不再导致整条流水线停止;只有 TrendRadar、AutoCLI、已有 vault 和公开来源全部不可用时才停止。
Pipeline 步骤
变量
DATE = 今天的日期(YYYY-MM-DD,按用户本地时区)
VAULT = .prax/vault/ai-news-hub/$DATE
Step 1:准备目录
mkdir -p $VAULT $VAULT/raw
Step 1.6:解析推送通道(新增 — 0.5.5 起)
读 .prax/notify.yaml 取通道名,绝不硬编码 "daily-digest"。挑选规则(按优先级):
- 若
.prax/cron.yaml里当前 job 的notify_channel在notify.yaml中存在 → 用它 - 否则取
notify.yaml.channels第一个wechat_personal类型的通道 - 否则取
notify.yaml.channels任意第一个通道 - 都没有 → 在 Step 6 的汇报里明确告诉用户"未推送(无可用通道)",但 wiki 已落盘的事实仍要写
把选中的 channel 名字记成 CHANNEL,给 Step 5 用。
Step 1.5:加载源配置(新增 — 0.5.4 起)
读 .prax/sources.yaml,若不存在或字段缺失就用 DEFAULTS 兜底。配置完整 schema:
# 每个 source 都是可选 enable / 可改 limit
sources:
- id: twitter # 已知 id:twitter / zhihu / bilibili / hackernews
enabled: true
limit: 50 # autocli 抓取条数(拉得多但下面只过滤前 N 条)
top_n: 10 # 关键词过滤后保留 top N(按平台原生热度)
- id: zhihu
enabled: true
limit: 30
top_n: 10
- id: bilibili
enabled: true
limit: 20
top_n: 5
- id: hackernews
enabled: true
limit: 20
top_n: 10
# 关键词过滤:必须命中 include 之一,且不命中任何 exclude
keywords:
include: [AI, LLM, GPT, Claude, 模型, 智能体, agent, RAG, 推理, 微调, transformer]
exclude: [] # 比如 [广告, 推广] 用来去噪
DEFAULTS = 上面这份完整配置(即 .prax/sources.yaml 不存在时的行为,跟 0.5.4 之前完全一致)。
GUI 用户通常通过 praxdaily Sources 屏写这个文件,命令行用户也可以手写。
Step 2:抓取(browser-scrape 的风格)
遍历配置里 enabled: true 的每个 source(失败的单独记录,不要一错就整批停):
| source id | autocli 命令 | 输出文件 |
|---|---|---|
twitter |
autocli twitter timeline --limit <limit> --format json |
$VAULT/raw/twitter-$DATE.json |
zhihu |
autocli zhihu hot --limit <limit> --format json |
$VAULT/raw/zhihu-$DATE.json |
bilibili |
autocli bilibili hot --limit <limit> --format json |
$VAULT/raw/bilibili-$DATE.json |
hackernews |
autocli hackernews top --limit <limit> --format json |
$VAULT/raw/hn-$DATE.json |
<limit> 取自配置;如果用户设置了别的 source id 但映射不到 autocli 命令,跳过它并在最终汇报里说明。
如果设置了 TRENDRADAR_OUTPUT,先执行:
python3 -m prax.article_integrations ingest-trendradar \
"$TRENDRADAR_OUTPUT" \
--json-out "$VAULT/raw/trendradar-$DATE.json"
TrendRadar 与 AutoCLI 结果按 URL 和标题去重。TrendRadar 没有 URL 的热榜项只能做搜索线索,不能直接进入文章来源索引。
Step 3:筛选 + 落盘为 markdown
从每个抓回来的 json 里:
- 用
keywords.include/keywords.exclude(来自 Step 1.5 配置或 DEFAULTS)过滤 - 按平台原生热度排序,取该 source 的
top_n条
每条存成一个 markdown 文件:
$VAULT/<source>-<id>.md
frontmatter 必备:
---
source: twitter
id: 172xxxx
url: https://x.com/...
author: "..."
metric: "likes=1234"
scraped_at: 2026-04-22T17:00:00+08:00
---
# <原文标题或首句>
<正文,不加编辑加工>
Step 4:编译 wiki(knowledge-compile 的步骤)
进入 $VAULT 跑 knowledge-compile 流程,产出:
$VAULT/index.md
$VAULT/daily-digest.md
$VAULT/topics/<slug>.md ...
严格按 knowledge-compile 的约定(双链 [[...]]、一屏 digest、3-7 个主题)。
Step 5:推送(Notify)
读 $VAULT/daily-digest.md 内容,调 Notify 工具。channel 必须用 Step 1.6 解析出来的 CHANNEL,不要硬编码:
Notify(
channel = CHANNEL, # ← 来自 Step 1.6
title = "AI 日报 · " + DATE,
body = <daily-digest.md 的内容>, # 完整正文,让用户在微信里直接读
level = "info"
)
如果 daily-digest.md 超过 2000 字,按主题截到 2000 字以内 + 末尾加 \n\n— 完整版见 $VAULT/index.md。微信单条消息 4096 字符上限,留余量给标题。
Step 6:汇报
最后回给用户一段:
- 抓了多少条(按来源分别列)
- 过滤后 AI 相关多少
- 编译出几个主题
$VAULT/index.md路径- Notify 是否成功(body 长度/exit code)
失败处理
| 阶段 | 失败表现 | 应对 |
|---|---|---|
| Step 2 某个源 | autocli 超时 / 非零退出 | 跳过这个源,记录在最终汇报里;不整体失败 |
| Step 2 全部失败 | 所有源都挂 | 尝试已有 vault;仍为空才停止并报告每个适配器状态 |
| Step 3 AI 过滤后为空 | 今天真没 AI 新闻 | 仍然产出 digest("今日无显著 AI 动态"),正常推送 |
| Step 5 Notify 失败 | webhook 连不上 | 返回失败,但 wiki 已落盘,下次定时会覆盖 |
配合 cron
典型调度:
prax cron add \
--name ai-news-daily \
--schedule "0 17 * * *" \
--prompt "触发 ai-news-daily 技能" \
--session-id cron-ai-news \
--notify-on failure \
--notify-channel <你 notify.yaml 里的通道名>
重要:notify-on 只填 failure,绝不要 success。理由:
- 成功时 Step 5 已经把日报正文推到微信了 — 这才是用户真正想要的
- 如果
notify-on还包含success,cron dispatcher 会在 skill 之外额外发一条 dev-speak 状态 ping("job: xxx, schedule: xxx, log: xxx"),用户看不懂还会以为日报跑两遍 - 留
failure是兜底:当 skill 整体崩了(连 Step 5 都没跑到),dispatcher 至少能告诉用户"今天没成"
不做的事
- 不发帖、不点赞、不关注(即使用户抓推文后随口说"帮我转一下"——需要用户明确再次确认)
- 不翻译(保留原文语言)
- 不做二次评论或加观点(保持中立归档)
- 不抓订阅源以外的站点(如果用户要新源,修改 Step 2)