從 demo 到 production,AI Agent 少了什麼
最近玩了一下 Simon Willison 做的 Datasette Agent,它讓你用自然語言對 SQLite 資料庫對話、自動跑 SQL、還能接 charts、imagegen、sandbox 這些 plugin。底層可以換成本地模型(他測的是 gemma-4-26b-a4b),demo 用的是 Gemini 3.1 Flash-Lite,整個東西不到幾百行就架起來了。
老實說第一眼看的感想是「哇又一個 demo 很漂亮的 Agent」。但我多看了一下他的說明,發現他特別強調了兩件事:tool call 的可靠性,還有 SQL 的正確性。這兩點不是加分項,是基本條件。他在說的其實是:這東西能不能交給真人用,不是能不能跑起來。
這讓我想到一件事——AI Agent 的 demo gap 不是技術問題,是可維運性問題。
「可以跑」跟「可以用」差了多遠
我自己做過幾個 Agent 專案,踩過最痛的一個坑是這樣的:我做了一個幫內部同事查報表的 Agent,本機測試時成功率大概 85%,上線第一週就被反映「每次問有關「本季」的問題都答錯」。
原因是什麼?模型在生成 SQL 的時候,CURRENT_DATE 跟 DATE('now') 在不同 context 下行為不一致,加上我沒有對 SQL 結果做任何驗證,直接把回傳值餵給 LLM 整理答案。模型很有信心地回答了一個錯誤的數字,沒有任何警示。
修法很簡單:加一個 SQL 執行後的 schema check,確認欄位存在、型別符合預期、結果筆數在合理範圍。但這個 check 一開始根本不在我的「要做的事」清單裡,因為 demo 的時候它看起來好好的。
這就是 demo 到 production 的落差——不是功能壞了,是邊界條件沒有被看見。
可擴充:plugin 不是裝飾,是維運界線
Datasette Agent 的設計讓我注意到一點:它把 charts、imagegen、code sandbox 都做成獨立 plugin,而不是塞進主 Agent 的核心邏輯。這不只是「架構乾淨」,它有一個實際的維運意義——你知道哪些功能是核心路徑,哪些是附加的。
在 production 環境裡,這很重要。核心路徑要穩,測試覆蓋率要高,容錯要多。附加功能可以容忍更高的失敗率,可以降級(fallback to plain text)而不是整個崩掉。
我在一個內部工具裡學到這件事是被迫的:當初把所有工具 function 都塞進同一個 agent context,結果只要 imagegen 那個外部 API 超時,整個 Agent 就卡死。後來把它拆出來,設了 3 秒 timeout + fallback message,主流程的穩定性直接從 78% 拉到 96%(用 7 天 request log 算的)。
可擴充的真正意義是:讓你能控制邊界,而不只是「方便加功能」。
可觀測:你看不到的東西,就是你不知道在壞的東西
Agent 最詭異的失敗模式是「沉默失敗」——它不報錯,但答案是錯的。或者它執行了一個 tool call,結果沒有被正確解讀,然後模型用不完整的資訊繼續往下走。
我現在做每個 Agent 都一定會加的東西:
# 每次 tool call 前後都 log
def wrap_tool(fn):
def wrapper(*args, **kwargs):
logger.info(f"[TOOL] {fn.__name__} called with {args[:50]}")
result = fn(*args, **kwargs)
logger.info(f"[TOOL] {fn.__name__} returned {str(result)[:200]}")
return result
return wrapper
這段東西本身沒什麼技術含量,但它讓我第一次看到「原來模型在某些情況下會把同一個 tool 連呼叫三次,然後用最後一次的結果當作『正確答案』」。在那之前我根本不知道這件事在發生。
Datasette Agent 的架構讓每個 SQL query 都是顯式的,可以被看見、被 log、被驗證。這不是偶然,這是有意識的設計選擇。
可驗證:模型的自信不是準確度
這是最重要也最常被略過的一點。LLM 對錯誤的答案和對正確的答案,表達出來的「信心程度」幾乎是一樣的。它不會說「這個我不確定」,它會很流暢地告訴你一個可能完全錯誤的 SQL。
Simon 在說 SQL 正確性的時候
,我覺得他在暗示一件事:Agent 本身要能驗證自己的輸出,不能只靠模型說「我做完了」就算完。
實作上我試過幾種方式:
- 執行 SQL 後比對 row count 是否在預期範圍(0 筆可能是 query 錯,100 萬筆可能是沒有 WHERE)
- 關鍵 query 加一個「反向驗證 query」:如果原本的 query 是找出「今天新增的 user」,就再跑一次
COUNT WHERE created_at = today來確認數字一致 - 對 Agent 產生的 SQL 先做靜態分析,檢查是否有
DROP、DELETE、沒有 WHERE 的UPDATE
這些都很土,但都管用。
做一個能說明白自己在做什麼的 Agent
最後回到 Datasette Agent 這個案例,它讓我覺得有意思的地方不是「哇又一個新工具」,而是它的設計取向是可解釋的——你問它一個問題,它告訴你它跑了什麼 SQL、拿到什麼結果、然後才給你答案。這個透明度在 demo 裡是賣點,在 production 裡是必要條件。
我現在評估一個 Agent 是否「做好了」,用的不是「它能回答這個問題嗎」,而是:
- 我知道它在哪個步驟可能失敗嗎?
- 當它失敗的時候,我看得到嗎?
- 它的輸出有沒有被驗證,還是直接信任模型說的?
答不出來的,就還沒做好。
很多 Agent 從 demo 到沒人用的原因,不是功能不夠,是沒人信任它。而信任是從可觀測、可驗證、可控邊界這些無聊的工程細節累積出來的,不是從一個漂亮的 demo 來的。
作者:AutoKitty