Telegram 傳影片/論文連結 → Claude agent 用 notebooklm-skill 產「繁體中文、逐段整理、用 markdown header 分類」的網誌式報告 → 寫進 Notion → 回傳 Notion 連結。
骨架取自 cwc-long-running-agents:
第一優先是「證據 evaluator」 —— 回報「完成」前,必須真的把產出讀回來確認有內容,
否則狀態強制停在 passes:false 並如實告訴你哪裡失敗,絕不回假連結。
每個 job 都有一份 Default-FAIL 契約(jobs/<id>/contract.json),兩道閘門初始都是 false:
builderA(產報告) → 閘A(報告) → builderB(寫Notion) → 閘B(Notion) → 只有兩閘皆 PASS 才回「✅ 完成」
每道閘門跑兩種獨立檢查、兩個都要過:
- 確定性本地檢查(
evaluators.py,無網路):抓空白、登入頁、字數不足、分段 header 不足。 - fresh-context LLM 稽核員(
agent.run_evaluator,read-only、全新脈絡):語意上判斷是不是 真的對應來源的中文分段報告,而非錯誤頁或編造。
典型會被擋下的失敗:NotebookLM session 過期回空報告、Playwright 卡 login gate、Notion 頁建了卻空白。
- 整理既有筆記本(sweep):傳
#topic 整理現有(或sweep/sync),bot 會把該 topic 筆記本裡 每個還沒匯出的來源各自產一份報告、各自一頁 Notion;已匯出的自動跳過(可反覆執行、不重做)。 URL 來源用正規化網址比對,所以跟#topic <連結>走的是同一套防重複——已用連結匯過的不會再做一次。 - 連結或檔案都收:傳 http/https 連結,或直接上傳附件檔(PDF / DOCX / TXT / MD,NotebookLM 能吃的)。
附件的
#topic標籤放在 caption。bot 用 Telegram getFile 下載到.state/downloads/,再source add --type file進對應筆記本。限制:Telegram bot API 只能下載 ≤20MB 的檔;超過會如實回失敗(不假裝成功)。 - 依 topic 分流(每個 topic 一本筆記本、一個 Notion DB):傳連結時用
#標籤指定 topic,例如#ai 你的連結或你的連結 #phd。bot 依topics.json把來源加進對應的 NotebookLM 筆記本 (如(AgentNote) AI agent),整理後寫進對應的 Notion 資料庫(如NotebookLM 報告 - AI agent)。 好處是順手在 NotebookLM 建立分主題的 RAG 語料庫。沒標、標到不存在、或一次標多個 topic → bot 不處理, 直接回你目前可用的 topic 清單(fail-closed,避免 typo 汙染語料庫)。 - 同一筆記本內仍鎖來源:每次提問用
ask -s <source_id>把範圍鎖在這次新增的來源,避免被同一本裡其他來源汙染。 - 防重複:每個 topic 一份「已處理索引」(
.state/processed/<topic>.json,gitignored)。同一個 (topic, 連結) 成功處理過後再傳一次 → bot 不重跑,直接回你上次的 Notion 連結。比對用的是正規化後的 URL (youtu.be↔watch?v=、utm_*/si/fbclid等追蹤參數、結尾斜線、www.、http/https、#fragment都會被忽略)。 索引只在整條成功後才寫,失敗的 job 仍可重試。要強制重做就在訊息加#force。 - topic 設定兩層:定義在
topics.json(進 repo:別名 + 筆記本名 + Notion DB 標題); 自動建出來的topic -> notion_db_id快取在.state/topic_dbs.json(gitignored)。新增 topic = 在topics.json加一筆,之後該 topic 的筆記本與 Notion DB 會在第一次用到時自動建立。 - Notion 頁開頭自動加目錄:每頁第一個區塊是 Notion 原生 Table of Contents block,依
##標題自動產生, 方便點擊跳轉。
src/agentnotes/
contract.py Default-FAIL 證據契約(核心防線)
evaluators.py 確定性檢查 + 稽核員 prompt(核心防線)
agent.py Agent SDK 封裝:builder(可寫/可 Notion)vs evaluator(read-only、新 context)
pipeline.py 單一來源的證據閘 pipeline(_run_source);run_pipeline=新來源、run_sweep=整理既有
notebooklm_cli.py Python 直呼 notebooklm CLI(找筆記本 id、列來源)—— sweep 用來逐來源防重複
topics.py topic registry:#標籤 → (NotebookLM 筆記本, Notion DB) 路由 + db_id 快取
dedup.py 防重複:URL 正規化 + 每 topic 的「已處理索引」(#force 可覆寫)
ingest/base.py InputAdapter 介面(Line 之後接同介面)
ingest/telegram.py getUpdates 長輪詢 + sendMessage + 解析 #topic + 下載附件檔
main.py 派工迴圈
topics.json topic 定義(別名 / 筆記本名 / Notion DB 標題)—— 進 repo
.state/topic_dbs.json 自動建立後的 topic → notion_db_id 快取(gitignored)
.claude/agents/evaluator.md 稽核員子代理人定義(產出 PASS/FAIL)
.claude/agents/test-runner.md 測試執行子代理人:全新脈絡跑 pytest+ruff 回報
deploy/com.ron.agentnotes.plist launchd 常駐
.github/workflows/ci.yml CI:push/PR 自動跑 ruff + pytest(含覆蓋率)
tests/ pytest 套件(全離線,不需外部服務)
conftest.py 共用 fixtures:cfg / registry / index / good_report /
fake_agents(可調 gate 結果的假 builder+evaluator)/ poll_once(驅動 poll)
test_evidence.py 證據防線:確定性檢查 + Default-FAIL 契約
test_evaluators.py 確定性檢查的邊界(marker、門檻、失敗標題、計數)
test_contract.py 契約不變量(無證據不過、未知 gate 報錯、磁碟往返)
test_config.py .env 解析 + chat-id allowlist
test_topics.py #標籤 解析、別名正規化、db_id 快取
test_dedup.py URL 正規化、檔案雜湊、索引、跳過/入帳/#force
test_gate.py verdict 解析 + Gate B 對真實頁面正文做檢查
test_attachments.py 附件型別 gate、報告 prompt 檔案分支、poll 文件路由
test_sweep.py 整理既有:指令偵測、逐來源防重複/失敗/#force
test_telegram.py poll() URL 路由(topic 缺/多、多連結、白名單、offset)
test_main.py _title_hint / _job_id / handle_job 派工
認證:靠本機已登入的 Claude 訂閱(2026-06-15 分池計費),不要設 ANTHROPIC_API_KEY,
Agent SDK 會走訂閱池、與終端機 Claude Code 額度分開。
# 1) Python 環境 + 依賴
python3 -m venv .venv
.venv/bin/python -m pip install -e . # 或:pip install claude-agent-sdk httpx
# 2) 裝 notebooklm-skill 並登入 Google(一次性、互動)
bash scripts/install_notebooklm.sh
.venv/bin/python -m notebooklm login # 開瀏覽器登入,session 存 ~/.notebooklm/
# 3) 設定
cp .env.example .env
# 填 TELEGRAM_BOT_TOKEN(BotFather 新開的一支 bot)
# 填 NOTION_PARENT_PAGE_ID(Notion MCP 能存取、讓 bot 在底下建資料庫的一頁)
# NOTION_DATABASE_ID 第一次留空——首跑會自動建「NotebookLM 報告」DB,再把 id 填回 .env
# 4) Notion MCP 授權(hosted):首次會走 OAuth;用 .venv 的 claude 在本目錄跑一次互動指令完成授權,
# 或設 NOTION_MCP_TOKEN(internal integration token)。詳見 .env.example。# 0) 一次性:裝開發/測試工具(pytest、pytest-cov、ruff)
.venv/bin/python -m pip install -e ".[dev]"
# A) 整個離線測試套件(不需任何外部服務)—— pytest 自動 discovery
.venv/bin/python -m pytest # 跑全部
.venv/bin/python -m pytest --cov=agentnotes --cov-report=term-missing # 帶覆蓋率
.venv/bin/ruff check src tests # lint
# 涵蓋:證據防線(防謊報)、contract、config、topic 路由、防重複、附件、sweep、
# telegram poll 路由、main 派工。共用 fixtures 在 tests/conftest.py。
# 也可用「test-runner」子代理人在全新脈絡自動跑 pytest+ruff 並回報 PASS/FAIL
# (.claude/agents/test-runner.md)。CI 在 push/PR 會自動跑同一套(.github/workflows/ci.yml)。
# B) NotebookLM 段:對一支已知影片實跑,人工讀 jobs/<id>/report.md 確認繁中、逐段、## header
# C) 整鏈:把一個連結傳給 bot → 收到 ✅ + 可開的 Notion 連結
# 反向:故意讓 NotebookLM 登出 → 應收到「需重登」失敗通知,而不是假完成# 前景測試
PYTHONPATH=src .venv/bin/python -m agentnotes.main
# 本機常駐(launchd,非 cron)—— 編輯 plist 內絕對路徑後:
cp deploy/com.ron.agentnotes.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.ron.agentnotes.plist
tail -f logs/agentnotes.err.logcwc 的 build→evaluate→rebuild 迴圈是給「自主寫 code」用的。本 pipeline 是固定單趟
(NotebookLM → Notion),所以只取 cwc 的兩塊:①Default-FAIL 證據契約 + fresh-context evaluator
(最重要),②PROGRESS.md 續跑記錄。閘門失敗就停下通知你,不自動重試編造。
- 入口目前只有 Telegram;Line 之後接
ingest/base.py的同一介面。 - jobs 一次處理一個(避免兩個 NotebookLM/Playwright session 互撞)。
- Notion MCP 的 hosted OAuth 授權是一次性手動步驟。