فراخوانی ابزار و حلقههای عامل
توسعه عاملهای هوشمند (Agentic Ai) با Langchain و Langgraph
- Bind tools to a model and inspect generated tool calls.
- Execute tool calls and append tool results as messages.
- Implement the model-tool loop until a final response is produced.
چرا حلقه ابزار؟
**از مدل زبانی تا عامل**
- در 2.2 یاد گرفتیم: `model.invoke(messages)` یک پاسخ میسازد.
- در 6.1 یاد گرفتیم: `tool schema` ابزار و ورودی معتبر آن را مشخص میکند.
- در این درس: مدل تصمیم میگیرد → ابزار اجرا میشود → نتیجه به مدل برمیگردد.
```mermaid
flowchart LR
A["درخواست کاربر"] --> B["مدل با ابزارها"]
B -->|"پاسخ نهایی"| E["پاسخ به کاربر"]
B -->|"tool call"| C["اجرای ابزار"]
C --> D["tool result"]
D --> B
```
**Builds on ← Invoking Chat Models with LangChain:** پیامها ورودی و خروجی مدلاند.
**Where this leads → Tool Safety and Human Approval:** قبل از بعضی فراخوانیها حلقه متوقف و تأیید میشود.
**Where this leads → Agent Graph with ToolNode:** همین چرخه به گرههای مدل و ابزار تبدیل میشود.
فراخوانی را با اجرا قاطی نکن
**تعریف دقیق**
- `tool_call`: پیام ساختاریافته مدل برای درخواست اجرای ابزار.
- `tool_result`: نتیجه اجرای واقعی ابزار، با همان شناسه فراخوانی.
- `tool_call` هنوز عمل انجامشده نیست.
```mermaid
flowchart TD
A["assistant message"] --> B{"tool_calls دارد؟"}
B -->|"بله"| C["نام و آرگومانها را بررسی کن"]
C --> D["ابزار واقعی را اجرا کن"]
D --> E["ToolMessage با tool_call_id"]
B -->|"خیر"| F["پاسخ نهایی"]
```
**نمونه با calculator**
داده مدل:
\[
name=calculator,\quad args=\{a:3,b:4,operation:add\},\quad id=call_1
\]
اجرای برنامه:
\[
3+4=7
\]
پیام نتیجه:
\[
role=tool,\quad tool\_call\_id=call_1,\quad content=7
\]
**چرا شناسه مهم است؟** نتیجه باید دقیقاً به همان درخواست وصل شود.
اتصال ابزار و بازرسی
**Bind کردن ابزار به مدل**
```python
from langchain_core.tools import tool
@tool
def calculator(a: int, b: int) -> int:
return a + b
llm_with_tools = llm.bind_tools([calculator])
```
- `bind_tools`، schema ابزار را همراه مدل میفرستد.
- مدل میتواند پیشنهاد فراخوانی بدهد؛ برنامه هنوز مجری است.
**مثال کاملِ بازرسی**
```python
messages = [HumanMessage(content="Add 3 and 4.")]
response = llm_with_tools.invoke(messages)
```
```python
response.tool_calls
# [{"name": "calculator",
# "args": {"a": 3, "b": 4},
# "id": "call_1", "type": "tool_call"}]
```
**چکهای لازم پیش از اجرا**
1. نام ابزار در `tools_by_name` وجود دارد.
2. آرگومانها با schema سازگارند.
3. `id` ذخیره میشود.
**Builds on ← Tool Design and Tool Schemas:** schema ورودی معتبر را تعیین میکند.
یک چرخه کامل با عدد
**مسئله:** «۳ و ۴ را جمع کن.»
**گام ۱ — پیام اولیه**
```python
messages = [HumanMessage(content="Add 3 and 4.")]
```
**گام ۲ — مدل ابزار میخواهد**
```python
llm_response = llm_with_tools.invoke(messages)
# tool_call: calculator(a=3, b=4), id=call_1
```
**گام ۳ — اجرای واقعی، خطبهخط**
```python
tool_call = llm_response.tool_calls[0]
name = tool_call["name"] # "calculator"
args = tool_call["args"] # {"a": 3, "b": 4}
tool = tools_by_name[name]
observation = tool.invoke(args) # 3 + 4 = 7
```
**گام ۴ — ساخت پیام نتیجه**
```python
tool_message = ToolMessage(
content="7",
tool_call_id="call_1"
)
messages = add_messages(
messages,
[llm_response, tool_message]
)
```
**گام ۵ — مدل دوباره میخواند**
```python
llm_response = llm_with_tools.invoke(messages)
# content = "The answer is 7."
# tool_calls = []
```
```mermaid
flowchart LR
A["HumanMessage: Add 3 and 4"] --> B["LLM: call_1"]
B --> C["calculator: 3 + 4"]
C --> D["ToolMessage: 7"]
D --> E["LLM: پاسخ نهایی 7"]
```
حلقه تا پاسخ نهایی
**الگوی عمومی**
```python
messages = [HumanMessage(content=user_text)]
llm_response = llm_with_tools.invoke(messages)
while True:
if not llm_response.tool_calls:
break
tool_results = []
for tool_call in llm_response.tool_calls:
name = tool_call["name"]
if name not in tools_by_name:
raise ValueError(f"Unknown tool: {name}")
tool = tools_by_name[name]
result = tool.invoke(tool_call["args"])
tool_results.append(
ToolMessage(
content=str(result),
tool_call_id=tool_call["id"]
)
)
messages = add_messages(
messages,
[llm_response, *tool_results]
)
llm_response = llm_with_tools.invoke(messages)
messages = add_messages(messages, llm_response)
final_answer = llm_response.content
```
**مثال چندمرحلهای**
- فراخوانی اول: `search(query="temperature")` → `20°C`
- فراخوانی دوم: `calculator(a=20, b=5, operation=add)` → `25`
- سپس: `tool_calls = []` → پاسخ نهایی: `25°C`
**شرط پایان**
\[
\texttt{last\_message.tool\_calls}=[]
\quad\Longrightarrow\quad
\text{پاسخ نهایی}
\]
- یک tool call ممکن است به tool call بعدی منجر شود.
- برای تولید واقعی، خطای ابزار، آرگومان نامعتبر و سقف تکرار را کنترل کنید.
**Where this leads → Agent Graph with ToolNode:** `llm_call → tool_node → should_continue` همین حلقه را به گراف اجرایی تبدیل میکند.
بازگشت به دوره