PydanticAI چیست؟ آموزش ساخت AI Agent با پایتون و API درواره
در این آموزش یاد میگیرید با PydanticAI، پایتون و API درواره یک AI Agent واقعی بسازید؛ از نصب و اتصال مدل تا Tool Calling، خروجی ساختاریافته، مدیریت وابستگیها، FastAPI و تست.
PydanticAI یا Pydantic AI یک فریمورک پایتون برای ساخت برنامهها و Agentهای مبتنی بر هوش مصنوعی مولد است. این فریمورک توسط تیم Pydantic توسعه داده شده و تلاش میکند تجربهای شبیه FastAPI را وارد دنیای توسعه Agentهای هوش مصنوعی کند.
اگر قبلاً با FastAPI و Pydantic کار کرده باشید، بسیاری از مفاهیم PydanticAI برای شما آشنا خواهند بود: Type Hint، اعتبارسنجی داده، Dependency Injection، مدلهای ساختاریافته و قابلیت تستپذیری.
در این آموزش یک پروژه عملی میسازیم که:
- از طریق API درواره به مدل هوش مصنوعی متصل میشود.
- پاسخ فارسی تولید میکند.
- خروجی ساختاریافته و معتبر برمیگرداند.
- ابزار یا Tool در اختیار مدل قرار میدهد.
- دادههای برنامه را با Dependency Injection به Agent منتقل میکند.
- از طریق FastAPI به یک API قابلاستفاده تبدیل میشود.
- کلید API را بهشکل امن در بکاند نگه میدارد.
- برای تست، مدیریت خطا و محیط Production آماده میشود.
PydanticAI چیست؟
PydanticAI یک فریمورک Agent برای پایتون است که به توسعهدهندگان اجازه میدهد برنامههای مبتنی بر مدلهای زبانی را با ساختاری Type-safe و قابلاعتبارسنجی پیادهسازی کنند.
بر اساس مستندات رسمی PydanticAI، این فریمورک برای ساخت برنامهها و Workflowهای هوش مصنوعی مولد در محیط واقعی طراحی شده و امکاناتی مانند Agent Loop، ابزارها، Dependency Injection، خروجی ساختاریافته، Streaming، MCP، Evals و Observability را ارائه میدهد.
یک Agent در PydanticAI معمولاً از اجزای زیر تشکیل میشود:
- مدل هوش مصنوعی
- Instructions یا دستورهای توسعهدهنده
- پرامپت کاربر
- Toolها
- وابستگیهای اجرایی
- نوع خروجی
- تنظیمات مدل
- تاریخچه پیامها
- محدودیتهای مصرف
- منطق Retry و اعتبارسنجی
چرا از PydanticAI استفاده کنیم؟
برای ارسال یک درخواست ساده به مدل، استفاده مستقیم از API کافی است. اما وقتی برنامه بزرگتر میشود، مدیریت ابزارها، خروجیهای JSON، تاریخچه، وابستگیها و خطاها دشوارتر خواهد شد.
PydanticAI این اجزا را داخل یک ساختار منظم قرار میدهد.
خروجی Type-safe
میتوانید مشخص کنید پاسخ نهایی باید نمونهای معتبر از یک مدل Pydantic باشد. در این حالت بهجای پردازش متن آزاد، با یک شیء تایپشده پایتون کار میکنید.
Tool Calling
میتوانید توابع پایتون را بهعنوان ابزار در اختیار مدل قرار دهید. مدل در صورت نیاز ابزار مناسب را انتخاب میکند، آرگومانهای آن را میسازد و نتیجه ابزار را برای تولید پاسخ نهایی به کار میگیرد.
Dependency Injection
اتصال پایگاه داده، تنظیمات برنامه، شناسه کاربر، سرویسهای داخلی یا Repositoryها را میتوان بهشکل کنترلشده در اختیار Toolها قرار داد.
اعتبارسنجی با Pydantic
ورودی ابزارها و خروجی نهایی با مدلهای Pydantic بررسی میشوند. در صورت ناسازگاری، فریمورک میتواند خطا را به مدل بازگرداند تا خروجی خود را اصلاح کند.
پشتیبانی از اجرای همزمان و غیرهمزمان
PydanticAI هم run_sync برای اجرای همزمان و هم run برای اجرای Async ارائه میدهد.
سازگاری با مدلهای مختلف
PydanticAI به یک مدل مشخص وابسته نیست. در این مقاله از OpenAIChatModel و OpenAIProvider برای اتصال به API سازگار درواره استفاده میکنیم. روش پیکربندی base_url سفارشی در مستندات رسمی مدلهای سازگار توضیح داده شده است.
PydanticAI چه تفاوتی با Pydantic دارد؟
Pydantic یک کتابخانه اعتبارسنجی و مدلسازی داده در پایتون است. PydanticAI یک فریمورک ساخت Agent است که از Pydantic برای مدیریت دادههای ساختاریافته استفاده میکند.
برای مثال، مدل زیر متعلق به Pydantic است:
from pydantic import BaseModel
class Product(BaseModel):
name: str
price: int
available: bool
اما در PydanticAI میتوان همین مدل را بهعنوان نوع خروجی Agent تعریف کرد:
from pydantic_ai import Agent
agent = Agent(
model,
output_type=Product,
)
در این حالت، نتیجه نهایی Agent باید با ساختار Product سازگار باشد.
تفاوت PydanticAI با فراخوانی مستقیم API
در فراخوانی مستقیم API، خودتان باید این موارد را مدیریت کنید:
- ساخت آرایه پیامها
- اجرای حلقه Tool Calling
- Parse کردن آرگومان ابزار
- اجرای تابع
- ارسال نتیجه ابزار به مدل
- استخراج پاسخ نهایی
- اعتبارسنجی JSON
- Retry هنگام خروجی نامعتبر
- مدیریت تاریخچه
- محدودیت تعداد مراحل
در PydanticAI بخش بزرگی از این فرایند در قالب Agent Loop مدیریت میشود.
بااینحال، برای درخواستهای بسیار ساده که فقط یک ورودی و خروجی متنی دارند، فراخوانی مستقیم API ممکن است سادهتر باشد. انتخاب فریمورک باید بر اساس پیچیدگی واقعی پروژه انجام شود.
پیشنیازهای آموزش
برای اجرای مثالها به موارد زیر نیاز دارید:
- Python نسخه 3.10 یا جدیدتر
- یک محیط مجازی پایتون
- کلید API درواره
- شناسه یکی از مدلهای در دسترس درواره
- آشنایی مقدماتی با Python و Pydantic
PydanticAI طبق راهنمای نصب رسمی به Python 3.10 یا نسخه جدیدتر نیاز دارد.
برای مشاهده مدلها، قابلیتها و قیمت بهروز آنها، صفحه مدلهای درواره را بررسی کنید.
اگر قصد استفاده از Tool Calling یا Structured Output را دارید، مدلی را انتخاب کنید که از قابلیت موردنیاز شما پشتیبانی کند.
ساخت پروژه
ابتدا یک پوشه جدید بسازید:
mkdir pydantic-ai-darvareh
cd pydantic-ai-darvareh
محیط مجازی را ایجاد کنید:
python -m venv .venv
فعالسازی در Linux و macOS:
source .venv/bin/activate
فعالسازی در Windows PowerShell:
.venv\Scripts\Activate.ps1
نصب PydanticAI
نسخه کامل را میتوان با دستور زیر نصب کرد:
pip install pydantic-ai
برای پروژهای سبکتر که فقط به مدلهای سازگار با OpenAI API نیاز دارد:
pip install "pydantic-ai-slim[openai]"
برای مثال FastAPI این وابستگیها را نیز نصب کنید:
pip install fastapi "uvicorn[standard]" python-dotenv
یک فایل requirements.txt نمونه:
pydantic-ai
fastapi
uvicorn[standard]
python-dotenv
برای پروژه Production بهتر است پس از آزمایش، نسخه وابستگیها را Pin کنید تا بهروزرسانی ناگهانی باعث تغییر رفتار برنامه نشود.
ساخت فایل تنظیمات محیطی
در ریشه پروژه فایل .env را بسازید:
DARVAREH_API_KEY=YOUR_DARVAREH_API_KEY
DARVAREH_MODEL_ID=YOUR_MODEL_ID
فایل .env را به .gitignore اضافه کنید:
.venv/
.env
__pycache__/
*.pyc
کلید API را داخل کد، مخزن Git، اپلیکیشن فرانتاند یا فایل قابل دانلود قرار ندهید.
اتصال PydanticAI به API درواره
آدرس پایه API درواره:
https://api.darvareh.ir/v1
از آنجا که API درواره با ساختار OpenAI-compatible قابلاستفاده است، میتوانیم از OpenAIChatModel و OpenAIProvider استفاده کنیم.
فایل model.py را بسازید:
import os
from dotenv import load_dotenv
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.providers.openai import OpenAIProvider
load_dotenv()
api_key = os.getenv("DARVAREH_API_KEY")
model_id = os.getenv("DARVAREH_MODEL_ID")
if not api_key:
raise RuntimeError("DARVAREH_API_KEY is not configured")
if not model_id:
raise RuntimeError("DARVAREH_MODEL_ID is not configured")
darvareh_model = OpenAIChatModel(
model_id,
provider=OpenAIProvider(
base_url="https://api.darvareh.ir/v1",
api_key=api_key,
),
)
در این کد:
- کلید API از متغیر محیطی دریافت میشود.
- شناسه مدل خارج از کد نگهداری میشود.
- آدرس پایه روی API درواره تنظیم شده است.
- یک نمونه قابلاستفاده از مدل ساخته میشود.
ساخت اولین Agent
فایل simple_agent.py را ایجاد کنید:
from pydantic_ai import Agent
from model import darvareh_model
agent = Agent(
darvareh_model,
instructions=(
"تو یک دستیار فنی فارسیزبان هستی. "
"پاسخها را دقیق، روشن و کاربردی بنویس. "
"اگر اطلاعات کافی نداری، آن را صریح اعلام کن."
),
)
result = agent.run_sync(
"تفاوت REST API و Webhook را در سه بند توضیح بده."
)
print(result.output)
اجرای برنامه:
python simple_agent.py
متد run_sync برای اسکریپتهای ساده و محیطهای همزمان مناسب است. در FastAPI و برنامههای Async بهتر است از await agent.run() استفاده کنید.
ساخت خروجی ساختاریافته با Pydantic
یکی از مهمترین قابلیتهای PydanticAI، تبدیل پاسخ مدل به یک شیء معتبر پایتون است.
فرض کنید میخواهیم پیام مشتری را تحلیل کنیم. فایل ticket_agent.py را بسازید:
from typing import Literal
from pydantic import BaseModel, Field
from pydantic_ai import Agent
from model import darvareh_model
class TicketAnalysis(BaseModel):
category: Literal[
"technical",
"billing",
"feature_request",
"general",
]
priority: Literal["low", "medium", "high"]
summary: str = Field(
min_length=5,
max_length=300,
description="خلاصه کوتاه پیام به زبان فارسی",
)
sentiment: Literal["positive", "neutral", "negative"]
requires_human_review: bool
confidence: float = Field(
ge=0,
le=1,
description="میزان اطمینان مدل بین صفر و یک",
)
ticket_agent = Agent(
darvareh_model,
output_type=TicketAnalysis,
instructions=(
"پیام کاربر را فقط بر اساس اطلاعات موجود تحلیل کن. "
"اگر پیام مبهم است، confidence را کاهش بده و "
"requires_human_review را فعال کن."
),
)
result = ticket_agent.run_sync(
"سلام، از دیشب درخواستهای API من با خطای Timeout مواجه میشوند."
)
print(result.output)
print(result.output.model_dump())
خروجی برنامه یک رشته JSON خام نیست؛ بلکه نمونهای از TicketAnalysis است:
analysis = result.output
print(analysis.category)
print(analysis.priority)
print(analysis.summary)
print(analysis.confidence)
PydanticAI برای خروجیهای ساختاریافته از مدل Pydantic و JSON Schema استفاده میکند و نتیجه را اعتبارسنجی میکند. جزئیات این رفتار در مستندات Structured Output آمده است.
چرا خروجی ساختاریافته مهم است؟
متن آزاد برای نمایش به کاربر مناسب است، اما برای منطق نرمافزار قابلاعتماد نیست.
برای مثال، این کد شکننده است:
if "فوری" in model_response:
priority = "high"
اما با خروجی ساختاریافته میتوان نوشت:
if result.output.priority == "high":
send_to_priority_queue(result.output)
خروجی ساختاریافته برای این کاربردها مناسب است:
- دستهبندی تیکت
- استخراج اطلاعات سند
- تولید داده برای پایگاه داده
- تحلیل بازخورد
- ساخت پیشنهاد محصول
- تولید تنظیمات برنامه
- تبدیل متن به ساختار JSON
- ساخت Workflowهای چندمرحلهای
حتی با وجود اعتبارسنجی Pydantic، قواعد کسبوکار را نیز در بکاند بررسی کنید. معتبر بودن ساختار به معنی صحیح بودن محتوای مدل نیست.
ساخت Tool برای Agent
Agent زمانی کاربردیتر میشود که بتواند اطلاعات خارج از دانش مدل را از ابزارهای برنامه دریافت کند.
در این مثال، یک ابزار برای مشاهده قیمت و موجودی محصول ایجاد میکنیم.
فایل product_agent.py:
from pydantic_ai import Agent
from model import darvareh_model
PRODUCTS = {
"کیبورد مکانیکی": {
"price": 4_500_000,
"available": True,
},
"ماوس بیسیم": {
"price": 1_800_000,
"available": True,
},
"پایه لپتاپ": {
"price": 2_200_000,
"available": False,
},
}
product_agent = Agent(
darvareh_model,
instructions=(
"تو دستیار فروشگاه هستی. "
"برای قیمت و موجودی فقط از ابزار get_product استفاده کن. "
"هیچ قیمت یا موجودیای را حدس نزن."
),
)
@product_agent.tool_plain
def get_product(product_name: str) -> dict:
"""قیمت و موجودی یک محصول را بر اساس نام آن برمیگرداند."""
product = PRODUCTS.get(product_name)
if product is None:
return {
"found": False,
"message": "محصول پیدا نشد",
}
return {
"found": True,
"name": product_name,
"price": product["price"],
"available": product["available"],
}
result = product_agent.run_sync(
"قیمت کیبورد مکانیکی چقدر است و آیا موجود است؟"
)
print(result.output)
در این فرایند، مدل میتواند:
- سؤال کاربر را تحلیل کند.
- تشخیص دهد به اطلاعات محصول نیاز دارد.
- ابزار
get_productرا با آرگومان مناسب فراخوانی کند. - نتیجه ابزار را دریافت کند.
- پاسخ نهایی را تولید کند.
توضیحات Docstring ابزار اهمیت زیادی دارد؛ زیرا به مدل کمک میکند کاربرد تابع و آرگومانهای آن را درک کند.
تفاوت tool و tool_plain
در PydanticAI دو روش رایج برای تعریف ابزار وجود دارد.
استفاده از tool_plain
اگر تابع به Dependency یا Run Context نیاز ندارد:
@agent.tool_plain
def calculate_discount(price: int, percent: int) -> int:
"""قیمت نهایی را پس از اعمال درصد تخفیف محاسبه میکند."""
if percent < 0 or percent > 100:
raise ValueError("percent must be between 0 and 100")
return price - (price * percent // 100)
استفاده از tool
اگر تابع باید به Dependencyهای اجرای Agent دسترسی داشته باشد:
from pydantic_ai import RunContext
@agent.tool
def get_user_plan(ctx: RunContext[AppDependencies]) -> str:
return ctx.deps.user_plan
Dependency Injection در PydanticAI
قرار دادن اتصال پایگاه داده یا سرویسهای برنامه داخل متغیرهای Global میتواند تست و نگهداری پروژه را دشوار کند.
PydanticAI امکان تعریف نوع Dependency را فراهم میکند.
from dataclasses import dataclass
@dataclass
class AppDependencies:
user_id: int
user_name: str
user_plan: str
product_repository: "ProductRepository"
Agent را با deps_type تعریف میکنیم:
support_agent = Agent(
darvareh_model,
deps_type=AppDependencies,
instructions=(
"تو یک دستیار پشتیبانی هستی. "
"برای دریافت اطلاعات حساب فقط از ابزارهای تعریفشده استفاده کن."
),
)
اکنون Tool میتواند از Dependency استفاده کند:
from pydantic_ai import RunContext
@support_agent.tool
async def get_user_profile(
ctx: RunContext[AppDependencies],
) -> dict:
"""اطلاعات عمومی حساب کاربر فعلی را دریافت میکند."""
return {
"user_id": ctx.deps.user_id,
"name": ctx.deps.user_name,
"plan": ctx.deps.user_plan,
}
هنگام اجرا، Dependency را ارسال میکنیم:
deps = AppDependencies(
user_id=125,
user_name="امیر",
user_plan="professional",
product_repository=repository,
)
result = await support_agent.run(
"پلن فعلی من چیست؟",
deps=deps,
)
مزایای این روش عبارتاند از:
- تست سادهتر
- حذف Global State
- کنترل بهتر دسترسی Toolها
- Type Checking
- امکان جایگزینی سرویس واقعی با Mock
- تفکیک منطق Agent از زیرساخت
Instructions ثابت و پویا
دستورهای ثابت هنگام ساخت Agent تعریف میشوند:
agent = Agent(
darvareh_model,
instructions=(
"پاسخها را به فارسی بنویس. "
"اطلاعاتی را که از ابزار دریافت نکردهای، قطعی اعلام نکن."
),
)
برای اضافه کردن دستور پویا بر اساس Dependency:
@support_agent.instructions
def add_user_context(
ctx: RunContext[AppDependencies],
) -> str:
return (
f"نام کاربر {ctx.deps.user_name} است و "
f"پلن او {ctx.deps.user_plan} است."
)
داده حساس یا غیرضروری را وارد Instructions نکنید. فقط اطلاعاتی را در اختیار مدل قرار دهید که برای انجام وظیفه لازم است.
ترکیب Tool و Structured Output
یک Agent میتواند ابتدا از Tool استفاده کند و سپس نتیجه ساختاریافته برگرداند.
from typing import Literal
from pydantic import BaseModel, Field
from pydantic_ai import Agent
class ProductRecommendation(BaseModel):
product_name: str | None
status: Literal["recommended", "not_found", "unavailable"]
reason: str = Field(max_length=300)
price: int | None
recommendation_agent = Agent(
darvareh_model,
output_type=ProductRecommendation,
instructions=(
"برای بررسی محصولات از ابزار get_product استفاده کن. "
"قیمت یا موجودی را حدس نزن. "
"در پایان خروجی را مطابق ساختار تعیینشده برگردان."
),
)
@recommendation_agent.tool_plain
def get_product(product_name: str) -> dict:
"""اطلاعات محصول را از فهرست فروشگاه دریافت میکند."""
product = PRODUCTS.get(product_name)
if product is None:
return {"found": False}
return {
"found": True,
"name": product_name,
**product,
}
result = recommendation_agent.run_sync(
"آیا کیبورد مکانیکی برای خرید موجود است؟"
)
print(result.output.model_dump())
برای اجرای این مثال، مدل انتخابی باید از Tool Calling و Schema موردنیاز پشتیبانی کند.
مدیریت تاریخچه مکالمه
هر اجرای مستقل Agent لزوماً تاریخچه اجراهای قبلی را نمیداند. برای ادامه مکالمه میتوانید پیامهای اجرای قبلی را به اجرای جدید منتقل کنید.
first_result = agent.run_sync(
"من یک فروشگاه آنلاین لوازم جانبی کامپیوتر دارم."
)
history = first_result.all_messages()
second_result = agent.run_sync(
"سه قابلیت هوش مصنوعی مناسب فروشگاه من پیشنهاد کن.",
message_history=history,
)
print(second_result.output)
در پروژه واقعی بهتر است تاریخچه را بر اساس شناسه مکالمه در پایگاه داده ذخیره کنید.
بهمرور زمان، تاریخچه میتواند بزرگ و پرهزینه شود. برای مدیریت آن میتوانید:
- تعداد پیامهای قبلی را محدود کنید.
- پیامهای قدیمی را خلاصه کنید.
- فقط اطلاعات مرتبط را نگه دارید.
- اطلاعات پایدار کاربر را در پروفایل جداگانه ذخیره کنید.
- دادههای مرجع را با RAG بازیابی کنید.
اجرای Async
در برنامههای وب و سرویسهایی که همزمان چند درخواست دارند، از API غیرهمزمان استفاده کنید:
import asyncio
from pydantic_ai import Agent
from model import darvareh_model
agent = Agent(
darvareh_model,
instructions="به زبان فارسی و دقیق پاسخ بده.",
)
async def main() -> None:
result = await agent.run(
"سه مزیت استفاده از Type Hint در پایتون چیست؟"
)
print(result.output)
if __name__ == "__main__":
asyncio.run(main())
از فراخوانی run_sync داخل Endpoint غیرهمزمان FastAPI خودداری کنید؛ زیرا میتواند Event Loop را مسدود کند.
Streaming پاسخ
برای نمایش تدریجی پاسخ در رابط چت میتوان از Streaming استفاده کرد:
import asyncio
from pydantic_ai import Agent
from model import darvareh_model
agent = Agent(
darvareh_model,
instructions="پاسخ را به زبان فارسی بنویس.",
)
async def main() -> None:
async with agent.run_stream(
"مفهوم Dependency Injection را با یک مثال توضیح بده."
) as response:
async for text in response.stream_text(delta=True):
print(text, end="", flush=True)
if __name__ == "__main__":
asyncio.run(main())
پشتیبانی عملی از Streaming به مدل و مسیر API انتخابشده نیز وابسته است.
ساخت API با FastAPI
اکنون Agent را از طریق یک Endpoint در اختیار برنامه قرار میدهیم.
ساختار پروژه:
pydantic-ai-darvareh/
├── .env
├── .gitignore
├── model.py
├── agent.py
├── main.py
└── requirements.txt
فایل agent.py:
from typing import Literal
from pydantic import BaseModel, Field
from pydantic_ai import Agent
from model import darvareh_model
class AssistantOutput(BaseModel):
answer: str = Field(
min_length=1,
max_length=2000,
)
topic: Literal[
"programming",
"artificial_intelligence",
"api",
"other",
]
needs_more_information: bool
assistant_agent = Agent(
darvareh_model,
output_type=AssistantOutput,
instructions=(
"تو دستیار فنی فارسیزبان هستی. "
"پاسخ را دقیق و کاربردی بنویس. "
"اگر اطلاعات کافی نیست، needs_more_information را true قرار بده."
),
)
فایل main.py:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from pydantic_ai.exceptions import ModelHTTPError, UnexpectedModelBehavior
from agent import AssistantOutput, assistant_agent
app = FastAPI(
title="Darvareh PydanticAI Agent",
version="1.0.0",
)
class ChatRequest(BaseModel):
message: str = Field(
min_length=2,
max_length=4000,
)
class ChatResponse(BaseModel):
data: AssistantOutput
@app.get("/health")
async def health() -> dict[str, str]:
return {"status": "ok"}
@app.post("/chat", response_model=ChatResponse)
async def chat(body: ChatRequest) -> ChatResponse:
try:
result = await assistant_agent.run(body.message)
return ChatResponse(data=result.output)
except ModelHTTPError as exc:
raise HTTPException(
status_code=502,
detail=f"Model provider error: {exc.status_code}",
) from exc
except UnexpectedModelBehavior as exc:
raise HTTPException(
status_code=502,
detail="The model returned an invalid response",
) from exc
اجرای سرور:
uvicorn main:app --reload
آدرس مستندات Swagger:
http://127.0.0.1:8000/docs
نمونه درخواست:
curl -X POST "http://127.0.0.1:8000/chat" \
-H "Content-Type: application/json" \
-d '{"message":"Pydantic چه کاربردی در FastAPI دارد؟"}'
نمونه پاسخ:
{
"data": {
"answer": "Pydantic برای اعتبارسنجی و تبدیل دادههای ورودی و خروجی در FastAPI استفاده میشود.",
"topic": "programming",
"needs_more_information": false
}
}
این API هنوز برای محیط Production به احراز هویت، Rate Limit، ثبت رخداد و کنترل مصرف نیاز دارد.
مدیریت خطاها
فراخوانی مدل ممکن است با خطا مواجه شود:
- کلید API نامعتبر
- شناسه مدل اشتباه
- Timeout
- محدودیت نرخ
- پاسخ نامعتبر
- پشتیبانی نشدن Tool Calling
- ناسازگاری Schema
- اختلال موقت شبکه
- عبور از محدودیت Context Window
خطاها را به پیام عمومی و قابلکنترل تبدیل کنید. متن کامل پاسخ Provider یا اطلاعات داخلی سرور نباید مستقیماً به کاربر نمایش داده شود.
نمونه کلی:
try:
result = await agent.run(user_message)
except ModelHTTPError as exc:
logger.exception(
"Model HTTP error",
extra={"status_code": exc.status_code},
)
raise ServiceUnavailableError()
except UnexpectedModelBehavior:
logger.exception("Unexpected model behavior")
raise InvalidModelOutputError()
در Logها از ثبت کلید API، اطلاعات احراز هویت، دادههای شخصی یا تمام متن محرمانه کاربر خودداری کنید.
Retry را کجا انجام دهیم؟
Retry در چند سطح ممکن است رخ دهد:
- Retry درخواست HTTP
- Retry خروجی نامعتبر
- Retry Tool
- Retry کل Workflow
این سطوح را بدون محدودیت روی یکدیگر قرار ندهید. برای مثال، سه Retry در هرکدام از سه لایه ممکن است تعداد درخواستها را بهشکل ناخواسته افزایش دهد.
برای خطاهای دائمی Retry انجام ندهید:
- کلید نامعتبر
- مدل ناموجود
- ورودی نامعتبر
- Schema پشتیبانینشده
- دسترسی غیرمجاز
برای خطاهای موقت میتوان از Backoff محدود استفاده کرد:
- Timeout موقت
- خطای شبکه
- پاسخ 429
- برخی خطاهای 5xx
محدود کردن مصرف Agent
Agent ممکن است چند بار مدل و Toolها را فراخوانی کند. بنابراین باید برای آن سقف مصرف تعریف شود.
کنترلهای پیشنهادی:
- حداکثر تعداد درخواست مدل در هر Run
- حداکثر تعداد Tool Call
- Timeout کلی
- محدودیت ورودی
- سقف هزینه برای هر کاربر
- Rate Limit
- محدودیت تعداد Agent Step
- لغو عملیات هنگام قطع اتصال کاربر
Agent بدون محدودیت ممکن است در یک حلقه غیرضروری قرار گیرد یا هزینه بیشتری از انتظار ایجاد کند.
طراحی امن Toolها
هر Tool یک قابلیت اجرایی واقعی در اختیار مدل قرار میدهد. بنابراین Tool باید مانند یک Endpoint داخلی طراحی شود.
اصول مهم:
- ورودی Tool را اعتبارسنجی کنید.
- دسترسی کاربر را داخل Tool بررسی کنید.
- فقط دادههای لازم را برگردانید.
- Timeout تعیین کنید.
- عملیات جانبی را Idempotent طراحی کنید.
- اجرای عملیات مهم را به تأیید صریح کاربر وابسته کنید.
- به مدل اجازه اجرای دستور دلخواه سیستم ندهید.
- نام جدول، Query یا مسیر فایل را مستقیماً از مدل نپذیرید.
- نتیجه Tool را ثبت و قابلردیابی کنید.
این Tool مناسب است:
@agent.tool
async def get_order_status(
ctx: RunContext[AppDependencies],
order_id: int,
) -> dict:
return await ctx.deps.orders.get_status_for_user(
user_id=ctx.deps.user_id,
order_id=order_id,
)
این طراحی نامناسب است:
@agent.tool_plain
def run_any_sql(query: str):
return database.execute(query)
در نمونه اول، Tool فقط عملیات مشخصی انجام میدهد و مالکیت سفارش نیز در Repository بررسی میشود. در نمونه دوم، مدل میتواند Query دلخواه بسازد.
آیا Agent باید عملیات واقعی انجام دهد؟
در مرحله اول بهتر است Toolها فقط خواندنی باشند:
- مشاهده موجودی
- دریافت وضعیت سفارش
- جستوجوی مستندات
- مشاهده پروفایل
- دریافت فهرست محصولات
برای عملیات تغییردهنده مانند ثبت سفارش، حذف اطلاعات یا ارسال پیام، یک مرحله تأیید ایجاد کنید:
درخواست کاربر
↓
ساخت پیشنهاد عملیات
↓
نمایش جزئیات به کاربر
↓
تأیید صریح کاربر
↓
اعتبارسنجی سمت سرور
↓
اجرای عملیات
↓
ثبت نتیجه
تصمیم اجرای عملیات نباید فقط بر اساس متن تولیدشده توسط مدل گرفته شود.
تست Agent
تست یک Agent حداقل سه سطح دارد.
تست توابع معمولی
Toolها را تا حد ممکن به توابع ساده و مستقل تقسیم کنید:
def calculate_discount(price: int, percent: int) -> int:
if price < 0:
raise ValueError("price cannot be negative")
if not 0 <= percent <= 100:
raise ValueError("invalid percent")
return price - (price * percent // 100)
تست:
def test_calculate_discount():
assert calculate_discount(1_000_000, 10) == 900_000
تست بدون فراخوانی مدل واقعی
PydanticAI یک مدل داخلی test ارائه میدهد که بدون کلید Provider و بدون فراخوانی LLM اجرا میشود. این مدل بیشتر برای Smoke Test ساختار Agent، ابزارها و خروجیها مناسب است.
from pydantic_ai import Agent
test_agent = Agent(
"test",
instructions="پاسخ کوتاه بده.",
)
def test_agent_can_run():
result = test_agent.run_sync("سلام")
assert result.output
مدل Test پاسخ معنایی واقعی تولید نمیکند؛ بنابراین جایگزین ارزیابی کیفیت مدل نیست.
Evals روی نمونههای واقعی
یک Dataset شامل سؤالها و خروجیهای موردانتظار بسازید:
TEST_CASES = [
{
"input": "درخواستهای من Timeout میشوند.",
"expected_category": "technical",
},
{
"input": "مبلغ کیف پول من اشتباه نمایش داده میشود.",
"expected_category": "billing",
},
{
"input": "لطفاً قابلیت خروجی Excel اضافه کنید.",
"expected_category": "feature_request",
},
]
سپس معیارهایی مانند این موارد را اندازهگیری کنید:
- دقت دستهبندی
- نرخ خروجی معتبر
- تعداد Retry
- زمان پاسخ
- تعداد Tool Call
- توکن مصرفی
- هزینه هر Run
- صحت استفاده از Tool
- کیفیت متن فارسی
- نرخ نیاز به بررسی انسانی
PydanticAI مجموعهای از ابزارهای Evals نیز ارائه میدهد، اما میتوانید در شروع از تستهای ساده و Dataset اختصاصی خود استفاده کنید.
انتخاب مدل مناسب
برای PydanticAI فقط کیفیت مکالمه مهم نیست. مدل باید قابلیتهای موردنیاز Agent را نیز داشته باشد.
معیارهای انتخاب مدل:
- پشتیبانی از Tool Calling
- توانایی تولید Structured Output
- کیفیت زبان فارسی
- رعایت Instructions
- دقت انتخاب ابزار
- سرعت پاسخ
- Context Window
- هزینه ورودی و خروجی
- ثبات Schema
- کیفیت استدلال
- تعداد خطاهای Tool Calling
مدلهای مختلف را روی یک Dataset یکسان ارزیابی کنید. انتخاب مدل فقط بر اساس شهرت یا اندازه آن روش دقیقی نیست.
فهرست مدلها و قیمتهای جاری در صفحه مدلهای درواره منتشر میشود.
معماری پیشنهادی Production
یک معماری مناسب میتواند چنین باشد:
کاربر
↓
وب یا اپلیکیشن
↓
API Gateway
↓
احراز هویت و Rate Limit
↓
FastAPI
↓
Agent Service
↓
PydanticAI
↓
API درواره
↓
مدل انتخابشده
Toolها نیز از Agent Service به سرویسهای داخلی متصل میشوند:
PydanticAI Agent
├── Product Repository
├── User Service
├── Search Service
├── Knowledge Base
└── Internal APIs
در محیط Production بهتر است این موارد از هم تفکیک شوند:
- تعریف Agent
- تنظیمات مدل
- Toolها
- Repositoryها
- Schemaهای Pydantic
- Endpointهای FastAPI
- مدیریت تاریخچه
- ثبت Usage
- Evals
- تنظیمات محیطی
ساختار پیشنهادی پروژه بزرگتر
app/
├── api/
│ ├── routes/
│ │ └── chat.py
│ └── dependencies.py
├── agents/
│ ├── support_agent.py
│ ├── sales_agent.py
│ └── schemas.py
├── tools/
│ ├── products.py
│ └── orders.py
├── repositories/
│ ├── product_repository.py
│ └── order_repository.py
├── services/
│ ├── conversation_service.py
│ └── usage_service.py
├── core/
│ ├── config.py
│ ├── model.py
│ └── logging.py
└── main.py
این ساختار مانع تبدیل شدن یک فایل Agent به مجموعهای بزرگ از پرامپتها، Toolها و اتصالهای پراکنده میشود.
بهینهسازی هزینه
یک Agent ممکن است برای یک درخواست چند بار مدل را فراخوانی کند. برای کنترل هزینه:
- از مدل متناسب با پیچیدگی وظیفه استفاده کنید.
- تعداد مراحل Agent را محدود کنید.
- Toolهای دقیق و کمابهام بسازید.
- نتیجه Toolهای تکراری را Cache کنید.
- تاریخچه غیرضروری را حذف کنید.
- خروجی Tool را کوتاه نگه دارید.
- اسناد کامل را بدون Retrieval وارد Context نکنید.
- مصرف هر Run را ثبت کنید.
- برای وظایف ساده از Agent چندمرحلهای استفاده نکنید.
- مدلها را بر اساس کیفیت بهازای هزینه مقایسه کنید.
چه زمانی از PydanticAI استفاده کنیم؟
PydanticAI انتخاب مناسبی است اگر:
- پروژه با Python نوشته شده است.
- به Structured Output نیاز دارید.
- Agent باید Tool فراخوانی کند.
- Type Safety برای شما مهم است.
- از FastAPI و Pydantic استفاده میکنید.
- میخواهید Dependencyها را قابلتست نگه دارید.
- Agent باید چند مرحله اجرا شود.
- به Streaming، تاریخچه یا Evals نیاز دارید.
چه زمانی استفاده نکنیم؟
ممکن است به PydanticAI نیاز نداشته باشید اگر:
- فقط یک درخواست متنی ساده دارید.
- هیچ Tool یا خروجی ساختاریافتهای وجود ندارد.
- پروژه شما پایتونی نیست.
- تأخیر و سربار حداقلی مهمتر از امکانات فریمورک است.
- منطق Workflow کاملاً قطعی است و به تصمیم مدل نیاز ندارد.
- یک تابع یا State Machine ساده مسئله را بهتر حل میکند.
Agent را فقط به دلیل محبوبیت مفهوم Agent وارد پروژه نکنید. ابتدا بررسی کنید آیا مدل واقعاً باید میان چند ابزار یا مرحله تصمیمگیری کند.
اشتباهات رایج
ساخت Agent بدون تعریف هدف
Agent باید یک مسئولیت مشخص داشته باشد. Agent همهکاره معمولاً Instructions پیچیده، Toolهای زیاد و رفتار غیرقابلپیشبینی پیدا میکند.
تعریف Toolهای بیشازحد
هرچه تعداد Toolها بیشتر باشد، احتمال انتخاب ابزار اشتباه نیز افزایش مییابد. Toolها را بر اساس وظیفه Agent محدود کنید.
اعتماد به نوع خروجی بهعنوان تضمین صحت
Pydantic شکل داده را تضمین میکند، نه واقعیت محتوای آن را. یک قیمت ساختگی همچنان میتواند یک عدد صحیح معتبر باشد.
استفاده از run_sync در FastAPI
در Endpointهای Async از await agent.run() استفاده کنید.
ذخیره نکردن نسخه پرامپت و مدل
بدون ثبت نسخه نمیتوانید علت تغییر کیفیت خروجی را پیدا کنید.
نبود محدودیت مصرف
Agent Loop بدون سقف درخواست، Timeout و Tool Call میتواند هزینه و زمان پاسخ را افزایش دهد.
قرار دادن API Key در فرانتاند
PydanticAI باید در بکاند اجرا شود. کلید API نباید در مرورگر یا اپلیکیشن کاربر قرار گیرد.
چکلیست انتشار
پیش از انتقال Agent به محیط واقعی بررسی کنید:
- هدف Agent دقیق و محدود است.
- مدل انتخابی Tool Calling موردنیاز را پشتیبانی میکند.
- کلید API در Secret یا متغیر محیطی نگهداری میشود.
- ورودی کاربر محدود و اعتبارسنجی میشود.
- خروجی ساختاریافته در بکاند بررسی میشود.
- Toolها حداقل دسترسی لازم را دارند.
- عملیات تغییردهنده نیازمند تأیید هستند.
- Timeout مشخص شده است.
- تعداد Run و Tool Call محدود شده است.
- خطاهای موقت و دائمی از هم تفکیک شدهاند.
- تاریخچه مکالمه بدون کنترل رشد نمیکند.
- مصرف توکن و هزینه ثبت میشود.
- Dataset ارزیابی ساخته شده است.
- Toolها Unit Test دارند.
- مدل و پرامپت Versioning دارند.
- داده حساس وارد Log نمیشود.
- مسیر Fallback یا بررسی انسانی تعریف شده است.
پرسشهای متداول
PydanticAI برای چه کاری استفاده میشود؟
برای ساخت برنامهها و Agentهای هوش مصنوعی در Python، بهخصوص پروژههایی که به Tool Calling، Structured Output، Dependency Injection و Type Safety نیاز دارند.
آیا PydanticAI فقط برای FastAPI است؟
خیر. PydanticAI مستقل از FastAPI است، اما به دلیل استفاده مشترک از Python، Type Hint و Pydantic بهخوبی با FastAPI ترکیب میشود.
آیا میتوان PydanticAI را به API درواره متصل کرد؟
بله. با استفاده از OpenAIChatModel، OpenAIProvider و آدرس پایه https://api.darvareh.ir/v1 میتوان اتصال را انجام داد.
آیا تمام مدلها از Tool Calling پشتیبانی میکنند؟
خیر. قابلیتها با توجه به مدل متفاوتاند. پیش از انتخاب مدل، صفحه مدلهای درواره را بررسی و عملکرد آن را با ابزارهای پروژه آزمایش کنید.
تفاوت Tool Calling با Structured Output چیست؟
Tool Calling به مدل اجازه میدهد یک تابع را فراخوانی کند. Structured Output مدل را ملزم میکند پاسخ نهایی را با ساختار مشخص برگرداند. یک Agent میتواند از هر دو قابلیت همزمان استفاده کند.
آیا PydanticAI از اجرای Async پشتیبانی میکند؟
بله. متد agent.run() برای اجرای Async و agent.run_sync() برای اجرای همزمان در دسترس است.
آیا Pydantic خروجی مدل را کاملاً قابلاعتماد میکند؟
Pydantic اعتبار ساختاری داده را بررسی میکند، اما صحت واقعی محتوای تولیدشده را تضمین نمیکند. برای دادههای واقعی باید از Tool، RAG، قواعد کسبوکار و اعتبارسنجی مستقل استفاده شود.
آیا استفاده از Agent همیشه بهتر از درخواست ساده است؟
خیر. برای کارهای ساده، درخواست مستقیم سریعتر و کمهزینهتر است. Agent زمانی ارزشمند میشود که انتخاب ابزار، اجرای چند مرحله یا مدیریت خروجی ساختاریافته لازم باشد.
جمعبندی
PydanticAI یکی از گزینههای مناسب برای توسعهدهندگان پایتون است که میخواهند Agentهای هوش مصنوعی را با ساختاری منظم، Type-safe و قابلتست بسازند.
در این آموزش یاد گرفتیم چگونه:
- PydanticAI را نصب کنیم.
- آن را به API درواره متصل کنیم.
- Agent متنی بسازیم.
- خروجی ساختاریافته تعریف کنیم.
- Tool در اختیار مدل قرار دهیم.
- Dependency Injection پیادهسازی کنیم.
- تاریخچه و Streaming را مدیریت کنیم.
- Agent را با FastAPI ارائه دهیم.
- برای تست و Production آماده شویم.
برای شروع، یک Agent کوچک با یک مسئولیت مشخص و یک یا دو Tool خواندنی بسازید. سپس با استفاده از Dataset واقعی، کیفیت مدل، دقت Tool Calling، هزینه و زمان پاسخ را اندازهگیری کنید.
برای دریافت کلید API و استفاده از مدلهای هوش مصنوعی میتوانید به درواره مراجعه کنید. همچنین شناسه مدلها، قابلیتها و قیمت بهروز آنها در صفحه مدلهای درواره در دسترس است.
منابع
- مستندات رسمی PydanticAI
- راهنمای نصب PydanticAI
- مستندات Agent در PydanticAI
- مستندات خروجی ساختاریافته
- راهنمای مدلهای OpenAI-compatible در PydanticAI
مقالات مرتبط
- AI Agent چیست؟ راهنمای کامل ایجنت هوش مصنوعی
- ساخت AI Agent با Python، FastAPI و درواره
- راهنمای Agent Skills، ابزارها و MCP
- Tool Calling چیست؟
- Function Calling چیست؟
- آموزش Structured Output و JSON Schema
- API سازگار با OpenAI چیست؟
- ارزیابی مدلهای هوش مصنوعی و Evals
- آموزش کامل FastAPI با پایتون و هوش مصنوعی
برای مطالعه شرایط استفاده و محدودیتهای مسئولیت، صفحه «سلب مسئولیت» را مشاهده کنید.