📝 Tool Description สำคัญ
เขียน description ให้ชัดเจน เพราะ LLM ใช้ตัดสินใจว่าจะเลือก tool ไหน
Agent คือ AI ที่สามารถ ตัดสินใจ ว่าจะใช้เครื่องมือ (Tools) อะไร เพื่อตอบคำถามหรือทำงานให้สำเร็จ
graph TD
Q["คำถาม"] --> Agent["Agent (LLM)<br/>ฉันควรจะทำอะไรดี?"]
Agent --> Search["Search Tool"]
Agent --> Calc["Calc Tool"]
Agent --> Code["Code Tool"]
Search --> Agent
Calc --> Agent
Code --> Agent
Agent --> Answer["คำตอบ"]
Loop["คิด -> ทำ -> สังเกต -> คิดต่อ"] -.-> Agent
ReAct (Reasoning + Acting) คือหัวใจของ Agent ยุคใหม่ โดย Agent จะวนลูปดังนี้:
sequenceDiagram
participant User
participant Agent
participant Tool
participant LLM
User->>Agent: Query (Ask Question)
loop ReAct Loop
Agent->>LLM: Thought + Action?
LLM->>Agent: Call Tool(X)
Agent->>Tool: Execute Tool(X)
Tool->>Agent: Observation (Result)
Agent->>LLM: Observation
LLM->>Agent: Final Answer or Next Action
end
Agent->>User: Final Answer
คำถาม: "สภาพอากาศกรุงเทพวันนี้เป็นยังไง?"
Thought: ฉันต้องค้นหาข้อมูลสภาพอากาศกรุงเทพAction: search("สภาพอากาศกรุงเทพวันนี้")Observation: อุณหภูมิ 33°C, มีเมฆบ้าง, ความชื้น 65%
Thought: ได้ข้อมูลแล้ว สรุปให้ผู้ใช้Final Answer: วันนี้กรุงเทพอุณหภูมิ 33°C มีเมฆบ้าง ความชื้น 65%คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน
# Searchfrom langchain_community.tools import DuckDuckGoSearchRunsearch = DuckDuckGoSearchRun()
# Wikipediafrom langchain_community.tools import WikipediaQueryRunfrom langchain_community.utilities import WikipediaAPIWrapperwikipedia = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper(lang="th"))
# Python REPL (รันโค้ด Python)from langchain_community.tools import PythonREPLToolpython_repl = PythonREPLTool()คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน
from langchain_core.tools import toolfrom datetime import datetime
@tooldef get_current_time() -> str: """ดึงวันที่และเวลาปัจจุบัน""" now = datetime.now() return now.strftime("%Y-%m-%d %H:%M:%S")
@tooldef calculate_bmi(weight_kg: float, height_cm: float) -> str: """คำนวณค่า BMI จากน้ำหนัก (กก.) และส่วนสูง (ซม.)
Args: weight_kg: น้ำหนักเป็นกิโลกรัม height_cm: ส่วนสูงเป็นเซนติเมตร """ height_m = height_cm / 100 bmi = weight_kg / (height_m ** 2)
if bmi < 18.5: category = "น้ำหนักน้อย" elif bmi < 25: category = "ปกติ" elif bmi < 30: category = "น้ำหนักเกิน" else: category = "อ้วน"
return f"BMI = {bmi:.1f} ({category})"
# ทดสอบprint(get_current_time.invoke({}))print(calculate_bmi.invoke({"weight_kg": 70, "height_cm": 175}))คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน
from langchain_core.tools import StructuredToolfrom pydantic import BaseModel, Field
class WeatherInput(BaseModel): city: str = Field(description="ชื่อเมือง") unit: str = Field(default="celsius", description="หน่วยอุณหภูมิ: celsius หรือ fahrenheit")
def get_weather(city: str, unit: str = "celsius") -> str: """ดึงข้อมูลสภาพอากาศของเมือง""" # ในแอพจริงจะเรียก Weather API return f"สภาพอากาศที่ {city}: 32°C, แดดจัด"
weather_tool = StructuredTool.from_function( func=get_weather, name="get_weather", description="ดึงข้อมูลสภาพอากาศของเมือง", args_schema=WeatherInput,)วิธีมาตรฐานใน LangChain v1.0+ — ใช้ความสามารถ function calling ของ LLM:
from langchain_openai import ChatOpenAIfrom langchain_core.tools import tool
@tooldef add(a: int, b: int) -> int: """บวกเลข 2 จำนวน""" return a + b
@tooldef multiply(a: int, b: int) -> int: """คูณเลข 2 จำนวน""" return a * b
# Bind tools กับ LLMllm = ChatOpenAI(model="gpt-4o-mini")llm_with_tools = llm.bind_tools([add, multiply])
# LLM จะ "เลือก" tool ที่เหมาะสมresponse = llm_with_tools.invoke("3 + 5 เท่าไหร่?")print(response.tool_calls)# [{'name': 'add', 'args': {'a': 3, 'b': 5}}]
# เรียก tool ตามที่ LLM เลือกfor tool_call in response.tool_calls: if tool_call["name"] == "add": result = add.invoke(tool_call["args"]) print(f"ผลลัพธ์: {result}") # 8คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน
from langchain_openai import ChatOpenAIfrom langchain_core.tools import toolfrom langchain.agents import create_agentimport astimport operator as op
@tooldef search_web(query: str) -> str: """ค้นหาข้อมูลจากอินเทอร์เน็ต""" from langchain_community.tools import DuckDuckGoSearchRun return DuckDuckGoSearchRun().invoke(query)
_ALLOWED_OPS = { ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul, ast.Div: op.truediv, ast.Pow: op.pow, ast.USub: op.neg,}
def safe_eval(expression: str) -> float: """ประเมินนิพจน์คณิตศาสตร์พื้นฐานอย่างปลอดภัย""" def _eval(node): if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)): return node.value if isinstance(node, ast.BinOp) and type(node.op) in _ALLOWED_OPS: return _ALLOWED_OPS[type(node.op)](_eval(node.left), _eval(node.right)) if isinstance(node, ast.UnaryOp) and type(node.op) in _ALLOWED_OPS: return _ALLOWED_OPS[type(node.op)](_eval(node.operand)) raise ValueError("รองรับเฉพาะ + - * / ** และตัวเลขเท่านั้น")
tree = ast.parse(expression, mode="eval") return float(_eval(tree.body))
@tooldef calculate(expression: str) -> str: """คำนวณนิพจน์ทางคณิตศาสตร์""" try: return str(safe_eval(expression)) except Exception as e: return f"Error: {e}"
# สร้าง Agentllm = ChatOpenAI(model="gpt-4o-mini")tools = [search_web, calculate]
agent = create_agent( model=llm, tools=tools, system_prompt="คุณเป็นผู้ช่วยอัจฉริยะ ตอบเป็นภาษาไทย ใช้เครื่องมือเมื่อจำเป็น",)
# ใช้งานresult = agent.invoke({ "messages": [("human", "ราคา Bitcoin วันนี้เท่าไหร่? แปลงเป็นบาทให้หน่อย")]})
# แสดงผลลัพธ์for message in result["messages"]: print(f"{message.type}: {message.content}")คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน
from langchain_openai import ChatOpenAIfrom langchain_core.tools import toolfrom langchain.agents import create_agentfrom datetime import datetimeimport jsonimport astimport operator as op
@tooldef get_datetime() -> str: """ดึงวันที่และเวลาปัจจุบัน""" return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@tooldef search_knowledge(query: str) -> str: """ค้นหาข้อมูลจากฐานความรู้""" # จำลองการค้นหา (ในแอพจริงจะเชื่อม RAG) knowledge = { "python": "Python เป็นภาษาโปรแกรมยอดนิยม สร้างโดย Guido van Rossum", "langchain": "LangChain เป็น framework สำหรับสร้าง LLM application", } for key, value in knowledge.items(): if key in query.lower(): return value return "ไม่พบข้อมูล"
@tooldef create_todo(task: str, priority: str = "medium") -> str: """สร้าง todo item
Args: task: รายละเอียดงาน priority: ระดับความสำคัญ (high/medium/low) """ todo = {"task": task, "priority": priority, "status": "pending"} return f"✅ สร้าง todo สำเร็จ: {json.dumps(todo, ensure_ascii=False)}"
@tooldef calculate_math(expression: str) -> str: """คำนวณนิพจน์คณิตศาสตร์
Args: expression: นิพจน์คณิตศาสตร์ เช่น "2 + 3 * 4" """ try: result = safe_eval(expression) return f"ผลลัพธ์: {expression} = {result}" except Exception as e: return f"ไม่สามารถคำนวณได้: {e}"
# ใช้ safe_eval จากตัวอย่างก่อนหน้า_ALLOWED_OPS = { ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul, ast.Div: op.truediv, ast.Pow: op.pow, ast.USub: op.neg,}
def safe_eval(expression: str) -> float: def _eval(node): if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)): return node.value if isinstance(node, ast.BinOp) and type(node.op) in _ALLOWED_OPS: return _ALLOWED_OPS[type(node.op)](_eval(node.left), _eval(node.right)) if isinstance(node, ast.UnaryOp) and type(node.op) in _ALLOWED_OPS: return _ALLOWED_OPS[type(node.op)](_eval(node.operand)) raise ValueError("รองรับเฉพาะ + - * / ** และตัวเลขเท่านั้น")
tree = ast.parse(expression, mode="eval") return float(_eval(tree.body))
# สร้าง Agentagent = create_agent( model=ChatOpenAI(model="gpt-4o-mini"), tools=[get_datetime, search_knowledge, create_todo, calculate_math], system_prompt="""คุณเป็น AI Assistant ที่ชาญฉลาด - ตอบเป็นภาษาไทย - ใช้เครื่องมือที่มีอยู่เมื่อต้องการข้อมูลหรือทำงาน - อธิบายขั้นตอนการทำงานของคุณ""",)
# ทดสอบresult = agent.invoke({ "messages": [("human", "ช่วยสร้าง todo สำหรับเรียน LangChain priority high แล้วก็บอกวันที่วันนี้ด้วย")]})รวม Agent กับ RAG เพื่อสร้าง Agent ที่ค้นหาเอกสารได้:
from langchain_core.tools import toolfrom langchain_community.vectorstores import Chromafrom langchain_openai import OpenAIEmbeddings
# สร้าง RAG tool@tooldef search_documents(query: str) -> str: """ค้นหาข้อมูลจากเอกสารของบริษัท""" vectorstore = Chroma( persist_directory="./chroma_db", embedding_function=OpenAIEmbeddings() ) docs = vectorstore.similarity_search(query, k=3) return "\n\n".join(doc.page_content for doc in docs)
@tooldef search_web(query: str) -> str: """ค้นหาข้อมูลจากอินเทอร์เน็ต (ใช้เมื่อไม่พบในเอกสาร)""" from langchain_community.tools import DuckDuckGoSearchRun return DuckDuckGoSearchRun().invoke(query)
# Agent ที่ค้นเอกสารก่อน แล้วค่อยค้นเว็บagent = create_agent( model=ChatOpenAI(model="gpt-4o-mini"), tools=[search_documents, search_web], system_prompt="""คุณเป็นผู้ช่วยของบริษัท - ค้นหาจากเอกสารบริษัทก่อน - ถ้าไม่พบ ค่อยค้นจากอินเทอร์เน็ต - อ้างอิงแหล่งข้อมูลเสมอ""",)📝 Tool Description สำคัญ
เขียน description ให้ชัดเจน เพราะ LLM ใช้ตัดสินใจว่าจะเลือก tool ไหน
🛡️ Validate Input
ตรวจสอบ input ก่อนรัน tool ป้องกัน injection attacks
⚡ จำกัด Tools
ไม่ควรมีมากเกินไป (5-10) เพราะ LLM อาจสับสนได้
🔄 Error Handling
จัดการ errors ใน tool ให้ดี return error message แทนที่จะ crash