一句話理解

Agent 讓 LLM 自己決定「要不要呼叫工具、呼叫哪個、用什麼參數」,而不是你寫死流程;適合不確定步驟的複雜任務。


Agent vs Chain

ChainAgent
流程你定義好的固定步驟LLM 自主決定
適合步驟確定的任務步驟不確定的任務
可預測性
彈性

內建工具

from langchain_community.tools import WikipediaQueryRun, DuckDuckGoSearchRun
from langchain_community.utilities import WikipediaAPIWrapper
 
search = DuckDuckGoSearchRun()
wiki = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())
 
# 手動測試工具
print(search.invoke("LangChain 最新版本"))
print(wiki.invoke("Python programming language"))

自訂工具

from langchain_core.tools import tool
 
# 用 @tool decorator 定義
@tool
def get_product_price(product_name: str) -> str:
    """查詢商品價格,輸入商品名稱"""
    # 這裡可以呼叫你的資料庫或 API
    prices = {"iPhone": "NT$ 32,900", "MacBook": "NT$ 39,900"}
    return prices.get(product_name, "找不到此商品")
 
@tool
def calculate_discount(price: float, discount_rate: float) -> str:
    """計算折扣後價格,輸入原價和折扣率(0-1)"""
    final = price * (1 - discount_rate)
    return f"折扣後價格:NT$ {final:.0f}"
 
# 查看工具的 schema(LLM 就是看這個決定怎麼呼叫)
print(get_product_price.name)
print(get_product_price.description)
print(get_product_price.args_schema.schema())

建立 Agent(ReAct)

from langchain_openai import ChatOpenAI
from langchain.agents import create_react_agent, AgentExecutor
from langchain import hub
 
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
tools = [get_product_price, calculate_discount, DuckDuckGoSearchRun()]
 
# 載入 ReAct prompt
prompt = hub.pull("hwchase17/react")
 
# 建立 agent
agent = create_react_agent(llm, tools, prompt)
executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,  # 印出思考過程
    max_iterations=5  # 最多執行 5 步(防止無限迴圈)
)
 
# 執行
result = executor.invoke({"input": "iPhone 打 8 折是多少?"})
print(result["output"])

Tool Calling Agent(更推薦)

from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
 
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一個購物助理,可以查詢商品價格和計算折扣。"),
    MessagesPlaceholder(variable_name="chat_history"),
    ("human", "{input}"),
    MessagesPlaceholder(variable_name="agent_scratchpad"),  # agent 思考過程
])
 
agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
 
result = executor.invoke({
    "input": "幫我查 iPhone 的折扣後價格(打 9 折)",
    "chat_history": []
})

Agent 思考過程(ReAct)

問題:iPhone 打 8 折是多少?

Thought: 我需要先查 iPhone 的價格
Action: get_product_price
Action Input: "iPhone"
Observation: NT$ 32,900

Thought: 現在我有價格了,來計算 8 折
Action: calculate_discount
Action Input: {"price": 32900, "discount_rate": 0.2}
Observation: 折扣後價格:NT$ 26,320

Thought: 我有答案了
Final Answer: iPhone 打 8 折後是 NT$ 26,320

帶記憶的 Agent

from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
 
store = {}
def get_session_history(session_id):
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()
    return store[session_id]
 
agent_with_memory = RunnableWithMessageHistory(
    executor,
    get_session_history,
    input_messages_key="input",
    history_messages_key="chat_history"
)
 
config = {"configurable": {"session_id": "user_001"}}
agent_with_memory.invoke({"input": "我想買 iPhone"}, config=config)
agent_with_memory.invoke({"input": "它折扣後多少錢?"}, config=config) # 記得上文

常見錯誤

錯誤原因解法
Agent 無限迴圈LLM 一直呼叫工具找不到答案max_iterations=5
Tool 參數格式錯docstring 不清楚寫清楚 docstring,加上範例
工具沒被呼叫工具 description 沒說清楚用途改寫 description

相關筆記