iT邦幫忙

2026 iThome 鐵人賽

DAY 2
0
Claude AI

Claude Code 下班之後:30 天把 CLI 工具養成會自己交差的 AI 員工系列 第 2

「幫我修 bug」和「幫我看著這件事」差在哪

  • 分享至 

  • xImage
  •  

Day 2|「幫我修 bug」和「幫我看著這件事」差在哪

https://ithelp.ithome.com.tw/upload/images/20260818/20183634X4jClnPEU8.png

有個很簡單的方法可以測出你的 agent 有多自主:把螢幕關掉,出門三天。

回來以後問它一句「這三天發生什麼事」。多數人的 agent 這時候會很誠實地告訴你:它不知道。它甚至不知道自己這三天被叫醒過幾次。

今天要拆的就是這個落差。我把它整理成四個維度,每一個維度都是一次身分轉換:從任務變成職責


一次性任務 vs 持續職責

先看一個對照表。左邊是你熟悉的用法,右邊是它自己跑的時候。

維度 一次性任務 持續職責
誰啟動 你按 Enter 時間到了,或某個事件發生
上下文從哪來 你的螢幕、你的記憶 得自己有一份
什麼時候算做完 你看到答案就關掉 得有人判斷,而那個人不在
誰批准危險操作 你,逐次 得預先宣告
成本誰管 你看到貴就停 沒人看,燒到你收到帳單

這五行右邊的欄位,就是接下來 29 天要一格一格填的東西。

值得先說清楚的是:這五件事不是能力問題。模型完全有能力做完,缺的是它周圍那一圈東西。同一個 Claude,你給它一個空的環境跟給它一個有記憶、有驗收、有權限邊界的環境,行為天差地遠。

那一圈東西有個名字,叫 harness。

一張圖

https://ithelp.ithome.com.tw/upload/images/20260818/20183634OHEighifDm.png

左邊是有人監督的迴圈(SUPERVISED):你按下 Enter,它回答,你看一眼覺得可以,結束。

右邊是沒人監督的迴圈(UNSUPERVISED):時鐘或事件負責觸發,中間那個 HARNESS 負責把昨天的狀態注入進去、同時用虛線框住權限邊界和成本上限,產出交給 VERIFY 判斷,通過就結束,不通過就繞回去重跑,真的卡住才響鈴找人。

左邊那個迴圈裡,「你」同時扮演四個角色:觸發器、記憶體、驗收員、審批人。右邊要把這四個角色都做出來。

多數人做 agent 卡住的地方,是只做了觸發器(排程),然後期待模型自己補上其他三個。


缺口一:觸發

這是最簡單的一個,也是唯一一個大部分人都做對的。

# crontab
0 8 * * * claude -p "檢查昨天的錯誤日誌" >> /var/log/agent.log 2>&1

會動。但這行有兩個坑,我兩個都踩過。

第一個坑:cron 的環境不是你的環境claude 這個指令在你的 shell 裡找得到,在 cron 裡找不到,因為 PATH 不一樣。用 Homebrew、nvm、volta、bun 裝的 CLI 特別容易中招。解法是寫絕對路徑,或者在程式裡主動去幾個常見位置探測。

第二個坑:沒有互斥。上一輪還在跑,下一輪時間到了又起一個。兩個 agent 同時寫同一個檔案,你會拿到交錯的半行 JSON。這件事在 Day 3 會用一個檔案鎖解決。

缺口二:上下文

claude -p 是無狀態的。每次呼叫都是新對話。

很多人的第一直覺是把歷史全部塞進 prompt:

claude -p "$(cat history.txt)

今天的任務:檢查錯誤日誌"

這會動,直到 history.txt 長到把你的 context window 撐爆,或是燒錢燒到你有感。我量過,光是把三十輪對話原文塞進去,就要 4 萬多個 token,而其中真正有用的資訊大概兩百個字。

Day 4 到 Day 18 有一半的篇幅在處理這件事。核心觀念先放這裡:歷史不等於記憶。歷史是流水帳,記憶是被壓縮、被排序、會遺忘、會被新事實推翻的東西。

缺口三:終止條件

這是最容易被輕忽的一個,我認為也是最重要的一個。

互動模式下,「做完」的定義是「你看到答案,覺得可以了」。這個判斷發生在你腦袋裡,沒有寫進任何地方。

自動模式下你必須把它寫出來。而寫出來的第一個版本,通常長這樣:

result = run_agent(task)
if "完成" in result:
    mark_done()

這叫做讓考生自己改考卷。模型講「我已完成」的機率,跟它真的完成的機率,是兩件事。我在 Day 20 到 Day 24 會用四篇講怎麼做一個真的能擋住的驗收層,包括我自己那個驗收層曾經有兩個洞的實例。

缺口四:授權

互動模式下每個危險操作都會跳出來問你。無人模式下這個問句沒有對象。

於是很多人加 --dangerously-skip-permissions,然後就再也沒有任何東西擋在中間了。

比較好的做法是把「逐次批准」換成「預先宣告」:明確列出這個 agent 能用哪些工具、能碰哪些路徑、哪些操作必須留給人。Claude Code 本身就有 --allowedTools--disallowedTools,這是最省力的起點:

claude -p "檢查昨天的錯誤日誌" \
  --allowedTools "Read,Grep,Glob" \
  --disallowedTools "Bash,Write,Edit"

一個只需要讀日誌的 agent,本來就不該有寫檔和執行 shell 的能力。這一行的防禦效果,比你寫任何 prompt 都好。

缺口五:成本

沒人看的時候,成本是唯一會無聲增長的東西。

三個實際會發生的燒錢情境:

一個是重試迴圈。任務失敗,程式自動重試,模型每次都失敗,你的迴圈跑了 200 次。我後來在自己的平台裡加了迭代上限(預設 5 次)和 24 小時的牆鐘上限,就是被這個燙到。

一個是 context 膨脹。歷史越塞越長,每次呼叫都比上一次貴一點,曲線是往上翹的。

還有一個最陰險:快取失效。你以為你有 prompt cache,命中率 90%,結果某天你改了系統提示裡的一個字,整條前綴全部重算。Day 29 會講怎麼定位「是哪一塊在破快取」。


今天的最小實作

把五個缺口濃縮成一個 40 行的骨架,之後每一天都在往裡面填東西。

#!/usr/bin/env python3
import json, subprocess, fcntl, pathlib, datetime

HOME = pathlib.Path.home() / ".myagent"
HOME.mkdir(exist_ok=True)
STATE = HOME / "state.json"
LOCK = HOME / "run.lock"

def load_state() -> dict:
    if STATE.exists():
        return json.loads(STATE.read_text())
    return {"last_run": None, "notes": []}

def save_state(s: dict) -> None:
    tmp = STATE.with_suffix(".tmp")
    tmp.write_text(json.dumps(s, ensure_ascii=False, indent=2))
    tmp.replace(STATE)          # 原子寫入,避免半個檔案

def run_once(task: str) -> str:
    state = load_state()
    context = "\n".join(state["notes"][-5:])     # 只帶最近 5 條,不是全部歷史
    prompt = f"# 你昨天記下的事\n{context}\n\n# 今天的任務\n{task}"

    proc = subprocess.run(
        ["claude", "-p", prompt,
         "--allowedTools", "Read,Grep,Glob"],    # 缺口四:預先宣告
        capture_output=True, text=True, timeout=600,
    )
    if proc.returncode != 0:
        raise RuntimeError(f"agent failed: {proc.stderr[:500]}")

    out = proc.stdout.strip()
    state["last_run"] = datetime.datetime.now().isoformat()
    state["notes"].append(out[:200])             # 缺口二:留下狀態
    save_state(state)
    return out

if __name__ == "__main__":
    with open(LOCK, "w") as lk:
        fcntl.flock(lk, fcntl.LOCK_EX | fcntl.LOCK_NB)   # 缺口一:互斥
        print(run_once("檢查昨天的錯誤日誌,有異常就摘要成三行"))

這 40 行處理了缺口一(互斥)、缺口二的粗糙版(狀態檔)、缺口四(工具白名單)。缺口三和缺口五完全沒碰。

代價

這個骨架有三個明顯的問題,我先講在前面,免得有人直接拿去用。

state["notes"] 會無限成長,跑三個月會變成一個很大的 JSON。壓縮策略在 Day 13。

notes[-5:] 這個「最近五條」是拍腦袋決定的。憑什麼是五?憑什麼是最近的而不是最相關的?Day 14 會用遺忘曲線把這個排序做對。

還有,這個骨架完全信任 agent 的輸出。它說什麼就記什麼。如果它產生幻覺,幻覺就進了狀態檔,明天再被當成事實讀回來。這個問題最麻煩,Day 18 專門處理。


明天做一件更基本的事:讓它真的自己醒過來,而且醒來的時候知道自己是誰。


上一篇
我讓 Claude Code 自己跑了五個月
下一篇
最小可行常駐:讓 agent 自己醒來
系列文
Claude Code 下班之後:30 天把 CLI 工具養成會自己交差的 AI 員工3
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言