Vector Database — مغز جستجوی RAG

قسمت ۴ ۲۰ دقیقه

مقدمه: AI بدون دست و پا

تصور کن یه آدم خیلی باهوش رو توی یه اتاق بذاری که فقط یه روزنه کوچیک داره. از اون روزنه بهش سوال می‌دی، جواب می‌ده. ولی نمی‌تونه از اتاق بیاد بیرون، نمی‌تونه چیزی رو لمس کنه، نمی‌تونه اینترنت رو ببینه. این تقریباً وضعیت یه LLM بدون Tool Use ه.

حالا تصور کن در اتاق رو باز کنی و بهش بگی: “اینا ابزارهاته — ماشین حساب، مرورگر وب، ایمیل، دیتابیس. هر وقت لازم شد استفاده کن.” این می‌شه Tool Use.

توی این اپیزود یاد می‌گیری Tool Use چیه، چطور کار می‌کنه، و چطور خودت ابزار بسازی و به Agent بدی.

Tool Use چیه؟

Tool Use (یا Function Calling) یه مکانیزمه که بهت اجازه می‌ده ابزارهایی رو تعریف کنی و به LLM بدی. وقتی LLM تشخیص بده که برای جواب دادن به یه سوال نیاز به ابزار داره، بهت می‌گه “من می‌خوام این ابزار رو با این پارامترها صدا بزنم”. تو ابزار رو اجرا می‌کنی، نتیجه رو برمی‌گردونی، و LLM از اون نتیجه استفاده می‌کنه.

نکته مهم: LLM خودش ابزار رو اجرا نمی‌کنه. فقط تصمیم می‌گیره کدوم ابزار رو صدا بزنه و با چه پارامترهایی. اجرای واقعی ابزار وظیفه کد توئه.

چرخه Tool Use

چرخه Tool Use معمولاً اینطوری کار می‌کنه:

  1. کاربر یه سوال می‌پرسه
  2. LLM تشخیص می‌ده که برای جواب دادن به ابزار نیاز داره
  3. LLM یه “درخواست ابزار” برمی‌گردونه (اسم ابزار + پارامترها)
  4. کد تو ابزار رو اجرا می‌کنه
  5. نتیجه ابزار به LLM برگردونده می‌شه
  6. LLM از نتیجه استفاده می‌کنه و جواب نهایی رو می‌ده
# شبه‌کد چرخه Tool Use

user_asks("هوای تهران الان چطوره؟")

# LLM تصمیم می‌گیره:
# → باید از ابزار get_weather استفاده کنم
# → پارامتر: city = "Tehran"

result = get_weather(city="Tehran")
# → {"temp": 28, "condition": "آفتابی"}

# LLM جواب نهایی:
# "هوای تهران الان ۲۸ درجه و آفتابیه 🌞"

تعریف ابزار با JSON Schema

برای اینکه LLM بفهمه چه ابزارهایی در دسترسشه، باید ابزارها رو با یه فرمت مشخص تعریف کنی. این فرمت معمولاً JSON Schema ه:

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "دریافت وضعیت آب و هوای فعلی یک شهر",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "نام شهر به انگلیسی، مثلاً Tehran"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "واحد دما"
                    }
                },
                "required": ["city"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "calculate",
            "description": "محاسبه یک عبارت ریاضی",
            "parameters": {
                "type": "object",
                "properties": {
                    "expression": {
                        "type": "string",
                        "description": "عبارت ریاضی، مثلاً 2+2 یا sqrt(16)"
                    }
                },
                "required": ["expression"]
            }
        }
    }
]
نکته مهم: توضیحات (description) ابزارها خیلی مهمه! LLM از روی همین توضیحات تصمیم می‌گیره کدوم ابزار رو استفاده کنه. هر چی توضیحات واضح‌تر باشه، LLM بهتر تصمیم می‌گیره.

پیاده‌سازی عملی با OpenAI API

بیا یه مثال کامل بزنیم. یه Agent ساده می‌سازیم که هم آب و هوا رو چک می‌کنه و هم محاسبات انجام می‌ده:

import json
from openai import OpenAI

client = OpenAI()

# تعریف ابزارها
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get current weather for a city",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "City name in English"
                    }
                },
                "required": ["city"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "calculate",
            "description": "Calculate a math expression",
            "parameters": {
                "type": "object",
                "properties": {
                    "expression": {
                        "type": "string",
                        "description": "Math expression like 2+2"
                    }
                },
                "required": ["expression"]
            }
        }
    }
]

# پیاده‌سازی واقعی ابزارها
def get_weather(city: str) -> dict:
    """شبیه‌سازی API آب و هوا"""
    fake_data = {
        "Tehran": {"temp": 28, "condition": "Sunny"},
        "London": {"temp": 15, "condition": "Rainy"},
        "Tokyo": {"temp": 22, "condition": "Cloudy"},
    }
    return fake_data.get(city, {"temp": 20, "condition": "Unknown"})

def calculate(expression: str) -> str:
    """ماشین حساب ساده"""
    try:
        return str(eval(expression))
    except Exception as e:
        return f"Error: {e}"

# مپ کردن اسم ابزار به تابع
tool_functions = {
    "get_weather": get_weather,
    "calculate": calculate,
}

def run_agent(user_message: str):
    messages = [
        {"role": "system", "content": "تو یه دستیار فارسی‌زبان هستی."},
        {"role": "user", "content": user_message}
    ]
    
    # مرحله ۱: ارسال به LLM
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=messages,
        tools=tools,
    )
    
    message = response.choices[0].message
    
    # مرحله ۲: چک کن آیا ابزاری صدا زده شده
    if message.tool_calls:
        messages.append(message)
        
        # مرحله ۳: اجرای هر ابزار
        for tool_call in message.tool_calls:
            func_name = tool_call.function.name
            func_args = json.loads(tool_call.function.arguments)
            
            print(f"ابزار صدا زده شد: {func_name}({func_args})")
            
            # اجرای تابع
            result = tool_functions[func_name](**func_args)
            
            # اضافه کردن نتیجه به مکالمه
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": json.dumps(result)
            })
        
        # مرحله ۴: ارسال دوباره به LLM با نتایج ابزارها
        final_response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=tools,
        )
        
        return final_response.choices[0].message.content
    
    return message.content

# تست
print(run_agent("هوای تهران چطوره و ۲۵ ضربدر ۱۳ چند می‌شه؟"))

توی این مثال، وقتی کاربر هم درباره آب و هوا و هم محاسبه سوال می‌پرسه، LLM ممکنه هر دو ابزار رو همزمان صدا بزنه! این یکی از قابلیت‌های جالب Tool Use ه — اجرای موازی ابزارها.

LLM چطور تصمیم می‌گیره؟

یه سوال مهم: LLM از کجا می‌فهمه باید ابزار استفاده کنه؟

جواب: از روی توضیحات ابزار و سوال کاربر. وقتی توضیح ابزار می‌گه “دریافت وضعیت آب و هوا” و کاربر می‌پرسه “هوا چطوره؟”، LLM متوجه تطابق معنایی می‌شه.

چند نکته مهم:

  • توضیحات واضح بنویس: “Get weather” بهتر از “weather” ه. ولی “Get current weather for a specific city including temperature and conditions” از همه بهتره.
  • پارامترها رو خوب توضیح بده: مثلاً بگو “City name in English, e.g. Tehran” نه فقط “city”.
  • تعداد ابزارها رو کنترل کن: اگه ۱۰۰ تا ابزار بدی، LLM گیج می‌شه. ابزارهای مرتبط رو گروه‌بندی کن.

مقایسه Tool Use در OpenAI و Anthropic

دو تا از بزرگ‌ترین ارائه‌دهنده‌های LLM هر دو Tool Use دارن، ولی با تفاوت‌هایی:

OpenAI (GPT-4o)

# OpenAI Tool Use
response = client.chat.completions.create(
    model="gpt-4o",
    messages=messages,
    tools=[{
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get weather",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string"}
                },
                "required": ["city"]
            }
        }
    }]
)

# دسترسی به tool call
tool_call = response.choices[0].message.tool_calls[0]
print(tool_call.function.name)       # "get_weather"
print(tool_call.function.arguments)  # '{"city": "Tehran"}'

Anthropic (Claude)

import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    tools=[{
        "name": "get_weather",
        "description": "Get weather",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "City name"
                }
            },
            "required": ["city"]
        }
    }],
    messages=[{"role": "user", "content": "هوای تهران؟"}]
)

# دسترسی به tool call
for block in response.content:
    if block.type == "tool_use":
        print(block.name)   # "get_weather"
        print(block.input)  # {"city": "Tehran"}

تفاوت‌های کلیدی

  • فرمت تعریف: OpenAI از parameters استفاده می‌کنه، Anthropic از input_schema
  • ساختار پاسخ: OpenAI ابزارها رو توی tool_calls برمی‌گردونه، Anthropic توی content با نوع tool_use
  • نتیجه ابزار: OpenAI با role=tool، Anthropic با role=user و نوع tool_result
  • اجرای موازی: هر دو پشتیبانی می‌کنن

ساخت ابزارهای پیچیده‌تر

ابزارها می‌تونن خیلی پیچیده‌تر باشن. بیا یه ابزار جستجوی دیتابیس بسازیم:

database_tool = {
    "type": "function",
    "function": {
        "name": "query_database",
        "description": "جستجو در دیتابیس محصولات. "
                       "می‌تونه بر اساس نام، دسته‌بندی، "
                       "قیمت و موجودی فیلتر کنه.",
        "parameters": {
            "type": "object",
            "properties": {
                "search_term": {
                    "type": "string",
                    "description": "عبارت جستجو در نام محصول"
                },
                "category": {
                    "type": "string",
                    "enum": ["electronics", "clothing", "books", "food"],
                    "description": "دسته‌بندی محصول"
                },
                "max_price": {
                    "type": "number",
                    "description": "حداکثر قیمت به تومان"
                },
                "in_stock_only": {
                    "type": "boolean",
                    "description": "فقط محصولات موجود",
                    "default": True
                }
            },
            "required": ["search_term"]
        }
    }
}
نکته: وقتی از enum استفاده می‌کنی، LLM فقط از بین مقادیر مشخص‌شده انتخاب می‌کنه. این خیلی کمک می‌کنه که ورودی‌های نامعتبر نداشته باشی.

الگوی Tool Use Loop

گاهی اوقات Agent نیاز داره چندین بار ابزار صدا بزنه تا به جواب برسه. مثلاً اول جستجو کنه، بعد نتیجه رو فیلتر کنه، بعد قیمت رو حساب کنه. برای این حالت، باید یه حلقه بنویسی:

def run_agent_loop(user_message: str, max_iterations: int = 10):
    messages = [
        {"role": "system", "content": "تو یه دستیار هستی."},
        {"role": "user", "content": user_message}
    ]
    
    for i in range(max_iterations):
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=tools,
        )
        
        message = response.choices[0].message
        messages.append(message)
        
        # اگه tool call نداره، یعنی جواب نهایی آماده‌ست
        if not message.tool_calls:
            return message.content
        
        # اجرای همه ابزارهای درخواست‌شده
        for tool_call in message.tool_calls:
            func_name = tool_call.function.name
            func_args = json.loads(tool_call.function.arguments)
            result = tool_functions[func_name](**func_args)
            
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": json.dumps(result)
            })
    
    return "حداکثر تعداد تکرار رسید"

این الگو خیلی مهمه و توی اکثر فریمورک‌های Agent ازش استفاده می‌شه. max_iterations هم یه safety guard ه که Agent توی حلقه بی‌نهایت نیفته.

بهترین شیوه‌ها (Best Practices)

  • توضیحات ابزار رو به انگلیسی بنویس: LLM ها با توضیحات انگلیسی بهتر کار می‌کنن، حتی اگه کاربر فارسی حرف بزنه.
  • ابزارها رو atomic نگه دار: هر ابزار باید یه کار مشخص انجام بده. “get_weather” خوبه، “get_weather_and_send_email” بد.
  • خروجی ابزار رو ساده نگه دار: فقط اطلاعات لازم رو برگردون. یه JSON بزرگ با ۱۰۰ فیلد، LLM رو گیج می‌کنه.
  • خطاها رو هم برگردون: اگه ابزار fail شد، خطا رو به LLM بگو تا بتونه به کاربر توضیح بده یا راه دیگه‌ای امتحان کنه.
  • حد ایمنی بذار: همیشه max_iterations داشته باش. همیشه ورودی ابزار رو validate کن.

امنیت Tool Use

Tool Use قدرت زیادی به Agent می‌ده، ولی با قدرت، مسئولیت هم میاد:

هشدار امنیتی: هیچ‌وقت مستقیماً از eval() برای اجرای ورودی کاربر استفاده نکن. مثال ماشین حساب ما فقط برای نمایش بود — توی production حتماً از کتابخانه‌های امن مثل numexpr یا asteval استفاده کن.
  • ورودی‌ها رو validate کن: قبل از اجرای هر ابزار، چک کن ورودی‌ها معتبر هستن.
  • دسترسی‌ها رو محدود کن: Agent نباید به هر چیزی دسترسی داشته باشه. اصل least privilege رو رعایت کن.
  • لاگ بگیر: هر tool call رو لاگ کن. برای دیباگ و آدیت لازمه.
  • تایید انسانی: برای عملیات‌های حساس (مثل حذف داده یا پرداخت)، از کاربر تایید بگیر.

جمع‌بندی

Tool Use یه مفهوم ساده ولی قدرتمنده:

  • ابزارها رو با JSON Schema تعریف می‌کنی
  • LLM تصمیم می‌گیره کدوم ابزار رو با چه پارامترهایی صدا بزنه
  • تو ابزار رو اجرا می‌کنی و نتیجه رو برمی‌گردونی
  • این چرخه می‌تونه چندین بار تکرار بشه

با Tool Use، Agent از یه موجود فقط-حرف‌زن تبدیل می‌شه به یه موجود عمل‌گرا که واقعاً می‌تونه کار انجام بده.

اپیزود بعدی درباره حافظه Agent ه — چطور Agent می‌تونه چیزها رو یادش بمونه و از تجربه‌های قبلی استفاده کنه. تا اپیزود بعد!

نظرات

هنوز نظری ثبت نشده. اولین نفر باشید!

نظر خود را بنویسید