create_agent وطبقة الوساطة في لانغ تشين: كيف تبني وكيلاً قابلاً للتخصيص خطوة بخطوة
create_agent هي الدالة الوحيدة لبناء وكيل في لانغ تشين 1.0 بعد أن حلّت محل create_react_agent، وسبب قوتها الحقيقي هو طبقة الوساطة البرمجية middleware التي تُدخل ستّة خطّافات في دورة الوكيل. يشرح المقال الخطّافات الستة في جدول، ويغطّي الحالة AgentState والمخرجات المهيكلة عبر response_format، وكتابة وسيطتك الخاصة التي تختار النموذج حسب خبرة المستخدم.

create_agent هي الدالة الوحيدة التي تحتاجها لبناء وكيل في لانغ تشين (LangChain) 1.0، وسبب قوتها ليس أنها تصنع وكيلاً، بل ما يحيط بحلقة النموذج: طبقة الوساطة البرمجية (middleware) التي تُدخل خطّافات جاهزة في الدورة لتلتقط نداءات النموذج والأدوات وتغيّر سلوكها. في هذا المقال تبدأ من أبسط وكيل ممكن، وتنتهي بوسيطة تكتبها بنفسك وتختار النموذج حسب خبرة المستخدم.
المصطلحات التي ستقابلها في هذا المقال:
- وكيل (agent): نموذج يستدعي أدوات في حلقة حتى تكتمل المهمة.
- بنية تشغيل الوكيل (harness): كل ما يحيط بحلقة النموذج — الموجّه النظامي والأدوات والوساط.
- طبقة وسيطة (middleware): قطعة تلتقط سلوك الوكيل عند نقطة محددة من الدورة وتعدّله.
- خطّاف (hook): الدالة التي تعمل عند تلك النقطة، وتعيد نتيجة أو تعديلاً.
- الحالة (state) و
AgentState: بيانات الوكيل التي تُقرأ وتُحدَّث كل دورة، وحقلها المدمجmessages. - سياق النداء (context): بيانات ترسلها مع الاستدعاء ولا تُحفظ بعد انتهائه.
- محادثة (thread): سلسلة استدعاءات متتابعة يميّزها
thread_id. - مخرجات مهيكلة (structured output): بيانات مُتحقَّق منها في مُخطَّط (schema) محدّد بدل نصّ حر.
- تدخّل بشري في الحلقة (HITL): إيقاف التنفيذ وانتظار موافقة إنسان قبل أداة حسّاسة.
- حواجز حماية (guardrails): قيود تفرضها على ما يمرّ عبر الوكيل قبل النموذج وبعده.
ما هي create_agent ولماذا حلّت محل create_react_agent؟
في لانغ تشين 1.0، create_agent صارت المعيار الوحيد لبناء الوكلاء، وقد حلّت محل langgraph.prebuilt.create_react_agent. أي أن كل مقال أو درس قديم مبنيّ على create_react_agent يتحدّث عن واجهة تصمّمها الدالة نيابةً عنك، لا عن بنية تركّبها أنت.
السبب أعمق من تغيير اسم: create_agent لم تعد تغلّف حلقة فيصممها لك، بل تسلّمك بنية تشغيل الوكيل (harness) كمجموعة قطع تمرّرها أنت. والوثائق نفسها تصف الوساطة بأنها السمة المميزة لهذه الدالة، لأنها «مدخل قابل للتخصيص بدرجة عالية، يرفع سقف ما تستطيع بنائه».
عملياً: مع create_agent تحدّد model= وtools= وsystem_prompt=، ثم تبني قدرةً طبقةً طبقة عبر middleware= بدل إعادة كتابة رسم بياني كامل في لانغ غراف (LangGraph).
إن كنت تتبع درساً قديماً، احذف
LLMChainوConversationChainمن ذهنك: النطاقlangchainنُظِّف، وما تقدّمه الآن هو لبنات بناء الوكلاء. تخصيص أعمق للوكيل نفسه — تحكّم بالخطوة التالية، تقسيم فرعي، تحكّم في التنفيذ — يبقى عمل لانغ غراف.
ما الحد الأدنى الذي يعطيك وكيلاً يعمل؟
ثلاثة معاملات تكفي: model= وtools= وsystem_prompt=. لا رسم بياني، ولا تجميع، ولا إعداد بيئة تشغيل.
from langchain.agents import create_agent
def check_weather(location: str) -> str:
"""Return the weather forecast for the specified location."""
return f"It's always sunny in {location}"
agent = create_agent(
model="openai:gpt-5.5",
tools=[check_weather],
system_prompt="You are a helpful assistant. Be concise and accurate.",
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "what is the weather in sf"}]}
)
print(result["messages"][-1].content)
صيغة معرّف النموذج "المزوّد:الموديل" تعني أنك تبدّل المزوّد بسطر واحد: openai:gpt-5.5 أو anthropic:claude-sonnet-5 أو google_genai:gemini-3.6-flash، والبنية نفسها تعمل.
ملاحظة صدق عن الوثائق: معرّفات الموديلات لا تتّسق بين صفحات لانغ تشين نفسها — فمثلاً صفحة الوكلاء تعرض
anthropic:claude-sonnet-5بينما أمثلة صفحات أخرى تعرضclaude-sonnet-4-6. أعرض هنا ما ورد حرفياً في أمثلة الوثائق التي أقرأ منها الكود، لكن لا تعتمد على أي معرّف معرّف زمنياً؛ تحقّق من صفحة المزوّد قبل النشر.
الدالة تُعيد CompiledStateGraph مجمّعاً، جاهزاً للاستدعاء. ما تفعله بـthread_id وcheckpointer موضوع المقالة القادمة عن الذاكرة، أما هنا فندخل في الإنتاجية خطوة بخطوة.
ما هي AgentState وكيف تبني حالةً مخصّصة فوقها؟
كل وكيل يدير سياق تنفيذه عبر AgentState — وهو TypedDict، أي مُخطَّط مفاتيحه نصوص، يحمل سجلّ المحادثة وأي حقول تحتاجها أدواتك ووساطك. الحقل المدمج الوحيد هو messages: قائمة BaseMessage تحمل كامل سجلّ المحادثة للحالة الحالية، وهو append-only: الرسائل الجديدة تُضاف ولا تستبدل أبداً.
هذا يعني أنك لا تحتاج أن تعرف كيف تُحدَّث رسالة واحدة، ولا تكتب دالة دمج (reducer) خاصة بسجلّ الرسائل. كل ما تحتاجه هو وراثة AgentState وإضافة حقولك، ثم تمريرها بـstate_schema=:
from langchain.agents import AgentState, create_agent
class MyState(AgentState):
user_id: str
call_count: int
agent = create_agent(
model="openai:gpt-5.5",
tools=[],
state_schema=MyState,
)
result = agent.invoke({
"messages": [{"role": "user", "content": "من أنا؟"}],
"user_id": "user-123",
"call_count": 0,
})
معلمة state_schema تقبل «مُخطَّط TypedDict يوسّع AgentState». استخدمه للحقول التي تحتاجها أداتك أو خطوطك على نطاق الوكيل كله.
لكن هناك خيار أنظف في كثير من الحالات: لو كانت حقولك تخصّ خطّافات بعينها، فاكتبها داخل state_schema الخاص بتلك الوساطة نفسها بدل توسيع حالة الوكيل كلها. الوثائق توصي بذلك صراحةً: إبقاء التوسيع في نطاق الخطّاف الذي يحتاجه يحفظ المشهد.
تحذير عملي: create_agent لا يدعم مخطّطات Pydantic للحالة
هذا من أكثر الأسئلة التي تصلني، وهو شائع جداً لأن منهج بناء الرسم البياني في لانغ غراف يقود الناس إليه. حين تبني رسماً بيانياً موجّهاً بنفسك في لانغ غراف، تُعرَّف الحالة عادةً بنموذج Pydantic لأجل التحقّق. create_agent لا تدعم ذلك: توثيق لانغ غراف نفسه ينصّ صراحةً أن «المصنع الأعلى مستوى create_agent في langchain لا يدعم مخطّطات Pydantic للحالة».
ما يعني عملياً: إذا كان قالبك الحالي يمرّر class State(BaseModel) إلى create_agent، فلن يعمل. استخدم TypedDict مثل MyState أعلاه. إن كنت تشتغل على رسم في لانغ غراف وتريد التحقّق من الحقول، فذاك إطار آخر وموضوع آخر — لا تحاول المزج بين المنهجيين.
كيف تحصل على مخرجات مهيكلة عبر response_format؟
response_format= تخبر الوكيل أن يُعيد بيانات ضمن مُخطَّط تتحقّق منه المكتبة نيابةً عنك، وتضعها في مفتاح structured_response داخل الحالة النهائية — بدل أن تحلّل نصاً حراً بنفسك.
المعامل يقبل أربعة أشياء:
ToolStrategy: يحقّق المُخطَّط عبر استدعاء الأدوات، ويعمل مع كل نموذج يدعم استدعاء الأدوات.ProviderStrategy: يستعمل المخرجات المهيكلة الأصلية في مزوّد النموذج، وهي الأدقّ حين تكون متاحة.- صنف المُخطَّط مباشرة (نموذج Pydantic أو dataclass أو
TypedDict): تختار لانغ تشين الاستراتيجية الأنسب تلقائياً حسب قدرات النموذج —ProviderStrategyلمزوّدات مثل OpenAI وAnthropic (Claude) وxAI (Grok)، وToolStrategyلغيرها. None: لا مخرجات مهيكلة.
مثال عملي مع ToolStrategy:
from typing import Literal
from pydantic import BaseModel, Field
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
class ProductReview(BaseModel):
"""Analysis of a product review."""
rating: int | None = Field(description="The rating of the product", ge=1, le=5)
sentiment: Literal["positive", "negative"] = Field(description="The sentiment of the review")
key_points: list[str] = Field(
description="The key points of the review. Lowercase, 1-3 words each."
)
agent = create_agent(
model="gpt-5.5",
tools=[],
response_format=ToolStrategy(ProductReview),
)
result = agent.invoke({
"messages": [{
"role": "user",
"content": "Analyze this review: 'Great product: 5 out of 5 stars. Fast shipping, but expensive'",
}]
})
print(result["structured_response"])
# ProductReview(rating=5, sentiment='positive', key_points=['fast shipping', 'expensive'])
فائدة التكلفة هنا حقيقية لا نظرية. المنهج القديم كان يطلب من النموذج صياغة الجواب، ثم يستدعي النموذج مرة ثانية للتحويل إلى JSON — نداء إضافي كامل في كل مرة. في 1.0، المخرجات المهيكلة تُولَّد داخل الحلقة الرئيسية نفسها بدل نداء إضافي للّغة، «ما يلغي التكلفة الإضافية من نداءات LLM» كما تقول الوثائق.
عملياً: كل استخراج حقل متكرر — تصنيف تذاكر، تلخيص ملاحظات عملاء، تحويل نصّ حر إلى مُخطَّط — يوفّر عندك نداءً كاملاً في كل طلب. على وكيل يخدم آلاف الطلبات يومياً، هذا فرق في الفاتورة لا في الكود فقط.
تحذيران عمليان من الوثائق: إذا مرّرت قاموس JSON Schema مباشرة إلى response_format فلن يُكتشف تلقائياً — لازم تغلّفه صراحةً في ProviderStrategy أو ToolStrategy. وثانياً: عند أخطاء التحقّق يعيد الوكيل محاولةً تلقائياً افتراضياً، وتحكم في ذلك بـhandle_errors (نصّ ثابت، أو نوع استثناء، أو دالة تُرجع الرسالة).
لماذا الطبقة الوسيطة هي محور create_agent؟
الوساطة ليست تزييناً ولا طبقة HTTP — هي الأداة الأساسية للتخصيص عند لانغ تشين. الفكرة كلها: «كل قطعة تعني اهتماماً واحداً، وتتداخل في الحلقة في التوقيت الصحيح، وتنسجم بحرية مع أي أخرى». خذ فقط ما يحتاجه حالتك، واترك الباقي.
ما تعطيك إياه عملياً:
- تتبّع سلوك الوكيل عبر التسجيل والتحليلات والتنقيح.
- تحويل الموجّهات وأدوات الاختيار وتنسيق المخرجات قبل وصولها للنموذج.
- إضافة إعادة المحاولة والبدائل الاحتياطية ومنطق الإنهاء المبكر.
- تطبيق حدود المعدّل وحواجز الحماية وكشف بيانات التعريف الشخصية.
وهي ليست بيئة تشغيل منفصلة: الخطّافات تعمل داخل الرسم المُجمَّع الذي تعيده create_agent. يمكنك وضع الوكيل كله — بوساطه — داخل رسم أكبر في لانغ غراف كعقدة أو رسم فرعي، وستستمر كل خطّافاته بالعمل.
الترتيب الذي تمرّر به الوسائط يمرّره لانغ تشين باحترام:
- خطّافات
before_*: من الأول إلى الأخير. - خطّافات
after_*: من الأخير إلى الأول (معكوسة). - خطّافات
wrap_*: متداخلة — الوسيط الأول في القائمة يلتفّ حول البقية كلّها.
ما الخطّافات الستة ومتى يعمل كل خطّاف؟
هذه أهم جدول في المقال. من يفهم هذه اللحظات الستّ يعرف أين يضع كل سطر تخصيص يكتبه.
| الخطّاف | متى يعمل داخل الدورة | مثال استخدام |
|---|---|---|
before_agent |
مرة واحدة قبل أن يبدأ الوكيل (لكل نداء) | تهيئة تعليمات من ملفّ تعريف المستخدم قبل أول خطوة؛ رفض الطلب مبكراً إن كان غير مسموحاً |
before_model |
قبل كل نداء للنموذج في الحلقة | تصفية سجلّ الرسائل قبل الإرسال؛ حقن سياق محدّث؛ فحص سقف عدد الرسائل |
wrap_model_call |
يلتفّ حول كل نداء نموذج — أنت تقرّر إن نُادي handler مرة، أو صفراً (اختصار)، أو أكثر من مرة (إعادة محاولة) |
اختيار النموذج حسب المستخدم أو عبء الطلب؛ بديل احتياطي عند فشل المزوّد؛ إعادة محاولة تلقائية؛ موجّه ديناميكي؛ تخزين مؤقّت |
wrap_tool_call |
يلتفّ حول كل نداء أداة، بنفس منطق التفاف | تسجيل تنفيذ الأداة ومخرجاتها؛ التقاط الاستثناءات وتحويلها إلى رسالة خطأ يفهمها النموذج؛ إعادة محاولة الأداة |
after_model |
بعد كل ردّ نموذج (قبل تنفيذ أدواته) | تسجيل الردّ؛ فحص محتوى محظور؛ قياس التكلفة؛ إيقاف مبكر بـjump_to |
after_agent |
مرة واحدة بعد انتهاء الوكيل (لكل نداء) | تدقيق نهائي؛ تسجيل مقاييس النداء؛ تنظيف موارد أو إرسال إشعار «اكتمل» |
القاعدة الفاصلة بين المجموعتين: الخطّافات بأسلوب عقدة (before_agent، before_model، after_model، after_agent) تعمل بشكل تسلسلي عند نقطة محدّدة، وتُحدّث الحالة بإرجاع dict تُدمج عبر دوالّ الدمج (reducers) في الرسم. أما الخطّافات بأسلوب الالتفاف (wrap_model_call، wrap_tool_call) فتحيط بالنداء نفسه وتعطيك تحكّماً في التدفّق: هل تُنادي التنفيذ أم لا، وكم مرّة.
استعمل أسلوب العقدة للأمور التسلسلية — تسجيل، تحقّق، حساب. واستعمل أسلوب الالتفاف للتحكّم بالتدفّق — إعادة محاولة، بديل احتياطي، تخزين مؤقّت، اختصار.
متى تقفز خارج الدورة؟
كل خطّاف عقدة يستطيع إرجاع jump_to لقفزة مبكرة: 'end' يقفز إلى نهاية تنفيذ الوكيل أو إلى أول خطّاف after_agent، و'tools' يقفز إلى عقدة الأدوات، و'model' يقفز إلى عقدة النموذج أو أول خطّاف before_model. هكذا توقف حلقة لا تتوقّف دون أن تلمس رسمك البياني.
مثال سقف رسائل — لاحظ أنه يُعيد قفزة end قبل أن يدفع النموذج فاتورة زائدة:
from typing import Any
from langchain.agents.middleware import before_model, AgentState, hook_config
from langchain.messages import AIMessage
from langgraph.runtime import Runtime
@before_model(can_jump_to=["end"])
def check_message_limit(
state: AgentState, runtime: Runtime
) -> dict[str, Any] | None:
if len(state["messages"]) >= 50:
return {
"messages": [AIMessage("Conversation limit reached.")],
"jump_to": "end",
}
return None
ما الوسيطات الجاهزة التي تستحق أن تبدأ بها؟
لا تبدأ من صفر. لانغ تشين يوفّر طبقة كاملة من الوساط الجاهزة للإنتاج، موزّعة على ست فئات — بيئة التنفيذ، وإدارة السياق، والتخطيط والتكليف، والتحمّل عند الأعطال، وحواجز الحماية، والتوجيه البشري. وأشهر ما فيها هو التالي:
تلخيص المحادثة (SummarizationMiddleware) — يضغط سجلّ المحادثة قبل أن تملأ نافذة السياق. تحدّد متى يلخّص (trigger) وكم يحتفظ (keep):
SummarizationMiddleware(
model="gpt-5.4-mini",
trigger=("tokens", 4000),
keep=("messages", 20),
),
يمكنك أيضاً الضبط بـfraction (نسبة من حجم السياق)، أو بـmessages، أو بقائمة شروط (أيّها يتحقّق = OR) أو قاموس شروط (كلّها تتحقّق = AND).
تدخّل بشري في الحلقة (HumanInTheLoopMiddleware) — يوقف التنفيذ قبل أداة حسّسة وينتظر موافقة أو تعديلاً أو رفضاً. الشرط مهم: هذه الوساطة تتطلب checkpointer للحفاظ على الحالة عبر الانقطاع، فأنشئ الوكيل بـcheckpointer=InMemorySaver() إن جرّبت محلياً.
HumanInTheLoopMiddleware(
interrupt_on={
"your_send_email_tool": {
"allowed_decisions": ["approve", "edit", "reject"],
},
"your_read_email_tool": False,
}
),
كشف بيانات التعريف الشخصية (PIIMiddleware) — يكتشف بيانات التعريف في الرسائل ويطبّق استراتيجية: redact (استبدال بـ[REDACTED_…])، أو mask (إخفاء جزئي)، أو hash (بصمة ثابتة)، أو block (رفع استثناء). تتحكم أين يفحص: apply_to_input لرسائل المستخدم، وapply_to_output لردّ النموذج، وapply_to_tool_results لنتائج الأدوات.
وحدّ الاستدعاءات — لتفادي انفجار في الفاتورة: ModelCallLimitMiddleware وToolCallLimitMiddleware يأخذان وسيطين لكل مستوى: thread_limit يحدّ إجمالي النداءات على المحادثة كلها، وrun_limit يحدّها داخل النداء الواحد. مثال موثّق من الوثائق: thread_limit=10 مع run_limit=5 لعداد استدعاءات النموذج، وthread_limit=20 مع run_limit=10 لعداد استدعاءات الأدوات.. الحدّ على مستوى المحادثة يحتاج checkpointer.
التحمّل عند الأعطال — ModelRetryMiddleware وToolRetryMiddleware بإعادة محاولة تلقائية، وModelFallbackMiddleware ينتقل إلى نموذج بديل حين يفشل الأساسي، وToolErrorMiddleware يحوّل استثناء الأداة إلى رسالة خطأ يفهمها النموذج بدل أن يفشل النداء كله.
تنبيه إصدارات: بعض هذه الوساط مؤهَّلة لإصدارات حديثة من
langchain. مثال حرفي من الوثائق:ToolErrorMiddlewareيتطلبlangchain>=1.3.14. ثبّت أحدث نسخة ولا تفترض أن اسم الوسيط موجود في إصدارك.
كيف تكتب وسيطتك الخاصة؟
الآن الجزء الأمتع. الوسيط إمّا دالة واحدة يعلوها مُزخرِف (decorator) مثل @wrap_model_call حين يكون الخطّاف واحداً، وإمّا صنف يورث AgentMiddleware إن أردت عدة خطّافات أو إعدادات وقت الإنشاء.
المثال الكامل هنا يختار نموذجاً حسب خبرة المستخدم، ويقرأ الخبرة من runtime.context — أي من سياق هذا النداء، لا من الحالة المحفوظة:
from collections.abc import Callable
from dataclasses import dataclass
from langchain.agents import create_agent
from langchain.agents.middleware import (
AgentMiddleware,
ModelRequest,
ModelResponse,
)
from langchain.chat_models import init_chat_model
@dataclass
class RequestContext:
experience_level: str # "beginner" | "advanced"
ADVANCED_MODEL = init_chat_model("gpt-5.5")
BEGINNER_MODEL = init_chat_model("gpt-5-nano")
class ExperienceRoutingMiddleware(AgentMiddleware):
"""يختار نموذج أثقل للمتمرّسين وأخفّ للمبتدئين، داخل نفس الوكيل."""
def wrap_model_call(
self,
request: ModelRequest,
handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
level = getattr(request.runtime.context, "experience_level", "advanced")
model = BEGINNER_MODEL if level == "beginner" else ADVANCED_MODEL
return handler(request.override(model=model))
agent = create_agent(
model="gpt-5.5",
tools=[check_weather],
context_schema=RequestContext,
middleware=[ExperienceRoutingMiddleware()],
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "ما هو thread_id؟"}]},
context=RequestContext(experience_level="beginner"),
)
لماذا wrap_model_call لهذا بالذات؟ لأنه خطّاف الالتفاف: أنت تستقبل الطلب، تعدّله، ثم تنادي handler(request.override(model=model)) لتتابع الدورة. لو استخدمت before_model لتغيير النموذج لما استطعت — ذلك الخطّاف يعدّل الحالة والموجّه، لا الطلب المُمرَّر إلى النموذج نفسه. وبما أن الالتفافات متداخلة، يمكنك أن تحيط بهذا الوسيط وسيط إعادة محاولة أو بديل احتياطي آخر في القائمة نفسها، وينسجمان معاً بالترتيب الذي كتبته.
الأعراف عملية: اجعل كل وسيط مسؤولاً عن اهتمام واحد، تعامل مع الأخطاء بلطف حتى لا تُسقط الوكيل، اكتب تعريف حقول الحالة المخصّصة بوضوح، وثّق أي سند لسلوك الوسيط عند الحاجة.
ما الفرق بين runtime وcontext؟ ومتى يخطئ المبتدئ؟
الخلط بينهما خطأ شائع جداً وله أعراض صامتة: الوسيط يقرأ بيانات النداء الأخير في حين كان يقصد بيانات المحادثة، فلا تعرف من أين جاء الخلل.
thread_id |
context |
|
|---|---|---|
| ما هو | مُعرّف يختار محادثة بعينها | كائن بيانات ترسله مع الاستدعاء |
| أين يمرّ | داخل config في configurable |
معامل مستقل إلى جانب config |
| عمره | يبقى عبر الاستدعاءات ما دام checkpointer موجوداً |
هذا النداء فقط، ثم يُرمى |
| ما يصل منه | نقاط الحفظ وسجلّ الرسائل | الأدوات والوساط عبر runtime.context |
| غرضه | استمرارية المحادثة | بيانات هذا الطلب: معرّف المستخدم، مفاتيح واجهة برمجية، أعلام ميزة |
بصياغة الوثائق: «thread_id يحدّد المحادثة (سجلّ الرسائل، نقاط الحفظ)، بينما context يحمل بيانات لكل نداء تقرأها أدواتك ووساطك وقت الاستدعاء. ويُمرَّر الاثنان معاً عادةً».
المعنى العملي: ضع في context ما يخصّ هذا الطلب فقط (من المستخدم؟ ما اللغة المفضّلة؟ ما المفتاح؟)، وفي الحالة ما يجب أن يتذكّره الوكيل بين النداءات. والوسيط يقرأ الأول عبر runtime.context، والثاني من state.
ولاحظ أن runtime هو ما يجمع الاثنين: العقدة تستقبل state وruntime، وruntime.context هو مدخلك إلى بيانات النداء، بينما الحالة هي ما يُحفظ.
ماذا ستبني في هذا المقال خطوة بخطوة؟
اجمع ما سبق في وكيل واحد حقيقي. ابدأ بالحد الأدنى، ثم أضف القدرات واحدة واحدة، كل واحدة وسط مستقل:
from langchain.agents import create_agent
from langchain.agents.middleware import (
PIIMiddleware,
SummarizationMiddleware,
HumanInTheLoopMiddleware,
)
from langgraph.checkpoint.memory import InMemorySaver
from langchain_core.utils.uuid import uuid7
agent = create_agent(
model="gpt-5.5",
tools=[check_weather, read_email, send_email],
checkpointer=InMemorySaver(),
context_schema=RequestContext,
middleware=[
PIIMiddleware("email", strategy="redact", apply_to_input=True),
SummarizationMiddleware(
model="gpt-5.4-mini",
trigger=("tokens", 4000),
keep=("messages", 20),
),
HumanInTheLoopMiddleware(interrupt_on={"send_email": True}),
ExperienceRoutingMiddleware(),
],
)
config = {"configurable": {"thread_id": str(uuid7())}}
result = agent.invoke(
{"messages": [{"role": "user", "content": "راسل فريق الدعم عن حالة طلبي"}]},
config=config,
context=RequestContext(experience_level="beginner"),
)
قائمة تحقق قبل أن تسلّم هذا الوكيل:
- الوكيل يُنشأ بـ
create_agentلا بـcreate_react_agent، وأي استيراد قديم محذوف. - حالة الوكيل مبنية على
AgentStateبـTypedDict، لا على نموذج Pydantic. - لكل خطّاف مهم سبب واضح للاختيار بين أسلوب العقدة وأسلوب الالتفاف.
- بيانات النداء في
context، وبيانات المحادثة في الحالة عبرthread_id. response_formatيعطيstructured_responseيتحقّق منه مُخطَّط، لا نصّاً تحلّله يدوياً.- حدّ استدعاءات يحمي الفاتورة، ووساطة التدخّل البشري تحمي الأدوات الحسّاسة.
الخطوة التالية: أضف state_schema= مخصّصاً لكل وسيطة تحتاج عدّادات، أو انتقل إلى مقالة الذاكرة لتعرف كيف يبني checkpointer محادثة تصمد بعد إغلاق البرنامج.
تحتاج وكيلاً يعمل لفريقك؟
نصمّم ونبني أنظمة وكلاء للشركات والأفراد فوق لانغ تشين (LangChain) ولانغ غراف (LangGraph) — من إثبات المفهوم في أسبوع إلى نظام إنتاجي يُشغَّل يومياً. ابدأ بطلبك من صفحة التواصل.
المصادر
كل ادعاء في المقال مرتبط بمصدره. الروابط تفتح في نافذة جديدة.
- 01
- 02Structured output — LangChain ↗
docs.langchain.com
ToolStrategy هي الاستراتيجية المختارة تلقائياً للنماذج التي لا تدعم المخرجات المهيكلة أصلاً في مزوّدها
- 03LangChain v1 release notes ↗
docs.langchain.com
create_agent هي الطريقة القياسية لبناء الوكلاء في LangChain 1.0 وقد حلت محل langgraph.prebuilt.create_react_agent
- 04Structured output — LangChain ↗
docs.langchain.com
response_format يقبل ToolStrategy أو ProviderStrategy أو صنف مُخطط مباشرة، وتوضع النتيجة في structured_response
- 05Agents — LangChain ↗
docs.langchain.com
thread_id ينسّق المحادثة بينما context يحمل بيانات لكل نداء يقرأها الوكلاء ووساطهم وقت الاستدعاء ويُمرّران معاً عادة
- 06Prebuilt middleware — LangChain ↗
docs.langchain.com
استراتيجيات كشف بيانات التعريف الشخصية هي block وredact وmask وhash مع تحكم في apply_to_input وapply_to_output
- 07Agents — LangChain ↗
docs.langchain.com
الحقل messages في AgentState مدمج وسجلّه append-only: تُضاف الرسائل ولا تستبدل
- 08LangChain v1 release notes ↗
docs.langchain.com
الخطّافات الستة هي before_agent وbefore_model وwrap_model_call وwrap_tool_call وafter_model وafter_agent
- 09LangChain v1 release notes ↗
docs.langchain.com
الوساطة هي السمة المميزة لـ create_agent لأنها تمنح مدخلاً قابلاً للتخصيص بدرجة عالية
- 10Agents — LangChain ↗
docs.langchain.com
الوكيل نموذج يستدعي أدوات في حلقة حتى تكتمل المهمة، وبنية تشغيل الوكيل هي كل ما يحيط بحلقة النموذج
- 11Custom middleware — LangChain ↗
docs.langchain.com
ترتيب التنفيذ يحترم قبل_* من الأول إلى الأخير وafter_* بالعكس وwrap_* متداخلة بحيث يلتفّ أول وسيط على البقية
- 12Custom middleware — LangChain ↗
docs.langchain.com
خطّافات العقدة تعمل تسلسلياً عند نقاط محدّدة وتُحدّث الحالة بإرجاع dict، بينما خطّافات الالتفاف تعطي تحكما في التدفق بعدد استدعاءات handler
- 13LangChain v1 release notes ↗
docs.langchain.com
في v1 تُولَّد المخرجات المهيكلة داخل الحلقة الرئيسية بدل نداء إضافي للغة، ما يلغي التكلفة الإضافية
- 14Structured output — LangChain ↗
docs.langchain.com
قاموسات JSON Schema يجب تغليفها صراحة في ProviderStrategy أو ToolStrategy فلا تُكتشف تلقائياً
- 15Custom middleware — LangChain ↗
docs.langchain.com
قفزات jump_to المتاحة هي end وtools وmodel للخروج المبكر من الحلقة
- 16LangGraph — Graph API (state, reducers, nodes) ↗
docs.langchain.com
مصنع create_agent الأعلى مستوى في langchain لا يدعم مخططات Pydantic للحالة
- 17LangChain v1 release notes ↗
docs.langchain.com
نطاق langchain نُظِّف ليُركّز على لبنات بناء الوكلاء الأساسية ونُقلت وظائفه القديمة إلى langchain-classic
- 18Prebuilt middleware — LangChain ↗
docs.langchain.com
وساطة تدخّل بشري في الحلقة تتطلب checkpointer للحفاظ على الحالة عبر الانقطاع
- 19Agents — LangChain ↗
docs.langchain.com
يمكن توسيع حالة الوكيل بوراثة AgentState وتمريرها إلى create_agent عبر state_schema



