昨天 (Day 7) 我們介紹了 Planning Pattern:AI 會先生成一份完整的計劃,再依序執行。
這種模式雖然條理清楚,但有一個限制:計劃在一開始就固定,無法靈活應變。
例如景點臨時休館,AI 只能刪掉該步驟,而無法動態調整或找替代方案。
昨天的 Planning 屬於「先想完再做」;而今天要介紹的 ReAct,則是「邊做邊想」。這樣的對比,正好補足 Planning 缺乏的彈性。
這就是今天的主角 ReAct Pattern:Reason + Act。
ReAct 的精神是 邊推理 (Reason),邊行動 (Act)。
它不像 Planning Pattern 那樣一次性產出完整的靜態計劃(Planning),ReAct 會在每一步驟中交替進行「思考」與「行動」,直到找到最終答案。
這個循環通常被簡稱為 TAO 流程:
Thought(思考)
Action(行動)
Observation(觀察)
Final Answer(最終答案)
圖:ReAct Pattern 流程圖。AI 交替進行「推理」與「行動」,直到得到最終答案。
Yao, et al. ReAct: Synergizing reasoning and acting in language models. (ICLR 2023)
這篇是 ReAct 模式的原始論文,重點概念就是將 推理 (Reasoning) 與 行動 (Acting) 融合在同一流程中,透過交替的 Thought → Action → Observation 循環,讓語言模型能夠一邊思考、一邊互動,最終得到更靈活與可靠的解答。
我們延續昨天的例子:規劃一日三景點(美泉宮、美景宮、聖史蒂芬大教堂)。
不同的是,這次 AI 不會一次性輸出完整行程,而是透過 ReAct 過程逐步完成。
import google.generativeai as genai
import json
import re
from datetime import datetime, timedelta
# ---- 初始化 LLM ----
genai.configure(api_key="你的_API_KEY")
llm = genai.GenerativeModel("gemini-2.0-flash")
# ---- 模擬工具 ----
def get_weather(city: str):
return "今天維也納是下雨天" # 假設今天下雨
def check_open(place: str):
closed_places = ["美景宮"] # 假設美景宮休館
return f"{place} 正常開放" if place not in closed_places else f"{place} 今日休館"
# ---- 工具路由表 ----
TOOLS = {
"查天氣": get_weather,
"查詢開放狀態": check_open,
}
# ---- ReAct 循環 ----
def react_loop():
prompt = """你是一個旅行規劃助理,使用 ReAct 模式回答問題,思考請用繁體中文。
格式必須包含以下幾種:
Thought: 你的推理
Action: 你要執行的工具(格式:查天氣(city) 或 查詢開放狀態(place))
Observation: 工具回傳的結果
Final Answer: 請用 JSON 格式輸出最終行程,例如:
[
{"place": "美泉宮", "minutes": 120},
{"place": "聖史蒂芬大教堂", "minutes": 120}
]
現在任務:幫我規劃今天去美泉宮、美景宮、聖史蒂芬大教堂的一日行程。"""
for step in range(8): # 最多 8 回合
response = llm.generate_content(prompt).text
print(response)
# ---- 如果是最終答案 ----
if "Final Answer" in response:
try:
json_str = response.split("Final Answer:")[-1].strip()
if json_str.startswith("```"):
json_str = json_str.split("```")[1]
json_str = json_str.replace("json", "", 1).strip()
plan = json.loads(json_str)
print("\n最終解析後的行程:", plan)
return plan
except Exception as e:
print("\nJSON 解析失敗:", e)
return []
# ---- 嘗試解析 Action ----
match = re.search(r"Action:\s*([^\(]+)\((.+)\)", response)
if match:
action, arg = match.groups()
action = action.strip()
arg = arg.strip()
if action in TOOLS:
print(f"\n>> 執行工具:{action}({arg})")
obs = TOOLS[action](arg)
print(f">> 工具回傳:{obs}\n")
prompt += f"\nObservation: {obs}"
else:
print(f"\n>> 無效的工具動作:{action}")
prompt += "\nObservation: 無效的動作"
else:
print("\n>> 無法解析動作")
prompt += "\nObservation: 無法解析動作"
# ---- 執行行程模擬 ----
def simulate_schedule(plan, weather="rain"):
print("\n=== 行程時間模擬 ===")
time = datetime.strptime("09:00", "%H:%M")
end = datetime.strptime("18:00", "%H:%M")
for i, task in enumerate(plan):
duration = task["minutes"]
finish = time + timedelta(minutes=duration)
print(f"{time.strftime('%H:%M')}–{finish.strftime('%H:%M')} 參觀 {task['place']}")
time = finish
if i < len(plan) - 1: # 景點之間交通
travel_time = int(30 * (1.5 if weather == "rain" else 1))
finish = time + timedelta(minutes=travel_time)
if weather == "rain":
print(f"{time.strftime('%H:%M')}–{finish.strftime('%H:%M')} 前往下一景點(因下雨延誤)")
else:
print(f"{time.strftime('%H:%M')}–{finish.strftime('%H:%M')} 前往下一景點")
time = finish
print("行程可行!" if time <= end else "行程超時!")
# ---- 主程式 ----
if __name__ == "__main__":
plan = react_loop()
if plan:
simulate_schedule(plan, weather="rain")
圖:ReAct Demo 實際輸出結果。AI 先逐步推理 (Thought),再選擇行動 (Action) 呼叫工具,取得觀察結果 (Observation),並根據外部資訊不斷修正計劃。最後產生 Final Answer(JSON 格式的行程),並進一步模擬完整時間表。
Thought → Action → Observation 循環
動態應變
最終輸出
執行結果清楚展現 ReAct 與 Planning 的差異:
Planning 是先給定靜態藍圖,遇到變數只能刪掉步驟;而 ReAct 則能邊查邊想,讓行程動態調整。
圖:Planning 與 ReAct 的對比。前者靜態、清晰;後者動態、靈活。
接著我們會進一步探討 Reflection(反思),讓 AI 不只是當下靈活應變,還能記取經驗、避免重蹈覆轍。
如果 ReAct 是讓 AI 能在當下隨情境調整,那麼下一步的 Reflection,則是讓 AI 從過去學會改進。
圖:維也納國立歌劇院(Wiener Staatsoper)。巴洛克華麗的階梯與穹頂燈光,是電影《不可能的任務:失控國度》著名的歌劇院段落中,阿湯哥與女主角在此相會並臨機應變尋找出路的經典場景。舞台上的情境變化莫測,需要臨場反應與即時調度,如同 ReAct Pattern 強調的「邊想邊做」:AI 不僅能規劃,更能在情境中不斷調整,學會臨機應變。(攝影:作者自攝)