# AI News Daily

> 端到端 pipeline —— 抓 X/知乎/Bilibili AI 相关热门 → 整理成 wiki → 推送飞书日报

- Skill: `channinglua/ai-news-daily` (Agent Skill)
- Install (CLI): `npx skillmds@latest add channinglua/ai-news-daily`
- Raw SKILL.md: https://api.skillmd.com/api/skills/channinglua/ai-news-daily/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: channinglua (https://skillmd.com/u/channinglua)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/channinglua/ai-news-daily

---


# 每日 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 <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`，先执行：

```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/<source>-<id>.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/<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

典型调度：

```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）

