لم تبنِ Anthropic نموذج Claude فحسب، بل غلّفت آلية الوكيل نفسها التي تشغّل Claude Code وأصدرتها كمكتبة. تمنحك Claude Agent SDK للغة بايثون حلقة الوكيل ذاتها، وتنفيذ الأدوات، وإدارة السياق، ونظام الأذونات، كشيفرة قابلة للاستيراد والتركيب.
إذا كنت تعمل بلغة بايثون — خطوط بيانات، خدمات خلفية، سكربتات أتمتة، أدوات تعلّم آلي — فهذه أسرع طريقة لإضافة وكيل مستقل حقيقي إلى منظومتك. في هذا الدرس ستنتقل من بيئة افتراضية فارغة إلى وكيل يقرأ الملفات، ويشغّل الأوامر، ويستدعي أدواتك المخصصة، ويفرض قواعد الأمان التي تحدّدها أنت.
فرق الـ Agent SDK عن واجهة Messages الخام. تمنحك واجهة Messages استجابة نموذج واحدة. أمّا الـ Agent SDK فيمنحك الحلقة المحيطة بها: تستدعي الأدوات، وتعيد النتائج إلى النموذج، وتدير نافذة السياق، وتعيد المحاولة، وتتوقف عند اكتمال المهمة. أنت تصف الهدف، والـ SDK يشغّل الوكيل.
ما الذي ستتعلمه
بنهاية هذا الدرس ستكون قادرًا على:
- إعداد مشروع بايثون مع Claude Agent SDK
- تشغيل وكيل مستقل لمرة واحدة عبر دالة
query - إدارة محادثة متعددة الأدوار مع البث باستخدام
ClaudeSDKClient - كشف دوال بايثون الخاصة بك كأدوات عبر مُزخرِف
@tool - تقييد الإجراءات الخطيرة عبر دالة أذونات
can_use_tool - مراقبة وتدقيق كل استدعاء أداة عبر خطاطيف دورة الحياة
- تطبيق أنماط الإنتاج المتعلقة بالتكلفة والأمان والموثوقية
المتطلبات المسبقة
قبل البدء، تأكد من توفّر:
- بايثون 3.10 أو أحدث مثبّتًا (
python --version) - Node.js 18+ مثبّتًا — إذ يتخاطب الـ SDK مع واجهة Claude Code CLI في الخلفية
- مفتاح Anthropic API من لوحة التحكم، أو اشتراك Claude فعّال
- إلمام أساسي بـ
async/awaitوasyncioفي بايثون
الخطوة 1: إعداد المشروع
أنشئ بيئة معزولة وثبّت الـ SDK. سنستخدم venv المدمجة، لكن uv أو poetry يعملان بالكفاءة نفسها.
mkdir claude-agent-python && cd claude-agent-python
python -m venv .venv
source .venv/bin/activate # على ويندوز: .venv\Scripts\activate
pip install claude-agent-sdkيعتمد الـ SDK على واجهة Claude Code CLI، فثبّتها أيضًا:
npm install -g @anthropic-ai/claude-codeأخيرًا، صدّر مفتاحك ليتمكن الـ SDK من المصادقة:
export ANTHROPIC_API_KEY="sk-ant-..."نصيحة: لا تضع مفتاح الـ API في الشيفرة أبدًا. استخدم متغير بيئة أو مدير أسرار. أضف ملف .env إلى .gitignore وحمّله باستخدام python-dotenv إن كنت تفضّل الاحتفاظ بالمفاتيح في ملف محليًا.
الخطوة 2: أول وكيل لك باستخدام query
دالة query هي أبسط نقطة انطلاق. تُشغّل حلقة وكيل مستقلة بالكامل وتُنتج الرسائل أثناء بثّها. أنشئ ملف first_agent.py:
import anyio
from claude_agent_sdk import query, ClaudeAgentOptions
async def main():
options = ClaudeAgentOptions(
system_prompt="You are a concise senior Python engineer.",
allowed_tools=["Read", "Glob", "Grep"],
cwd=".",
)
async for message in query(
prompt="List the Python files in this folder and summarize what each does.",
options=options,
):
print(message)
anyio.run(main)شغّله:
python first_agent.pyيقرر الوكيل من تلقاء نفسه استدعاء Glob للعثور على الملفات، وRead لفتحها، ثم يكتب ملخصًا. لم تكتب أنت أي تنسيق للحلقة — الـ SDK هو من شغّلها. لاحظ أننا مرّرنا allowed_tools لتقييد الوكيل على عمليات القراءة فقط؛ فهو لا يستطيع الكتابة أو تشغيل أوامر الصدفة هنا.
فهم دفق الرسائل
تُنتج query كائنات رسائل ذات أنواع محددة. أكثرها فائدة:
AssistantMessage— نصوص وكتل استخدام الأدوات التي أنتجها النموذجUserMessage— نتائج الأدوات المُعادة إلى الحلقةResultMessage— الملخص النهائي، متضمنًا استهلاك الرموز والتكلفة
لنُرشّح الدفق لطباعة نص المساعد فقط والتكلفة النهائية:
from claude_agent_sdk import (
query, ClaudeAgentOptions,
AssistantMessage, TextBlock, ResultMessage,
)
async def main():
options = ClaudeAgentOptions(allowed_tools=["Read", "Glob"])
async for message in query(prompt="Summarize README.md", options=options):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text)
elif isinstance(message, ResultMessage):
print(f"\n--- done in {message.num_turns} turns, "
f"cost ${message.total_cost_usd:.4f} ---")الخطوة 3: المحادثات متعددة الأدوار مع ClaudeSDKClient
دالة query عديمة الحالة — طلب واحد، تشغيل واحد. للجلسات التفاعلية التي يتذكّر فيها الوكيل السياق عبر الأدوار، استخدم ClaudeSDKClient. فهو يُبقي الجلسة حيّة لإرسال طلبات متابعة.
import anyio
from claude_agent_sdk import (
ClaudeSDKClient, ClaudeAgentOptions,
AssistantMessage, TextBlock,
)
async def main():
options = ClaudeAgentOptions(
system_prompt="You are a helpful coding assistant.",
allowed_tools=["Read", "Write", "Edit", "Bash"],
permission_mode="acceptEdits",
)
async with ClaudeSDKClient(options=options) as client:
# الدور الأول
await client.query("Create a file hello.py that prints today's date.")
async for msg in client.receive_response():
if isinstance(msg, AssistantMessage):
for block in msg.content:
if isinstance(block, TextBlock):
print(block.text)
# دور المتابعة — يتذكّر الوكيل hello.py
await client.query("Now add a function that returns the date as ISO 8601.")
async for msg in client.receive_response():
if isinstance(msg, AssistantMessage):
for block in msg.content:
if isinstance(block, TextBlock):
print(block.text)
anyio.run(main)تُدير كتلة async with دورة حياة الجلسة. تُرسل client.query() طلبًا، بينما تبثّ client.receive_response() الرسائل حتى اكتمال ذلك الدور. ولأن العميل يبقى قائمًا، يعمل الطلب الثاني على الملف الذي أُنشئ في الطلب الأول.
قيمة permission_mode مهمّة. الوضع الافتراضي يطلب تأكيدًا قبل التعديلات. وقد ضبطناه هنا على acceptEdits ليتمكن الوكيل من كتابة الملفات دون مطالبة — وهو مناسب لسكربت موثوق، لكنك ستتعلم في الخطوة 5 كيفية تقييد الإجراءات المسموح بها بدقة.
الخطوة 4: الأدوات المخصصة عبر مُزخرِف @tool
تغطّي الأدوات المدمجة نظام الملفات والصدفة. ولربط وكيلك بـ عالمك — قاعدة بيانات، واجهة برمجية داخلية، محرك تسعير — عرّف أدوات مخصصة. يكشفها الـ SDK عبر خادم MCP داخل العملية نفسها، فلا توجد قفزة عبر الشبكة.
إليك وكيلًا يستطيع البحث عن الطلبات وحساب تكلفة الشحن، مدعومًا بدوال بايثون بسيطة:
import anyio
from claude_agent_sdk import (
tool, create_sdk_mcp_server,
ClaudeSDKClient, ClaudeAgentOptions,
AssistantMessage, TextBlock,
)
# مخزن بيانات وهمي للعرض التوضيحي
ORDERS = {
"A100": {"customer": "Layla", "weight_kg": 2.5, "country": "TN"},
"A101": {"customer": "Omar", "weight_kg": 8.0, "country": "SA"},
}
@tool("get_order", "Fetch an order by its ID", {"order_id": str})
async def get_order(args):
order = ORDERS.get(args["order_id"])
if not order:
return {"content": [{"type": "text", "text": "Order not found"}]}
return {"content": [{"type": "text", "text": str(order)}]}
@tool("shipping_cost", "Estimate shipping cost in USD",
{"weight_kg": float, "country": str})
async def shipping_cost(args):
base = 5.0 if args["country"] == "TN" else 12.0
cost = base + args["weight_kg"] * 1.5
return {"content": [{"type": "text", "text": f"${cost:.2f}"}]}
async def main():
server = create_sdk_mcp_server(
name="fulfillment",
version="1.0.0",
tools=[get_order, shipping_cost],
)
options = ClaudeAgentOptions(
mcp_servers={"fulfillment": server},
allowed_tools=[
"mcp__fulfillment__get_order",
"mcp__fulfillment__shipping_cost",
],
)
async with ClaudeSDKClient(options=options) as client:
await client.query("What will it cost to ship order A101?")
async for msg in client.receive_response():
if isinstance(msg, AssistantMessage):
for block in msg.content:
if isinstance(block, TextBlock):
print(block.text)
anyio.run(main)ثلاثة أمور تستحق الانتباه:
- يأخذ مُزخرِف
@toolاسمًا ووصفًا ومخططًا يربط أسماء الوسائط بأنواعها. يقرأ Claude الوصف ليقرر متى يستدعيها. - تُجمّع
create_sdk_mcp_serverأدواتك في خادم تسجّله تحت مساحة أسماء. - تُعنوَن الأدوات بالصيغة
mcp__<server>__<tool>. يجب إدراجها فيallowed_toolsوإلا لن يتمكن الوكيل من استدعائها.
سيسلسل الوكيل الأدوات من تلقاء نفسه: يجلب الطلب A101، يقرأ الوزن والدولة، ثم يستدعي shipping_cost بتلك القيم — دون أي ربط يدوي.
الخطوة 5: حماية الإجراءات عبر دالة أذونات
الاستقلالية دون ضوابط مسؤولية محفوفة بالمخاطر. تعمل دالة can_use_tool قبل كل استدعاء أداة، فتتيح لك السماح أو الرفض أو إعادة الصياغة بناءً على منطقك الخاص.
from claude_agent_sdk import (
ClaudeSDKClient, ClaudeAgentOptions,
PermissionResultAllow, PermissionResultDeny,
ToolPermissionContext,
)
async def gatekeeper(tool_name: str, tool_input: dict,
context: ToolPermissionContext):
# حظر أوامر الصدفة التدميرية مباشرة
if tool_name == "Bash":
command = str(tool_input.get("command", ""))
for danger in ("rm -rf", "sudo", "mkfs", ":(){"):
if danger in command:
return PermissionResultDeny(
message=f"Blocked dangerous command: {danger}",
interrupt=True,
)
# كل ما عدا ذلك مسموح
return PermissionResultAllow()
async def main():
options = ClaudeAgentOptions(
allowed_tools=["Read", "Write", "Bash"],
can_use_tool=gatekeeper,
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Clean up temp files in this directory.")
async for msg in client.receive_response():
print(msg)إذا حاول الوكيل تنفيذ أي شيء يطابق قائمة الحظر، تُعيد الدالة PermissionResultDeny مع interrupt=True، ما يوقف التشغيل فورًا. وأعِد PermissionResultAllow للمتابعة. هذا خطّ دفاعك الأخير — فليكن منطقه هنا بسيطًا وصريحًا ومختبَرًا جيدًا.
الدفاع بعمق. ادمج ثلاث طبقات: allowed_tools لإدراج القدرات في قائمة بيضاء، وcan_use_tool لفحص كل استدعاء أثناء التشغيل، وcwd مع عزل على مستوى نظام التشغيل لتقييد نطاق الضرر. لا تعتمد أبدًا على حُسن سلوك النموذج وحده.
الخطوة 6: القابلية للمراقبة عبر الخطاطيف
لأي شيء يعمل دون إشراف، تحتاج إلى سجلّ تدقيق. تنطلق الخطاطيف (Hooks) عند أحداث دورة الحياة لتسجّل أو تقيس أو تعترض الإجراءات دون المساس بشيفرة أدواتك. سجّلها باستخدام HookMatcher:
from claude_agent_sdk import ClaudeAgentOptions, HookMatcher
async def audit_pre_tool(input_data, tool_use_id, context):
print(f"[AUDIT] about to run {input_data['tool_name']} "
f"(id={tool_use_id})")
return {} # قاموس فارغ = السماح والمتابعة بشكل طبيعي
async def audit_post_tool(input_data, tool_use_id, context):
print(f"[AUDIT] finished {input_data['tool_name']}")
return {}
options = ClaudeAgentOptions(
allowed_tools=["Read", "Bash"],
hooks={
"PreToolUse": [HookMatcher(hooks=[audit_pre_tool])],
"PostToolUse": [HookMatcher(hooks=[audit_post_tool])],
},
)يمكن لوسيط matcher في HookMatcher أن يستهدف أداة محددة (مثل matcher="Bash") لينطلق الخطّاف لتلك الأداة فقط. في الإنتاج، استبدل استدعاءات print بإطار التسجيل لديك، وأرسل السجلات إلى منظومة المراقبة.
الخطوة 7: وكيل مصغّر متكامل
لنجمع كل شيء في وكيل صغير لكنه واقعي: مساعد مستودع بأداة مخصصة، وبوابة أذونات، وخطاطيف تدقيق.
import anyio
from claude_agent_sdk import (
tool, create_sdk_mcp_server,
ClaudeSDKClient, ClaudeAgentOptions,
HookMatcher, PermissionResultAllow, PermissionResultDeny,
AssistantMessage, TextBlock, ResultMessage,
)
@tool("count_todos", "Count TODO comments in a source file",
{"path": str})
async def count_todos(args):
try:
with open(args["path"], encoding="utf-8") as fh:
n = sum(1 for line in fh if "TODO" in line)
return {"content": [{"type": "text", "text": f"{n} TODOs"}]}
except FileNotFoundError:
return {"content": [{"type": "text", "text": "File not found"}]}
async def gatekeeper(tool_name, tool_input, context):
if tool_name == "Bash" and "rm " in str(tool_input.get("command", "")):
return PermissionResultDeny(message="No deletions allowed")
return PermissionResultAllow()
async def audit(input_data, tool_use_id, context):
print(f"[hook] {input_data['tool_name']}")
return {}
async def main():
server = create_sdk_mcp_server(name="repo", tools=[count_todos])
options = ClaudeAgentOptions(
system_prompt="You are a meticulous code auditor.",
mcp_servers={"repo": server},
allowed_tools=["Read", "Glob", "Grep", "mcp__repo__count_todos"],
can_use_tool=gatekeeper,
hooks={"PreToolUse": [HookMatcher(hooks=[audit])]},
max_turns=10,
)
async with ClaudeSDKClient(options=options) as client:
await client.query(
"Find the Python file with the most TODO comments and report it."
)
async for msg in client.receive_response():
if isinstance(msg, AssistantMessage):
for block in msg.content:
if isinstance(block, TextBlock):
print(block.text)
elif isinstance(msg, ResultMessage):
print(f"\nUsage: {msg.usage}")
anyio.run(main)يمسح هذا الوكيل الشجرة، ويقرأ الملفات المرشّحة، ويستدعي أداتك count_todos، ويبلّغ عن الفائز — بينما يُدقَّق كل استدعاء أداة وتُحظر عمليات الحذف. لاحظ max_turns=10، الذي يحدّ من الحلقة كي لا يعمل وكيل مرتبك إلى ما لا نهاية (فيراكم التكلفة).
اختبار تطبيقك
تحقّق من عمل كل جزء:
- الوكيل للقراءة فقط — شغّل الخطوة 2 في مجلد يحوي ملفَّي
.py. يجب أن ترى ملخصًا لكل ملف دون أي كتابة. - الأدوات المخصصة — شغّل الخطوة 4 وتأكد أن الوكيل يستدعي كلًا من
get_orderوshipping_costللطلبA101. - بوابة الأذونات — اطلب من وكيل الخطوة 5 تشغيل
rm -rf /tmp/xوتأكد أنه يُحظر برسالة الرفض التي كتبتها. - الخطاطيف — تأكد من طباعة أسطر
[AUDIT]قبل كل استدعاء أداة في الخطوة 6.
استكشاف الأخطاء وإصلاحها
CLINotFoundErrorأو "command not found" — واجهة Claude Code CLI غير مثبّتة. شغّلnpm install -g @anthropic-ai/claude-code.- أخطاء المصادقة — تأكد من تصدير
ANTHROPIC_API_KEYفي الصدفة نفسها التي تشغّل السكربت. - الوكيل يرفض استدعاء أداة مخصصة — تحقق من إدراجها في
allowed_toolsبالاسم الدقيقmcp__server__tool، وأن وصفها يوضّح بجلاء متى تُستخدم. - حلقات جامحة أو تكلفة مرتفعة — اضبط
max_turnsوافحصResultMessage.total_cost_usdلالتقاط الطلبات المكلفة مبكرًا. RuntimeErrorحول حلقة الأحداث — شغّل الـ SDK دائمًا عبرanyio.run(main)أوasyncio.run(main())؛ ولا تستدعِ دوال الـ SDK غير المتزامنة بشكل متزامن.
أنماط الإنتاج
- أقل امتياز افتراضيًا. ابدأ بقائمة
allowed_toolsفارغة وأضف القدرات واحدة تلو الأخرى بحسب حاجة المهمة. - حدّد الأدوار والميزانية. استخدم
max_turnsوسجّلtotal_cost_usdمن كلResultMessageفي مخزن مقاييس. - أبقِ الأدوات نقية وسريعة. ينبغي أن تكون الأدوات المخصصة صغيرة وحتمية وسريعة. ادفع العمليات البطيئة خلف طابور بدلًا من إعاقة حلقة الوكيل.
- سجّل كل شيء عبر الخطاطيف. لمهام الكرون والعمّال في الخلفية، سجلات التدقيق المعتمدة على الخطاطيف غير قابلة للتفاوض — فبها تُشخّص تشغيلًا حدث في الثالثة فجرًا.
- ثبّت النموذج. اضبط
modelفيClaudeAgentOptionsصراحةً كي لا يغيّر تغييرٌ في افتراضيات المزوّد السلوكَ أو التكلفة خفيةً.
الخطوات التالية
- قارن مع نسخة TypeScript في درس Claude Agent SDK للغة TypeScript
- تعلّم بناء أدوات يستهلكها وكلاؤك في بناء خادم MCP باستخدام TypeScript
- استكشف أنماط تنسيق الوكلاء المتعددين لأحمال العمل الأكبر
- غلّف هذا الوكيل داخل نقطة نهاية FastAPI أو بوت تيليجرام لجعله تفاعليًا
الخلاصة
يحوّل Claude Agent SDK للغة بايثون آلية الوكيل التي تقف خلف Claude Code إلى مكتبة تتحكم بها. لقد شغّلت وكيلًا لمرة واحدة عبر query، وأدرت محادثة ذات حالة مع ClaudeSDKClient، وكشفت منطقك الخاص عبر مُزخرِف @tool، وأضفت طبقات من دوال الأذونات والخطاطيف من أجل الأمان والمراقبة.
النمط قابل للتوسّع بنظافة: صِف الهدف، وأدرج الأدوات في قائمة بيضاء، واحمِ الخطيرة منها، ودَع الـ SDK يشغّل الحلقة. ابدأ بوكيل للقراءة فقط على مهمة حقيقية في قاعدة شيفرتك، ثم امنح القدرات عمدًا مع تنامي الثقة. هكذا تُطلق وكلاء مستقلين نافعين وآمنين في آنٍ معًا.