LangChain 課程 – Agents:自主智能體
主題:ReAct Agent(思考‑行動‑觀察)
簡介
在大型語言模型(LLM)日益普及的今天,單純的「一次性」提示已難以滿足需要多步推理、外部工具呼叫或動態決策的應用。ReAct(Reason‑Act‑Observe)Agent 正是為了解決這類需求而設計的,它將 思考(Reasoning)、行動(Action) 與 觀察(Observation) 三個環節緊密結合,使智能體能在執行過程中不斷迭代、修正自己的策略。
ReAct 的核心概念是讓 LLM 先產生「思考」的文字描述,再根據描述決定要呼叫哪個工具(如搜尋、計算、資料庫),最後把工具回傳的結果視為「觀察」回饋給模型,進一步產出下一輪的思考。這樣的迴圈不僅提升了 答案的正確性,也讓開發者能以 模組化、可追蹤 的方式構建複雜的對話系統。
在本單元,我們將從概念說明、程式碼實作、常見陷阱與最佳實踐,最後延伸到實務應用,帶你一步步掌握 ReAct Agent 的使用方法。
核心概念
1. ReAct 的三大步驟
| 步驟 | 目的 | 範例 |
|---|---|---|
| 思考 (Reason) | LLM 產生自然語言的推理或指令說明 | 「我需要先查詢今天的天氣,才能決定是否建議外出。」 |
| 行動 (Act) | 根據思考結果呼叫對應的工具(Tool) | 呼叫 search_weather API |
| 觀察 (Observe) | 收集工具回傳的資訊,作為下一輪思考的輸入 | 取得「晴,最高 28°C」的天氣回應 |
這三個環節在 LangChain 中透過 AgentExecutor 與 Tool 物件自動串接,開發者只需要定義好工具與提示模板,剩下的迴圈由框架負責。
2. Prompt Template:把思考與行動寫進 Prompt
ReAct 需要在提示中明確告訴模型「如果需要執行工具,請以 Action: 開頭」的格式。例如:
Question: {input}
Thought: {your reasoning}
Action: {tool_name}[{tool_input}]
Observation: {tool_output}
LangChain 提供 ReActPromptTemplate(或自行組合 ChatPromptTemplate)來自動產生這樣的結構。
3. Tool 的設計與註冊
每個外部工具必須實作 Tool 介面,最重要的屬性有:
name:工具名稱,必須與 Prompt 中的Action名稱對應。description:說明工具的功能,會被放入系統訊息讓模型了解可使用的工具。run(input_str: str) -> str:執行工具的實際邏輯,回傳文字型別的結果。
程式碼範例
以下範例採用 Python(LangChain 官方語言),每段程式碼皆附有說明註解,方便初學者快速上手。
範例 1:最簡單的 ReAct Agent(搜尋與計算)
# 1️⃣ 載入必要套件
from langchain.agents import AgentExecutor, ReActAgent
from langchain.tools import Tool
from langchain.llms import OpenAI
from langchain.prompts import ChatPromptTemplate
# 2️⃣ 定義兩個簡易工具
class SearchTool(Tool):
name = "search"
description = "在網路上搜尋關鍵字,回傳搜尋結果的摘要。"
def _run(self, query: str) -> str:
# 這裡僅示範,實際可接 Google SERP API
return f"搜尋結果:{query} 的相關資訊..."
class CalculatorTool(Tool):
name = "calculator"
description = "計算數學表達式,回傳結果。"
def _run(self, expression: str) -> str:
try:
return str(eval(expression))
except Exception:
return "計算失敗"
# 3️⃣ 建立 Prompt(ReAct 樣板)
prompt = ChatPromptTemplate.from_messages(
[
("system", "你是一個能使用工具的助理。"),
("human", "{input}"),
]
)
# 4️⃣ 建立 LLM 與 Agent
llm = OpenAI(model="gpt-3.5-turbo", temperature=0)
react_agent = ReActAgent.from_llm_and_tools(
llm=llm,
tools=[SearchTool(), CalculatorTool()],
prompt=prompt,
)
# 5️⃣ 執行 Agent
executor = AgentExecutor(agent=react_agent, tools=[SearchTool(), CalculatorTool()], verbose=True)
question = "台北今天的天氣如何?請算出最高溫度減去最低溫度的差值。"
response = executor.invoke({"input": question})
print(response["output"])
重點:
verbose=True可讓你看到 Thought / Action / Observation 的完整迭代過程,方便除錯。eval僅作示範,實務上需使用安全的計算庫(如sympy)。
範例 2:結合資料庫查詢
假設我們有一個 SQLite 資料庫,裡面存放產品資訊。以下示範如何把資料庫查詢封裝為 Tool,讓 ReAct Agent 能根據使用者問題自動查詢。
import sqlite3
from langchain.tools import StructuredTool
# 建立資料庫連線(一次性建立,後續可重用)
conn = sqlite3.connect("products.db")
cursor = conn.cursor()
class ProductLookupTool(StructuredTool):
name = "product_lookup"
description = "根據產品名稱查詢庫存與價格。輸入 JSON,例如 {'name': 'iPhone 15'}"
def _run(self, input_json: str) -> str:
import json
data = json.loads(input_json)
name = data.get("name")
cursor.execute(
"SELECT stock, price FROM products WHERE name = ?", (name,)
)
row = cursor.fetchone()
if row:
stock, price = row
return f"產品 {name} 庫存 {stock} 件,價格 ${price}"
return f"找不到產品 {name}"
# 重新建立 Agent(加入新工具)
react_agent = ReActAgent.from_llm_and_tools(
llm=llm,
tools=[SearchTool(), CalculatorTool(), ProductLookupTool()],
prompt=prompt,
)
executor = AgentExecutor(agent=react_agent, tools=[SearchTool(), CalculatorTool(), ProductLookupTool()], verbose=True)
question = "我想知道 iPhone 15 現在的庫存與價格,還有今天台北的天氣。"
print(executor.invoke({"input": question})["output"])
說明:
StructuredTool允許我們定義 JSON 輸入,讓模型更精確地傳遞參數。- 使用
json.loads解析後再執行 SQL,避免 SQL 注入。
範例 3:多輪對話與記憶(使用 ConversationBufferMemory)
在實務應用中,使用者往往會在同一對話中提出多個相關問題。下面示範如何把 記憶 (Memory) 加入 ReAct Agent,使其能參考前一次的觀察結果。
from langchain.memory import ConversationBufferMemory
# 建立記憶物件
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
# 重新建立 Prompt,加入記憶占位符
prompt = ChatPromptTemplate.from_messages(
[
("system", "你是一個會使用工具的助理,請善用先前的對話記錄。"),
("human", "{chat_history}\nUser: {input}"),
]
)
react_agent = ReActAgent.from_llm_and_tools(
llm=llm,
tools=[SearchTool(), CalculatorTool()],
prompt=prompt,
memory=memory,
)
executor = AgentExecutor(agent=react_agent, tools=[SearchTool(), CalculatorTool()], verbose=True)
# 第一次詢問
print(executor.invoke({"input": "今天紐約的天氣如何?"})["output"])
# 第二次詢問(與第一次相關)
print(executor.invoke({"input": "如果要出門,請告訴我需要帶的衣物。"})["output"])
關鍵:
ConversationBufferMemory會自動把每次的 Human、Assistant、Observation 訊息累積到chat_history,在 Prompt 中提供上下文。- 這樣的設計讓 Agent 能在 多輪對話 中保持一致性與邏輯。
範例 4:自訂 ReAct 思考模板(加入「自我檢查」)
有時候我們希望模型在執行行動前先做一次 自我檢查,確保指令正確。下面示範如何在 Prompt 中加入「Check」區塊。
custom_prompt = ChatPromptTemplate.from_messages(
[
("system", "你是一個能使用工具的助理,請遵循以下流程:\n"
"1. 思考 (Thought)\n"
"2. 檢查 (Check) – 確認是否需要工具\n"
"3. 行動 (Action) – 若需要則呼叫工具\n"
"4. 觀察 (Observation)"),
("human", "{input}"),
]
)
class CheckTool(Tool):
name = "check"
description = "檢查是否需要使用其他工具,回傳 'yes' 或 'no'。"
def _run(self, _: str) -> str:
# 這裡僅示範,實際可根據需求實作
return "yes"
react_agent = ReActAgent.from_llm_and_tools(
llm=llm,
tools=[SearchTool(), CalculatorTool(), CheckTool()],
prompt=custom_prompt,
)
executor = AgentExecutor(agent=react_agent, tools=[SearchTool(), CalculatorTool(), CheckTool()], verbose=True)
print(executor.invoke({"input": "請告訴我今天的匯率,然後把它乘以 100。"})["output"])
技巧:
- 透過自訂 Prompt,我們可以 擴充 ReAct 流程,加入額外的驗證或安全檢查步驟。
- 在實務上,這類「Check」工具常用於 授權驗證、成本控制(如限制 API 呼叫次數)等。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 最佳實踐 |
|---|---|---|
| 工具名稱不一致 | Prompt 中的 Action: 必須與 Tool.name 完全相同,否則模型找不到對應工具。 |
於 測試階段 使用 verbose=True,確認每次 Action: 的字串正確。 |
| 輸入格式不明確 | 若工具需要 JSON、特定語法,模型有時會產生不符合格式的字串。 | 在 Tool.description 中加入 範例,並在 Prompt 加入「請使用以下格式」的指示。 |
| 迴圈無止境 | 當模型無法正確產生 Finish 訊息時,Agent 可能會無限循環。 |
設定 max_iterations(例如 5)或在 Tool.run 中加入 安全檢查。 |
| 安全風險 | 直接使用 eval、SQL 拼接等會產生執行任意程式碼的危險。 |
使用 安全的計算庫、參數化查詢,或在 Tool 前加入 沙箱 檢查。 |
| 記憶體爆炸 | 長時間對話會讓 ConversationBufferMemory 變大,影響效能。 |
定期 截斷(例如保留最近 10 條訊息)或改用 SummarizerMemory。 |
其他實用技巧
- 使用
tool_choice="auto":讓 LLM 自行決定是否需要工具,減少手動判斷的錯誤。 - 加入
tool_error處理:在AgentExecutor中捕捉工具執行失敗,回傳給模型作為新的Observation,促使模型重新思考。 - 觀察日誌:
verbose=True會印出完整的 Thought/Action/Observation 訊息,建議在開發階段開啟,正式上線後可改為寫入日誌檔。
實際應用場景
| 場景 | 為何適合使用 ReAct Agent |
|---|---|
| 客服機器人 | 需要即時查詢訂單、庫存、物流資訊,且要在同一回合內完成多步驗證。 |
| 金融分析助理 | 先從網路抓取最新股價,接著計算技術指標,最後根據結果給出投資建議。 |
| 旅遊規劃 | 先搜尋目的地天氣、景點評價,再計算最佳路線,最終產出行程表。 |
| 醫療問診 | 先根據症狀搜尋相關文獻,再使用計算工具估算藥物劑量,最後回覆患者。 |
| 內部知識庫搜尋 | 先使用向量搜尋找出相關文件,然後根據文件內容做摘要或計算統計。 |
在這些情境下,ReAct 能夠 動態決定何時呼叫外部資源,避免一次性提示的資訊過載,同時保證 答案可追溯(每一步都有 Observation 記錄)。
總結
ReAct Agent 為 LangChain 中最具彈性、最實務導向的智能體模型之一。透過 思考 → 行動 → 觀察 的迴圈,我們可以:
- 把 LLM 的自然語言推理與外部工具結合,形成可執行的工作流程。
- 以 Prompt 模板明確指示模型,降低誤用工具的風險。
- 加入記憶、檢查與安全機制,讓智能體在多輪對話或高風險環境中仍保持可靠。
只要遵守 工具名稱一致、輸入格式明確、迭代次數受控 等最佳實踐,ReAct Agent 就能在客服、金融、旅遊、醫療等多種領域發揮威力。希望本篇教學能幫助你快速上手,並在自己的專案中構建出更聰明、更可靠的自主智能體!祝開發順利 🎉