RAG 讓模型「知道」,Agent 讓模型「能做」。今天進入第二常出現的任務訊號(47.6%)。
但我要先講一個可能讓你意外的建議:你需要的多半不是 Agent。
2025 年以來,「Agent」變成了一個什麼都能塞的詞。實務上多數的失敗是這樣的:
團隊做了一個「文件處理 Agent」,讓它自己決定要先分類還是先擷取、要不要查資料庫、要不要再確認一次。結果:同樣的文件,這次花 4 次 LLM 呼叫、下次花 11 次;成本浮動三倍,處理時間從 8 秒到 40 秒不等;出錯時完全不知道為什麼。
而這個流程本來是固定的——分類 → 擷取欄位 → 寫入資料庫。三個步驟,寫死就好。
用不確定性換來了不需要的彈性。
📊 職缺訊號
「Agent、工作流程與工具串接」出現在 47.6% 的生成式 AI 職缺中,僅次於 RAG。注意這個任務的完整名稱——它包含「工作流程」。市場要的不是「會做 autonomous agent」的人,是能判斷什麼時候該用哪一種的人。

圖 21-1:從工作流到代理的自主性光譜(三條曲線為作者依實務經驗歸納的示意評分)。每往右一步,換到彈性,失去可預測性與可除錯性,成本也上升。
這張圖的讀法:
判斷準則很簡單:
如果你能事先畫出流程圖,就用工作流。
只有當「下一步該做什麼」真的取決於前一步的結果,且組合太多無法窮舉時,才需要代理。
Anthropic 在《Building Effective Agents》整理了一組實用的模式體系。前四種都屬於工作流:

圖 21-2:從工作流到代理的六種設計模式(示意架構)。實務上八成的需求,前四種就能解決。
| 模式 | 適用情境 | 例子 |
|---|---|---|
| 提示鏈 | 步驟固定、每步都需要 LLM | 翻譯 → 潤稿 → 檢查術語一致性 |
| 路由 | 有明確分類、各類處理方式不同 | 客服分流:退貨/技術問題/帳務 |
| 平行化 | 多個獨立子任務、可同時進行 | 同時檢查一份合約的五個風險面向 |
| 協調者-工作者 | 子任務數量不固定,但類型已知 | 依查詢改寫出的 N 個子問題分別檢索 |
| 評估者-最佳化者 | 有明確的品質標準可自我檢查 | 生成程式碼 → 跑測試 → 依錯誤修正 |
| 自主代理 | 步驟數與順序無法預先決定 | 「幫我調查這個客訴的來龍去脈」 |
即使你最後用框架,手刻一次也是值得的——因為所有框架都是在包裝這 30 行。
"""agent.py —— ReAct(Reasoning + Acting)的最小完整實作。"""
import json
SYSTEM = """你是一個能使用工具的助理。你可以呼叫工具來取得資訊或執行動作。
工作方式:
1. 思考需要什麼資訊才能回答
2. 呼叫適當的工具(一次一個)
3. 觀察結果後,決定要繼續呼叫工具還是給出最終答案
原則:
- 只用工具回傳的事實回答,不要編造
- 若工具回傳錯誤,讀懂錯誤訊息後調整參數重試(最多兩次)
- 若三次仍無法取得資訊,誠實告知使用者"""
TOOLS = [
{"type": "function", "function": {
"name": "search_kb",
"description": "在公司知識庫中搜尋資料。當需要公司政策、產品規格、"
"作業流程等內部資訊時使用。",
"parameters": {"type": "object", "properties": {
"query": {"type": "string", "description": "搜尋關鍵字或問題"}},
"required": ["query"]}}},
{"type": "function", "function": {
"name": "calculate",
"description": "執行數學運算。當需要精確計算時使用——不要自己心算。",
"parameters": {"type": "object", "properties": {
"expression": {"type": "string", "description": "Python 算式,如 (1200*0.05)"}},
"required": ["expression"]}}},
]
def execute_tool(name: str, args: dict, ctx: dict) -> dict:
"""工具執行:所有安全檢查都在這裡,不在提示裡。"""
if name == "search_kb":
chunks = retrieve_and_rerank(args["query"], user=ctx["user"]) # 帶權限
if not chunks:
return {"error": "查無相關資料,請換個關鍵字或確認問題範圍"}
return {"results": [c["text"][:500] for c in chunks[:3]]}
if name == "calculate":
try:
# 正式環境請用安全的算式解析器,不要用 eval
return {"result": safe_eval(args["expression"])}
except Exception as e:
return {"error": f"算式無效:{e}"}
return {"error": f"未知的工具:{name}"}
async def run_agent(question: str, ctx: dict,
max_iterations: int = 6, max_cost_tokens: int = 20_000):
"""ReAct 迴圈。兩個上限是必要的護欄,不是可選項。"""
messages = [{"role": "system", "content": SYSTEM},
{"role": "user", "content": question}]
trace, used_tokens = [], 0
for i in range(max_iterations):
resp = await client.chat.completions.create(
model=MODEL, messages=messages, tools=TOOLS)
used_tokens += resp.usage.total_tokens
msg = resp.choices[0].message
if not msg.tool_calls: # 模型認為可以回答了
trace.append({"step": i, "action": "final_answer"})
return {"answer": msg.content, "trace": trace,
"iterations": i + 1, "tokens": used_tokens}
messages.append(msg)
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = execute_tool(call.function.name, args, ctx)
trace.append({"step": i, "tool": call.function.name,
"args": args, "ok": "error" not in result})
messages.append({"role": "tool", "tool_call_id": call.id,
"content": json.dumps(result, ensure_ascii=False)})
if used_tokens > max_cost_tokens: # 成本上限:防止失控燒錢
return {"answer": "這個問題較複雜,已轉由專人處理。",
"trace": trace, "stopped": "cost_limit"}
return {"answer": "我無法在合理步驟內完成這個查詢,已轉由專人處理。",
"trace": trace, "stopped": "max_iterations"}
這段程式碼有三個生產級的細節,比迴圈本身更重要:
max_iterations 與 max_cost_tokens——沒有這兩個上限,一個迴圈錯誤就能燒掉你整個月的預算。這不是可選項。
trace——記錄每一步做了什麼。出問題時,這是唯一能讓你重建現場的東西(Day 22 詳談)。execute_tool 裡——不在提示裡。這點 Day 17 強調過,Day 25 會看到不這樣做的後果。| 訊號 | 判斷 |
|---|---|
| 「流程圖畫得出來」 | 用工作流 |
| 「大部分情況固定,少數需要判斷」 | 工作流 + 一個路由節點 |
| 「步驟數不固定,但工具種類已知」 | 協調者-工作者,或限制迭代次數的代理 |
| 「連要用哪些工具都不確定」 | 自主代理——但先問自己這個需求是不是定義不清 |
| 「使用者的請求千奇百怪」 | 通常代表還沒收斂需求,先做前 20% 高頻情境 |
即使最終要做代理,也建議這樣分階段推進:
階段一:全部寫死。 流程固定,模型只做單點任務(分類、擷取、生成文字)。此時你會累積兩樣東西:真實的使用者請求分布,以及哪些情境是流程處理不了的。
階段二:加一個路由。 從階段一的資料中,你會發現請求其實集中在少數幾類。用一個 LLM 路由節點分流,各類走各自的固定流程。這一步通常能覆蓋 80–90% 的請求。
階段三:對剩下的長尾開放代理。 那 10–20% 無法歸類的請求,才交給有工具、有迭代上限的代理處理。而且要記錄它的每一次執行——這些紀錄會告訴你,其中有沒有哪些又可以被收斂成固定流程。
這個順序的好處是:每一階段都能上線、都能產生價值,而且風險是遞增而非一次到位。它也符合 Day 11 談過的原則——自動化程度應該隨著你對系統的理解而提高。
💡 一個我常用的說服方式
當有人堅持要做全自主代理時,我會問:「這個代理如果做錯,最壞會怎樣?」
- 如果答案是「回答錯誤,使用者再問一次」——那可以放心試。
- 如果答案是「刪掉資料 / 寄錯信 / 下錯單」——那就先做工作流,把不可逆的動作交給人確認。
自主性的上限,應該由「錯誤的代價」決定,而不是由技術的可能性決定。