前面我們把 Agent 的邊界放在「誰決定下一步」。但 Model 取得動態決策權之後,不會自動知道怎麼呼叫 Tool、把結果放回下一輪,也不會自己管理執行上限與 Trace。所以進入 Agent 篇,我們還是從 Baseline 開始:先用 Model 加上最小 Harness,讓第一個動態 Loop 真的跑起來,再看它怎麼失敗。
這篇會讓一個最小 Agent 執行「幫我退掉 order 123」,並故意注入一次回應遺失的 Timeout。它最後會說退款完成,系統裡卻留下兩筆退款紀錄。這個失敗,正好能讓我們看見 Model 之外,Harness 還缺了什麼。
看到 Agent,很容易立刻把 Planning、Memory、Guardrails 與 Multi-agent 全部塞進架構圖。但如果沒有最小版本,就無法知道每個元件解決了什麼問題。因此在拆解 Agent Loop 前,我們先建立一個可比較的起點。
先建立一個能執行、能觀察,也能暴露失敗的 Agent Baseline。後續每加入一個元件,都必須說清楚它修掉了哪一種失敗。
- 為什麼可以用
Agent = Model + Harness作為操作性模型- 第一個 Agent Baseline 最少需要哪些能力
- 如何用 Trace 與最終狀態,區分「Agent 停了」和「任務完成」

先說清楚:Agent = Model + Harness 不是業界共同制定的正式定義,而是本文用來拆解 Agent System 的操作性模型。
同一個 Model 放在 Chat UI 裡,通常只會接收訊息並產生回答;放進 Coding Agent,卻能讀檔案、執行指令、觀察錯誤,再繼續修改。差別不只是 Prompt,而是 Model 外面多了一套負責驅動執行的系統。
OpenAI 在拆解 Codex agent loop 的工程文章中,將 Harness 描述為 Codex 背後的核心 Agent Loop 與 Execution Logic,負責協調 User、Model 與 Tools。AWS AgentCore 也把 Harness 描述為會執行 Model、System Prompt、Tools、Memory 與 Limits 的受管 Orchestration Loop。
不過,各家的詞彙並不一致。Anthropic 使用的是 Augmented LLM:在 LLM 周圍加入 Retrieval、Tools 與 Memory,再依需求演進成 Workflow 或 Agent。因此這裡的公式不是要重新發明定義,而是要把兩種責任分開:
| 部分 | 主要責任 |
|---|---|
| Model | 理解模糊目標、根據 Observation 推理、提出下一步或產生回答 |
| Harness | 組裝 Context、提供 Tool、執行 Tool Call、回填結果、驅動 Loop、限制輪數並留下 Trace |
Model 產生的 Tool Call,本質上仍是一個「我想執行這個動作」的 Intent。它不會自己連上訂單系統,也不會在 Process Crash 後自動記得進行到哪裡。真正把 Intent 轉成執行、再把結果帶回下一輪的是 Harness。
而 Agent 之外還有另一個重要角色:Environment。
Agent(Model + Harness)↔ Environment
Order Service、Payment Service、Browser、檔案系統與使用者都位於 Agent 外部。Model 可以判斷下一步,Harness 可以驅動互動,但真實世界究竟有沒有改變,最後仍由 Environment 決定。
這也讓我們得到第一個設計原則:
Model 負責處理不確定性;Harness 負責管理執行;Environment 負責提供事實。
最小 Agent Loop 其實不複雜:
User Goal
↓
Build Context → Model
├─ Final Output → Stop
└─ Tool Call
↓
Tool Executor
↓
Observation
└──────→ 回到 Model
OpenAI Agents SDK 的 Runner 就呈現了這個基本骨架:呼叫 Model;若得到 Final Output 就結束;若得到 Tool Call,就執行 Tool、把結果放回 Context,再進入下一輪;超過 max_turns 則以錯誤終止。
把它整理成 Baseline,第一版只需要完成五個動作:
| 動作 | Baseline 現在要做什麼? |
|---|---|
| 準備 | 保存 Goal 與 Run ID,組裝 Instructions、對話和 Tool Schema |
| 決定 | 讓 Model 依目前 Observation 產生 Final Output 或 Tool Call |
| 執行 | 驗證參數並執行 Tool,將成功或錯誤包成 Observation |
| 繼續或結束 | 回填結果後進入下一輪;遇 Final Output、Error 或 Max Turns 時停止 |
| 留下證據 | 記錄每輪決策、Tool Result、Latency、Token 與 Termination Reason |
LangGraph 的官方文件 特別區分 Explicit Termination Condition 與 Recursion Limit:前者說明任務為何應該結束,後者只是防止 Loop 失控的 Safety Net。
但在這個 Baseline,我們刻意先接受一個簡化:Model 產生 Final Output 時,Harness 就結束這次 Run。稍後再用 Evaluation 判斷,這次結束究竟是不是真的成功。

現在給 Baseline 一個任務:
User:「幫我退掉 order 123。」
測試環境只提供三個 Tool:
get_order(order_id)
get_refund_policy(order_id)
issue_refund(order_id, amount)
Harness 把目標、對話紀錄與 Tool Schema 交給 Model。Model 依序查詢訂單與退款政策,再呼叫 issue_refund。沿用前面的退款事故,我們在 Sandbox 注入一次「服務端完成退款,但回應遺失」的 Timeout。Baseline 沒有 Reconciliation,因此 Model 直接重試,最後回答退款完成。
這裡的重點不是再次解釋 Timeout,而是把這次執行存成可比較的 Baseline Record:
run_id: run_order_123_001
goal: "幫我退掉 order 123"
turns:
- model: {decision: tool_call, tool: get_order}
tool: {arguments_valid: true, status: success}
- model: {decision: tool_call, tool: get_refund_policy}
tool: {status: success}
- model: {decision: tool_call, tool: issue_refund}
tool: {arguments_valid: true, status: timeout, error_class: network_timeout, result: unknown}
- model: {decision: tool_call, tool: issue_refund}
tool: {status: success}
- model: {decision: final_output}
termination_reason: final_output
metrics:
turn_count: 5
tool_call_count: 4
wall_latency_ms: 8420
total_tokens: 6810
evaluation:
task_success: false
final_state: {refund_records: 2}
duplicate_actions: 1
production_ready: false
這份 Record 同時保留 Model Decision、Tool Observation、Loop Termination 與 Environment Evaluation。它不是 Production Trace Schema 的標準答案,而是 Baseline 的實驗契約:未來加入任何能力後,都用同一組欄位重跑,才能知道結果究竟有沒有改善。
第一版刻意不加入完整的 Planning、Memory、Checkpoint、Human Approval 與 Multi-agent,否則還沒看到問題,就已經把答案塞進系統。但有一條安全邊界不能退讓:這個缺少 Permission 與 Idempotency 的 Baseline,只能連接 Fixture、Sandbox 或模擬後端,不能取得 Production 退款權限。

前面建立的 Evaluation 與 Trace,在這裡開始有了新的用途:它們要替 Agent Baseline 留下可以重跑與比較的執行證據,而不是重新建立另一套觀測系統。
這個對照組至少要保留五類訊號:任務結果與最終狀態、Tool 選擇與錯誤分類、Turn 與停止原因、Latency 與 Token,以及人工介入或重複副作用。YAML 中不一定要採用同一套欄位名稱,但每次實驗必須能回答相同問題。
OpenAI Agents SDK 能以 Span 記錄 LLM Call、Tool Execution、Handoff 與 Agent Run;Anthropic 對 Agent Evaluation 的整理 也將完整 Trial 視為包含 Tool Calls、Intermediate Results 與其他互動的 Transcript/Trace/Trajectory。
但 Trace 只告訴我們發生了什麼,成功條件仍要回到 Environment。τ-bench 評估客服 Agent 時,不是只看最後一句回答,而是比較對話結束後的資料庫狀態與目標狀態。
因此至少要分開三件事:
| 判斷 | order 123 的結果 |
|---|---|
| Protocol Termination | final_output,Runner 正常停止 |
| Task Outcome | 退款發生兩次,任務失敗 |
| Production Readiness | 缺少 Approval、Idempotency 與 Recovery,不可上線 |
只看第一層,幾乎每個 Demo 都會成功;開始看第二、第三層,才真正進入 System Design。
order 123 只證明了其中一種失敗。接著應該固定同一套 Baseline 與紀錄格式,替它準備一組 Failure Catalog:不同 Fixture 分別注入重複 Observation、錯誤使用者狀態、程序中斷、缺少核准、結果未知與無進展循環。這不是聲稱單一 Trace 發生了所有問題,而是建立後續元件必須通過的測試地圖。
| 問題群 | Baseline 暴露的失敗 | 後續設計方向 |
|---|---|---|
| 不知道下一步 | 重複查詢、沒有新資訊、遇到變化仍沿用舊路徑 | Planning、Replanning、Progress Detection |
| 不知道現在在哪裡 | 混用文字中的狀態;中斷後必須重頭開始 | Typed State、Memory、Checkpoint、Resume |
| 不知道動作是否安全完成 | 未經核准就執行;結果未知時直接 Retry | Permission、Human Approval、Idempotency、Reconciliation |
| 不知道是否該結束 | Model 自稱完成但外部狀態不正確;不斷循環直到 Max Turns | Success Assertion、Stop Condition、Budget、Handoff |
這裡最容易犯的錯,是看到 Agent 表現不好就直接換更大的 Model。但很多失敗根本不屬於 Model:結果未知時如何確認副作用、誰能批准退款、程序崩潰後如何恢復,以及什麼才算任務成功,都是 Harness 與外部系統的責任。
也因此,Agent = Model + Harness 最有價值的地方不是公式本身,而是它逼我們問:
這次失敗應該改善 Model,還是改善 Harness?

這篇沒有做出一套 Production-ready Agent,只建立了第一個可觀測的對照組:Model 能根據 Observation 動態選擇下一步,Harness 能執行 Tool、回填結果、限制 Loop,並留下足以重建過程的 Trace。
它仍然會犯很多錯,而這正是我們需要的結果。
因為從下一天開始,每加一個元件都要回答同一個問題:
它修掉了哪一種可以被重現、被量測的 Baseline Failure?
有了這個 Baseline,下一步就能重跑同一組 Failure Catalog,比較不同 Loop 設計如何讓 Agent 判斷進展、修正下一步,並在正確的時候停下來。之後才是 State 與 Memory、Checkpoint 與 Resume、Tool Safety 與 Human-in-the-loop。如果單一 Agent 最後真的出現能力或 Context 過載,再討論是否需要 Multi-agent。
複雜度不應該來自 Framework 的功能選單,而應該來自 Baseline 已經證明存在的能力缺口。

如果你現在有一套 Agent,它最常出現哪一種問題?
留下一個字母與情境。下一篇,我們會用同一套 Baseline,實際比較 Agent Loop、Planning、Progress 與 Stop Condition 如何改變執行結果。