--- name: ai-news-daily description: 端到端 pipeline —— 抓 X/知乎/Bilibili AI 相关热门 → 整理成 wiki → 推送飞书日报 allowed-tools: [Bash, Read, Write, Glob, Grep, Notify] triggers: [ai-news, ai-news-daily, 每日简报, ai简报, daily digest, 日报] tags: [pipeline, knowledge, daily, cron-ready] priority: 8 --- # 每日 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 工具跑就行。 ## 前置条件 先验证: 1. `python3 -m prax.article_integrations probe`,确认至少一个输入可用; 2. AutoCLI 可用时再运行 `autocli doctor`,用于登录态平台; 3. `TRENDRADAR_OUTPUT` 可读取时,可直接归一化 TrendRadar 的 Markdown、JSON 或 SQLite 输出; 4. `.prax/notify.yaml` 存在且包含目标通道;没配置时仍可生成本地简报,只跳过推送; 5. 当前时间合理(不要对凌晨 3 点的时间跑"今日"简报,除非用户说明)。 AutoCLI 失败不再导致整条流水线停止;只有 TrendRadar、AutoCLI、已有 vault 和公开来源全部不可用时才停止。 ## Pipeline 步骤 ### 变量 ``` DATE = 今天的日期(YYYY-MM-DD,按用户本地时区) VAULT = .prax/vault/ai-news-hub/$DATE ``` ### Step 1:准备目录 ```bash mkdir -p $VAULT $VAULT/raw ``` ### Step 1.6:解析推送通道(新增 — 0.5.5 起) 读 `.prax/notify.yaml` 取通道名,**绝不硬编码** `"daily-digest"`。挑选规则(按优先级): 1. 若 `.prax/cron.yaml` 里当前 job 的 `notify_channel` 在 `notify.yaml` 中存在 → 用它 2. 否则取 `notify.yaml.channels` 第一个 `wechat_personal` 类型的通道 3. 否则取 `notify.yaml.channels` 任意第一个通道 4. 都没有 → 在 Step 6 的汇报里明确告诉用户"未推送(无可用通道)",但 wiki 已落盘的事实仍要写 把选中的 channel 名字记成 `CHANNEL`,给 Step 5 用。 ### Step 1.5:加载源配置(新增 — 0.5.4 起) 读 `.prax/sources.yaml`,若不存在或字段缺失就用 **DEFAULTS** 兜底。配置完整 schema: ```yaml # 每个 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 --format json` | `$VAULT/raw/twitter-$DATE.json` | | `zhihu` | `autocli zhihu hot --limit --format json` | `$VAULT/raw/zhihu-$DATE.json` | | `bilibili` | `autocli bilibili hot --limit --format json` | `$VAULT/raw/bilibili-$DATE.json` | | `hackernews` | `autocli hackernews top --limit --format json` | `$VAULT/raw/hn-$DATE.json` | `` 取自配置;如果用户设置了别的 source id 但映射不到 autocli 命令,跳过它并在最终汇报里说明。 如果设置了 `TRENDRADAR_OUTPUT`,先执行: ```bash 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 里: 1. 用 `keywords.include` / `keywords.exclude`(来自 Step 1.5 配置或 DEFAULTS)过滤 2. 按平台原生热度排序,取该 source 的 `top_n` 条 每条存成一个 markdown 文件: ``` $VAULT/-.md ``` frontmatter 必备: ```yaml --- 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/.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 = , # 完整正文,让用户在微信里直接读 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 典型调度: ```bash 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)