本文 AI 產出,尚未審核

LangChain

單元:Agents — 自主智能體

主題:自訂 Agent 行為與策略


簡介

LLM(大型語言模型) 的應用中,單純的「問答」或「文字生成」已無法滿足日益複雜的商業需求。
Agents(智能體)則提供了一個 「思考 + 行動」 的框架,讓模型可以根據環境資訊自行決策、呼叫外部工具、甚至調整自己的推理策略。

LangChain 作為目前最成熟的 LLM 應用開發套件,已內建多種 Agent 形態(Zero‑Shot、ReAct、Structured、Planner 等),並支援 自訂 Prompt、Tool、以及決策策略。掌握如何 客製化 Agent 的行為,不僅能提升系統的可靠度與可解釋性,還能讓開發者在同一個程式碼基礎上快速切換不同任務(客服、資料分析、流程自動化…),大幅降低開發與維運成本。

本篇文章將從 核心概念 切入,示範 3~5 個實用程式碼範例,說明如何在 LangChain 中打造符合業務需求的自訂 Agent,並分享常見的陷阱與最佳實踐,最後列出幾個典型的 實際應用場景,幫助初學者到中階開發者快速上手並落地。


核心概念

1. Agent 的基本構成

元件 說明
LLM 文字生成的核心模型(如 OpenAI gpt-4o、Anthropic claude-3)。
Tool Agent 可以呼叫的外部功能,如搜尋 API、資料庫查詢、或自訂函式。
Prompt 引導 LLM 思考的文字模板,決定模型的行為與回應格式。
Output Parser 解析 LLM 回傳的文字,將指令或結果轉成結構化資料(JSON、Python dict 等)。
AgentExecutor 負責「迭代」:將 Prompt → LLM → Parser → Tool → 迴圈,直到達成目標。

重點:Agent 的「智慧」來自 Prompt + Tool 的組合,透過迭代回合(loop)讓模型在每一步都能根據最新的資訊調整策略。


2. 自訂 Prompt:從 Zero‑Shot 到 ReAct

  • Zero‑Shot Agent:直接把使用者問題交給 LLM,讓模型自行決定是否需要工具。適合簡單查詢或對話,但缺乏「思考過程」的可追蹤性。
  • ReAct Agent(Reason+Act):在 Prompt 中加入「思考 → 行動」的格式,LLM 必須先產出思考文字,再給出要呼叫的工具。這樣的設計讓 推理過程透明,也方便除錯。

範例 Prompt(ReAct)

You are an AI assistant equipped with the following tools:
{tool_names}
When you receive a user query, follow these steps:
1. Think step-by-step about what information you need.
2. If a tool can help, output "Action: <tool_name>" and the necessary arguments in JSON.
3. After the tool returns a result, incorporate it and continue reasoning until you can answer the user.

技巧:在 Prompt 中加入 「工具清單」 ({tool_names}) 以及 「思考步驟」 的指示,可大幅提升模型的正確率與可解釋性。


3. 設計自訂 Tool

LangChain 的 Tool 只是一個 Python callable(或 JavaScript function)加上一段說明文字。
自訂 Tool 時,建議遵守以下原則:

  1. 單一職責:每個 Tool 只負責一件事(例如「搜尋新聞」或「計算稅額」),避免過度耦合。
  2. 清晰的參數介面:使用 JSON schema 描述輸入,讓 LLM 能夠正確產生參數。
  3. 錯誤處理:工具需要回傳可讀的錯誤訊息,Agent 才能在迴圈中重新嘗試或改變策略。

4. 策略:Zero‑Shot、ReAct、Structured、Planner

策略 特色 適用情境
Zero‑Shot 無需額外指示,直接由 LLM 判斷是否使用工具。 簡單問答、快速原型。
ReAct 明確分離 思考行動,回傳 Action:Action Input: 需要多步推理、需追蹤決策過程。
Structured LLM 必須回傳符合 JSON schema 的結構化結果。 資料抽取、表格生成、需要嚴格驗證的任務。
Planner 先產生「計畫」(sub‑tasks),再依序執行子 Agent。 複雜工作流程、長程任務(如旅行規劃、專案管理)。

5. 程式碼範例

以下範例均以 Python 為主(LangChain 主要語言),若您使用 JavaScript/TypeScript,只需要把 ToolAgentExecutor 換成相應的 langchainjs 實作即可。

5.1 建立最簡單的 Zero‑Shot Agent

from langchain.llms import OpenAI
from langchain.agents import initialize_agent, AgentType, Tool

# 1️⃣ 建立 LLM(此處使用 OpenAI 的 gpt-4o)
llm = OpenAI(model_name="gpt-4o", temperature=0)

# 2️⃣ 定義一個簡單的搜尋工具
def simple_search(query: str) -> str:
    """使用 DuckDuckGo 搜尋關鍵字,回傳前 3 個結果摘要。"""
    # 這裡僅示意,實務上請使用官方 API
    return f"搜尋結果(模擬): {query[:20]}..."

search_tool = Tool(
    name="SimpleSearch",
    func=simple_search,
    description="在網路上搜尋資訊,適合回答需要即時資料的問題。"
)

# 3️⃣ 初始化 Zero‑Shot Agent
agent = initialize_agent(
    tools=[search_tool],
    llm=llm,
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True,          # 讓執行過程印出 log,方便除錯
)

# 4️⃣ 測試
response = agent.run("請告訴我今天台北的天氣與最近的新聞頭條")
print("\n=== 最終回覆 ===")
print(response)

說明:此範例展示了最基本的 Zero‑Shot + ReAct 代理人,LangChain 會自動判斷何時呼叫 SimpleSearch,並把搜尋結果帶回模型繼續推理。


5.2 使用 ReAct Prompt 並自訂工具:計算稅額

from langchain.llms import OpenAI
from langchain.agents import AgentExecutor, ReActAgent, Tool
import json

# 1️⃣ LLM
llm = OpenAI(model_name="gpt-4o-mini", temperature=0)

# 2️⃣ 自訂稅額計算工具
def tax_calculator(income: str) -> str:
    """
    計算個人所得稅(台灣簡易版)。
    輸入: JSON 字串,例如 {"income": 800000}
    回傳: 稅額文字描述
    """
    data = json.loads(income)
    inc = data["income"]
    # 簡化稅率:10% 以下免稅,10%~30% 按 5% 稅率
    tax = 0 if inc <= 540000 else (inc - 540000) * 0.05
    return f"應納稅額為 {tax:,.0f} 元"

tax_tool = Tool(
    name="TaxCalculator",
    func=tax_calculator,
    description="計算個人所得稅,接受 JSON 輸入 {\"income\": number},回傳稅額文字。"
)

# 3️⃣ 建立 ReAct Agent(手動指定 Prompt)
react_prompt = """
You are a financial assistant equipped with the following tool:
{tool_names}
When answering a user query, follow these steps:
1. Think step‑by‑step about the required data.
2. If you need to calculate tax, output:
   Action: TaxCalculator
   Action Input: <JSON string>
3. Incorporate the tool result and give the final answer.
"""

agent = ReActAgent.from_llm_and_tools(
    llm=llm,
    tools=[tax_tool],
    prompt_template=react_prompt,
    verbose=True,
)

executor = AgentExecutor(agent=agent, tools=[tax_tool], verbose=True)

# 4️⃣ 測試
question = "我去年收入 85 萬,請幫我算出應付的所得稅。"
print("❓ 使用者問題:", question)
result = executor.run(question)
print("\n✅ 最終回覆:", result)

重點

  • Tool 參數採 JSON,讓 LLM 能夠精確產生 Action Input
  • 透過自訂 Prompt,我們明確告訴模型「什麼情況下要呼叫 TaxCalculator」,提升正確率。

5.3 Structured Agent:產生 JSON 格式的產品比較

from langchain.llms import OpenAI
from langchain.agents import initialize_agent, AgentType, Tool
from langchain.output_parsers import JsonOutputParser
import json

llm = OpenAI(model_name="gpt-4o", temperature=0)

# 1️⃣ 工具:取得商品資訊(模擬)
def get_product_info(product: str) -> str:
    """回傳商品的主要規格與價格(模擬)。"""
    dummy = {
        "iPhone 15": {"price": 29900, "camera": "48MP", "battery": "3279mAh"},
        "Samsung S24": {"price": 28900, "camera": "50MP", "battery": "3700mAh"},
    }
    return json.dumps(dummy.get(product, {}))

product_tool = Tool(
    name="GetProductInfo",
    func=get_product_info,
    description="取得指定手機的規格與價格,回傳 JSON 字串。"
)

# 2️⃣ 設定 Structured Prompt + JSON parser
schema = {
    "type": "object",
    "properties": {
        "product_a": {"type": "string"},
        "product_b": {"type": "string"},
        "comparison": {
            "type": "object",
            "properties": {
                "price_diff": {"type": "string"},
                "camera_advantage": {"type": "string"},
                "battery_advantage": {"type": "string"},
            },
            "required": ["price_diff", "camera_advantage", "battery_advantage"],
        },
    },
    "required": ["product_a", "product_b", "comparison"],
}
parser = JsonOutputParser(pydantic_object=schema)

structured_prompt = f"""
You are an e‑commerce analyst.  
You have access to a tool called GetProductInfo that returns product specs in JSON.  

Given two product names, produce a JSON object that follows this schema:
{json.dumps(schema, ensure_ascii=False, indent=2)}

Steps:
1. Use GetProductInfo to fetch specs for each product.
2. Compute price difference, camera, and battery advantages.
3. Output ONLY the JSON object (no additional text).
"""

agent = initialize_agent(
    tools=[product_tool],
    llm=llm,
    agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
    output_parser=parser,
    prompt=structured_prompt,
    verbose=True,
)

# 3️⃣ 測試
question = "請比較 iPhone 15 與 Samsung S24 的價格、相機與電池優勢。"
result = agent.run(question)
print("\n📊 比較結果 JSON:")
print(result)

說明

  • 使用 JsonOutputParser 強制 LLM 必須回傳符合 schema 的 JSON,適合後續自動化流程(如寫入資料庫或產生報表)。
  • 透過 Structured Agent,模型在每一步都以「工具 → 產出 → 解析」的方式運作,錯誤率大幅降低。

5.4 Planner Agent:多階段任務(旅行行程規劃)

from langchain.llms import OpenAI
from langchain.agents import initialize_agent, AgentType, Tool, PlannerAgent, AgentExecutor
import json

llm = OpenAI(model_name="gpt-4o", temperature=0)

# 工具 1:搜尋景點
def search_attractions(city: str) -> str:
    """回傳指定城市的前 5 大旅遊景點(模擬)。"""
    dummy = {
        "東京": ["淺草寺", "東京塔", "上野公園", "迪士尼樂園", "築地市場"],
        "台北": ["101 大樓", "士林夜市", "淡水老街", "陽明山", "國立故宮博物院"],
    }
    return json.dumps(dummy.get(city, []))

search_tool = Tool(
    name="SearchAttractions",
    func=search_attractions,
    description="取得城市的旅遊景點清單,回傳 JSON 陣列。"
)

# 工具 2:計算交通時間(簡化版)
def travel_time(origin: str, destination: str) -> str:
    """回傳兩地之間的估計交通時間(分鐘),模擬固定 30 分鐘。」
    return "30"

travel_tool = Tool(
    name="TravelTime",
    func=travel_time,
    description="計算兩個景點之間的交通時間(分鐘),回傳文字數字。"
)

# 建立 Planner Agent(會先產生子任務清單)
planner = PlannerAgent.from_llm_and_tools(
    llm=llm,
    tools=[search_tool, travel_tool],
    verbose=True,
)

executor = AgentExecutor(agent=planner, tools=[search_tool, travel_tool], verbose=True)

# 測試:規劃 2 天東京行程
question = "幫我安排兩天的東京旅遊行程,要求每天至少三個景點,並考慮交通時間不超過 1 小時。"
result = executor.run(question)
print("\n🗓️ 行程規劃結果:")
print(result)

要點

  • Planner Agent 會先產出「子任務清單」(例如「搜尋景點 → 計算交通」),再分別交給子 Agent 處理,適合 長程、多人協作 的情境。
  • 只要提供足夠的工具,Planner 就能自動拆解問題,減少開發者手動編寫流程的工作量。

常見陷阱與最佳實踐

陷阱 說明 解決方案 / 最佳實踐
Prompt 過長或資訊過載 把所有工具說明一次塞進 Prompt,模型可能忽略關鍵指示。 分段說明:先列出工具清單,後續再用 {{tool_names}} 動態插入。
工具參數格式不一致 LLM 產生的 JSON 缺少引號或多餘逗號,導致解析失敗。 使用 JsonOutputParser 或自行驗證 json.loads,在 Tool 內部捕捉 JSONDecodeError
迴圈無止境 Agent 產生的 Action 永遠不會滿足終止條件,造成無限回合。 設定 max_iterations(如 AgentExecutor(..., max_iterations=5)),並在 Prompt 明確說明「當你有答案時直接回覆」。
工具回傳速度太慢 異步呼叫未使用 async,導致整體回應時間過長。 若使用 langchainjs 或 Python 3.11+,把 Tool 定義為 async function,配合 AsyncAgentExecutor
安全性與隱私 直接把使用者輸入傳給外部 API,可能洩漏個資。 在 Tool 前加入 資料過濾與脫敏,只傳遞必要欄位。
模型成本失控 ReAct 需要多輪呼叫 LLM,成本會比一次性回答高。 根據任務複雜度選擇合適的 模型與溫度,或在 AgentExecutor 中使用 early_stopping_method="generate"

最佳實踐清單

  1. 先寫測試案例:用 pytest 撰寫每個 Tool 的輸入/輸出測試,確保 Agent 在迴圈中不會因工具失敗崩潰。
  2. 使用 verbose=True 觀察每一步的 Prompt、LLM 回覆與 Action,幫助除錯。
  3. 把 Prompt 抽成檔案.prompt),讓團隊可共用、版本控制。
  4. 分層設計:先建立 基礎 Zero‑Shot Agent,再逐步加上 ReActStructuredPlanner,避免一次性過度複雜。
  5. 監控成本與 latency:在生產環境加入 OpenTelemetryPrometheus,即時觀測每次呼叫的 token 數與耗時。

實際應用場景

場景 需求 建議的 Agent 策略
客服機器人 必須即時查詢訂單、退貨政策,且要把推理過程記錄下來供管理者審核。 ReAct + Structured:使用 OrderLookupPolicySearch 工具,最終回傳符合 JSON schema 的回覆,方便日後分析。
金融風控 需要根據多個資料源(交易紀錄、信用分數)計算風險分數,且必須符合合規的審計追蹤。 Planner + Structured:先產生「取得交易 → 取得信用 → 計算分數」子任務,每一步都使用工具並回傳 JSON,整體流程可被審計。
內容產出(部落格/行銷文案) 結合即時新聞與品牌資料,產出符合 SEO 要求的長文。 Zero‑Shot + ReAct:先用 NewsSearch 抓取最新資訊,再用 BrandInfo 取得品牌聲音,最後一次性生成文章。
企業內部報表自動化 使用者只說「給我上個月的銷售 KPI」即可得到圖表與數據。 Structured Agent:工具 SQLQuery 回傳資料,PlotGenerator 產生圖表,最終回傳符合報表 schema 的 JSON。
旅遊行程規劃平台 需要根據使用者的時間、預算、偏好自動產生多日行程。 Planner + ReAct:分解為「搜尋景點 → 計算交通 → 排程」三階段,每階段使用相應工具,最終回傳行程表。

總結

Agents 為 LLM 帶來 「思考 + 行動」 的新能力,使其能在 動態環境 中完成多步推理、資料抓取與決策。透過 LangChain,我們可以:

  1. 快速組合 LLM、Prompt、Tool,形成可迭代的智能體。
  2. 自訂 Prompt 讓模型清楚知道何時、如何呼叫工具,提升可解釋性。
  3. 使用不同的策略(Zero‑Shot、ReAct、Structured、Planner)對應不同複雜度與合規需求。
  4. 以 JSON Schema 為橋樑,把 LLM 的文字輸出轉成機器可直接使用的結構化資料。
  5. 遵循最佳實踐(測試、除錯、成本監控)避免常見陷阱,確保系統在生產環境的穩定與安全。

掌握上述概念與範例後,你就能在 客服、金融、內容生成、報表自動化、旅遊規劃 等多種實務場景中,快速構建 高效、可維護且具備可追蹤性的 AI 智能體。未來隨著模型與工具的持續進化,Agent 的應用範圍只會更廣,現在就從這些基礎開始實作吧!