iT邦幫忙

2026 iThome 鐵人賽

DAY 26
0

昨天我們替 Memora 加入第一個 Tool:

count_english_words

模型可以判斷是否需要精確計算英文單字數量,再由 Application 執行 Python Function,最後把 Tool Result 送回模型。

現在很容易出現一個問題:

Memora 已經會使用 Tool,所以它現在就是 Agent 了嗎?

如果只看功能,可能會想直接回答「是」。但從程式控制流程來看,目前更精確的說法是:

Day 25 的 Memora 是一個具備 Memory 的 Tool-using Chatbot,而且 Tool 執行被包在固定 Workflow 裡;它已經有 Agent 的部分元素,但還沒有 Agent Loop。

今天不另外建立一套程式,也不提前完成 Day 27。這一篇會直接拆開昨天的 run_model_with_tools(),確認現在到底是誰在決定下一步。


一、先建立這個系列使用的 Agent 定義

「Agent」在不同文章、產品或 Framework 裡,範圍可能不完全相同。如果只用名稱判斷,很容易變成:

有 System Prompt
→ Agent

有 Memory
→ Agent

有 Tool
→ Agent

這些說法都太寬。

為了讓後面的程式可以驗證,這個系列採用一個操作型定義:

Agent 是一個能根據目前目標與 Observation,在受限制的執行環境中反覆選擇下一個 Action,直到符合 Stop Condition 的系統。

核心不是名稱,也不是模型是否看起來很聰明,而是執行期間是否真的存在:

Goal
→ Decide
→ Action
→ Observation
→ Decide Again
→ Stop

OpenAI 的 Agent 文件也把 Agent Loop 描述為:呼叫模型、檢查輸出、執行 Tool Call、繼續執行,直到模型產生不再需要 Tool 的最終答案。


二、Chatbot、Tool-using Chatbot、Workflow 與 Agent

先用同一組標準比較:

類型 誰決定主要步驟 能否使用 Tool 能否根據結果再次決策 如何停止
Chatbot Application 呼叫一次模型 不一定 通常不能 模型回傳回答
Tool-using Chatbot 模型可選擇 Tool,但流程通常有限 可以 視實作而定 多半由固定流程結束
Workflow Python 或 Graph 預先決定路徑 可以 只走預先寫好的 Branch 程式規則
Agent 模型依 Goal 與 Observation 選擇下一步 通常可以 可以反覆決策 Final Answer、限制或中止條件

這四個名稱不是互斥的產品分類。一個實際系統可以同時是:

對外看起來是 Chatbot
內部使用固定 Workflow
其中某一段包含 Agent Loop

真正需要分辨的是每一段流程的 Control Flow 由誰掌握。


三、Memory 和 Agent 是兩個不同維度

Memora 在 Day 25 以前已經有:

Conversation History
Summary
User Profile
Long-term Memory
Semantic Retrieval
Memory Policy
Importance、Recency 與 Reconciliation

但這些能力不會自動把 Chatbot 變成 Agent。

Memory 回答的是:

系統能保留什麼狀態?
這一輪應該取回哪些過去資訊?

Agent Control Flow 回答的是:

為了完成目前目標,下一個動作是什麼?
看到執行結果後,是否還需要其他動作?
什麼時候應該停止?

因此可能存在:

有 Long-term Memory、但沒有 Agent Loop 的 Chatbot

沒有 Long-term Memory、但能完成多步任務的 Agent

同時具有 Memory 與 Agent Loop 的 Agent

這個系列前 24 天先建立 Memory,是為了讓 Day 28 的 Agent 可以操作一套已經清楚分層的 Memory System,而不是因為「有 Memory」就已經等於 Agent。


四、直接檢查 Day 25 的 Control Flow

昨天的 run_model_with_tools() 可以先縮成以下結構:

response = client.responses.create(
    tools=TOOLS,
    tool_choice="auto",
    parallel_tool_calls=False,
    # 其他參數保留
)

function_calls = [
    item
    for item in response.output
    if item.type == "function_call"
]

if not function_calls:
    return response.output_text

tool_call = function_calls[0]
tool_output = execute_tool_call(
    tool_call
)

final_response = client.responses.create(
    tools=TOOLS,
    tool_choice="none",
    parallel_tool_calls=False,
    # response.output 與 tool_output
    # 已加入新的 Input
)

return final_response.output_text

第一個 Request 中:

tool_choice="auto"

所以模型可以選擇直接回答,或要求一次 Tool Call。這部分已經具有有限的決策能力。

但是第二個 Request 使用:

tool_choice="none"

表示模型看完 Tool Result 後,只能整理 Final Response,不能再選擇另一個 Action。

因此整條路徑已經被 Application 寫死:

最多一次 Tool Call
→ 一定進入 Final Response
→ Return

這是一個有條件分支的 Workflow,還不是可以反覆決策的 Agent Loop。


五、Day 25 已經有 Action 與 Observation

雖然還不是完整 Agent Loop,昨天其實已經完成兩個重要元件。

Action

模型回傳:

function_call
name = count_english_words
arguments = {...}

這代表模型選擇了一個可執行動作。

Observation

Application 執行 Function 後回傳:

function_call_output
call_id = ...
output = {...}

這是 Action 的執行結果,也就是下一次模型判斷可以觀察到的資料。

目前真正缺少的是:

Observation
→ 再次決定 Action 或 Final Answer

Day 25 的模型雖然看得到 Observation,但 tool_choice="none" 已經替它決定:下一步只能回答。


六、一個 Tool Call 不一定足以完成任務

假設使用者要求:

請比較下面兩句話的英文單字數量,告訴我哪一句比較長:

A: I study English every day.
B: I have studied English for three years.

目前的 Tool 一次只接受:

{
    "text": "一段英文"
}

合理的處理步驟可能是:

Action 1
count_english_words(A)

Observation 1
A = 5

Action 2
count_english_words(B)

Observation 2
B = 7

Final Answer
B 比 A 多 2 個單字

Day 25 執行完 Action 1 後就強制進入 Final Response,因此模型不能再呼叫同一個 Tool 計算B。

這個例子不需要新增第二個 Tool,就能看出:

Agent Loop 的關鍵不是 Tool 數量,而是模型能否根據 Observation 繼續選擇下一個步驟。


七、固定 Workflow 和 Agent Loop 的差別

假設我們直接在 Python 寫:

count_a = count_english_words(
    sentence_a
)

count_b = count_english_words(
    sentence_b
)

result = compare_counts(
    count_a,
    count_b
)

這可以正確完成任務,但執行順序完全由程式預先決定,因此它是 Workflow。

Agent Loop 則會讓模型在每一步判斷:

目前已有什麼資料?
還缺少什麼?
下一個 Tool Call 是什麼?
資訊是否已經足以回答?

兩者沒有誰一定比較好。

當步驟明確、規則穩定時,Workflow 通常更容易測試、成本更低,也更可預測。只有當任務路徑會依中間結果變化時,才需要增加 Agentic Control。因此「能寫成 Agent」不是採用 Agent 的充分理由。


八、Agent 需要哪些最小元件?

在這個系列裡,一個最小 Agent Run 至少包含:

元件 在 Memora 中的對應內容
Goal Current User Message
Instructions SYSTEM_PROMPT
State input_messages 與累積的 Output Items
Actions TOOLS 中允許的 Function
Executor execute_tool_call()
Observation function_call_output
Decision Maker LLM
Loop 重複 Model → Tool → Model
Stop Condition Final Answer、Step Limit 或錯誤中止

Day 25 已經有其中大部分元件,卻缺少通用的 Loop 與 Stop Control。

這也是為什麼明天不需要推翻整個程式。Day 27 的主要修改會集中在:

run_model_with_tools()

其餘 Memory、Tool Definition 與 Dispatcher 都可以繼續沿用。


九、Agent 的自主性仍然受到 Application 限制

Agent 並不是讓模型取得所有權限,再自行處理一切。Application 仍然要決定:

提供哪些 Tools
每個 Tool 可以接受哪些 Arguments
哪些動作需要人工確認
一次 Run 最多可以執行幾步
Tool Output 可以有多大
發生錯誤時如何處理
哪些資料不能送給模型

模型的自主性只存在於這些邊界之內:

Application 定義 Action Space
LLM 在 Action Space 中選擇下一步

Day 25 的 TOOL_HANDLERS Allowlist、Argument Validation 與長度限制都要保留。加入 Agent Loop 之後,它們反而更重要,因為同一個 Run 可能執行多次 Tool Call。


十、Stop Condition 不是一句「完成就停止」

如果只有:

while True:

卻沒有明確停止條件,Agent 可能反覆呼叫 Tool,持續消耗 Token,甚至重複執行具有副作用的動作。

Day 27 至少要處理三種停止結果:

正常停止
模型沒有再要求 Tool,已產生 Final Answer

限制停止
達到 MAX_AGENT_STEPS

錯誤停止
API、Arguments 或 Tool Execution 發生無法繼續的錯誤

未來如果 Tool 會修改資料,還可能需要:

Approval Pause

也就是先請使用者確認,再執行具有風險或不可逆的 Action。

今天先定義這些邊界,不在 Day 26 偷跑實作 Loop。


十一、Agent 不需要把內部思考全部顯示出來

有時候會把 Agent 流程寫成:

Thought
→ Action
→ Observation

但工程上真正需要保存與檢查的是:

模型輸出的 Tool Call
Application 實際執行的 Action
Tool 回傳的 Observation
最後的 Result 或 Stop Reason

不需要要求模型輸出隱藏的逐步推理,也不應把一段看起來像思考過程的文字當成可靠的控制訊號。

Memora 會繼續使用 Responses API 的結構化 Output Items 判斷:

function_call
function_call_output
output_text

控制流程依據的是可驗證資料,不是自由格式的「我接下來想做……」。


十二、Conversation State 的管理方式也不要突然改掉

Day 25 使用 Application-managed State:

next_input = list(input_messages)
next_input += response.output
next_input.append(
    function_call_output
)

Day 27 會繼續使用同一種策略,把每一輪新的:

response.output
function_call_output

加入同一個 Working Input。

不會在中途突然改用:

previous_response_id

因為同時重播完整 Local Input,又使用 Server-managed Continuation,可能讓相同 Context 被重複帶入。

OpenAI 的 Agent 文件也建議一段 Conversation 選擇一種主要 State Strategy。Memora 目前重視可觀察與可控制,所以繼續由 Application 管理這次 Agent Run 的 State。


十三、Day 25 的 Memora 應該怎麼分類?

現在逐項檢查:

能力 Day 25 是否具備
多輪 Conversation State
Long-term Memory
Memory Retrieval
Tool Definition
模型選擇是否使用 Tool
Application 執行 Tool
Tool Result 回到模型
根據 Observation 再選 Action 沒有
通用 Agent Loop 沒有
Step Limit 沒有
動態 Stop 判斷 沒有

所以目前最精確的定位是:

一個有狀態、有長期記憶,並能在固定流程中使用一次 Tool 的 Tool-using Chatbot。

它距離 Agent 已經不遠,但這個差距不是再加一個更長的 Prompt,而是補上 Runtime Control Loop。


十四、Day 27 會保留什麼、修改什麼?

明天會完整保留:

count_english_words()
TOOLS
TOOL_HANDLERS
execute_tool_call()
SYSTEM_PROMPT
Short-term Memory
User Profile
Long-term Memory Retrieval
Day 24 的 Memory Write Path

主要只修改 Day 25 的:

run_model_with_tools()

目前是:

第一次 Model Call
→ 最多一次 Tool Call
→ 強制 Final Response

Day 27 會變成:

在 Step Limit 內重複:

Model Call
→ 有 Tool Call:執行並加入 Observation
→ 沒有 Tool Call:回傳 Final Answer

這樣才是從昨天的程式自然演進,而不是突然改用另一套 Agent Framework。


Day 26 小結

今天沒有增加新的 Tool,也沒有改動 Day 24 的 Memory System。這是刻意保留的一個 Architecture Checkpoint:先確認目前的控制流程,再開始增加自主性。

四個概念可以簡化成:

Chatbot
→ 主要目標是產生回答

Tool-using Chatbot
→ 可以要求 Application 執行 Function

Workflow
→ 程式預先決定執行路徑

Agent
→ 模型能根據 Observation 反覆選擇下一個 Action,
  直到符合 Stop Condition

昨天的 Memora 已經有:

Goal
Action
Executor
Observation

但仍然缺少:

Observation 後再次 Decision
可重複的 Loop
明確的 Step Limit
通用 Stop Condition

今天最重要的觀念是:

會使用 Tool 不等於已經是 Agent。真正的分界在於:模型能不能根據執行結果持續決定下一步,而不是只走完 Application 預先寫好的單次路徑。

Day 27|打造 Agent Loop:Action、Observation 與 Stop

下一篇會直接修改 Day 25 的 run_model_with_tools():移除第二次 Request 的強制 tool_choice="none",加入有限次數的 Loop,讓 Memora 可以反覆執行:

Decision
→ Action
→ Observation
→ Decision

同時實作 MAX_AGENT_STEPS、Final Answer 判斷、Token 累積與錯誤停止,完成第一個可控的 Agent Loop。


參考資料


上一篇
Day 25|Tool Calling 是什麼?讓 LLM 不只會回答
下一篇
Day 27|打造 Agent Loop:Action、Observation 與 Stop
系列文
從 Stateless LLM 到 Agentic Memory:30 天打造會記憶的 AI Agent30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言