# 筆記自動化系統架構指引

## 整體概觀：Obsidian as OS

### 核心隱喻

把 Obsidian Vault 想像成一個作業系統，CODE 是這個 OS 的**核心排程原則**，Python 自動化程式是運行在上面的**系統服務**，AI 是負責語意理解的**智慧核心**。

```
┌─────────────────────────────────────────────────────────┐
│                    Obsidian Vault (OS)                   │
│                                                         │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌────────┐  │
│  │  Inbox/  │  │Organize/ │  │ Distill/ │  │Express/│  │
│  │ Capture  │  │ (Index)  │  │  (LYT)   │  │(Output)│  │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘  └───┬────┘  │
│       │              │              │              │      │
│       └──────────────┴──────────────┴──────────────┘     │
│                      ↑ CODE Pipeline                      │
└─────────────────────────────────────────────────────────┘
```

---

### 四個 Layer 的角色定義

| Layer | 資料夾 | AI/程式的角色 | 人的角色 |
|-------|--------|--------------|---------|
| **C – Capture** | `Inbox/` | 搬運工、去重工、格式標準化 | 隨意寫，不用想放哪裡 |
| **O – Organize** | `Organize/` | 時間索引建立者、唯一來源維護者 | 確認今日摘要，偶爾手動補標籤 |
| **D – Distill** | `Distill/` | 知識萃取、概念連結、LYT Map 生成 | 決定哪些概念值得發展 |
| **E – Express** | `Express/` | 草稿生成、結構建議 | 最終寫作與發布 |

---

### Vault 資料夾結構（完整版）

```
MyVault/
├── Inbox/                        ← C 階段：所有原始入口
│   ├── _staging/                 ← 已抓取、待 O 處理的暫存區
│   ├── _processed/               ← 已完成 C→O 的歸檔紀錄（可選）
│   └── _sources/                 ← 各 Capturer 的 metadata 紀錄
│       ├── memos.json
│       ├── joplin.json
│       └── discord.json
│
├── Organize/                     ← O 階段：時間索引，唯一真實來源
│   └── 2026/
│       └── 03/
│           └── 2026-03-17.md    ← 當天所有筆記的聚合點
│
├── Distill/                      ← D 階段：LYT / 卡片盒（Phase 2+）
│   ├── Maps/                     ← MOC (Map of Content)
│   ├── Concepts/                 ← 原子概念卡片
│   └── Sources/                  ← 書摘/文摘
│
├── Express/                      ← E 階段：有時效產出（Phase 3+）
│   ├── Projects/
│   └── Drafts/
│
├── _system/                      ← 系統本身的設定與紀錄
│   ├── config.yaml               ← 全域設定
│   ├── run_log.md                ← 自動化執行紀錄
│   └── schemas/                  ← 各 Capturer 的筆記規格定義
│
└── Templates/                    ← Obsidian 模板
    ├── daily-organize.md
    └── note-captured.md
```

---

### 系統邊界與設計原則

**1. 單向資料流**
C → O → D → E，資料只往下游流動，上游永遠是來源事實

**2. 冪等性（Idempotency）**
任何 Capturer 重複執行，結果相同，不產生重複筆記

**3. 可溯源性（Traceability）**
每一則在 O 的筆記，都能追溯回 C 的來源系統與原始 ID

**4. Obsidian 原生友善**
所有產物都是標準 Markdown，支援 Obsidian 的雙向連結、標籤、Properties（YAML frontmatter）

**5. 人機協作邊界清晰**
程式負責搬運與格式化；AI 負責語意理解；人只需要確認與決策

---

---

## Phase 1 規格書：C → O Pipeline

### 目標陳述

> 無論筆記寫在哪個工具，系統自動在當天的 `Organize/YYYY/MM/YYYY-MM-DD.md` 中建立對應的索引條目，人不需要手動整理。

---

### 架構圖

```
外部來源系統
┌─────────────────────────────────────────────────────┐
│  Memos API  │  Joplin API  │  Markdown 資料夾        │
│  Discord    │  Mattermost  │  （未來擴充）            │
└──────┬──────┴──────┬───────┴──────┬──────────────────┘
       │              │               │
       ▼              ▼               ▼
┌─────────────────────────────────────────────────────┐
│              Capturer Layer（各自獨立模組）            │
│   MemorsCapturer  JoplinCapturer  FolderCapturer    │
│                                                     │
│   每個 Capturer 負責：                               │
│   1. 拉取原始內容                                    │
│   2. 轉換為標準 Note Schema                          │
│   3. 去重（對照 source registry）                    │
│   4. 寫入 Inbox/_staging/                           │
│   5. 更新 source registry（記錄已抓取的 ID/時間）     │
└──────────────────────────┬──────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────┐
│              Organizer（排程核心）                    │
│                                                     │
│   1. 掃描 Inbox/_staging/ 所有待處理筆記             │
│   2. 依 created_at 決定歸屬日期                      │
│   3. 找到或建立對應的 Organize/YYYY/MM/YYYY-MM-DD.md │
│   4. 將筆記條目 append 至當天檔案                    │
│   5. 清空 _staging（或移至 _processed）              │
└─────────────────────────────────────────────────────┘
                            │
                            ▼
              Organize/2026/03/2026-03-17.md
```

---

### 標準 Note Schema（C 階段產物）

每個 Capturer 輸出統一格式，存於 `Inbox/_staging/` 作為 `.md` 檔：

```yaml
---
# 必填欄位
note_id: "memos-20260317-abc123"    # 全域唯一 ID，格式：{source}-{date}-{原始ID}
source: "memos"                      # 來源系統識別碼
source_id: "abc123"                  # 在來源系統的原始 ID
source_url: "http://memos.local/m/abc123"  # 可選，原始連結
created_at: "2026-03-17T09:30:00+08:00"   # 原始建立時間（ISO 8601）
captured_at: "2026-03-17T10:00:00+08:00"  # 本系統抓取時間
status: "staging"                    # staging | organized
tags: []                             # 來源系統的原始標籤（保留）
---

{筆記原始內容，Markdown 格式}
```

---

### Organize 日誌格式

`Organize/2026/03/2026-03-17.md` 的結構：

```markdown
---
date: 2026-03-17
note_count: 5
sources: [memos, joplin, folder]
---

# 2026-03-17

## 📥 今日筆記

### 09:30 · memos
> [原始來源](http://memos.local/m/abc123) · `memos-20260317-abc123`

{筆記內容}

---

### 11:45 · joplin
> [原始來源](joplin://x-callback-url/openNote?id=xyz) · `joplin-20260317-xyz`

{筆記內容}

---
```

---

### Capturer 模組規格

#### 共用介面（Abstract Base）

每個 Capturer 都實作這個介面：

| 方法 | 說明 |
|------|------|
| `fetch()` | 從來源拉取新內容，回傳 `List[RawNote]` |
| `to_note_schema(raw)` | 轉換為標準 Note Schema |
| `get_last_synced()` | 讀取 source registry，取得上次同步時間點 |
| `update_registry(notes)` | 更新已抓取紀錄，防止重複 |

#### Memos Capturer 設定項目

```yaml
memos:
  site_url: "https://your-memos.example.com"
  api_token: "${MEMOS_API_TOKEN}"
  sync_strategy: "since_last"        # since_last | full
  content_filter:
    min_length: 10                   # 少於 10 字不抓
    exclude_tags: ["#private"]
  output_format:
    include_visibility: true
    include_resources: false         # 附件（Phase 2 再處理）
```

#### Folder Capturer 設定項目

```yaml
folder:
  watch_paths:
    - "/path/to/joplin-export"
    - "/path/to/other-md-folder"
  move_after_capture: false          # true = 移走原檔；false = 只標記
  marker_strategy: "frontmatter"     # 在原檔 frontmatter 加 captured: true
  filename_as_title: true
  recursive: true
```

#### Source Registry 格式（`Inbox/_sources/memos.json`）

```json
{
  "source": "memos",
  "last_synced_at": "2026-03-17T10:00:00+08:00",
  "captured_ids": ["abc123", "def456"],
  "total_captured": 42
}
```

---

### 執行模式

| 模式 | 觸發方式 | 說明 |
|------|---------|------|
| **排程模式** | cron / launchd | 每 15 分鐘自動執行一次 C→O |
| **手動模式** | CLI 指令 | `python run.py --stage capture` 或 `--stage organize` |
| **監聽模式**（可選）| watchdog | 監聽 Folder Capturer 的來源資料夾，有新檔即觸發 |

---

### Phase 1 的明確邊界

**Phase 1 包含：**
- 所有 Capturer 的實作（Memos, Folder, Joplin, Discord webhook）
- Organizer 核心邏輯
- Source Registry 去重機制
- Obsidian-friendly 的 Markdown 輸出格式
- CLI 介面與排程設定

**Phase 1 不包含（留給後續 Phase）：**
- AI 語意分析或自動標籤
- Distill 階段的任何邏輯
- Obsidian Plugin 整合
- 衝突解決（同一筆記在多個來源出現）

---

### 技術選型建議

| 元件 | 建議 | 理由 |
|------|------|------|
| 語言 | Python 3.11+ | 生態完整，你已熟悉 |
| 設定管理 | `pydantic-settings` + YAML | 型別安全，支援環境變數注入 |
| Markdown 處理 | `python-frontmatter` | 讀寫 YAML frontmatter 最方便 |
| 排程 | `APScheduler` 或系統 cron | 輕量，不需要額外服務 |
| 日誌 | `loguru` | 比 logging 更好讀 |
| 測試 | `pytest` + mock | 每個 Capturer 可獨立單元測試 |

---

確認這個方向後，Phase 1 的程式實作可以從**設定層（config schema）和 Capturer 抽象基類**開始，再逐一實作各個 Capturer 模組。你想先從哪個 Capturer 開刀？