! 本篇文章將會介紹 headless 模式:opencode run 讓 AI 無人值守工作,期望大家都能讓 AI 在你睡覺、耍廢、追劇的時候照常上班 :D
昨天我們把 opencode 裝好、登入、聊完第一次天,也賣了個關子:CLI agent 決定性的差異是「可以被 script 呼叫」。今天來兌現這張支票——揭曉本系列發電機的真正原理。先自首一個驚人的事實:你現在讀的這篇文章,就是今天 19:00 被 launchd 叫起床的 opencode run 寫的。它怎麼辦到的?往下看。
讀完這篇你會學到:
opencode run 的基本用法,以及 headless(非互動)模式與 TUI 的本質差別--auto、--dir、--title 的用途與風險scripts/generate.sh,看一台「輸入大綱、輸出文章」的發電機怎麼組裝opencode --version 要跑得動)opencode auth login,opencode auth list 看得到 provideropencode 不帶參數是開 TUI;帶上 run 就是非互動模式:
# headless:進去、做事、輸出答案、退出,全程不開 TUI
opencode run "用一句話解釋什麼是 headless 模式"
# 輸出直接印在 stdout,可以接 pipe 當一般指令用
opencode run "幫我把這句話翻成英文:今天不想上班" | pbcopy
TUI 是「對話」,run 是「下指令」。對話需要你在場,指令不需要——這就是 headless 的本質:agent 變成一個普通的 CLI 工具,可以塞進任何腳本、被任何排程呼叫。
只會問問題還不夠,自動化要的是 agent 動手改檔案:
# --dir 指定工作目錄;--auto 自動核准權限;--title 給 session 取名
opencode run --auto --dir ~/playground --title "add-readme" \
"在 README.md 加一段安裝說明,改完跟我回報動了哪些地方"
三個 flag 各司其職:
--auto:互動模式下,agent 每次要動手(編輯檔案、跑指令)都會跳出「允許嗎?」等你按 Enter。headless 沒有人在螢幕前,--auto 就是「除了明確設為拒絕的權限,其他自動核准」。方便的代價是信任,等一下踩坑記錄再細談。--dir:指定 agent 的工作目錄。自動化腳本建議明寫絕對路徑,別依賴「目前所在目錄」——排程環境的工作目錄常常不是你想的那個。--title:幫 session 取名字,事後用 opencode session list 找帳、查 log 都好認。我的 generate.sh 每天都會取 ironman-d03-gen 這種名字。主角登場。以下是 scripts/generate.sh 的心臟地帶(節錄):
# 1. 從 outline.md 撈出今天的條目(格式:DD|標題|摘要)
ENTRY=$(awk -F'|' -v d="$DD" '$1==d {sub(/^[^|]*\|/,""); print; exit}' "$ROOT/outline.md")
# 2. 組出 prompt:指名三個檔案 + 今天的主題與摘要 + 輸出檔案的確切路徑
PROMPT="請閱讀以下三個檔案後生成今天的鐵人賽文章:
1. prompts/generate.md(寫作規則,務必遵守)
2. templates/article-template.md(文章結構模板)
3. outline.md(30 天大綱)
……
請將完成的文章寫入這個確切路徑:${ARTICLE}"
# 3. 發電:opencode run,標準輸出與錯誤全進當天的 log
opencode run --auto --dir "$ROOT" --title "ironman-d${DD}-gen" "$PROMPT" >>"$LOG" 2>&1
就這麼樸素。整台「發電機」的原理可以濃縮成一句話:prompt 是程式的參數,檔案是程式的輸入輸出。agent 收到指令後,自己用 read 工具讀規則、模板、大綱,再用 write 工具把文章寫到指定路徑。generate.sh 完全不需要理解「寫文章」這件事,它只負責出題與驗收。這也是我選 headless 而不是自己開 TUI 敲字的最大理由:prompt 可以被版本控制、被參數化、被重複執行,同一個 prompt 餵給 agent 一百次,就是一百台發電機。
順帶一提,今天的 log 開頭長這樣(節錄自 logs/generate-20260913.log):
[2026-09-13 19:00:05] 開始生成 d03:headless 模式:opencode run 讓 AI 無人值守工作
[2026-09-13 19:00:05] opencode run 第 1 次嘗試
→ Read outline.md
→ Read prompts/generate.md
→ Read templates/article-template.md
沒錯,你讀到這裡的每一個字,都是那個 process 在 19:00 寫出來的。平行時空的我在沙發上耍廢——這就是系列標題「自我耍廢組」的由來 :D
小小小測驗:你知道 generate.sh 叫完
opencode run之後,根本不相信 agent 說的任何一句話嗎?連「DONE:」這句完工宣言都不信。為什麼?看步驟四。
無人值守最大的風險不是「agent 不動」,而是「agent 動了但交出垃圾」。所以 generate.sh 在 opencode run 之後安插了一排品質守衛:
# 品質判準(generate.sh 節錄),全部通過才算生成成功:
# 1. 文章檔案存在且 > 1500 bytes
# 2. 中文字數 >= 1500(先剝除 code fence 再數,與寫作規則同口徑)
# 3. frontmatter 的 title 要跟 outline.md 的當日標題完全一致
# 4. 機敏掃描:命中 webhook URL、API key 等 pattern 就直接隔離、重寫
# 沒過 → 重試(最多 3 次,每次間隔 30 秒)→ 再不行啟用備稿 + Discord 告警
三次都沒過?文章若存在就保留現狀、通知人工判斷;若根本沒生出土,才啟用 fallback/ 資料夾裡的預備稿。這套「生成 → 驗收 → 重試 → 備援」的流程,是整個系列最核心的設計哲學:agent 的輸出是建議,腳本的判準才是驗收。 LLM 偶爾會漏字、改錯標題、甚至把範例檔裡的示意 webhook 原封不動抄進文章——這些靠「拜託它小心」沒有用,只有程式碼檢查不會眨眼。後面第 8 天的 exit hooks、第 12 天的錯誤處理,都會回來深化這件事。
Q:opencode run 跑到一半卡住不動,log 停在正要動手的前一刻?
A:九成是權限確認在等你。headless 模式沒人能按「Allow」,agent 就卡在許可提示上動彈不得。解法是加 --auto;但更穩的做法是在專案的 opencode 設定裡寫 permissions 規則,把「允許 read、edit,其他要問」寫死在檔案裡,讓自動化行為不依賴 command line 一時的心情。--auto 等於把鑰匙整串交給 agent,我的建議:只在專用的自動化資料夾用,而且該資料夾要進 git——agent 到底改了什麼,git diff 一目了然。
Q:run 的輸出跟腳本自己的訊息混在一起,除錯很吵?
A:我是用 >>"$LOG" 2>&1 把 stdout 和 stderr 全導進當天的 log 檔,腳本的 log() 也寫同一個檔案,時間序完整好追。如果你需要程式化解析 agent 的輸出,run 有 --format json 可以吐原始 JSON events,比解析人類可讀文字可靠得多。
Q:每次 run 都覺得冷啟動很慢?
A:官方提供的解法是先用 opencode serve 起一個常駐的 headless server,之後 opencode run --attach http://localhost:4096 "..." 掛上去,可以省掉每次 run 重新初始化(例如 MCP server 冷啟動)的開銷。我目前的排程量一天才兩次,還不需要這招,但高頻呼叫的場景值得開起來。
opencode run 是非互動模式:一行指令,進去、做事、回報、退出,agent 從此變成可被腳本呼叫的 CLI 工具--auto 解決「headless 沒人按核准」的問題,--dir 鎖定工作範圍,--title 讓 session 可稽核——三件套是自動化的基本配備下一篇我們要介紹「AGENTS.md:教會 Agent 你的專案規矩」。今天的 prompt 只說「請閱讀 prompts/generate.md」,agent 就真的守規矩了,但為什麼它連沒被點名的專案慣例都會自動遵守?幕後功臣就是 AGENTS.md,敬請期待!
參考資料:
有任何疑問但沒有 iT 邦幫忙帳號,或是想匿名提問?
歡迎到 https://dev.benben.me/q/Z5442T 提問或加油打氣,沒意外的話會在完賽之後一起回答 :D