Agent 的核心循環:ReAct、Plan-and-Execute 與 Reflection
深入三種主流 Agent 設計模式,用 Python 程式碼演示它們如何讓 LLM 從被動回答變成主動解決問題。
Agent 的核心循環:ReAct、Plan-and-Execute 與 Reflection
上一篇我們理解了 Agent 的基本概念:它不再是只會生成文字的 Chatbot,而是能感知、推理、行動、記憶與反思的自主系統。
但「觀察 → 思考 → 行動 → 反思」這個循環,具體要怎麼實作?
LLM 要如何在每一步決定「現在該想什麼、該做什麼」?
這一篇,我們要深入三種最主流的 Agent 設計模式:ReAct、Plan-and-Execute 與 Reflection,並用實際的 Python 程式碼演示它們的運作邏輯。
一、為什麼需要設計模式?
如果你直接對 LLM 說:「幫我規劃一趟東京五天四夜的行程」,它可能會直接生成一份看起來合理的行程表。
但這份行程表有幾個問題:
- 它可能不知道最新的航班時間
- 它可能不知道某些景點已經關閉
- 它可能沒有考慮匯率、交通時間、營業時間
- 它可能只是「生成」了一份聽起來合理的內容
要讓 Agent 真正解決問題,我們需要給它一套結構化的運作方式,讓它知道:
- 什麼時候該思考?
- 什麼時候該行動?
- 什麼時候該停下來檢查?
- 什麼時候該重新規劃?
這就是 Agent 設計模式要解決的問題。
二、ReAct:推理與行動交錯進行
什麼是 ReAct?
ReAct 是 Reasoning + Acting 的縮寫,是目前最廣泛使用的 Agent 設計模式之一。
它的核心思想非常直觀:
讓 LLM 在每一步交替進行「推理」與「行動」。
具體來說,ReAct 的運作流程如下:
- Thought(思考):模型先推理當前狀態,決定下一步要做什麼。
- Action(行動):模型選擇一個工具並執行。
- Observation(觀察):模型接收工具回傳的結果。
- 重複上述步驟,直到任務完成。
程式碼演示:ReAct 迴圈
以下是一個簡化的 Python 實作,展示 ReAct 的核心迴圈:
import json
import re
# 1. 定義工具
def search_weather(city: str, day: str) -> str:
"""模擬天氣查詢 API"""
fake_data = {
("台北", "今天"): "多雲,降雨機率 60%,氣溫 22-28°C",
("台北", "明天"): "晴時多雲,降雨機率 20%,氣溫 24-30°C",
}
return fake_data.get((city, day), "查無資料")
def search_weather_alert(city: str) -> str:
"""模擬天氣警報查詢 API"""
return "目前無豪雨特報,但山區午後有陣雨機率"
# 2. 工具註冊表
TOOLS = {
"search_weather": search_weather,
"search_weather_alert": search_weather_alert,
}
# 3. ReAct 系統提示
REACT_PROMPT = """你是一個 ReAct Agent,必須按照以下格式回答:
Thought: 你的推理過程
Action: 工具名稱
Action Input: 工具參數(JSON 格式)
當你得到足夠資訊後,使用:
Thought: 我已經知道答案了
Final Answer: 你的最終回答
可用工具:
- search_weather(city, day): 查詢指定城市與日期的天氣
- search_weather_alert(city): 查詢指定城市的天氣警報
"""
def parse_action(text: str):
"""從 LLM 輸出中解析 Action 與 Action Input"""
action_match = re.search(r"Action:\s*(\w+)", text)
input_match = re.search(r"Action Input:\s*(\{.*?\})", text, re.DOTALL)
if action_match and input_match:
return action_match.group(1), json.loads(input_match.group(1))
return None, None
def react_agent(question: str, max_steps: int = 5):
"""ReAct Agent 主迴圈"""
scratchpad = f"Question: {question}\n"
for step in range(max_steps):
# 1. 呼叫 LLM,產生 Thought + Action
llm_output = call_llm(REACT_PROMPT + scratchpad)
scratchpad += llm_output + "\n"
# 2. 檢查是否已完成
if "Final Answer:" in llm_output:
return llm_output.split("Final Answer:")[-1].strip()
# 3. 解析並執行工具
action, action_input = parse_action(llm_output)
if action is None:
scratchpad += "Observation: 無法解析 Action,請重新輸出。\n"
continue
if action not in TOOLS:
observation = f"錯誤:找不到工具 {action}"
else:
try:
observation = TOOLS[action](**action_input)
except Exception as e:
observation = f"工具執行錯誤:{e}"
# 4. 把觀察結果寫回 scratchpad
scratchpad += f"Observation: {observation}\n"
return "已達到最大步數,任務未完成。"
實際執行結果
假設使用者問:「台北今天適合爬山嗎?」
Step 1:
Thought: 我需要先查今天台北的天氣。
Action: search_weather
Action Input: {"city": "台北", "day": "今天"}
Observation: 多雲,降雨機率 60%,氣溫 22-28°C
Step 2:
Thought: 降雨機率偏高,我需要再查一下是否有豪雨特報。
Action: search_weather_alert
Action Input: {"city": "台北"}
Observation: 目前無豪雨特報,但山區午後有陣雨機率
Step 3:
Thought: 我已經知道答案了
Final Answer: 今天台北降雨機率 60%,山區午後有陣雨,
不建議爬山。建議改期或選擇室內活動。
ReAct 的優點與缺點
優點
- 直觀易懂,思考與行動交錯,符合人類解決問題的方式
- 靈活應變,每一步都根據上一步的結果調整
- 易於實作,只需要在 Prompt 中定義好格式
缺點
- 容易短視,每一步只關注當下,可能缺乏全局規劃
- 可能繞遠路,沒有事先規劃,可能走冤枉路
- 容易陷入迴圈,如果一直找不到答案,可能反覆嘗試類似行動
三、Plan-and-Execute:先規劃,再執行
什麼是 Plan-and-Execute?
ReAct 是「邊想邊做」,而 Plan-and-Execute 則是「先想清楚,再動手」。
它的核心思想是:
先讓 LLM 制定一個完整的計畫,再逐步執行每個步驟。
具體流程如下:
- Planning(規劃):LLM 根據目標,生成一個步驟清單。
- Execution(執行):逐步執行每個步驟,可能使用工具。
- Re-planning(重新規劃):如果執行結果不如預期,重新調整計畫。
程式碼演示:Plan-and-Execute
from dataclasses import dataclass, field
@dataclass
class Plan:
steps: list[str] = field(default_factory=list)
current_step: int = 0
results: dict = field(default_factory=dict)
PLANNER_PROMPT = """你是一個規劃器。請將以下任務拆解成有序的步驟清單。
每個步驟應該是一個具體、可執行的動作。
任務:{task}
請以 JSON 格式輸出,例如:
{{"steps": ["步驟1", "步驟2", "步驟3"]}}
"""
EXECUTOR_PROMPT = """你是一個執行器。請根據以下步驟執行任務。
原始任務:{task}
完整計畫:{plan}
已完成步驟與結果:{results}
當前步驟:{current_step}
請執行當前步驟,並輸出結果。如果需要使用工具,請呼叫對應工具。
"""
REPLANNER_PROMPT = """你是一個重新規劃器。
原始任務:{task}
原計畫:{plan}
目前執行結果:{results}
請判斷是否需要調整計畫。如果需要,輸出新的步驟清單(JSON 格式);
如果不需要,輸出 {{"steps": null}}。
"""
def plan_and_execute(task: str, max_replans: int = 2):
"""Plan-and-Execute Agent 主流程"""
# 1. 規劃階段
plan_json = call_llm(PLANNER_PROMPT.format(task=task))
plan = Plan(steps=json.loads(plan_json)["steps"])
print(f"計畫:{plan.steps}")
# 2. 執行階段
for i, step in enumerate(plan.steps):
plan.current_step = i
print(f"\n執行步驟 {i+1}:{step}")
result = call_llm(EXECUTOR_PROMPT.format(
task=task,
plan=plan.steps,
results=plan.results,
current_step=step,
))
plan.results[step] = result
print(f"結果:{result}")
# 3. 重新規劃(可選)
if i < len(plan.steps) - 1:
replan_json = call_llm(REPLANNER_PROMPT.format(
task=task,
plan=plan.steps,
results=plan.results,
))
new_steps = json.loads(replan_json).get("steps")
if new_steps:
print(f"重新規劃:{new_steps}")
plan.steps = plan.steps[:i+1] + new_steps
return plan.results
實際執行結果
假設任務是:「幫我規劃一趟東京五天四夜的行程。」
計畫:
1. 查詢台北到東京的航班與價格
2. 查詢東京五天四夜的天氣預報
3. 查詢熱門景點與營業時間
4. 查詢交通方式與所需時間
5. 根據以上資訊,生成每日行程
6. 檢查行程是否合理,必要時調整
執行步驟 1:查詢航班
結果:找到長榮、華航、虎航等選項,來回約 12,000 元
執行步驟 2:查詢天氣
結果:第一天晴、第二天雨、第三天晴、第四天多雲、第五天晴
...
執行步驟 6:檢查行程
結果:發現第二天排了戶外行程但會下雨,調整為室內行程
Plan-and-Execute 的優點與缺點
優點
- 全局視野,先規劃再執行,能考慮整體結構與資源分配
- 效率較高,避免重複嘗試,減少不必要的工具呼叫
- 易於除錯,每個步驟清楚分明,出錯時容易定位問題
缺點
- 規劃可能過時,如果執行過程中出現新資訊,原本的計畫可能不再適用
- 缺乏靈活性,如果計畫不夠詳細,執行時可能卡住
- 規劃品質依賴 LLM,如果 LLM 規劃能力不足,整個流程都會受影響
四、Reflection:讓 Agent 學會自我修正
什麼是 Reflection?
無論是 ReAct 還是 Plan-and-Execute,Agent 都可能犯錯。
Reflection 的核心思想是:
讓 Agent 檢視自己的行動與結果,從錯誤中學習,並調整後續策略。
Reflection 通常不是獨立的模式,而是附加在 ReAct 或 Plan-and-Execute 之上的機制。
具體做法包括:
- 自我批評(Self-Critique):Agent 檢視自己的輸出,找出問題。
- 自我修正(Self-Correction):根據批評,重新生成或調整。
- 經驗累積(Experience Replay):將失敗經驗存入記憶,避免重複犯錯。
程式碼演示:Reflection
GENERATOR_PROMPT = """請根據以下要求生成內容:
任務:{task}
參考資料:{context}
"""
CRITIC_PROMPT = """你是一個嚴格的審查者。請檢視以下輸出,找出問題:
任務:{task}
輸出:{output}
請從以下角度評估:
1. 是否完整回答了任務?
2. 是否有事實錯誤或幻覺?
3. 是否缺乏具體細節?
4. 結構是否清晰?
請以 JSON 格式輸出:
{{"has_issue": true/false, "critique": "具體問題描述"}}
"""
REVISER_PROMPT = """請根據批評意見修正以下輸出:
任務:{task}
原輸出:{output}
批評意見:{critique}
請輸出修正後的版本。
"""
def reflection_agent(task: str, max_iterations: int = 3):
"""帶有 Reflection 的 Agent"""
# 1. 初始生成
output = call_llm(GENERATOR_PROMPT.format(task=task, context=""))
print(f"初稿:\n{output}\n")
for i in range(max_iterations):
# 2. 自我批評
critique_json = call_llm(CRITIC_PROMPT.format(task=task, output=output))
critique = json.loads(critique_json)
if not critique["has_issue"]:
print(f"第 {i+1} 輪審查:無問題,任務完成。")
break
print(f"第 {i+1} 輪批評:{critique['critique']}")
# 3. 自我修正
output = call_llm(REVISER_PROMPT.format(
task=task,
output=output,
critique=critique["critique"],
))
print(f"修正後:\n{output}\n")
return output
實際執行結果
假設任務是:「寫一份無線耳機的產品說明。」
初稿:
這款產品採用先進技術,品質優良,適合各種場合使用。
第 1 輪批評:
這段文字太籠統,沒有具體說明產品功能、規格與適用對象。
應該補充具體資訊,例如尺寸、材質、使用場景。
修正後:
這款無線耳機採用藍牙 5.3 技術,支援主動降噪,
單次充電續航 8 小時,搭配充電盒可達 32 小時。
重量僅 4.5 克,適合通勤、運動與辦公使用。
第 2 輪審查:無問題,任務完成。
Reflection 的優點與缺點
優點
- 提升品質,透過自我檢視,能發現並修正錯誤
- 持續改進,累積經驗後,Agent 的表現會越來越好
- 適用性廣,可以附加在任何 Agent 模式上
缺點
- 增加成本,每次反思都需要額外的 LLM 呼叫
- 可能過度修正,有時原本的答案是對的,反思後反而改錯
- 需要良好的評估標準,如果沒有明確的判斷依據,反思可能流於形式
五、三種模式的比較
| 面向 | ReAct | Plan-and-Execute | Reflection |
|---|---|---|---|
| 核心思想 | 邊想邊做 | 先規劃再執行 | 自我檢視與修正 |
| 優點 | 靈活、直觀 | 全局視野、效率高 | 提升品質、持續改進 |
| 缺點 | 短視、可能繞路 | 規劃可能過時 | 成本高、可能過度修正 |
| 適用場景 | 簡單任務、不確定性高 | 複雜任務、步驟明確 | 需要高品質輸出 |
| 是否獨立 | 是 | 是 | 通常附加於其他模式 |
在實務中,這三種模式常常混合使用。
例如:
- 先用 Plan-and-Execute 制定高階計畫
- 再用 ReAct 執行每個步驟
- 最後用 Reflection 檢查整體結果
這樣兼顧了全局規劃、靈活執行與品質控管。
六、一個混合範例
假設我們要打造一個「自動研究助理 Agent」,任務是:
幫我整理一份「2024 年 AI Agent 發展趨勢」的報告。
混合模式可能是這樣運作:
def research_assistant(task: str):
# 第一階段:Plan-and-Execute 制定計畫
plan = plan_and_execute(f"制定研究計畫:{task}")
# 第二階段:ReAct 執行每個步驟
results = []
for step in plan["steps"]:
result = react_agent(f"執行以下步驟:{step}")
results.append(result)
# 第三階段:Reflection 檢查與修正
draft = "\n".join(results)
final = reflection_agent(f"根據以下草稿撰寫最終報告:{draft}")
return final
實際運作:
第一階段(Planning):
1. 搜尋 2024 年 AI Agent 相關新聞與論文
2. 整理主要發展方向
3. 找出代表性產品與研究
4. 撰寫報告大綱
5. 生成完整報告
6. 檢查與修正
第二階段(ReAct 執行):
Step 1: search_web("2024 AI Agent trends")
→ 找到多篇報導,提到多 Agent 協作、工具使用、自主規劃
Step 2: 整理出三大方向:工具使用、多 Agent 協作、自主規劃
Step 3: 找到 AutoGPT、BabyAGI、CrewAI 等案例
...
第三階段(Reflection):
初稿完成 → 批評:缺少具體案例與最新引用
→ 修正:補充案例與更新來源
七、總結:三種模式,三種思維
讓我們回顧這一篇的核心:
- ReAct:推理與行動交錯,適合不確定性高、需要靈活應變的任務。
- Plan-and-Execute:先規劃再執行,適合步驟明確、需要全局視野的複雜任務。
- Reflection:自我檢視與修正,適合需要高品質輸出、可迭代改進的場景。
這三種模式並不是互斥的,而是可以互補與組合。
理解它們的運作邏輯與適用場景,是設計高效 Agent 的關鍵。
不過,Agent 要能真正「行動」,還需要一個核心能力:工具呼叫(Tool Use)。
LLM 要如何決定呼叫哪個工具?工具的回傳結果要如何整合進推理流程?
這就是下一篇要談的主題。
下一篇預告
《工具呼叫與 Function Calling:讓 LLM 連接外部世界》
我們會解釋 Function Calling 的運作原理、Tool Schema 的設計方式、錯誤處理與重試機制,並用實際的 Python 程式碼演示如何讓 LLM 從「只會說」變成「真的能做」。