درس ۲ از ۷، مقدمات
پیامها، استریم، batch و مصرف توکن
در این درس
نوتبوک این درس
04_01-Models_Messages_StructuredOutput.ipynb (بخشهای ۴، ۶ و ۸)
سه نوع پیام#
در LangChain همهچیز با پیام کار میکند و این پیامها یکبهیک به نقشهای API اوپنایآی نگاشت میشوند:
| کلاس | نقش | کاربرد |
|---|---|---|
SystemMessage |
system |
شخصیت و قواعد دستیار |
HumanMessage |
user |
پیام کاربر |
AIMessage |
assistant |
پاسخ مدل؛ خروجی invoke هم از همین نوع است |
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage, AIMessage, SystemMessage
model = init_chat_model("gpt-5.2", model_provider="openai", temperature=0)
conversation = [
SystemMessage("You are a helpful Persian-speaking assistant."),
HumanMessage("سلام! اسم من علی است."),
AIMessage("سلام علی! چطور میتونم کمکت کنم؟"),
HumanMessage("اسم من چیه؟"),
]
response = model.invoke(conversation)
print(response.content) # اسم شما **علی** است.
print(type(response)) # <class 'langchain_core.messages.ai.AIMessage'>
همانطور که در درس حافظهی دورهی اول دیدیم، مدل اسم را فقط به این دلیل میداند که کل گفتگو را دوباره فرستادهایم.
LangChain دست شما را نمیبندد. اگر به قالب OpenAI عادت دارید، دیکشنری هم پذیرفته میشود:
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is 2+2?"},
]
print(model.invoke(messages).content) # 2 + 2 = 4
متادیتای پاسخ#
AIMessage علاوه بر متن، اطلاعات دیگری هم دارد:
response = model.invoke("Tell me a joke")
print(response.content)
print(response.response_metadata.get("model_name"))
print(response.usage_metadata)
Why don't skeletons fight each other?
They don't have the guts.
gpt-5.2-2025-12-11
{'input_tokens': 10, 'output_tokens': 20, 'total_tokens': 30,
'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
response_metadata: اطلاعات خام ارائهدهنده، مثل نام دقیق نسخهی مدل وfinish_reason.usage_metadata: تعداد توکنها با قالب یکسان برای همهی ارائهدهندهها.reasoningتعداد توکنهای استدلال مدلهای استدلالی است وcache_readتوکنهایی که از کش پرامپت خوانده شدهاند و معمولاً ارزانترند.
استریم#
for chunk in model.stream("یک بلاگ پست ۲ پاراگرافی در مورد RAG بنویس"):
print(chunk.content, end="", flush=True)
هر chunk یک AIMessageChunk است که فقط تکهی تازهی متن را دارد. کدی که در دورهی قبل برای Ollama و LiteLLM جداگانه نوشتیم، اینجا برای همهی ارائهدهندهها یکی است.
استریم به ارائهدهنده هم بستگی دارد
همهی سرویسها واقعاً توکنبهتوکن نمیفرستند. در کلاس دیدیم که از طریق یک واسط، متن گاهی تکههای بزرگتر و با مکث میرسید، در حالی که اتصال مستقیم به OpenAI روانتر بود. کد شما درست است؛ رفتار به سرویس بستگی دارد.
batch: چند درخواست همزمان#
responses = model.batch([
"What is Python?",
"What is JavaScript?",
"What is Rust?",
])
for i, r in enumerate(responses, 1):
print(f"Response {i}: {r.content[:80]}...")
batch درخواستها را موازی میفرستد و نتیجهها را به همان ترتیب ورودی برمیگرداند. برای صد سؤال کوتاه، تفاوتش با یک حلقهی for و invoke چشمگیر است. یک بار هر دو را امتحان کنید و زمانشان را مقایسه کنید.
با Ollama این افزایش سرعت کمتر است. یک کارت گرافیک خانگی درخواستها را واقعاً موازی پردازش نمیکند، پس سه برابر سریعتر نمیشود. سرویسهای آنلاین و موتورهایی مثل vLLM برای پردازش همزمان ساخته شدهاند. اگر محدودیت نرخ دارید، با config={"max_concurrency": 5} تعداد درخواستهای همزمان را محدود کنید.
شمارش توکنها در چند فراخوانی#
در برنامهای که چند بار و با چند مدل مختلف LLM صدا میزند، و بعداً در ایجنتها، جمع زدن دستی usage_metadata سخت است. get_usage_metadata_callback همهی فراخوانیهای داخل یک بلوک را به تفکیک مدل جمع میزند:
from langchain_core.callbacks import get_usage_metadata_callback
model_gpt4o = init_chat_model("gpt-4o", model_provider="openai")
model_gpt52 = init_chat_model("gpt-5.2", model_provider="openai")
with get_usage_metadata_callback() as cb:
model_gpt4o.invoke("Hello!")
model_gpt4o.invoke("what is capital of Iran!")
model_gpt52.invoke("What is the capital of France?")
print(cb.usage_metadata)
{'gpt-4o-2024-08-06': {'input_tokens': 22, 'output_tokens': 16, 'total_tokens': 38, …},
'gpt-5.2-2025-12-11': {'input_tokens': 13, 'output_tokens': 5, 'total_tokens': 18, …}}
چون قیمت هر مدل فرق دارد، داشتن مصرف به تفکیک مدل برای حساب هزینه ضروری است.
خلاصه#
SystemMessage،HumanMessageوAIMessageهمان نقشهای system، user و assistant هستند. دیکشنری هم پذیرفته میشود.usage_metadataشمار توکنها را با قالب یکسان برای همهی ارائهدهندهها میدهد وget_usage_metadata_callbackآن را در چند فراخوانی جمع میزند.streamبرای نمایش همزمان با تولید، وbatchبرای اجرای موازی چند درخواست است.