Asagiri 朝霧

朝霧捲軸 · 絵巻 emaki

每天清晨,十個尚未凝結的新創點子。

Asagiri(朝霧,morning mist,指天亮前那層尚未散去的薄霧)是一台每日自動運行的新創點子研究引擎。 每晚它調度一條 CLI agent 鏈、向本機 SearXNG 搜尋網路訊號,再綜合出十個有根據的新創點子, 寫進 data/<日期>/ideas.json,隔天早上由 webapp 瀏覽。 像晨霧,多數點子會在中午前蒸發,少數會留下來。

往下展開卷軸,跟著一次完整的生成過程

其一 / Scene I

架構:一條薄的調度層 / The thin orchestrator

舊引擎綁死三家廠商 SDK,每家還各自帶一套網路搜尋。新引擎反過來:它不擁有任何 SDK, 只擁有一個路由器。搜尋與生成是兩個獨立的失敗面,刻意解耦,可以各自除錯、各自抽換。

引擎內部是三個互不相識的純模組,加一層膠水。下面這幅 橫向絵巻 把完整管線 從左到右畫成一幅手卷:領域挑選 → agent 鏈 → SearXNG → 綜合 → ideas.json → webapp。 用鍵盤方向鍵、拖曳、或捲動可在這條手卷裡左右移動。

Asagiri 生成管線手卷 / Asagiri generation pipeline handscroll 由左至右的六個場景:第一,依 config.yaml 的策略挑選十個領域槽位; 第二,agent 鏈依品質分級嘗試 claude、codex、gemini、ollama 四個 CLI agent; 第三,search.py 向本機 SearXNG(127.0.0.1:8888,Brave 為備援)抓網路訊號; 第四,agent_research.py 把領域、搜尋脈絡與 SKILL.md 渲染出的 prompt 綜合成點子; 第五,產物寫進 data 當日資料夾下的 ideas.json; 第六,webapp 純讀取 ideas.json 並以 dashboard、kanban、territory 呈現。 挑選領域 domain select config.yaml · strategy 10 槽位 / slots agent 鏈 claude → codex → gemini → ollama tiered dispatch SearXNG search.py 127.0.0.1:8888 Brave fallback 綜合 agent_research.py SKILL.md → Jinja synthesis ideas.json artifact data/<date>/ 10 ideas webapp Next.js 16 read-only UI dashboard · kanban
橫向手卷:管線六景由左至右展開。← → 方向鍵或拖曳可捲動 · 視窗 ≤ 640px 時自動改為直向堆疊。

想看引擎內部三個對等模組如何被同一層膠水串起,下面這張靜態分層圖標出全部細節:

引擎分層圖 / Engine layer diagram 最上層 main.py 呼叫 agent_research.run_agent_research。該函式向三個彼此獨立的對等模組扇出: search.py 打本機 SearXNG(Brave 為備援);skill_loader.py 讀 skills/research/SKILL.md 並做 Jinja 渲染; agents.py 依序嘗試 CLI agent:claude、codex、gemini、ollama。search、skill_loader、agents 三者互不相識, 只有 agent_research.py 同時認識它們。 main.py CLI entry / config router agent_research.py orchestration (the only glue layer) search.py SearXNG + Brave fallback skill_loader.py SKILL.md → Jinja render agents.py CLI subprocess dispatch localhost:8888 Docker SearXNG claude → codex → gemini → ollama (on PATH)
青色那塊是唯一讓三個模組相遇的地方。任一對等層都可整顆抽換 (把 SearXNG 換成 Kagi、把 subprocess 換成 HTTP),其他層不受影響。

四個階段,點開看細節

挑選領域 / Domain selection agent_research._select_domains

config.yaml 的 strategy 與 theme_schedule,每天從領域池洗牌挑出十個槽位 (themed 日會把一半灌進當日主題,其餘隨機)。當天還會依年內日序輪選一個「purpose lens」價值透鏡, 讓部分點子帶上特定框架,其餘維持純探索。

抓網路訊號 / Web context search.search → SearXNG (Brave fallback)

針對每個領域組查詢,先打本機 SearXNG(127.0.0.1:8888 的 Docker 容器), 回傳純 dict 清單 {title, url, snippet, engine}。SearXNG 掛掉或上游引擎全被擋時, 落到 Brave(需 BRAVE_API_KEY)。全部失敗則 prompt 標註「無即時網路結果」,agent 改用既有知識綜合。

為什麼不讓 LLM 自己搜尋? 搜尋與生成是不同的失敗面。耦合起來會讓搜尋問題長得像生成問題、反之亦然。 解耦才能各自除錯、各自抽換。
渲染 prompt / Prompt as a file skill_loader.load_skill('research')

研究 prompt 是一個檔案,住在 engine/skills/research/SKILL.md:YAML frontmatter + Jinja body。 把領域、搜尋脈絡、價值透鏡填進去渲染出最終 prompt。改這個檔案,下一次運行行為就變,不用改任何程式碼、不用重新部署。

調度 agent 鏈 / Tiered dispatch agents.dispatch · claude → codex → gemini → ollama

agents.dispatch 依品質分級(claude t10 → codex t9 → gemini t7 → ollama t6)逐一嘗試。 所有失敗模式(非零退出、逾時、空 stdout、OSError)都正規化成同一條「這個輸了,換下一個」的路徑。 預設 min_tier=8 只放行 claude 與 codex。最後把 agent 回傳的 JSON 解析成一個 Idea, 寫進當天的 ideas.json

其二 / Scene II

生成演練 / Watch today's run

真正的引擎一晚才跑完。這裡用今天(2026-05-31)真實的 ideas.json 在瀏覽器端重播一次: 先串流 agent 鏈的每一步,再讓十張真實點子卡像晨露凝結般一張張顯現。完全離線、不呼叫任何後端。

開啟「減少動態效果」(prefers-reduced-motion) 時,會略過動畫直接顯示全部結果。

其三 / Scene III

站長控制台 / Owner controls

以上每個點子,站長可以決定下一步。解鎖後,每張卡會長出站長操作,主要的一個是 「進行 MVP 製作 / Build MVP」。下面是一道僅供原型展示的密碼閘。

輸入站長密碼以解鎖控制項。

原型佔位,非真實驗證。 密碼是寫死的 asagiri-admin,全程在瀏覽器端比對,沒有後端、沒有 session、沒有任何安全性。 真實版本會走伺服器端身分驗證。This is a prototype stub, not real authentication.

已解鎖。 每張點子卡下方已出現站長操作。你也可以從這裡觸發一次新的生成(同樣是佔位)。

這些操作都是佔位 stub:不會真的呼叫引擎(引擎要跑一整晚),只會顯示確認對話框與通知。

進行 MVP 製作? / Build MVP?

準備為 這個點子 排入 MVP 製作佇列。這是原型佔位, 不會真的啟動任何建置流程。確認要排入嗎?