# Assemble 工作流架构

> 最后更新：2026-04-20\
> 状态：✅ 已跑通，事件驱动链路生效

本文档分两层：

* **抽象层**：Assemble 的 4 阶段概念模型（稳定，指导设计）
* **具体层**：当前的事件驱动实现（会随技术选型演变）

***

## 抽象层：Assemble 的 4 阶段概念模型

Assemble 的核心是把零散信息经过加工沉淀为可传播的内容。整个流程由 4 个阶段构成：

```
┌──────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│   hunting    │ ─→ │  processing  │ ─→ │   archive    │ ─→ │   publish    │
│   搜集信息   │    │   处理加工   │    │   存档成果   │    │   发布传播   │
└──────────────┘    └──────────────┘    └──────────────┘    └──────────────┘
      输入               转化                沉淀                扩散
```

| 阶段             | 职责              | 产出               |
| -------------- | --------------- | ---------------- |
| **hunting**    | 从外部源搜集原始信息      | 原始数据（新闻、笔记、RSS…） |
| **processing** | 筛选、提炼、加工、加入你的视角 | 可发布的内容草稿         |
| **archive**    | 长期存档已定稿的内容      | 结构化的历史沉淀（可被外部引用） |
| **publish**    | 分发到目标平台         | 触达读者的最终形态        |

### 为什么要区分这 4 个阶段

* **hunting 和 processing 分开**：避免"边搜边写"的混乱。先广撒网，再精挑。
* **processing 和 archive 分开**：区分"正在编辑"和"已经定版"。未定版的内容不应污染历史。
* **archive 和 publish 分开**：同一份内容可能分发到多个平台，archive 是单一真相源。

### 每个阶段的当前实现

| 阶段         | 当前实现                                                                    | 可演进方向                           |
| ---------- | ----------------------------------------------------------------------- | ------------------------------- |
| hunting    | 从 `qiao-925/news-digest` 拉取（`run_news_loop.py`）                         | 接入 One-Note、RSS 白名单、个人笔记等       |
| processing | `assemble-processing/assemble-archive/` 本地编辑 + `publish_pipeline.py` 渲染 | 加入 AI 辅助筛选、质量评分、模板化加工           |
| archive    | `qiao-925/assemble-archive` 仓库                                          | 保持不变，作为单一真相源                    |
| publish    | GitHub Actions → 博客园（MetaWeblog）                                        | 扩展 GitHub Pages / Notion / 其他平台 |

**抽象层稳定，具体层可替换**。换一个 hunting 源或 publish 目标，都不影响整体架构。

***

## 具体层：当前的事件驱动链路

每个具体节点右侧标注它对应的抽象阶段：

```
┌──────────────────┐
│   news-digest    │  ← [hunting]      外部聚合源（独立更新）
│ (qiao-925/news-digest) │
└────────┬─────────┘
         │  ⚠ 人工 checkpoint（不自动）
         │  运行 run_news_loop.py 或手动编辑
         ▼
┌─────────────────────────────┐
│     Assemble (私有)         │  ← [processing]  编辑、处理中心
│  assemble-archive/ 目录      │                  你在这里整理内容
└────────┬────────────────────┘
         │  git push（你主动触发）
         │  Action: Assemble Archive Sync
         ▼
┌──────────────────┐
│ assemble-archive │  ← [archive]      公开成果归档
│    (公开)        │
└────────┬─────────┘
         │  push event 自动触发
         │  Action: Publish to CNBlogs
         ▼
┌──────────────────┐
│     博客园       │  ← [publish]      触达读者
└──────────────────┘
```

## 仓库职责

| 仓库                             | 可见性 | 职责                                        | 本地路径                                      |
| ------------------------------ | --- | ----------------------------------------- | ----------------------------------------- |
| `qiao-925/news-digest`         | 公开  | 外部输入源（广覆盖新闻聚合）                            | —                                         |
| `qiao-925/assemble-processing` | 私有  | 编辑、加工、触发发布的中心（本地目录仍叫 `Assemble`）          | `c:/Users/nonep/Desktop/Assemble`         |
| `qiao-925/assemble-archive`    | 公开  | 发布内容的归档仓库                                 | `c:/Users/nonep/Desktop/assemble-archive` |
| `qiao-925/assemble-publish`    | 公开  | 博客园同步脚本（被 assemble-archive 的 workflow 引用） | `c:/Users/nonep/Desktop/assemble-publish` |

## 目录结构

### Assemble 仓库

```
Assemble/
├── assemble-archive/          ← 你编辑内容的地方（一个工作目录）
│   ├── daily/<date>/*.md
│   ├── latest.md
│   └── latest.json
├── v3.0/workspace/
│   ├── scripts/
│   │   ├── run_news_loop.py        # 从 news-digest 拉取、生成产物（本地人工运行）
│   │   └── publish_output_repo.py  # 推送 assemble-archive/ 到远程仓库（workflow 调用）
│   └── src/
│       ├── news_digest_import.py   # 拉取解析 news-digest
│       ├── publish_pipeline.py     # 渲染 Markdown
│       └── output_repo_sync.py     # 同步整个 archive 目录到远程
└── .github/workflows/
    └── assemble-archive-sync.yml   # push 到 assemble-archive/ 时自动同步
```

### assemble-archive 仓库

```
assemble-archive/
├── daily/<date>/*.md
├── latest.md / latest.json
├── README.md
└── .github/workflows/
    └── publish-to-cnblogs.yml      # push 到 daily/ 时自动发布
```

## 自动化机制

### 触发链

| 环节                         | 触发条件                              | 执行者            |
| -------------------------- | --------------------------------- | -------------- |
| **编辑内容**                   | 手动                                | 你              |
| **push 到 Assemble**        | 手动                                | 你              |
| **Archive Sync**           | push event（`assemble-archive/**`） | GitHub Actions |
| **Publish to CNBlogs**     | push event（`daily/**`）            | GitHub Actions |
| **news-digest → Assemble** | ⚠ 非自动，需要人工 checkpoint             | 你              |

### GitHub 配置

**assemble-processing 仓库**

* `vars.ASSEMBLE_OUTPUT_REPO` = `qiao-925/assemble-archive`
* `secrets.ASSEMBLE_OUTPUT_PAT` = fine-grained PAT
  * 对 `assemble-archive` 需要：**Contents: Read and write + Actions: Read and write**
  * Actions 权限是必须的，否则 push 不会触发下游 workflow

**assemble-archive 仓库**

* `secrets.CNBLOGS_RPC_URL`
* `secrets.CNBLOGS_USERNAME`
* `secrets.CNBLOGS_TOKEN`

## 关键设计决策

### 为什么 news-digest → Assemble 不自动？

需要人工 checkpoint — 不是所有新闻都要转成博客。这一步涉及内容筛选、整理、润色，是价值密度的关键节点，不该自动化。

### 为什么 Assemble 内嵌 assemble-archive/ 目录？

让 "编辑内容" 和 "推送触发" 在同一个 git 操作里完成。避免跨仓库切换，也让整个处理过程可审阅。

### 为什么由 assemble-archive 的 workflow 调用 assemble-publish 的脚本？

* 脚本源码留在 `assemble-publish`（单一真相源）
* 执行发生在 `assemble-archive`（有 archive 内容 + CNBlogs secrets）
* 通过 `actions/checkout` 克隆 `assemble-publish` 到临时目录 `_publish/` 使用

### 为什么不用 Zeabur 了？

Zeabur 之前是轮询（每 12 小时），延迟大。GitHub Actions 事件驱动，秒级响应。 Zeabur 服务目前还在跑但已冗余，可下线。

## 日常操作

### 发一篇新文章

```powershell
# 1. 在 Assemble/assemble-archive/daily/<date>/ 下编辑 Markdown
# 2. push
cd c:\Users\nonep\Desktop\Assemble
git add assemble-archive/
git commit -m "publish: <title>"
git push
# 3. 等 1~2 分钟，文章就到博客园了
```

### 排查失败

* **Archive Sync 失败**：`gh run view --repo qiao-925/assemble-processing`
* **Publish 失败**：`gh run view --repo qiao-925/assemble-archive`
* **PAT 权限问题**：<https://github.com/settings/tokens?type=beta>

## 历史遗留

* `Assemble/v1.0/`、`v2.0/`：早期版本，保留不动
* `assemble-publish` 的 Dockerfile、`run_sync_hourly.py`：Zeabur 遗留，待清理
* Zeabur 服务：可下线


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://assemble.gitbook.io/assemble/assemble_workflow.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
