iT邦幫忙

2026 iThome 鐵人賽

DAY 12
0
Modern Web

教練看不到的那六天|從 FIT 檔到 3D 軌跡,馬拉松訓練資料 Dashboard系列 第 12

Day 12|讓 bot 變聰明:Agent SDK vs 自幹 subprocess

  • 分享至 

  • xImage
  •  

昨天的 bot 只是一個遙控器—/run 去拿資料、/workout 取紀錄。

今天要讓它能理解「今天 12K 有點喘」的對話,然後自己決定要去讀哪個檔案、要寫進哪一天。

這需要一個會用工具的 agent,要接上這種 agent,有兩個方法。


先看它實際上做了什麼

整個「讓 bot 變聰明」的核心,是這幾行:

cmd = [
    CLAUDE_BIN,
    "-p",                                    # headless,不進互動介面
    "--output-format", "json",
    "--permission-mode", PERMISSION_MODE,    # 預設 acceptEdits
]
if ALLOWED_TOOLS:
    cmd += ["--allowedTools", ALLOWED_TOOLS]
if sid:
    cmd += ["--resume", sid]                 # 續上既有對話

proc = await asyncio.create_subprocess_exec(
    *cmd,
    cwd=str(COACH_DIR),                      # 在教練的工作目錄裡執行
    stdin=asyncio.subprocess.PIPE,
    stdout=asyncio.subprocess.PIPE,
)
out, err = await proc.communicate(input=text.encode())

開一個子行程,把使用者說的話從 stdin 餵進去,再把 JSON 讀回來。

那個 cwd=str(COACH_DIR) 是關鍵—CLI 會去讀那個目錄底下的 CLAUDE.md,教練的人設、記錄格式、判讀規則全在裡面。Day 13 那些課表判讀規則也是寫在同一個檔案。

檔案讀寫、多輪記憶、工具權限,全部是 CLI 內建的,這邊的程式碼只負責組指令和收 JSON。


回頭查文件

寫完之後才去翻 Agent SDK 的文件,這條路本身是官方認可的:

The SDK is available as a library for Python and TypeScript only. To drive the same agent loop from another language, run the CLI as a subprocess with the -p flag and --output-format json.

-p--output-format json—正是我們用的那兩個參數。只是它被放在「其他語言怎麼處理」的脈絡底下講,而這支 bot 是 Python,SDK 本來就有現成的。

語言當然不是選型的全部,計費方式、部署環境、要不要多開一個行程都算,但這讓自己想確認另一件事:自己當初避開 SDK 的理由,站不站得住。

前面以為 SDK 只給你一個對話迴圈,工具、記憶、設定載入都得自己接—相比之下「已經組好的 CLI」划算得多。

這個認知有點問題 ,文件上提及:

能力 Agent SDK 提供的
Built-in tools Read、write、edit files、run commands、search the web
Sessions Maintain context across exchanges,resume or fork later
Skills, commands, and memory Load automatically from your project's .claude/ and from ~/.claude/, same as Claude Code
Permissions Control which tools run automatically, which need approval

四項全中。 工具是內建的、session 能續接、CLAUDE.md 會自動載入、權限控制也有。

SDK 的定位就寫在標題:"Build production AI agents with Claude Code as a library"。它不是「組 agent 的零件」,它就是 Claude Code,只是變成可以被 import 的形式。

自己把它跟 Client SDK 搞混了,而文件其實把兩者分開列在同一張表裡:

想做的事 用哪個 為什麼
Building an agent without implementing the tool loop yourself Agent SDK 不用自己實作 tool loop
Calling the API directly and implementing the tool loop yourself Client SDK 直接存取 API 而非 Claude Code

前面以為的「工具、記憶、設定載入都得自己接」,是 Client SDK 那一列的事。


那 SDK 可以做到什麼程度

查到這裡自己以為差別是「有沒有子行程」—SDK 在你的行程裡跑,subprocess 要另外開一個。

但看 Python SDK 的文件會看到這個選項:

cli_path: str | Path | None = None    # Custom path to the Claude Code CLI executable

SDK 自己也是開 CLI 子行程。 它有一層 Transport 負責「communicate with the Claude process」,文件裡也直接寫著「The CLI subprocess reads several environment variables」。

所以「依賴一支裝在本機的 CLI 執行檔」根本不是這條路的代價—兩條路都要那支執行檔。

真正的差別是:那個子行程誰來管。

逾時要移除、session 檔不見了要重試,錯誤只有 stderr 的字串要自己解讀—這些 SDK 都包好了,走 subprocess 就得自己寫。等一下會看到那幾段長什麼樣。

所以查到這裡,天平是倒向 SDK 的。


但有一件事把天平扳回來

文件裡還有一段:

Unless previously approved, Anthropic does not allow third party developers to offer claude.ai login or rate limits for their products, including agents built on the Claude Agent SDK. Use the API key authentication methods instead.

SDK 要 API key,而 API 是按用量計費的。

這支 bot 自己每天都在用:記錄訓練、問今天練什麼、丟課表截圖進去讓它讀。一天十幾輪對話,每一輪背後都是一個會讀檔案、會判斷的 agent 在跑。走 API 的話,每一輪都在跳表,而且用得越勤跳得越快

subprocess 呼叫 CLI 用的是這台電腦上已經登入的那支 Claude Code—訂閱制,成本不會跟著對話次數線性往上長。

而且這不只是省不省錢,文件那句話說的是「不允許第三方開發者為他們的產品提供 claude.ai 登入」—這條路之所以能用訂閱,是因為它根本不是要交付給誰的產品,僅是自己在自己的電腦上跑 claude,只是換成程式去打而已。

上一節那些雜事可以自己寫,一次寫完就不用再碰,但按用量計費是每天都在發生的事,

所以最後還是選擇 subprocess 的方式。


幾個實作上真的踩過的坑

prompt 走 stdin,不走參數。

proc.communicate(input=text.encode())

一開始接在 cmd 後面當參數,結果只要訊息以 - 開頭(例如「-5 秒是什麼意思」),CLI 就當成未知選項報錯。

逾時要收屍。

except asyncio.TimeoutError:
    proc.kill()
    await proc.wait()   # 收屍,避免 zombie process 累積

kill() 只是送訊號,不 wait() 那個行程會變殭屍留在系統裡。跑久了會累積。

session 不見了要能自己救回來。

if resume and _sessions.get(chat_id) and "session" in msg.lower():
    log.warning("resume 失敗,改開新 session 重試:%s", msg[:200])
    _sessions.pop(chat_id, None)
    return await _run_claude(chat_id, text, resume=False)

--resume 的 session 檔存在本機,會過期也會被清掉。發生時不該讓使用者看到錯誤—丟掉舊 id、開新的重試一次。代價是那次對話失去上下文,但總比整個失敗好。

閒置太久自動開新對話。 預設 30 分鐘。不然早上問的訓練內容會一直掛在 context 裡,到晚上還在,會白白浪費 token。


明天:教練的課表還在 LINE 的記事本裡

bot 會用工具了,但它能讀的東西還是有限。

Day 9 排程那篇有個發現:整條資料管線裡,唯一還要人工搬運的是教練課表。教練發在 LINE 記事本,得自己儲存後轉傳到 Telegram,之後才能給 bot 分析。

明天要處理那一段!



上一篇
Day 11|Telegram Bot 從零開始:為什麼選 long polling,及讓電腦持續開著
下一篇
Day 13|搞定 LINE 記事本裡的教練課表:讓 LLM 讀圖
系列文
教練看不到的那六天|從 FIT 檔到 3D 軌跡,馬拉松訓練資料 Dashboard14
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言