بناء موظف رقمي بدوام كامل: دورة مكثفة في 4 ساعات
خمسة عشر مفهوماً وبناءٌ متقن: المهارات، ومصدر الحقيقة، وقناة MCP بينهما.
في الدورة السابقة بنيتَ وكيلاً. وفي هذه الدورة تخطو أول خطوة حقيقية من الوكيل إلى عامل الذكاء الاصطناعي. (هذه هي الدورة الثانية في الوضع 2، مسار التصنيع، النقلة الثانية من سبع.) ذلك الوكيل، من دورة بناء وكلاء الذكاء الاصطناعي، كان وكيل محادثة متدفقاً بجلسات وحواجز أمان وتتبع، يعمل على صندوق حماية يوفّر له الحوسبة. وقد نجح. لكنه أيضاً كان ينسى كل شيء في اللحظة التي تُغلق فيها الطرفية، وكل أداة لديه كانت مكتوبة داخل كود Python الخاص به.
هل تريد فقط أن تراه يعمل أولاً؟ انتقل إلى الإنجاز السريع في 15 دقيقة أدناه. ستُنشئ قاعدة بيانات حقيقية وعاملاً صغيراً يكتب إليها ويتذكّر، ثم تعود للمفاهيم التي تشرح لماذا جاء بهذا الشكل.
عامل الذكاء الاصطناعي هو وكيل المحادثة نفسه بعد أن نضج. يسميه الناس أيضاً موظف ذكاء اصطناعي أو موظفاً رقمياً بدوام كامل (Digital FTE): شيء واحد، يُسمّى تبعاً لكيفية بنائه، ومَن ينضمّ إليه، وما تكلفته. تبني هذه الدورة أساسه: وكيل يمكنك أن تنمّيه، يتذكّر، وتملكه أنت. العامل الكامل يعمل أيضاً على مدار الساعة، ويتصرّف من تلقاء نفسه، ويصل إليك على أي تطبيق، لكن ذلك يأتي لاحقاً. تبقى حزمة SDK وبيئة تشغيل SandboxAgent من الدورة السابقة كما هي؛ وكل ما حولهما هو ما يتغيّر.
ولإحداث هذا التغيير حركتان، إضافة إلى القناة التي تصل بينهما:
- قدراته تصبح مهارات: مجلدات صغيرة يعثر عليها الوكيل ويحمّلها بنفسه، بدلاً من أدوات مدمجة داخل كود Python الخاص به.
- والأشياء التي كان ينساها عند إعادة التشغيل تنتقل إلى Postgres، مصدر الحقيقة لديه: المتجر الموثوق الواحد الذي يعمل العامل في مقابله، مصدر الحقيقة الذي تقوم عليه الشركة، تماماً كما يقوم نظام CRM أو دفتر أستاذ. وتعيش فيه أنواع قليلة من البيانات:
- سجلات الأعمال: الحقيقة التشغيلية. العملاء، التذاكر، الطلبات. تبحث عنها وتحدّثها.
- مكتبة المراجع: المعرفة التي يبحث فيها بالمعنى. مكتبة السياسات، الوثائق المرجعية، الحالات السابقة.
- الحالة: كيف يبدو العمل الآن. أي المحادثات مفتوحة، وما الذي ينتظر الموافقة.
- الأثر: سجلّ لما فعله، حتى تستطيع الشركة إعادة تشغيل أفعاله والثقة بها.
- MCP (بروتوكول سياق النموذج) هو المعيار المفتوح لربط الوكلاء بالأدوات والبيانات الخارجية. وهو هنا القناة التي يستخدمها الوكيل للوصول إلى ذلك المتجر.
الاستدعاء الدلالي هو القطعة الواحدة التي يخطئ الناس في تسميتها. ومعناه إيجاد الأشياء بالمعنى، لا بمطابقة الكلمات الحرفية. إنه طريقة في البحث، لا متجر قائم بذاته: يمكنك تشغيله على مكتبة المراجع، أو على المحادثات السابقة، أو على سجلات الأعمال نفسها. وهو يأتي من للعامل قيد التشغيل جزآن ينشرهما الإنتاج بشكل منفصل. بيئة التشغيل (harness) هي زمن تشغيل الوكيل: حلقة SDK نفسها. أما الحوسبة (compute) فهي صندوق الحماية حيث يُشغَّل كود الوكيل فعلاً؛ فعندما يستدعي الوكيل أداة، يسلّم الكود إلى ذلك الصندوق. وفي هذه الدورة يبقى كلاهما محلياً. يشغّل شاهد العرض التقديمي الكامل — بناء أساس الموظف الرقمي بدوام كامل تسمّي الأطروحة سبعة ثوابت يجب أن يستوفيها كل نظام وكلاء إنتاجي. بنت الدورة السابقة المحرّك (الثابت 4): حزمة OpenAI Agents SDK على صندوق حماية. وتضيف هذه الدورة الثابت 5: كل عامل يعمل في مقابل مصدر حقيقة. المحرّك هو ما يعمل العامل عليه؛ ومصدر الحقيقة هو ما يعمل في مقابله. ويحافظ معياران مفتوحان على بقاء ذلك محمولاً. المهارات (نشأت في Anthropic، وصارت الآن على مستوى المنظومة في agentskills.io) تتيح للقدرات الانتقال بين الأدوات. وMCP هو القناة المعيارية التي يستخدمها الوكيل للوصول إلى السجل؛ لم يكن لدى الدورة السابقة منها شيء، وهو النمط الجديد الأساسي هنا. أما السجل نفسه فهو Neon Postgres + pgvector، اختير لأنه مجاني في البداية، ويتقلّص حجمه إلى الصفر حين يكون خاملاً، ويأتي بخادم MCP رسمي. والمنتج قابل للاستبدال؛ ويسرد دليل الاستبدال البدائل.pgvector، الذي يضيف البحث بالمعنى إلى Postgres.كيف يعمل العامل: بيئة التشغيل مقابل الحوسبة (لا تنشر أياً منهما هنا)
UnixLocalSandboxClient صندوق الحماية على جهازك (بلا بنية تحتية، ومفتاح API واحد)، ويمكنك توجيهه إلى Docker، أو Cloudflare، أو E2B، أو Modal بتغيير سطر واحد (دليل الاستبدال في الجزء الخامس). أما نشر بيئة التشغيل نفسها كخدمة سحابية دائمة العمل فهو دورته الخاصة، انشر بيئة تشغيل وكيلك إلى السحابة.أين تقع هذه الدورة من أطروحة Agent Factory
📚 وسيلة تعليمية
تنقسم هذه المفاهيم الخمسة عشر على ثلاث طبقات: المهارات، ومصدر الحقيقة، وMCP. والجدول أدناه هو الخريطة كاملةً.المفاهيم الخمسة عشر في لمحة (وسّع للحصول على الخريطة الكاملة)
# المفهوم الطبقة السؤال الذي يجيب عنه 1 ما مهارة الوكيل المهارات أين تعيش القدرة القابلة لإعادة الاستخدام؟ في مجلد، فيه SKILL.md إضافة إلى سكربتات/مراجع اختيارية.2 الكشف التدريجي المهارات لماذا تكون المهارات رخيصة الاحتفاظ بها قريبة؟ الاكتشاف ← التنشيط ← التنفيذ يحمّل فقط ما يلزم حين يلزم. 3 كتابة ملف SKILL.md المهارات ماذا يحتوي ملف المهارة فعلاً؟ بيانات وصفية، ووصف مُحفِّز، وتعليمات تشغيلية. 4 اتفاقيات تغليف المهارات المهارات كيف تنتقل المهارات بين الأدوات؟ المجلد نفسه يعمل في Claude Code وOpenCode وأي عميل متوافق. 5 تركيب المهارات المهارات متى تربط مهارات صغيرة عبر تسليم على نظام الملفات مقابل كتابة مهارة واحدة كبيرة. 6 لماذا Postgres المُدار مصدر الحقيقة أي متجر يستحق لقب "مصدر الحقيقة"؟ متجر فيه الديمومة والتفريع والحوكمة والأوّليات المتّجهة التي يحتاجها الوكيل. 7 مخطط العامل مصدر الحقيقة أي جداول يحتاجها الوكيل فعلاً؟ المحادثات، والمستندات، والتضمينات، وسجل التدقيق، واستدعاءات القدرات، إضافة إلى جلسة SDK للأدوار. 8 أساسيات pgvector مصدر الحقيقة كيف يعمل البحث الدلالي في Postgres؟ عمود التضمين، وعوامل المسافة، وأنواع الفهارس. 9 خط أنابيب التضمين مصدر الحقيقة كيف يصبح النص متّجهاً قابلاً للاستعلام؟ التقطيع، ونموذج التضمين، ومتى تُعيد التضمين. 10 سجل التدقيق بوصفه انضباطاً مصدر الحقيقة ماذا تعني "القراءة والكتابة" لعامل؟ كل فعل يقوم به العامل يترك أثراً تستطيع الشركة إعادة تشغيله. 11 ما MCP وما ليس به MCP بروتوكول للأدوات والموارد والتعليمات: ليس إطار عمل، وليس خدمة. 12 خادم Neon MCP MCP واجهة الوكيل إلى قاعدة بياناته: ما الذي يكشفه، وكيف يصادق. 13 ربط MCP بحزمة Agents SDK MCP تكامل MCP في الحزمة: كيف تسجّل خادماً، وما الذي يراه النموذج، وأين تقع حدود الثقة. 14 خوادم MCP المخصصة MCP متى تكتب خادمك الخاص مقابل الاكتفاء ب @function_tool. شجرة القرار.15 MCP تحت الحِمل MCP خيارات النقل، وتجميع الاتصالات، ومتى تستخدم طابوراً.
ما إن تمتلك هذه الخريطة حتى يصبح الباقي ميكانيكياً في معظمه. يعود أي إخفاق في الإنتاج إلى أحد ثلاثة: مهارة لم تُكتشف قط (وصف مفرط الغموض)، أو مصدر حقيقة يختلف حوله عاملان (سباق على المخطط)، أو قناة MCP تُسقط الأحداث (نقل خاطئ لطبيعة الحِمل). والتشخيص يخبرك بأيّها.
لمن هذه الدورة
متوسطة المستوى. ينبغي أن يكون لديك:
- يُفضَّل أن تكون أنجزت بناء وكلاء الذكاء الاصطناعي، وإن كان وكيلك يستطيع أن يبني حالته النهائية على القاعدة إذا تخطّيتها.
- عادتا وضع التخطيط وملف القواعد من دورة البرمجة الوكيلية المكثفة.
- دورة واحدة من إطار PRIMM-AI+ في رصيدك.
هذه دورة تكميلية بأولوية Python: لن تكتب Python أو SQL بيدك، بل يكتب وكيلك الكود بينما توجّهه أنت، ويزداد الجزآن 2 و3 كثافة (نماذج Pydantic، وتجمّعات asyncpg، وخادم MCP صغير مخصص)، فتوقّع مزيداً من التبادل هناك. تحفظ قاعدة البيانات المعلومات في جداول. تخيّل جدول بيانات: كل صف شيء واحد (عميل، تذكرة دعم) وكل عمود تفصيل واحد عنه (اسم، تاريخ، حالة). هذا هو النموذج الذهني الكامل الذي تحتاجه هنا. أنت لا تكتب كود قاعدة البيانات بنفسك أبداً؛ وكيلك يفعل، وهاتان الكلمتان تساعدانك فقط على قراءة ما يبنيه. خمس كلمات تستخدمها هذه الدورة وكأنك تعرفها:جديد على قواعد البيانات؟ النسخة في 60 ثانية
محدَّث حتى مايو 2026، ومُتحقَّق منه في مقابل openai-agents 0.17.x، وحزمة mcp SDK، ووثائق Neon ل MCP، وَpgvector 0.8+. ثبّت إصداراتك بمجرد أن تبني؛ وإن تعارضت الوثائق مع هذه الصفحة يوماً، فالكلمة الفصل ل دليل Cloudflare Sandbox ووثائق Neon.
أنت توجّه، والوكيل يبني، ولأن القاعدة تأتي بملف AGENTS.md يقرؤه عند الفتح، يمكن أن تبقى مطالباتك قصيرة: قل فقط ما الذي تريد بناءه تالياً.
الإنجاز السريع في خمس عشرة دقيقة: انجح مرة، ثم ادرس لماذا نجح
قبل أن تقرأ المفاهيم الخمسة عشر التي تشرح لماذا تنجح هذه البنية، ابنِ أصغر نسخة منها تعمل فعلاً. في النهاية سيكون لديك:
- مشروع Neon جديد بجدولين،
notesوَaudit_log، أنشأتهما عبر MCP ورأيتهما في لوحة التحكم، - عامل ذكاء اصطناعي بسيط كتب إلى كليهما في معاملة واحدة عبر أداته الخاصة
save_note، - وجواب عملي للسؤال "هل فعل مصدرُ الحقيقة شيئاً من أجلي حقاً؟": ملاحظتك وصفّ تدقيقها، يتشاركان معرّفاً واحداً.
هذه شاشة واحدة من المطالبات: يبني وكيل البرمجة لديك المتجرَ عبر Neon MCP، ثم يبني عاملاً صغيراً يكتب إليه، وتشاهد العامل يتذكّر. أما العامل الكامل (ثمانية قرارات، ومخطط من خمسة جداول) فيأتي في الجزء الرابع. إن لم يكن لديك سوى جلسة واحدة، فافعل هذا، ثم عُد للمفاهيم.
يجري في هذا مستويان، والإبقاء عليهما واضحين هو النموذج الذهني كله. وكيل البرمجة لديك (Claude Code أو OpenCode) يستخدم Neon MCP ل بناء قاعدة البيانات وفحصها. أما العامل الذي تبنيه فيستخدم أداته الخاصة للكتابة إليها في زمن التشغيل. العامل لا يلمس Neon MCP أبداً، ووثائق Neon نفسها صريحة في السبب: خادم MCP "للتطوير والاختبار فقط"، ولا يُوصَل أبداً بتطبيق قيد التشغيل.
احصل على القاعدة وافتحها
نزّل القاعدة وافتح المجلد في وكيلك العام. يقوم الوكيل بالإعداد بنفسه، انطلاقاً من المطالبات التالية مباشرة. تُجهّز هذا مرة واحدة: المجلد digital-fte/ هو مجلدك للدورة كلها، للإنجاز السريع وللجزء الرابع على حدّ سواء. كل عملية بناء تُجهّز مشروع Neon جديداً خاصاً بها (قاعدة بيانات)، لكنك لا تعيد التنزيل أو فكّ الضغط أبداً.
cd digital-fte
claude
cd digital-fte
opencode
تفترض هذه القاعدة وكيلاً عاماً قادراً (Claude Code، أو OpenCode يشغّل Claude Sonnet أو Opus، أو GPT-5، أو ما شابه). النموذج الأصغر سينحرف عن مطالبة البناء؛ فإن بدت خطته الأولى غامضة بدل أن تكون محدّدة، فانتقل إلى نموذج أقوى قبل أن تمضي أبعد.
جهّز القاعدة (~3 دقائق)
تأتي القاعدة بالقواعد والتوصيلات؛ أما المهارات ومفتاحك فيأتيان تالياً. اجعل وكيلك يجهّز نفسه. الصق هذا:
اقرأ AGENTS.md، ثم جهّز هذه القاعدة: ثبّت المهارات التي يسردها لأي وكيل أنت، وانسخ لي
.env.exampleإلى.env، وأخبرني بالضبط بما تحتاجه مني لتشغيل خادمَي MCP الخاصين ب Neon وContext7.
راقب: الوكيل وهو يثبّت skill-creator، وَmcp-builder، وَneon-postgres (ترى التثبيت يجري)، وينشئ .env، ثم يطلب منك أمرين: مفتاحك OPENAI_API_KEY لتلصقه في .env، ونقرة متصفّح واحدة لتفويض Neon عبر OAuth. Neon مجاني؛ وإن لم يكن لديك حساب بعد، فسجّل في neon.com في نحو دقيقة، أو أنشئ واحداً مباشرة عند شاشة التفويض. وحين يكتمل التثبيت والتوصيل، يطلب منك الوكيل إعادة تشغيله (الخروج وإعادة الإطلاق) كي تُحمَّل المهارات وخوادم MCP الجديدة؛ فلا شيء منها يُحمَّل في منتصف الجلسة.
يكتمل عندما: تكون المهارات مثبّتة، وَ.env يحمل مفتاحك، وَNeon مُفوَّضاً، وتكون قد أعدت تشغيل الوكيل كي تصبح المهارات وخوادم MCP الجديدة فعّالة.
البوابة: تأكّد أن الوكيل يستطيع الوصول إلى قاعدة البيانات (~دقيقة)
الشيء الجديد الوحيد فعلاً الذي تضيفه هذه الدورة هو وصول الوكيل إلى مصدر حقيقة حقيقي عبر MCP. فقبل أن تبني أي شيء، تأكّد أن تلك الحدود فعّالة. الصق هذا:
اسرد أدوات Neon التي تراها.
راقب: قائمة حقيقية بأسماء أدوات Neon (إنشاء مشروع، وتشغيل SQL، ووصف الجداول، وما إلى ذلك). تلك القائمة هي يد الوكيل على قاعدة البيانات، وكل ما يأتي تحت يعتمد عليها.
البوابة مفتوحة: الرد يسرد أسماء أدوات Neon حقيقية. إن لم يفعل: فأنت على الأرجح تخطّيت إعادة التشغيل، فلم تُحمَّل الأدوات بعد. اخرج، وأعد الإطلاق، واسأل مجدداً. لا شيء بعد؟ إذن لم يكتمل OAuth الخاص ب Neon: أعِده وحاول مرة أخرى.
ابنِ المتجر، واحصل على سلسلة اتصاله (~3 دقائق)
اجعل وكيل البرمجة لديك ينشئ قاعدة البيانات عبر Neon MCP، ثم يسلّم عاملك الشيء الوحيد الذي سيحتاجه للوصول إليها لاحقاً: سلسلة اتصال.
الصق هذا لوكيلك العام. خطّط أولاً؛ ونفّذ عند الموافقة.
على مشروع Neon جديد، أنشئ جدولين:
notes(نص الملاحظة) وَaudit_log(سجلّ لما حدث). ثم استدعِget_connection_stringواكتب ذلك الرابط في ملف.envلديّ بوصفهDATABASE_URL. استخدم أدوات Neon في كل ذلك؛ لا تكتب لي SQL لأشغّله.
راقب: الوكيل وهو يستدعي أدوات Neon MCP لإنشاء المشروع والجدولين (ترى تلك الاستدعاءات للأدوات، لا SQL كتبته أنت)، ثم يكتب DATABASE_URL في .env. تلك السلسلة هي التسليم: Neon MCP جهّز المتجر، وعاملك سيستخدم السلسلة، لا خادم MCP.
يكتمل عندما: يوجد مشروع Neon جديد بجدول notes وجدول audit_log، ويحمل .env قيمة DATABASE_URL.
شاهده بعينيك (~دقيقة)
قبل أن يعمل أي كود، انظر إلى الجداول الفارغة في لوحة تحكم Neon. هذه هي لحظة "إنه موجود فعلاً"، وتكلّفك علامة تبويب واحدة في المتصفح.

افتح console.neon.tech، واختر المشروع الذي صنعه الوكيل للتو، وافتح Tables. هناك يقبع notes وَaudit_log، فارغين الآن. الجدول مجرد جدول بيانات: كل صفّ شيء واحد، وكل عمود تفصيل واحد. ستحدّث هذا العرض في النهاية وتشاهد صفّاً يظهر.
ابنِ العامل وشغّله مرة (~دقيقتان)
الآن ابنِ العامل نفسه: SandboxAgent بسيط، بيئة التشغيل نفسها التي تستخدمها بقية الدورة، بلا أدوات بعد. تشغيله فارغاً أولاً يثبت أن بيئة التشغيل تعمل وأن مفتاحك سليم، قبل أن تضيف أي شيء آخر قد يفشل.
باستخدام
uv، جهّز مشروع OpenAI Agents SDK بسيطاً في هذا المجلد:SandboxAgentعلى نموذج من فئة gpt-5 (مثلgpt-5-mini) بلا أدوات بعد، يُشغَّل من الطرفية على صندوق حماية محلي، يقرأOPENAI_API_KEYمن.env. شغّله مرة واحدة ب "hello" كي أراه يجيب.
راقب: الوكيل وهو يجهّز المشروع ب uv، ويكتب سكربت SandboxAgent صغيراً مع Runner (على UnixLocalSandboxClient، بلا بنية تحتية)، ويشغّله. يعود ردّ.
هذه أول مرة يُستخدم فيها مفتاحك، فهي أول مكان يظهر فيه مفتاح خاطئ. إن أعاد التشغيل خطأ 401، فالمفتاح خاطئ أو مزوّدك ليس OpenAI: الصق "فشل التشغيل بخطأ 401؛ اقرأ الخطأ واقترح إصلاحاً واحداً أوافق عليه."
يكتمل عندما: يعمل العامل الفارغ ويجيب.
امنح العامل أداته، وشاهده يتذكّر (~3 دقائق)
الآن أضف القدرة الوحيدة: أداة تكتب ملاحظة وصفّ تدقيقها، في معاملة واحدة، إلى قاعدة البيانات التي بنيتها.
أضف أداة
save_noteإلى العامل، مكتوبةً بصيغة@function_tool، تُدرج صفّاً فيnotesوصفّاً مطابقاً فيaudit_logفي معاملة واحدة، باستخدامDATABASE_URLالموجود في.env. ثم شغّل العامل وأرسل إليه: "تذكّر هذا: نشر الإنتاج يحتاج متغيّر بيئة جديداً قبل الجمعة." أرني ما حدث.
راقب: النموذج وهو يطابق جملتك مع save_note بنفسه (وصف الأداة هو إشارة التوجيه الوحيدة لديه)، والأداة وهي تفتح اتصالاً ب DATABASE_URL وتكتب الصفّين في معاملة واحدة. يبلّغ العامل بأن الملاحظة حُفظت. لاحظ ما لم يفعله: لم يمدّ يده قط إلى Neon MCP. القناة الإدارية بنت المتجر؛ والعامل يستخدم أداته الضيّقة الخاصة.
يكتمل عندما: يؤكّد العامل أن الملاحظة حُفظت ويريك استدعاء save_note الذي فعل ذلك. جملة واحدة دخلت، واستدعاء أداة واحد، وصفّان كُتبا.
الإنجاز: اقرأه مجدداً (~دقيقتان)
حدّث عرض Tables في لوحة تحكم Neon من قبل قليل. ملاحظتك صارت الآن صفّاً في notes، وصفّ مطابق في audit_log يسجّل note_saved، مربوط بها بالمعرّف نفسه. (تفضّل البقاء في الطرفية؟ اطلب من وكيل البرمجة: "باستخدام أدوات Neon، أرني صفّ notes الجديد وصفّ audit_log المطابق له جنباً إلى جنب.")
تلك هي البنية كلها مصغّرة: مصدر حقيقة يحمل الحقيقة، وعامل كتب إليها عبر أداته الخاصة، وأثر تدقيق تستطيع إعادة تشغيله.
ما الذي بنيته، وأين ينمو
استخدمت @function_tool بسيطة لأن عاملاً واحداً يكتب إلى متجر واحد، وهذا هو الوضع الافتراضي الصحيح، لا اختصاراً. تمدّ يدك إلى خادم MCP صغير حين يظهر أحد ثلاثة: مستهلك ثانٍ يحتاج إلى save_note نفسها (عامل آخر، أو وكيل البرمجة لديك، أو Claude نفسه)، أو نطاق أضيق تريد فرضه، أو عزل العمليات. ذلك القرار، أداة دالة مقابل خادمك الخاص، هو المفهوم 14، والجزء الرابع يبني الخادم.
يوسّع الجزء الرابع هذا الشكل نفسه إلى عدة مهارات، ومخطط الجداول الخمسة، وبضع أدوات، وخط أنابيب تضمين. الشكل لا يتغيّر: مصدر حقيقة، وتدقيق في المعاملة نفسها، وخطّ نظيف بين القناة الإدارية ووصول العامل الخاص. إن نجح هذا الإنجاز السريع، فبقية الدورة مجرد شرح لماذا جاءت كل قطعة بالشكل الذي جاءت به.
إن لم ينجح شيء، فالصق حركة الإنقاذ الواحدة التي تغطّي كل شيء: "شيء لم ينجح. اقرأ الخطأ، وأخبرني بلغة واضحة بما تراه، واقترح إصلاحاً واحداً أوافق عليه." ثم عُد إلى هنا.
الجزء الأول: المهارات، القدرة بوصفها مجلدات محمولة
لقد استخدمت المهارات من قبل داخل Claude Code. يمنح الجزء الأول سيرَ العمل نفسها، عند الطلب وباحترافية، للوكيل الذي تبنيه أنت. المهارة قدرة قابلة لإعادة الاستخدام تسلّمها لوكيل: مجلد يغلّف سير عمل (تعليمات، إضافة إلى أي سكربتات أو مراجع) يحمّله الوكيل فقط حين تتطلّبه مهمة، محمولاً بين الوكلاء بدل أن يكون مدمجاً في كود وكيل واحد. تعلّمك هذه المفاهيم الخمسة أن تكتب مهارات تنطلق حين ينبغي، وينتهي الجزء الأول بتشغيل واحدة منها في حزمة SDK الخاصة بعاملك، في المجلد digital-fte نفسه.
المفهوم 1: ما مهارة الوكيل
مهارة الوكيل مجلد فيه ملف SKILL.md (إضافة إلى مجلدات اختيارية scripts/، وَreferences/، وَassets/). وملف SKILL.md هو نقطة الدخول. وهي معيار مفتوح من Anthropic يستطيع أي وكيل قراءته: Claude Code وOpenCode اليوم، وعامل OpenAI Agents SDK الذي تبنيه. أصغر مهارة ملف واحد:
---
name: hello-skill
description: Greets the user by name and time of day. Use when the user says hello or asks to be greeted.
---
# Hello skill
1. Check the local time of day.
2. Greet the user warmly, by name if known, in under 25 words.
لا كود، ولا نشر، ولا استدعاء SDK. ولأنها ملف على القرص، تُؤرشَف المهارة وتُنقَل وتُراجَع مثل أي نص، لا مثل كائن Python أو نقطة نهاية API.
PRIMM، التنبّؤ. ماذا يحمّل الوكيل عند بدء التشغيل، قبل وصول أي رسالة؟ (أ) ملف
SKILL.mdكاملاً؛ (ب) فقطnameوَdescription؛ (ج) لا شيء حتى يُستدعى. الثقة 1–5.
الجواب (ب): عند بدء التشغيل يقرأ الوكيل فقط بيانات كل مهارة الوصفية؛ ويُحمَّل المتن عند الطلب. ذلك هو الكشف التدريجي، المفهوم التالي.
المفهوم 2: الكشف التدريجي، نموذج التحميل بثلاث مراحل
تحميل خمسين مهارة دفعة واحدة سيُغرق النموذج بتعليمات لا يحتاجها. لذا تُحمَّل المهارة على ثلاث مراحل، تنطلق كلٌّ منها فقط حين تقول السابقة إنها ذات صلة.
المرحلة 1، الاكتشاف. عند بدء التشغيل يحمّل الوكيل name وَdescription لكل مهارة، نحو 100 رمز لكلٍّ. خمسون مهارة تكلّف نحو 5000 رمز لكل دور: ثمن معرفة ما في المكتبة.
المرحلة 2، التنشيط. حين يطابق النموذج مهمةً مع وصف، يحمّل متن SKILL.md كاملاً (أبقِه دون نحو 5000 رمز؛ ومعظمها بين 500 و2000). تُدفَع فقط في الأدوار التي تستخدم المهارة.
المرحلة 3، التنفيذ. الملفات التي يشير إليها المتن (سكربت في scripts/، أو وثيقة في references/) تُحمَّل فقط حين يمدّ الوكيل يده إليها.

PRIMM، التنبّؤ. لدى عامل 30 مهارة: أوصاف بنحو 100 رمز لكلٍّ، ومتون بنحو 1500 رمز، وملفّا مرجع (نحو 4000 رمز إجمالاً) لكلٍّ. في دور يُنشِّط مهارة واحدة ويقرأ أحد مراجعها، تكون تكلفة السياق التقريبية: (أ) ~3000 رمز؛ (ب) ~6500 رمز؛ (ج) ~135000 رمز. الثقة 1–5.
الجواب (ب)، ~6500 رمز: 30 × 100 للاكتشاف (3000)، إضافة إلى متن واحد ب 1500 رمز، إضافة إلى مرجع واحد بنحو 2000 رمز. الاكتشاف يتناسب مع حجم المكتبة؛ أما التنشيط والتنفيذ فيبقيان ثابتين لكل دور. بلا كشف تدريجي كنت ستدفع كل المتون الثلاثين ومراجعها في كل دور، نحو 165000 رمز فقط لمعرفة ما يستطيع الوكيل فعله. لا أحد يشغّل ذلك.
ينتج عن ذلك أمران، وهما يقودان المفاهيم الثلاثة التالية: ال description هو ما ينطلق في المرحلة 1، فهو يقرّر كل شيء؛ والمتون الطويلة تكلّفك في كل دور مطابق، فأبقِ SKILL.md مُحكَماً وادفع العمق إلى references/.
المفهوم 3: ال description هو المُحفِّز، والجزء الوحيد الذي تملكه
لملف SKILL.md جزآن: بيانات YAML الوصفية (العقد الذي يقرؤه النموذج) ومتن markdown (التعليمات التي يتّبعها). حقلان فقط في البيانات الوصفية مطلوبان:
| الحقل | مطلوب | ما هو |
|---|---|---|
name | نعم | مُعرّف المهارة (أحرف صغيرة، شُرَط، يطابق اسم المجلد). |
description | نعم | سطح التحفيز: ما يقرؤه الوكيل عند الاكتشاف ليقرّر ما إذا كان سينطلق بهذه المهارة. |
(license، وَcompatibility، وَmetadata، وَallowed-tools اختيارية ونادراً ما تلزم؛ يملؤها skill-creator.)
الوصف هو اللعبة كلها، وهو الجزء الذي يخطئ فيه القالب الجاهز. يكتب وصفاً دائرياً: "Summarizes a ticket into five sections. Use when the user wants to summarize a ticket." ينطلق ذلك على "summarize this ticket" لكنه يفوّت كيف يتكلّم الدعم فعلاً: "write a handoff note for #4471," "TL;DR this thread," "give my lead the rundown before I escalate." تلتقط النسخة العامة نحو 6 من 8 صياغات حقيقية؛ أما النسخة المكتوبة يدوياً فتلتقط الثمانية كلها.
الوصف الذي ينطلق بثبات يفعل ثلاثة أشياء، إضافة إلى حاجز وقائي:
- ماذا يُنتِج (سمِّ الناتج الفعلي: الأقسام الخمسة، على تذكرة واحدة).
- متى تمدّ يدك إليه (المواقف الحقيقية: التسليم، التصعيد، إحاطة مدير، التقاط سلسلة شخص آخر).
- كلمات مفتاحية يكتبها المستخدمون فعلاً، بما فيها تلك التي لا تذكر الكلمة الواضحة قط ("handoff note," "TL;DR this thread," "where does this stand").
- سطر "لا تنطلق" (do-NOT) للأشباه التي يجب أن تبقى صامتة (صياغة رد للعميل، فرز دفعة، تقرير عن حجم التذاكر).
فحص ذاتي يقتل الأوصاف الدائرية: احذف الكلمة المفتاحية الواضحة ("summarize") من وصفك. هل ما زال يقول متى ينطلق؟ إن لم يفعل، فهو أضيق من اللازم.
المتن، باتفاق عُرفي. لا صيغة مطلوبة، لكن المهارات الجيدة أمريّة ("Read the full thread. List what was tried.")، وتحمل مثالاً أو مثالين حقيقيين (يساويان نحو خمسة أضعاف الوصف في التوجيه)، وتسمّي حالتين أو ثلاث حالات حافّة كسرت العمل فعلاً.
PRIMM، التنبّؤ. مهارتان تتشاركان الاسم
summarize-document: واحدة في~/.claude/skills/(على مستوى المستخدم)، وأخرى في.claude/skills/(على مستوى المشروع). تطابق مهمةٌ كلتيهما. ماذا يحدث؟ (أ) اختيار عشوائي؛ (ب) تفوز نسخة المشروع؛ (ج) النموذج يختار. الثقة 1–5.
(ب)، تفوز نسخة المشروع عبر Claude Code وOpenCode: السياق الأكثر تحديداً يتجاوز الأعمّ، بالطريقة نفسها التي يتجاوز بها ملف قواعد المشروع ملفاً عاماً.
المفهوم 4: التغليف، أين تعيش المهارات وكيف تنتقل
المهارة مجرد مجلد على القرص، فموضعها يقرّر أي الوكلاء يجدها. قاعدة واحدة تغطّي هذه الدورة كلها: ضع مهاراتك في يفحص OpenCode مجلده أولاً، ثم يرجع احتياطياً إلى .claude/skills/. يقرأ Claude Code ذلك المجلد، وَOpenCode يرجع إليه احتياطياً، وحزمة SDK لعاملك تشير إليه مباشرة (LocalDir(src=".claude/skills")، من التطبيق العملي أعلاه). اكتب المهارة مرة واحدة وتُحمّلها الثلاثة جميعاً من المجلد نفسه، بايتاً ببايت.خريطة المسارات الكاملة (لكل أداة، على مستوى المشروع مقابل مستوى المستخدم)
الأداة على مستوى المشروع على مستوى المستخدم (عام) Claude Code .claude/skills/<name>/SKILL.md~/.claude/skills/<name>/SKILL.mdOpenCode .opencode/skills/<name>/SKILL.md~/.config/opencode/skills/<name>/SKILL.mdOpenCode (احتياطي) .claude/skills/<name>/SKILL.md~/.claude/skills/<name>/SKILL.md.claude/skills/؛ أما Claude Code فلا يقرأ إلا .claude/. ولهذا فإن .claude/skills/ هو الموضع الوحيد الذي يعمل في كل مكان.
مجلد المهارة فيه ملف واحد مطلوب وثلاثة مجلدات اختيارية، لكلٍّ مهمّة واحدة:
my-skill/
├── SKILL.md # required: frontmatter + body, the entry point
├── scripts/ # optional: code the agent runs (by relative path)
├── references/ # optional: deep docs, loaded on demand, one topic per file
└── assets/ # optional: templates, schemas, lookup tables
داخل SKILL.md، أشِر إلى تلك الملفات ب المسار النسبي (references/policies/us.md، وَscripts/extract.py)؛ فهي تُحَلّ من مجلد المهارة نفسه، لا من حيث يصادف الوكيل أن يعمل. أبقِ references/ ضحلاً، موضوعاً واحداً لكل ملف.
المفهوم 5: تركيب المهارات، واحدة كبيرة مقابل عدة صغيرة
"تقرير صحة العميل الأسبوعي" قد يكون مهارة واحدة تبحث وتصوغ وتنسّق وتراجع، أو أربع مهارات تتسلّم عبر نظام الملفات. كلاهما يعمل، بمقايضات متعاكسة.
- مهارة واحدة كبيرة: سهلة الاكتشاف، تنشيط واحد. لكن كل خطوة تجري في سياق واحد، ولا شيء قابل لإعادة الاستخدام وحده، والإخفاق في منتصف الطريق يترك النموذج يتعافى وفي سياقه عملٌ بائت.
- عدة مهارات صغيرة: كلٌّ يمكن اختبارها واستبدالها وإعادة استخدامها وحدها؛ والإخفاق موضعيّ؛ وكل خطوة تُنشَّط من جديد، فلا يتراكم سياق متبقٍّ. والثمن مزيد من مدخلات الاكتشاف وشيء يربطها.

اكتب مهارة واحدة حين تكون الخطوات متماسكة بشدّة ولا تُعاد وحدها أبداً. اكتب عدة حين قد تُستدعى خطوة وحدها، أو حين يكون إبقاء سياق كل خطوة نظيفاً أهمّ من إبقاء التوصيل بسيطاً. الفصل يفوز عادةً بعد خطوتين أو ثلاث.
اربطها عبر نظام الملفات، لا عبر المحادثة. المهارة (أ) تكتب tmp/research-{id}.md، والمهارة (ب) تقرؤه وتكتب tmp/draft-{id}.md، وهكذا. لا ترى المحادثة إلا النتيجة النهائية؛ أما الخطوات الوسيطة فتبقى على القرص للوكيل، ولك، ولأثر التدقيق. العزل نفسه الذي استخدمته الدورة السابقة للوكلاء الفرعيين، الآن بحجم مهارة.
وهو الجسر إلى الجزء الثاني: بعض عمليات التسليم لا مكان لها في ملف مؤقت، بل في مصدر الحقيقة. المهارة التي تكتب إلى tmp/ مسوّدة؛ والمهارة التي تكتب إلى مصدر الحقيقة فعلٌ. ذلك التمييز هو ما يبنيه الجزء الثاني.
جرّب مع الذكاء الاصطناعي
Compare two designs for a customer-refund workflow:
A: one "issue-refund" skill (eligibility, policy, amount, gateway, ticket, notify).
B: five small skills chained via tmp/ handoffs.
For each, name one situation where it's the right call and one failure mode
it's vulnerable to. Then say which you'd ship, and why.
أطلق مهارة في كلتا بيئتي التشغيل (~10 دقائق، تطبيق عملي)
قرأت ما يكفي؛ والآن شاهد مهارة تنطلق داخل الوكيل الذي تبنيه أنت. افتح المجلد digital-fte نفسه من الإنجاز السريع، حيث يعمل SandboxAgent لديك بالفعل. شغّل هذا مرة على مهارة قابلة للرمي كي تألف الميكانيكا (وهي الحركة التي يقوم بها القرار 4 على نحو حقيقي)، وشاهد ملفات .claude/skills/ التي تستخدمها أصلاً في Claude Code تعمل بالطريقة نفسها في حزمة SDK الخاصة بعاملك.
1. جهّزها. تحتاج إلى وكيل عام وَNode مثبَّتاً (لأجل npx). الصق هذا:
Use skill-creator to scaffold a summarize-ticket skill. It turns one support ticket into a
short five-section handoff. Make it fire on how support actually asks (handoff note, TL;DR
this thread, "what's the status and next step"), including phrasings that never say
"summarize", and not on look-alikes (drafting a reply, triaging a batch). Then check it:
delete "summarize" from the description; if it no longer says when to fire, sharpen it.
يعود المتن جيداً؛ اقرأ الوصف وشحذه حتى يجتاز فحص حذف-الكلمة-المفتاحية. تلك المراجعة هي المهارة، والجزء الذي لا يقوم به أي قالب جاهز نيابة عنك.
2. أطلقها في عميل (اختياري، بلا توصيل). إن كان لديك Claude Code أو OpenCode مثبَّتاً ومسجَّل الدخول، فافتح المجلد فيه واطلب منه معالجة تذكرة دون قول "summarize" (مثلاً "write a handoff note for case #4471 before I escalate"). يكتشف العميل .claude/skills/، ويطابق وصفك، وينشّط summarize-ticket. تنبيه واحد: إن كان الطلب بسيطاً بما يكفي ليجيب عنه النموذج مباشرة، فلن تنطلق أي مهارة، وذلك قرار النموذج لا خلل في الوصف؛ اختبر بتسليم حقيقي، لا بسؤال من سطر واحد. قُرّاء SDK فقط يمكنهم القفز إلى الخطوة 3.
3. أطلقها في OpenAI Agents SDK. الآن صِل المهارة ببيئة تشغيل عاملك الخاصة، وافعلها بالطريقة التي ستفعل بها كل شيء في الجزء الرابع: أنت تطالب، والوكيل يخطّط، وتوافق، فيبني ويشغّل. ما زلت في المجلد digital-fte، فمشروع uv وَOPENAI_API_KEY ينتقلان معك. الصق هذا:
صِل مهارة
summarize-ticketبSandboxAgentبسيط أستطيع تشغيله من هذا المجلد: قدرةSkillsموجَّهة إلى.claude/skills، مع إبقاء القدرات الافتراضية، ونموذج من فئة gpt-5، على صندوق حماية محلي. تأكّد أنopenai-agentsمثبَّتة. خطّط أولاً.
إنه شكل SandboxAgent نفسه كما في الإنجاز السريع، مع استبدال أداة save_note بقدرة Skills (نموذج من فئة gpt-5 مهمّ: تشمل القدرات الافتراضية أداة نظام ملفات ترفضها النماذج الأصغر بخطأ 400). حين تبدو الخطة صحيحة، وافق واختبرها حيّاً دفعة واحدة:
نفّذها، ثم شغّلها ب "write a handoff note for case #4471: no refund, two weeks" وأرني التتبّع كي أرى المهارة تنطلق.
تأكّد أنها انطلقت، في التتبّع. تتتبّع الحزمة كل تشغيل إلى لوحة OpenAI نفسها التي استخدمتها في الدورة السابقة: افتح platform.openai.com/traces وسترى استدعاء load_skill ل summarize-ticket في التشغيل، ثم الرد بأقسامه الخمسة. (لا لوحة؟ حلقة print تُظهر التحميل نفسه في طرفيّتك.) .claude/skills هو المصدر؛ وَ.agents/ هو حيث تُجهَّز مهارة محمَّلة في زمن التشغيل. الملف نفسه، بيئتا تشغيل: تلك هي القدرة المحمولة، والقرار 8 يصلها بالعامل الكامل.
تفترض هذه المفاهيم متّبِعاً قوياً للتعليمات (Claude Sonnet/Opus، أو فئة GPT-5). على نموذج أصغر (deepseek-chat، أو فئة Haiku، أو معظم النماذج المحلية)، تنحرف ثلاثة أمور:
- تسلسل المهارات المتعددة. "نفّذ X دائماً قبل Y" يثبت على النماذج القوية، وينزلق على الضعيفة. الحل: ضع الترتيب في تمهيد قصير لسير عام (GENERAL-FLOW) في تعليمة النظام؛ وأبقِ متون المهارات تقريرية.
- انحراف التنسيق. النموذج الأضعف يضيف رموزاً تعبيرية، أو جداول، أو يعيد صياغة مدخلاتك. كن صريحاً بما لا يجب فعله، لا فقط بما يجب.
- عمى التحفيز. الوصف الذي ينطلق على "summarize ticket TKT-1042" قد يفوّت "what's the story on #1042." انضباط المفهوم 3 يهمّ أكثر، لا أقل، على نموذج ضعيف.
قاعدة عامة: اصرف جهد النموذج القوي في SKILL.md، وجهد النموذج الضعيف في تعليمة النظام. البنية تصمد؛ تكتب فقط مزيداً من السقالات حولها.
الجزء الثاني: Neon Postgres + pgvector بوصفه مصدر حقيقة
أعطى الجزء الأول الوكيل قدرات. والآن يحتاج إلى مكان دائم يحفظ فيه ما لا يقدر على نسيانه: سجل العميل، ومكتبة السياسات، والحالات السابقة المحلولة، وأثراً لكل ما فعله.
ذلك المتجر هو مصدر الحقيقة لعاملك، المتجر الموثوق الذي يعمل في مقابله (فكرة CRM-أو-دفتر-الأستاذ من الخريطة الافتتاحية، صارت الآن ملموسة). إنه Postgres بامتداد pgvector؛ ويشرح المفهوم 6 لماذا هو لا قاعدة بيانات متّجهة مخصصة. نستخدم Neon: مجاني في البداية، لا يكلّف شيئاً وهو خامل، ويستطيع وكيل البرمجة لديك تشغيله مباشرة، لكن أي Postgres مُدار ب pgvector يفي بالغرض.
من أنواع البيانات الأربعة في تلك الخريطة، تكون سجلات الأعمال (العملاء، الطلبات، التذاكر) خاصة بعملك، فتبنيها في الجزء الرابع. أما ما يبنيه هذا الجزء فهو الثلاثة الأخرى، الأجزاء التي يتشاركها كل عامل، مربوطةً الآن بالجداول الحقيقية التي تحملها:
- مكتبة المراجع: المعرفة التي يبحث فيها العامل بالمعنى، مكتبة السياسات، ومقالات قاعدة المعرفة، وملخّصات الحالات السابقة المحلولة. تعيش في
documentsوَembeddings(المفهومان 8 و9). - الحالة: المحادثة الحيّة. تعيش أدوارها في Session الخاصة بحزمة SDK للوكيل، التي تنشئها الحزمة وتكتبها نيابة عنك، فلا تصمّم تلك الجداول أبداً (المفهوم 7)؛ ويجلس صفّ
conversationsبجانبها، مربوطاً بمعرّف الجلسة، بوصفه الغلاف: مَن، ومتى، وملخّص ختامي. - الأثر: سجلّ ما فعله العامل، دفتر
audit_log(المفهوم 10). (جدول رفيق اختياري،capability_invocations، يضيف مقاييس لكل مهارة ولكل أداة.)
المفهوم 6: لماذا Postgres المُدار، ولماذا Neon تحديداً
تبقى الأطروحة محايدة تجاه المنتجات في مصادر الحقيقة: "قواعد بيانات الشركة الأصلية المعتمِدة على الذكاء الاصطناعي، وسير عملها، ومنصّاتها التشغيلية (أنظمة CRM، وَERP، وأنظمة التذاكر، ومستودعات البيانات، ودفاتر الأستاذ) تخدم بوصفها مصدر الحقيقة." لكن لوكيل تبنيه من الصفر، عليك أن تختار شيئاً. والسؤال ليس "Postgres مقابل MongoDB مقابل قاعدة بيانات متّجهة." بل "أيّ Postgres."
لماذا Postgres، لا قاعدة بيانات متّجهة مخصصة. ثلاثة أسباب تصمد حتى في 2026.
-
قاعدة بيانات واحدة، معاملة واحدة، حدود مصادقة واحدة. قاعدة بيانات متّجهة منفصلة تعني متجرين تبقيهما متزامنين، ونظامي مصادقة، وخطّي نسخ احتياطي. يبقي
pgvectorالمتّجهات بجوار السجلات التي تتعلّق بها، فيظلّ الربط (JOIN) ربطاً، لا قفزة شبكية بين خدمتين. وكل Postgres مُدار رئيسي (AWS RDS، وَCloud SQL، وَAzure، وَSupabase، وَNeon) يأتي به، وهو من بين أكثر امتدادات Postgres تثبيتاً. ولمعظم أحمال العمل يكفي. -
Postgres يقوم بالأجزاء الصعبة أصلاً. المعاملات، والفهارس، والمفاتيح الأجنبية، وأمان مستوى الصف، والاستعادة إلى نقطة زمنية، وتخطيط الاستعلامات. أما القاعدة المتّجهة المخصصة فعليها أن تخترع هذه من الصفر، وعادةً تفعل بعضها على نحو أسوأ. الخيار المملّ الافتراضي له مزايا متراكمة.
-
خوادم MCP موجودة ل Postgres في كل طبقة. Neon تأتي بواحد (للإدارة). وخوادم MCP عامة ل Postgres موجودة (لتنفيذ SQL). ويمكنك كتابة خادمك الخاص (لوصول مُحدَّد النطاق في زمن التشغيل). منظومة MCP حول Postgres هي الأنضج.
متى تفوز قاعدة بيانات متّجهة مخصصة. أدوات مثل Pinecone، وَWeaviate، وَQdrant، وَMilvus تستحقّ حين يكون البحث بالمعنى هو المنتج، لا ميزة تجلس بجوار بيانات أعمالك. والعلامات متطرّفة: متّجهات كثيرة لدرجة أنها لم تعد تتّسع في ذاكرة خادم Postgres واحد، أو حركة بحث ثقيلة بما يكفي لتحتاج محرّكاً مبنياً للمتّجهات وحدها، أو متّجهات تستخدمها وحدها خدمات منفصلة كثيرة. ولا يوجد رقم ثابت يتوقّف عنده pgvector، فاختبر بياناتك أنت لا أن تثق برقم. أما عامل بجدول tickets وتضميناته بجواره فبعيد كل البعد عن تلك النقطة، ف pgvector هو الافتراضي الصحيح.
لماذا Neon تحديداً: ثلاثة فوارق.
-
يتقلّص حجمه إلى الصفر. حين تكون قاعدة البيانات خاملة، لا تكلّف شيئاً. عامل يعالج 50 محادثة في اليوم يجلس خاملاً معظم الوقت، فيبقى قريباً من 0 دولار بدل أن يدفع شهرياً لخادم يعمل دائماً. وذلك يهمّ حين تشغّل عمّالاً كثيرين كلٌّ منشغل في دفعات فقط.
-
يتفرّع. في ثوانٍ، تصنع Neon نسخة كاملة من قاعدة بياناتك الحيّة لتعمل عليها، دون أن تمسّ الأصل. الاستخدام المتعلّق بالوكيل: دع الوكيل يجرّب تغييراً على فرع، وإن ساء، فاحذف الفرع فحسب. أما على قاعدة بيانات لا تتفرّع، فالتراجع عن تغيير سيّئ يعني الاستعادة من نسخة احتياطية.
-
له خادم MCP رسمي. تأتي Neon ب خادم MCP يستطيع وكيل البرمجة لديك التحدّث إليه، فيستطيع إنشاء مشاريع، وإدارة فروع، وتشغيل عمليات ترحيل بلغة طبيعية. استخدمه أثناء البناء؛ ويشرح المفهوم 12 لماذا ليس للعامل قيد التشغيل.
جرّب مع الذكاء الاصطناعي
A teammate proposes splitting the stores: Postgres for the relational
data (customers, tickets, orders) AND a separate Pinecone index for the
embeddings, "because Pinecone is purpose-built for vectors."
Context for you, the assistant: keeping vectors in Postgres (via the
pgvector extension) next to the relational data means one query can
filter by business state, rank by similarity, and return the full
record in a single transaction. Splitting the stores forces the agent
to round-trip between two services, denormalize and sync metadata
across them, and give up cross-store transactional consistency.
1. Make the case against the split as concretely as you can on ONE
request: a support Worker gets a message and must answer "have we
seen this before, and what did we tell them?" Show exactly what that
request costs when the vectors live in Pinecone and the tickets live
in Postgres. Name the join, what happens to ranking at the LIMIT
boundary when you filter in application code, and how an embedding
goes stale after a resolution is updated.
2. Name the ONE condition under which the teammate is actually right and
a dedicated vector DB is the better call. Be specific about the scale
at which the crossover happens.
3. Neon adds two properties a plain Postgres box doesn't: scale-to-zero
(an idle Worker's database costs nothing) and branching (the agent
forks a production-fidelity copy of the data, experiments or migrates
on it in isolation, then verifies before merging). Which matters more
for an AI Worker specifically, and why? Defend your pick in two
sentences.
المفهوم 7: مخطط العامل، ما الجداول التي يحتاجها الوكيل فعلاً
المخطط في قاعدة البيانات هو الجداول التي تحتفظ بها والأعمدة في كلٍّ منها، أي شكل بياناتك. الجداول الخمسة التي يبنيها المثال العملي هي الأجزاء المشتركة من مصدر الحقيقة التي يحتاجها كل عامل؛ أما سجلات الأعمال نفسها فتأتي في الجزء الرابع. وتنقسم إلى مجموعتين، كي ترى ما هو جوهري وما هو اختياري.
أربعة جداول يحتفظ بها كل عامل، العمود الفقري المشترك. تحمل الحالة، ومكتبة المراجع، والأثر من افتتاحية الجزء، الآن بوصفها جداول:
conversations(الحالة): صفّ واحد لكل محادثة، مع مَن كانت، ومتى، وملخّص قصير في النهاية. (تُخزَّن رسائل الأدوار الواحدة تلو الأخرى منفصلة، بواسطة SDK؛ انظر أدناه.)documentsوَ**embeddings** (مكتبة المراجع): يحملdocumentsالنص (السياسات، الحالات السابقة)؛ وَembeddingsهو ما يجعله قابلاً للبحث بالمعنى. التضمين يحوّل قطعة نص إلى قائمة أرقام تلتقط موضوعها، فينتهي النص المتقارب قريباً بعضه من بعض، كأنك تثبّت ملاحظات على لوحة حيث يتجمّع المتشابه، فيصير "ابحث عن المتعلّق" هو "ابحث عن الأقرب". (يبني المفهوم 9 هذا؛ هنا، يكفي أن تعرف أنembeddingsهي طبقة البحث بالمعنى.)audit_log(الأثر): سجلّ متواصل لما فعله العامل، كل فعل بترتيبه، بما في ذلك أحداث الأعمال مثل إصدار استرداد.
واحد آخر تضيفه حين تحتاجه، تحليلات الاستخدام.
capability_invocations: صفّ واحد في كل مرة يشغّل فيها العامل مهارة أو يستدعي أداة (يتشاركان هذا الجدول الواحد؛ عمود يحدّد أيّهما، فلا تنمو جدولاً لكل أداة)، مع المدة التي استغرقها، وهل نجح أم فشل، وتكلفة تقريبية. أضِفه حين تريد تحليلات استخدام القدرات في SQL: كم مرة تنطلق مهارة، ومعدّل خطئها، وما الذي يسبق التصعيد عادةً.
جدولان آخران يعيشان خارج هذه المجموعة، كلاهما في الجزء الرابع: جداولك الخاصة بالأعمال (العملاء، التذاكر، الطلبات)، وَ**run_states**، الذي يخزّن موافقة مُعلَّقة حين يوقّع إنسان لاحقاً أو في عملية أخرى بدل أن يفعل فوراً. لا أيٌّ منهما جزء من العمود الفقري المشترك.
أين تذهب الرسائل نفسها؟ تخيّل محضراً وغلافاً. المحضر هو كل رسالة، سؤالك، ورد النموذج، وكل استدعاء أداة، كلٌّ محفوظ بوصفه صفّه الخاص؛ تكتبه الحزمة وتحفظه نيابة عنك (يُوصَل في القرار 3)، فلا تبنيه أبداً. أما الغلاف فهو صفّ conversations الواحد الذي تكتبه: مَن، ومتى، وملخّص، إضافة إلى تفاصيل أعمال مثل user_id لا تحملها جداول SDK نفسها. تحتفظ به لأن المحضر لا يستطيع الإجابة عن "أرني آخر خمس محادثات لهذا العميل"؛ فذلك بحث سريع على conversations، مربوط بالمحضر بمعرّف الجلسة الذي يتشاركانه. وهو اختياري: إن لم تحتج قطّ قوائم لكل مستخدم أو ملخّصات، فالمحضر وحده يكفي.
ملف SQL الكامل لكل الجداول الخمسة في الصندوق أدناه. يكتبه وكيل البرمجة لديك من الخطة في القرار 3، فيمكنك تجاوزه تصفّحاً؛ والذي يهمّ هو معرفة وظيفة كل جدول.المخطط، كاملاً (أربعة جداول مشتركة إضافة إلى capability_invocations الاختياري)
-- 1. CONVERSATIONS: business metadata per conversation (your app writes this row)
CREATE TABLE conversations (
session_id TEXT PRIMARY KEY, -- the SAME id you pass to SQLAlchemySession
user_id TEXT NOT NULL,
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
ended_at TIMESTAMPTZ,
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
-- searchable summary; your app writes it at conversation end
summary TEXT
);
CREATE INDEX idx_conversations_user ON conversations(user_id, started_at DESC);
-- The turns themselves live in the SDK Session's tables (agent_sessions /
-- agent_messages, via SQLAlchemySession), created automatically on this same
-- database and keyed by this session_id; you do not hand-build them.
-- 2. DOCUMENTS: the agent's reference library
CREATE TABLE documents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
source TEXT NOT NULL, -- 'policy_library', 'kb_article', 'past_case', etc.
title TEXT NOT NULL,
body TEXT NOT NULL,
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_documents_source ON documents(source);
-- 3. EMBEDDINGS: vector representations of documents AND past conversations
CREATE TABLE embeddings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- one of these is populated; the other is NULL
document_id UUID REFERENCES documents(id) ON DELETE CASCADE,
conversation_id TEXT REFERENCES conversations(session_id) ON DELETE CASCADE,
chunk_text TEXT NOT NULL,
chunk_index INT NOT NULL,
embedding VECTOR(1536) NOT NULL,
model TEXT NOT NULL, -- 'text-embedding-3-small', etc.
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CHECK (
(document_id IS NOT NULL)::int + (conversation_id IS NOT NULL)::int = 1
)
);
-- the key index for semantic search; see Concept 8
CREATE INDEX idx_embeddings_hnsw
ON embeddings USING hnsw (embedding vector_cosine_ops);
-- 4. AUDIT_LOG: replayable trace of how the Worker changed or used the record
CREATE TABLE audit_log (
id BIGSERIAL PRIMARY KEY,
conversation_id TEXT REFERENCES conversations(session_id) ON DELETE SET NULL,
actor TEXT NOT NULL, -- 'worker:customer-support', 'system', etc.
action TEXT NOT NULL CHECK (action IN (
'message_received', 'message_sent', 'skill_activated',
'capability_invoked', 'refund_issued', 'refund_blocked',
'guardrail_tripped', 'corpus_seeded'
)), -- closed vocabulary; widening it is a migration (Concept 10)
target TEXT, -- table name, skill name, etc.
payload JSONB NOT NULL, -- the data of the action
result JSONB, -- what happened
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_audit_conv ON audit_log(conversation_id, created_at);
CREATE INDEX idx_audit_action ON audit_log(action, created_at);
-- 5. CAPABILITY_INVOCATIONS: every skill or tool call, for replay and metrics
CREATE TABLE capability_invocations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
conversation_id TEXT NOT NULL REFERENCES conversations(session_id) ON DELETE CASCADE,
capability TEXT NOT NULL, -- 'skill:summarize-ticket', 'tool:search_docs', etc.
arguments JSONB NOT NULL,
result JSONB,
status TEXT NOT NULL CHECK (status IN ('ok', 'error', 'blocked', 'timeout')), -- 'blocked' = approval rejected
latency_ms INT,
cost_cents INT, -- approximate cost in 1/100 cents
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_cap_conv ON capability_invocations(conversation_id, created_at);
بضعة خيارات تصميمية جديرة بالفهم:
-
جدول
embeddingsواحد للمستندات والمحادثات معاً. قيدCHECKيجعل كل صفّ يشير إلى واحد بالضبط، مستند أو محادثة. فيستطيع بحث واحد أن يغطّي السياسات والمحادثات السابقة معاً، و"هل أجبنا عن هذا من قبل؟" يستخدم فهرساً واحداً لا اثنين. -
audit_logيستخدمBIGSERIAL(رقماً يتزايد تلقائياً)، لاUUID. صفوف التدقيق تتراكم بسرعة، والمفتاح الصحيح البسيط يبقي الكتابات سريعة والترتيب بيّناً. أما الجداول الأخرى فتستخدمUUID(معرّفات عشوائية فريدة عالمياً) لأن صفوفها تظهر في ردود API والروابط، حيث يُخفيUUIDكم صفّاً لديك. -
المهارات والأدوات تتشارك
capability_invocations. استدعاء مهارة واستدعاء أداة متشابهان لا متطابقين (كود مختلف، تكاليف مختلفة، طرق فشل مختلفة). إبقاء كليهما في جدول واحد، بعمود يقول أيّهما، يتيح لك أن تسأل "ماذا فعل الوكيل؟" عبر الاثنين، أو أن تفصلهما لتسأل "أي المهارات بطيئة أو تفشل؟". -
أعمدة
metadataمن نوع JSONB مخارج هروب. لا مخطط يستطيع تخمين كل حقل سيحتاجه عمل بعينه، فعمود JSONB يتيح لك إضافة حقول دون تغيير الجدول. استخدمه بحذر: أي شيء تستعلم عنه كثيراً ينبغي أن يصبح عموده الخاص.
ستضيف مزيداً من الجداول لعملك: جدول customers، وجدول tickets، وجدول orders، جداول علائقية عادية يقرؤها العامل ويكتبها عبر MCP.
PRIMM، التنبّؤ. عامل يعالج 200 محادثة/يوم، بمعدّل 10 أدوار لكلٍّ، مع 30% تُطلِق استدعاء مهارة واحداً و50% تكتب صفّي تدقيق إضافيين عدا صفّ المهارة. بعد شهر (30 يوماً)، أي متجر ينمو أسرع؟ ثلاثة خيارات: (أ) كلها بحجم متقارب؛ (ب)
audit_logينمو أسرع بفارق واسع؛ (ج) جدول التضمينات، لأن كل دور يُضمَّن. الثقة 1–5.
الجواب (ب): من بين الجداول التي تبنيها، ينمو audit_log أسرع، لأن تفاعلاً واحداً قد يكتب عدة صفوف أفعال (استدعاء مهارة أو أداة، وكتابة في السجل، وأحياناً استرداد) بينما يضيف صفّ conversations واحداً فقط ولا مستندات جديدة. فهو الجدول الذي تخطّط لاحتفاظه وفهرسته أولاً وأنت تنمو. (متجر أدوار SDK نفسه ينمو أسرع بعد، لكنك لا تديره.)
جرّب مع الذكاء الاصطناعي
I'm building a customer-support Worker. Its database already
has the four shared tables from Concept 7: `conversations` (one row per
conversation, plus a summary), `documents` and `embeddings` (a
searchable reference library), and `audit_log` (the record of what it
did). The turn-by-turn messages are held by the agent SDK's Session,
not a table I built.
I want to extend this for a Worker that handles software bug reports
specifically. What three additional tables would you add, and what
columns would they have? For each, say what the agent will use it for
(read access? write access? both?) and what foreign keys connect it to
the tables above.
المفهوم 8: أساسيات pgvector، الأنواع، وعوامل المسافة، والفهارس
جدول embeddings هو ما يتيح للعامل إيجاد النص ب المعنى، لا بمطابقة الكلمات فقط. عُد إلى اللوحة: كل قطعة نص (سياسة، حالة سابقة، سجلّ) تحصل على دبّوس، والمتعلّقات تجلس قريباً بعضها من بعض. موضع الدبّوس هو التضمين، قائمة أرقام. وَpgvector (امتداد Postgres) هو ما يتيح ل Postgres تخزين تلك الدبابيس وإيجاد الأقرب منها، فلا تحتاج قاعدة بيانات متّجهة منفصلة (المفهوم 6 هو السبب).
نوع المتّجه. VECTOR(n) عمود يحمل دبّوساً واحداً: قائمة ثابتة من n أرقام. النموذج الذي يصنع التضمينات هو من يقرّر n، أي 1536 ل text-embedding-3-small من OpenAI، و3072 ل text-embedding-3-large، وتختلف نماذج أخرى. القاعدة التي تلدغ الناس: نصّك المخزَّن واستعلام بحثك يجب أن يأتيا من النموذج نفسه. النموذجان كخريطتين رُسمتا بمقياسين مختلفين، فموضع يعني "وسط المدينة" على إحداهما يقع في المحيط على الأخرى. ضمّن مستنداتك بنموذج واستعلاماتك بآخر، فتعود نتائج "الأقرب" هراءً وإن جرى الاستعلام بلا خطأ. إنه أشيع أخطاء pgvector.
للتضمينات الكبيرة جداً (أكثر من 2000 رقم)، يخزّن عمود halfvec كل رقم بنصف الدقّة: يخفض ذلك التخزين إلى النصف تقريباً ويبقى قابلاً للفهرسة (حتى 4000 رقم)، بكلفة دقّة صغيرة. حالتنا ذات 1536 رقماً لا تحتاجه؛ وَvector(1536) العادي يكفي.
ثلاث طرق لقياس "مدى القرب". ما إن يُثبَّت النص حتى يصبح "المتشابه" يعني "القريب". يعطي pgvector ثلاث طرق لقياس المسافة بين دبّوسين. اختر واحدة والتزم بها؛ فالتبديل بينها في منتصف المشروع لا يفعل إلا أن يربك النتائج.
| العامل | الاسم | ما يقيسه | متى يُستخدم |
|---|---|---|---|
<=> | جيب التمام | مدى محاذاة دبّوسين، بصرف النظر عن الطول | النصوص، افتراضنا |
<-> | المسافة المستقيمة | المسافة البسيطة بين نقطتين | بحث الصور والبيانات الهندسية الأخرى |
<#> | الضرب القياسي | الاتجاه والطول معاً | نادر: فقط حين لا تكون متّجهاتك كلها بطول واحد |
للنصوص، استخدم جيب التمام (<=>). يقارن المعنى مهما طالت المتّجهات، وهو ما تريده، وهو الخيار المعياري (يُسمّى فهرسه vector_cosine_ops).
للبحث، تحوّل سؤال المستخدم إلى تضمين وتطلب من Postgres الصفوف التي مسافة <=> إليه هي الأصغر، الأقرب أولاً، أعلى بضعة. وكيلك يكتب ذلك ال SQL؛ وستشاهد استعلاماً حقيقياً يجري في "أحسسه يعمل" أدناه.
الفهارس: ما يجعل البحث سريعاً. فحص كل دبّوس واحداً واحداً يصير بطيئاً ما إن يكون لديك آلاف منها. الفهرس يصلح ذلك، كما يتيح لك الفهرس في آخر كتاب أن تقفز إلى موضوع بدل قراءة كل صفحة. يستطيع pgvector بناء هذا الفهرس بطريقتين، تُسمّيان HNSW وَIVFFlat؛ لا تحتاج معرفة ما تعنيه الأحرف، بل ما يفعله كلٌّ فقط. وحتى 2026 استقرّت النصيحة:
- ابدأ ب HNSW. يربط كل دبّوس بجيرانه فتستطيع عملية بحث أن تقفز مباشرة نحو الأقرب: بحث سريع، وبناء أبطأ، وذاكرة أكثر. الافتراضي الصحيح.
- استخدم IVFFlat فقط إن كانت سرعة البناء أهمّ من سرعة البحث. يفرز الدبابيس في دلاء ويبحث في أقربها: أسرع بناءً وأخفّ على الذاكرة، لكن بحثه أبطأ، ولا تستطيع بناءه إلا بعد أن تمتلئ الجداول ببيانات (يتعلّم الدلاء من الصفوف الموجودة أصلاً). يستحقّ إن كنت تعيد بناء الفهرس كثيراً.
- DiskANN (إضافة منفصلة) للفهارس الأكبر من أن تتّسع في الذاكرة. وأنت على الأرجح لا تحتاجه.
فهرس HNSW من المخطط أعلاه:
CREATE INDEX idx_embeddings_hnsw
ON embeddings USING hnsw (embedding vector_cosine_ops);
ل HNSW قُرصان، m وَef_construction. القيم الافتراضية مناسبة لمعظم أحمال العمل؛ فاتركها وحدها ما لم تكن قست سبباً لتغييرها.
فحص سريع. صحيح أم خطأ؟ (أ) يمكنك وضع أكثر من فهرس HNSW واحد على العمود نفسه، واحد لكل عامل مسافة. (ب) إضافة صفّ إلى جدول فيه فهرس HNSW تكلّف أكثر من إضافة صفّ إلى جدول بلا فهرس متّجه. (ج) يمكنك إنشاء فهرس HNSW قبل تحميل أي بيانات. الثلاثة صحيحة: يمكنك الفهرسة لعدة عوامل (نادراً ما يلزم)، وإبقاء الفهرس محدَّثاً مع وصول الصفوف له كلفة حقيقية (فبعض الفرق تحمّل البيانات بالجملة أولاً، ثم تبني الفهرس)، وَ HNSW لا يحتاج بيانات تدريب، بخلاف IVFFlat.
جرّب مع الذكاء الاصطناعي
Two scenarios. For each, pick HNSW or IVFFlat and justify with one
specific property of the index:
Scenario A: A research index of 10M scientific papers. Built once,
queried millions of times. Build time is "whatever it takes,
overnight is fine." Query latency directly affects user experience.
Scenario B: A live index of customer support tickets that's
re-indexed every 4 hours because thousands of new tickets stream in.
Query patterns are simple (top-5 nearest neighbors). The current
HNSW build takes 20 minutes, a third of the re-index cycle.
After you answer: name ONE thing that would change your answer for
each scenario. Be specific about what you'd need to see in
production metrics before switching.
المفهوم 9: خط أنابيب التضمين، نصّ يدخل، متّجه قابل للاستعلام يخرج
التضمين يحوّل قطعة نص إلى نقطة في الفضاء. نصّ عن المبالغ المستردّة يقع قرب نصوص أخرى عن المبالغ المستردّة؛ ونصّ عن أعطال تسجيل الدخول يقع في مكان آخر. فيصير "ابحث عن تذاكر متشابهة" هو "ابحث عن أقرب النقاط". تلك هي الفكرة كلها. والباقي سباكة.
السباكة أربع خطوات، لكلٍّ قرار واحد يهمّ:
- قطِّع المستند إلى قطع صغيرة بما يكفي ليحمل كلٌّ فكرة واحدة.
- ضمِّن كل قطعة باستدعاء النموذج؛ تستردّ نقطتها.
- خزِّن النص ونقطته وقليلاً من البيانات الوصفية في جدول
embeddings. - استعلِم بتحويل سؤال المستخدم إلى نقطة أيضاً، ثم إيجاد أقرب النقاط المخزَّنة.

التقطيع: قسّم النص الطويل أولاً. المستند الطويل لا ينبغي أن يصير تضميناً عملاقاً واحداً. تقسّمه إلى قطع، وحجم القطعة هو القرار الوحيد الذي يهمّ:
- قسّم عند الفواصل الطبيعية (العناوين، الفقرات). القطعة التي تتوقّف في منتصف الجملة تبحث بسوء.
- استهدف بضع مئات من الكلمات لكل قطعة. الأكبر من اللازم "يطابق كل شيء بضعف"؛ والأصغر من اللازم يفقد السياق الذي جعله ذا معنى.
- اجعل القطع تتداخل قليلاً، كي تظلّ فكرة تمتدّ عبر حدّ قابلة للإيجاد.
- لا تقطّع ما هو قصير أصلاً. تذكرة محلولة واحدة أو مدخل أسئلة شائعة قصير هو قطعة واحدة أصلاً؛ ضمّنه كما هو.
وكيلك يكتب كود التقسيم؛ والذي تقرّره أنت هو حجم القطعة والتداخل.
التضمين: حوّل كل قطعة إلى نقطة. تسلّم كل قطعة لنموذج التضمين وتخزّن النقطة التي يردّها (في دفعات، وهي أرخص بكثير من استدعاء لكل قطعة). أبقِ قاعدة المفهوم 8 سارية: ضمّن نصّك المخزَّن واستعلامات بحثك بالنموذج نفسه، وإلا عادت المطابقات ضجيجاً. مزلق إعداد واحد جدير بالمعرفة (وكيلك يتولّاه): يجب إخبار مشغّل قاعدة البيانات بنوع المتّجه، وإلا قد تفشل إدراجاتك بصمت.
ماذا لو لم تكن على OpenAI؟ OpenAI هي المزوّد الكبير الوحيد الذي يأتي أيضاً بواجهة تضمينات من الدرجة الأولى، فإن كنت تشغّل الاستدلال عبر DeepSeek، أو Anthropic، أو Gemini، أو نموذج محلي، فإنك تختار نموذج تضمين على حدة، والبُعد هو ما يجب أن يتطابق. والمخرج المعتاد نموذج sentence-transformers محلي مثل all-MiniLM-L6-v2 (384 بُعداً): بلا استدعاء API، ولا يغادر أي نص جهازك. وفي الحالتين تكون التضمينات أرخص بند في الفاتورة، فهذا الاختيار يحرّك بنيتك، لا ميزانيتك.
متى تعيد التضمين. ثلاثة مُطلِقات:
- تغيّر النص المصدر، أعِد تضمين تلك الصفوف.
- بدّلت نماذج التضمين، فكل نقطة قديمة تعيش الآن على خريطة مختلفة وعلى الأرجح بحجم مختلف، فأعِد بناء العمود وأعِد تضمين كل صفّ (أو أبقِ الاثنين أثناء الانتقال). لا يوجد "قريب بما يكفي".
- غيّرت حجم القطعة، أعِد التقطيع وأعِد التضمين.
PRIMM، التنبّؤ. ضمّنت 100,000 قطعة ب
text-embedding-3-small. ثم قرّرت أن تضمّن أيضاً محادثاتك السابقة (لا المستندات فقط) كي يستطيع الوكيل عمليات بحث "هل ناقشنا هذا من قبل؟". تكتب تضمينات المحادثات في جدولembeddingsنفسه بالعمود نفسه. يعود استعلام بحث دلالي (إيجاد أقرب 5 جيران لسؤال مستخدم، بلا تصفية) بنتائج مستندات ومحادثات مختلطة. هل هذا ما أردت؟ ما الشكل الصحيح للاستعلام؟ الثقة 1–5.
الجواب: على الأرجح ليس ما أردت. مع اختلاط المستندات والمحادثات السابقة في النتائج، قد يعامل الوكيل مقتطفاً من دردشة قديمة كأنه سياسة موثوقة. والإصلاح هو التصفية حسب المصدر عند البحث: اطلب المستندات فقط، أو شغّل بحثين وزِنهما، كي لا يختلط النوعان أبداً.
حين تبدو النتائج خاطئة، يكون السبب على الأرجح أحد ثلاثة: مرّ الاستعلام والنص المخزَّن عبر نماذج مختلفة (المطابقات ضجيج)، أو نسيت التصفية حسب نوع المصدر، أو قطعك أصغر من أن تحمل معنى. افحص هذه أولاً.
جودة الاسترجاع هي القاتل الصامت لدقّة العامل. قد يبدو الجواب النهائي معقولاً تماماً بينما يستشهد بالدليل الخطأ. والطريق الوحيد لالتقاط ذلك هو فحص الاسترجاع قبل الجواب.
جرّب مع الذكاء الاصطناعي
I'm chunking a corpus of legal contracts (each averaging 8,000 words)
for semantic search. The user will query things like "what's the
termination clause in this contract", phrases that map cleanly to
specific sections. Walk me through three chunking strategies:
A) Fixed 400-token chunks with 60-token overlap (the default)
B) Chunk at section headings only, with no overlap
C) A two-level approach: store both 400-token chunks AND
whole-section chunks, search both, combine results
For each, name (1) when it wins and (2) when it loses.
أحسسه يعمل: بحث دلالي في عشر دقائق
قرأت عن pgvector وخط أنابيب التضمين دون أن ترى أياً منهما يعيد نتيجة واحدة. قبل القطعة الأخيرة من المخطط، أثر التدقيق، خذ عشر دقائق لتشاهد البحث الدلالي يرتّب بالمعنى فعلاً. هذا قابل للرمي، لا العامل: جدول مؤقت، وخمس جمل، واستعلام واحد. الجزء الرابع يبني الشيء الحقيقي.
Neon لديك موصول أصلاً من الإنجاز السريع، فهذه مطالبة واحدة:
على فرع مؤقت جديد من مشروع Neon لديّ، أنشئ جدولاً صغيراً
notes(id, text, embedding vector(1536))بفهرس HNSW. ضمّن هذه الجمل الخمس بtext-embedding-3-smallوأدرجها: "the refund hasn't arrived"، و"my package is late"، و"how do I reset my password"، و"the charge appears twice"، و"I was billed for something I didn't buy". ثم ضمّن الاستعلام "I never got my money back"، وشغّل بحثاً بمسافة جيب التمام، وأرني الصفوف مرتّبة بالمسافة.
راقب: جملتا الفوترة والاسترداد تُرتَّبان فوق "my package is late" وأبعد بكثير فوق "reset my password"، مع أن الاستعلام لا يكاد يشارك أياً منها كلمة واحدة. الترتيب بالمعنى، لا بتداخل الكلمات المفتاحية، هو سبب وجود جدول embeddings كله.
يكتمل عندما: تكون قد رأيت القائمة المرتّبة وجملتا الاسترداد والفوترة في القمة. اطلب من وكيلك حذف الفرع المؤقت؛ المخطط الحقيقي هو الجزء الرابع.
إن لم تفز جمل الاسترداد، فالسبب المعتاد هو عدم تطابق نماذج المفهوم 9: مرّ الإدراج والاستعلام عبر نماذج تضمين مختلفة. النموذج نفسه على الطرفين، وإلا فالمسافات ضجيج.
المفهوم 10: سجل التدقيق بوصفه انضباطاً، ماذا تعني "القراءة والكتابة" لعامل
كل فعل ذي معنى يقوم به الوكيل ينبغي أن يترك صفّاً في قاعدة البيانات. بدون ذلك الصفّ، لا تستطيع لاحقاً أن تجيب عن "ماذا فعل الوكيل، ومتى؟". ذلك الأثر هو ما يفصل فعلاً حقيقياً عن ردّ يبدو معقولاً.
شيئان يجلسان متقاربين هنا ويُخلَط بينهما، فأبقهما متمايزين:
- الحقيقة نفسها: ما هو الحال الآن، فئة عميل، حالة تذكرة، نصّ سياسة. تعيش في سجلات الأعمال ومكتبة المراجع، ويقرؤها العامل ويحدّثها.
- أثر التدقيق: السجلّ القابل لإعادة التشغيل لما فعله العامل بتلك الحقيقة، أي أداة استدعى، وما الذي غيّره، وما الذي أعاده، ومَن وافق عليه. يعيش في
audit_log، في قاعدة البيانات نفسها، ويجيب عن سؤال مختلف، لا "ما الحقيقة؟" بل "ماذا فعل العامل، وهل تستطيع إثباته؟". وهو ليس نسخة ثانية من المحادثة (Session تحتفظ أصلاً بكل رسالة)؛ بل يسجّل الأفعال المُصنَّفة ونتائجها، بما فيها أفعال لا تظهر قط بوصفها رسالة، كتابة في قاعدة بيانات، أو استرداد، أو حجب حاجز أمان. (جدول منفصل اختياريcapability_invocationsيجلس بجانبه لمقاييس كل مهارة وكل أداة؛ انظر المفهوم 7.)
فكل فعل ذي معنى يكتب صفّ تدقيقه الخاص وإن كانت البيانات التي مسّها مخزَّنة في مكان آخر. واقعة أن الفعل حدث تعيش في audit_log؛ والاثنان مربوطان بمفتاح أجنبي.
ماذا يدخل فيه. الأفعال ذات المعنى، بتفصيل كافٍ لإعادة تشغيلها: كل استدعاء أداة أو مهارة (الاسم، والمدخلات، والنتيجة، والمدة، وهل نجح)، وكل تغيير في السجل (أي جدول، وما الذي تغيّر، وتحت أي محادثة)، وكل قرار حاجز أمان، وكل استدعاء نموذج بتكلفة رموزه.
ماذا يبقى خارجه. نصّ المحادثة الكامل، ف Session تحتفظ به أصلاً، فتخزينه مجدداً يضاعف تخزينك فقط. والبيانات الحسّاسة الخام في صفّ يقرؤه البشر، احتفظ بتجزئة أو ملخّص واقفل الشيء الكامل بعيداً. وتفكير النموذج الخاص.
الاختبار الذي يجعله أثر تدقيق، لا مجرد سجلات: بمعطيات محادثة ووقت، تستطيع إعادة بناء ما فعله العامل ولماذا، دون إعادة تشغيل النموذج. إن لم تستطع، فلديك سجلات.
اكتب الفعل وسجلّه معاً. أيّ كود يصدر استرداداً يكتب الاسترداد و صفّ تدقيقه في معاملة واحدة: كلاهما يهبط أو لا أحد. أثر تدقيق نصف مكتوب أسوأ من لا شيء، يبدو كاملاً وليس كذلك. (وكيلك يكتب هذا في الجزء الرابع.)
أعطِ كل فعل اسماً من مجموعة صغيرة متّفق عليها (refund_issued، وَmessage_sent، وهكذا) ولا تدع تلك الأسماء تنحرف. ثلاثة أسماء مختلفة للحدث نفسه، بعد ستة أشهر، هو ما يجعل الأثر مستحيل الاستعلام. أحداث المجال مثل refund_issued تحصل على اسمها الخاص كي يُقرأ الصفّ بوصفه إيصالاً لحدث العمل، لا لاستدعاء الأداة الذي أطلقه فقط.
ولأن تلك المجموعة صغيرة وثابتة، افرضها بقيد CHECK على audit_log.action (مخطط المفهوم 7 يفعل). والمأخذ الذي يصطدم به بناء بعد أسابيع: المفردات الآن مغلقة، فإدخال فعل جديد (صفّ guardrail_tripped في القرار 9، وصفّ corpus_seeded الذي يكتبه القرار 5 لتشغيل بذره الخاص) هو ترحيل من سطر واحد ALTER TABLE ... DROP/ADD CONSTRAINT، لا مجرد كود جديد، ويظهر الخطأ بوصفه انتهاك قيد في قاعدة البيانات لا يشير إطلاقاً إلى "نسيت تخطيط مفرداتك". فقرّر المجموعة الكاملة مقدّماً؛ وقيد المفهوم 7 يسرد أصلاً الثمانية التي تستخدمها هذه الدورة.
ما ليس به أثر التدقيق. ليس مجرد سجلات: إنه SQL قابل للاستعلام في قاعدة بياناتك الخاصة ("ماذا أخبر الوكيل العميل X الشهر الماضي، وأي سياسة استشهد بها؟" استعلام واحد)، لا grep على ملفات نصية، وهو مُنسَّخ احتياطياً ومُتحكَّم في الوصول إليه بجانب بيانات أعمالك. وليس مصدرة أحداث (event sourcing): إنه أثر للإلحاق فقط بجوار حالتك، لا الشيء الذي تعيد منه بناء الحالة (تذاكرك، ومستنداتك، وَSession هي الحالة). وليس تتبّعاتك: التتبّع (OpenTelemetry، ولوحة OpenAI) هو مسجّل الرحلة للتنقيح، يعيش في نظام منفصل، ويمكن إيقافه، وغير متاح تحت سياسة عدم الاحتفاظ بالبيانات؛ أما سجل التدقيق فهو الإيصال، مُلتزَم في المعاملة نفسها مع الفعل ومحفوظ ما دمت تحتاجه. شغّل كليهما: التتبّع للتنقيح، والدفتر للإثبات.
هذا ما تعنيه الأطروحة: "لا يصبح العمّال قابلين للحوكمة كقوة عاملة إلا حين يجعلهم دفترٌ مقروئين." وَaudit_log لديك هو ذلك الدفتر. والمقروئية هي ما يجعل العامل قابلاً للبيع: لا تستطيع أن تتقاضى ثمن نتيجة لا تستطيع إثبات حدوثها. التسعير بالمقعد يعدّ عمليات تسجيل الدخول؛ والتسعير بالنتيجة يعدّ ما فعله العامل، لكل تذكرة محلولة، ولكل فاتورة معالَجة، ولكل ردّ مصوغ. صفوف refund_issued وَticket_resolved هي تلك النتائج، تجلس في السجل نفسه مع الأحداث منخفضة المستوى، شيء تستطيع أن توجّه إليه العميل وأن تُصدِر له فاتورة. فالعامل يحتاج مصدر حقيقة لا فقط كي يتوقّف عن النسيان بين عمليات التشغيل، بل كي يصير عمله أثراً قابلاً للإثبات وللفوترة. ذلك هو الخطّ بين توصيل وكيل بقاعدة بيانات وبناء عامل تستطيع فعلاً أن تبيعه.
جرّب مع الذكاء الاصطناعي
Here's a customer support scenario: a customer claims the Worker told
them they would receive a $50 refund, but the actual refund issued was
$30. The Worker handled the conversation 19 days ago.
Walk me through the audit-trail query path to resolve this:
1. Find the conversation. (Which columns of which tables?)
2. Find the message where the refund amount was promised. (How do you
distinguish "discussed" from "promised"?)
3. Find the capability invocation that issued the refund.
4. Find the database write that recorded the $30 amount.
For each step, name the table you'd query and the WHERE clauses.
Then say what's MISSING from the five-table schema that would make
this query easier.
الجزء الثالث: MCP، توصيل الوكيل بمصدر الحقيقة
أعطى الجزء الأول الوكيل مكتبة من المهارات. وأعطاه الجزء الثاني مصدر حقيقة في Postgres. يصل الجزء الثالث الاثنين ببروتوكول سياق النموذج (MCP): المعيار المفتوح لكيفية وصول الوكلاء إلى الحالة الخارجية والقدرة الخارجية. الأطروحة مباشرة في موضع MCP: "MCP هو كيف تصل القوة العاملة إلى [مصادر حقيقتها]: كل متجر موثوق يصبح قابلاً للعَنوَنة لأي عامل عبر خادم MCP، تحت السياسة." هذا الجزء يجعل ذلك عملياً.
المفهوم 11: ما MCP وما ليس به
بروتوكول سياق النموذج (modelcontextprotocol.io) بروتوكول مفتوح بين عميل وخادم (نشأ في Anthropic، ويُحكَم الآن بوصفه معياراً مفتوحاً) لكيفية اتصال وكيل ذكاء اصطناعي بأدوات وبيانات وتعليمات خارجية. والتأطير الذي يتكرّر هو "USB-C لأدوات الذكاء الاصطناعي": بروتوكول واحد، وتطبيقات كثيرة، تبدّل أي طرف دون كسر الآخر. التأطير دقيق؛ ومثل كل الاستعارات، له حدود تستحقّ التسمية.
ما MCP. بروتوكول. ومواصفة. وثلاث أوّليات يستطيع الخادم كشفها للعميل.
- الأدوات: دوال يستطيع النموذج استدعاءها. العميل يسردها، والنموذج يختار واحدة، والخادم ينفّذها. مفهومياً تشبه مُزخرِف
@function_toolمن الدورة السابقة، لكن التطبيق يعيش في عملية خادم MCP، لا في عملية الوكيل. وهي الأوّلية الأكثر استخداماً بفارق كبير. - الموارد: بيانات للقراءة فقط يستطيع الوكيل جلبها. ملفات، ونتائج استعلام قاعدة بيانات، وردود API. فكّر فيها بوصفها الجانب GET-فقط من MCP. أقلّ شيوعاً من الأدوات عملياً، لكنها مفيدة ل "دع الوكيل يقرأ هذا المستند عند الطلب".
- التعليمات: قوالب تعليمات قابلة لإعادة الاستخدام يقدّمها الخادم. يستطيع فريق نشر تعليمات موحَّدة ("summarize-incident-report") يستطيع أي وكيل يتّصل بالخادم استدعاؤها. نادراً ما تُستخدم مقارنة بالأدوات والموارد.
ثلاثة وسائط نقل، مع التوصيات الحالية حتى 2026:
| النقل | متى يُستخدم | الحالة |
|---|---|---|
stdio | عملية فرعية محلية؛ الوكيل والخادم على الجهاز نفسه | ناضج. الافتراضي للأدوات المحلية. |
streamable HTTP | خادم بعيد؛ عمليات نشر إنتاجية | مُوصى به للعمل البعيد الجديد. نقطة نهاية واحدة عبر HTTPS عادي. |
SSE | خادم بعيد؛ عمليات نشر أقدم | قديم. كثير من الخوادم لا يزال يكشفه؛ والجديدة تتّجه أكثر فأكثر إلى streamable HTTP افتراضياً. |
يأتي streamable HTTP بنكهتين، والفرق يهمّ حين تنشر. عديم الحالة (Stateless) هو الافتراضي الذي تمدّ إليه يدك: كل استدعاء طلب وردّ مستقلّ، تماماً كاستدعاء API عادي، فتستطيع تشغيل نسخ كثيرة من الخادم خلف موازِن حِمل وأيٌّ منها يستطيع الإجابة. أما ذو الحالة (Stateful) فيبقي جلسة حيّة مفتوحة كي يستطيع الخادم بثّ نتائج جزئية أو دفع إشعارات في منتصف المهمة، وهو ما تحتاجه للعمل طويل الأمد، لكنه يثبّت كل عميل بنسخة خادم واحدة وتشغيله أكثر كلفة. استخدم عديم الحالة ما لم يكن لديك سبب محدّد (بثّ حيّ، رسائل يبادر بها الخادم) لتحتاج الجلسة المفتوحة.
ما ليس به MCP.
- ليس إطار عمل. إنه بروتوكول. وكيلك لا "يستخدم MCP" كما يستخدم حزمة Agents SDK؛ بل عميل MCP في وكيلك يتحدّث MCP إلى خادم MCP. وحزمة Agents SDK تتضمّن عميل MCP؛ وذلك هو نقطة التكامل.
- ليس خدمة. لا توجد "سحابة MCP". خوادم MCP برامج تشغّلها أنت (أو يشغّلها بائعون نيابة عنك). خادم Neon MCP مُستضاف في
mcp.neon.tech؛ وخادم نظام الملفات MCP يعمل بوصفه عملية فرعية محلية؛ وخادم MCP مخصص تكتبه يعمل حيثما تنشره. - ليس حدود أمان. يعرّف MCP النقل والبروتوكول؛ أما ما تكشفه خوادم MCP من أدوات وما تستطيع فعله فهو مسؤولية الخادم. خادم MCP خبيث يستطيع فعل أي شيء يفعله كوده من جانب الخادم. وحدود الثقة لا تزال هي حلقة الوكيل التي تقرّر أي الأدوات تستدعي، وصندوق الحماية الذي تُنفَّذ فيه الأدوات.
- ليس بديلاً عن
@function_tool. لكليهما مكان. شجرة القرار هي المفهوم 14.
فحص سريع. صحيح أم خطأ: (أ) عميل MCP يتحدّث إلى خادم MCP واحد بالضبط في كل مرة. (ب) الدالة نفسها بنمط
@function_tool، لو أردت، يمكن كشفها بوصفها أداة MCP أو إبقاؤها أداة دالة، ولن يعرف النموذج الفرق. (ج) خوادم MCP وَOpenAI Agents SDK مقترنة بإحكام، فلاستخدام MCP يجب أن تستخدم الحزمة. الأجوبة: (أ) خطأ: يستطيع الوكيل الاتصال بعدة خوادم MCP ورؤية اتحاد أدواتها. (ب) صحيح: للنموذج، كلاهما يبدو أدوات قابلة للاستدعاء بمخططات. والفرق هو أين يعيش التطبيق. (ج) خطأ: MCP محايد تجاه النموذج. ل Claude، وَGemini، وغيرهما عملاء MCP خاصون بهم. وحزمة OpenAI Agents SDK واحد من عملاء كثيرين.
جرّب مع الذكاء الاصطناعي
For each item, say which MCP primitive fits best (tool, resource, or
prompt), and why in one line:
A) The agent reads the current text of a policy document on demand,
but never writes it.
B) The agent issues a refund through the payment gateway.
C) Every Worker on the team should summarize incidents the same way,
from one shared, versioned template.
Then a judgment question. A teammate says: "We put the refund logic
behind an MCP server, so the agent can't do anything dangerous." Using
this concept's "what MCP is NOT," explain why that sentence is false,
and name where the real trust boundary actually lives.
المفهوم 12: خادم Neon MCP، مستوى التطوير، لا زمن التشغيل
التفاصيل في هذا المفهوم ستتقادم. أما النمط فلا. أدوات خادم Neon MCP، وتدفّق المصادقة، وسطح الأدوات الدقيق يتغيّر كل بضعة أشهر. والذي يبقى صحيحاً: بائع قاعدة بيانات مُدارة يكشف واجهة إدارته عبر MCP لعمليات باللغة الطبيعية، بينما حركة الإنتاج في زمن التشغيل تستخدم اتصالات مباشرة أو خوادم مخصصة محدودة النطاق. تحقّق في مقابل وثائق Neon قبل تثبيت التفاصيل.
لقد وصلت أصلاً خادم Neon MCP بوكيل البرمجة لديك أثناء الإعداد، وكنت تتّكئ عليه منذ ذلك الحين: تطلب المخطط بإنجليزية واضحة، وتفحص ما في الجداول، وتسحب سلسلة اتصال. ذلك الوصل الذي استغرق خمس عشرة دقيقة يستحقّ التوقّف عنده، لأنه يعلّمك أهمّ سطر في هذا الجزء كله: لماذا خادم Neon MCP، وبماذا يجب ألا يُوصَل أبداً.
إنه يكشف واجهة إدارة Neon (المشاريع، والفروع، والمخطط، وعمليات الترحيل، وَSQL المؤقت) بوصفها أدوات يستطيع وكيلك استدعاءها باللغة الطبيعية. وذلك يجعله أداة تطوير، لا أداة إنتاج. ووثائق Neon نفسها صريحة: "لا توصل وكلاء MCP أبداً بقواعد بيانات الإنتاج."
وإليك لماذا ذلك السطر صعب إلى هذه الدرجة. أداة run_sql في الخادم تشغّل أي SQL يكتبه النموذج. أثناء البناء، تلك هي الفائدة كلها: تقول "أرني المستخدمين الذين سجّلوا الأسبوع الماضي ولم يسجّلوا دخولهم قط"، فيكتب النموذج الاستعلام، ويشغّله الخادم، فتحصل على جوابك. وجّه تلك الأداة نفسها إلى قاعدة بياناتك الحيّة فتصير باباً. أيّ شخص يستطيع تمرير تعليمات إلى عاملك (عميل يكتب رسالة مصاغة بذكاء) يستطيع أن يطلب منه قراءة قاعدة بياناتك كلها، لأن مهمّة الأداة هي تشغيل أي SQL يُسلَّم إليها.
فاستمرّ في استخدامه حيث يتألّق، كلّه أثناء التطوير:
- المخطط وعمليات الترحيل. "أضف عمود
priorityإلى جدول tickets." يختبر الخادم التغيير على فرع قابل للرمي أولاً، ثم يدمجه. عادة الفرع-أولاً تلك هي الطريقة الآمنة لتطوير مخطط. - استكشاف بياناتك أنت. "كم تضميناً هناك، مُجمَّعاً حسب المصدر؟" أسرع من كتابة SQL يدوياً لسؤال عابر.
- البحث عن الأشياء. سلاسل الاتصال، وإعدادات المشروع، وأشكال الجداول، دون فتح لوحة تحكم Neon.
رأيت هذا في الإعداد: طلبت من وكيلك إنشاء المشروع، وتشغيل pgvector، وتشغيل المخطط، والإبلاغ عن سلسلة الاتصال، ففعل كل ذلك عبر هذه الأدوات، مختبِراً الترحيل على فرع قبل أن يمسّ main. لا SQL كُتب باليد.
PRIMM، التنبّؤ. عاملك المكتمل لدعم العملاء يحتاج إلى: (أ) البحث عن طلبات عميل؛ (ب) فحص سياسة الاسترداد لفئته؛ (ج) إصدار استرداد؛ (د) كتابة صفّ تدقيق لما فعله ولماذا. هل ينبغي أن يصل إلى Neon عبر خادم MCP نفسه، أم بطريقة أخرى؟ الثقة 1–5.
الجواب: بطريقة أخرى، للأربعة جميعاً. العامل الحيّ لا ينبغي أن يحمل أبداً أداة بنمط run_sql، فذلك باب لا تستطيع قفله تماماً. يحتاج بضع قدرات ضيّقة، لا قدرة تشغيل SQL اعتباطي. والنمطان الإنتاجيان هما خادم MCP مخصص يكشف فقط العمليات المحدّدة التي يحتاجها (المفهوم 14)، أو اتصال Postgres مباشر يغلّفها. الجزء الرابع يستخدم كليهما: خادم customer-data مخصص لعمليات الأعمال، واتصال مباشر فقط لنظام التدقيق الفرعي (القرار 7 يشرح لماذا يبقى التدقيق خارج حدود MCP التي يدقّقها).
هذا هو الثابت 5 بالضبط: القوة العاملة تقرأ وتكتب عبر متاجر محكومة. أداة run_sql واسعة ليست حوكمة، بل وجه ودود لانعدام حوكمة بالكامل. خادم Neon MCP هو كيف تبني أنت المتجر. وليس كيف يلمسه عاملك.
جرّب مع الذكاء الاصطناعي
Read Neon's MCP server documentation page and answer three questions:
1. List THREE management operations the Neon MCP server exposes that
would be useful while you're building a customer-support Worker.
2. List THREE things a running Worker NEEDS to do that you should NOT
use the Neon MCP server for, and why.
3. For each of the three in (2), say what the Worker should use instead
(direct Postgres connection? custom MCP server? function_tool?).
المفهوم 13: ربط MCP بحزمة OpenAI Agents SDK
كنت تقود خادم Neon MCP من وكيل البرمجة لديك. أما عاملك، الذي تبنيه في الجزء الرابع، فبرنامج مختلف: وكيل OpenAI Agents SDK. فالسؤال الذي يجيب عنه هذا المفهوم ببساطة: كيف يتحدّث ذلك الوكيل إلى خادم MCP؟ لن تكتب سباكة الاتصال بيدك، فالحزمة تأتي بها. والذي يستحقّ الفهم هو الشكل، كي تستطيع توجيه البناء وتنقيحه حين يسيء التصرّف.
إليك الصورة كاملة. للحزمة عميل MCP مدمج بموصِّل واحد لكل نقل: محلي ل stdio، وحديث ل streamable HTTP البعيد، وقديم ل SSE (تجنّب SSE لأي شيء جديد). تفتح اتصالاً بخادم، وتسلّمه لوكيلك، ومن هناك تفعل الحزمة كل شيء: تسأل الخادم ما أدواته، وتضع تلك الأدوات أمام النموذج بجوار @function_tool التي كتبتها بنفسك تماماً، وحين يختار النموذج واحدة، توجّه الاستدعاء إلى الخادم الصحيح وتُحضِر الجواب. النموذج لا يستطيع التمييز بين أداة MCP وأداة دالة محلية، ولا يحتاج إلى ذلك. ذلك التماثل هو المغزى: MCP مجرد طريقة أخرى لتسليم النموذج قدرة.

أربعة أشياء عليك تذكّرها، يتولّاها وكيلك كلها نيابة عنك ما إن تطلبها:
- افتح الاتصال نظيفاً، وأغلقه نظيفاً. اتصال MCP يبقي شيئاً مفتوحاً: عملية فرعية ل stdio، أو جلسة HTTPS ل البعيد. إن لم يُغلَق على نحو سليم، تسرّب الاتصال. كائنات اتصال الحزمة مبنية لتُفتَح وتُغلَق بوصفها كتلة مُدارة، فهذا متولّى ما دمت لا تقاومه.
- خزّن قائمة الأدوات مؤقتاً في الإنتاج. افتراضياً يعيد الوكيل سؤال الخادم "ما أدواتك؟" في كل تشغيل، رحلة شبكية مهدورة. تشغيل التخزين المؤقت يجعله يسأل مرة واحدة. والمأخذ الوحيد: إن غيّرت أدوات الخادم، أخبر الوكيل أن يحدّث المخزَّن المؤقت (أو أعد تشغيله). أثناء البناء، أبقِ التخزين المؤقت مطفأً كي تظهر التغييرات فوراً.
- الخوادم تتراكم. يمكنك تسليم وكيلك عدة خوادم MCP دفعة واحدة، وببساطة يرى النموذج المجموعة المدمجة من الأدوات. عامل الجزء الرابع يتّصل بخادمه
customer-dataالمخصص بهذه الطريقة. - بوّب الأدوات الخطرة خلف موافقة. افتراضياً تعمل استدعاءات الأدوات بلا تأكيد. وللحسّاسة منها يمكنك أن تطلب موافقة إنسان على كل استدعاء. هذا هو القرص العملي لفجوة التطوير-مقابل-زمن-التشغيل من المفهوم 12: حتى وأنت تستخدم خادم Neon MCP يدوياً، فإن وضع أدواته المدمّرة (أي شيء يُسقِط أو يعيد كتابة) خلف مطالبة موافقة هو مكسب أمان حقيقي.
مأخذ واحد يستحقّ الحفظ: إن حمّل خادم MCP شيئاً ثقيلاً عند بدء التشغيل (نموذج تعلّم آلي مثلاً)، فقد تكون نافذة "هل أجاب الخادم في الوقت؟" الافتراضية للوكيل أقصر من اللازم وسترى خطأ فشل اتصال مربكاً. والإصلاح إعداد واحد يطيل تلك النافذة. لن تلتقي بهذا إلا إن قام خادم بعمل حقيقي لحظة إقلاعه.
تطبيق عملي، للفهم فقط. هذه أسرع طريقة لجعل الشكل ملموساً. الصق المطالبة أدناه في وكيل البرمجة لديك. يبني سكربتاً صغيراً قابلاً للرمي يوجّه وكيل OpenAI Agents SDK إلى خادم Neon MCP الموصول لديك أصلاً، ويتيح لك مشاهدة الوكيل يسرد مشاريعك باللغة الطبيعية. هذا تمرين تعليمي، لا مسار الإنتاج: العامل الحقيقي لا يتّصل أبداً بخادم Neon MCP (المفهوم 12). أنت تفعلها مرة، هنا، لترى وكيل Agents SDK يقود خادم MCP من البداية إلى النهاية.
Write me a small throwaway Python script (call it scratch_neon_agent.py)
that uses the OpenAI Agents SDK to connect to the Neon MCP server over
its remote streamable-HTTP transport, then runs one agent turn asking it
to "list my Neon projects and show the schema of the largest one."
Use the current OpenAI Agents SDK MCP classes (check the docs for the
exact import and class name). Open the connection as a managed block so
it closes cleanly, turn on tool-list caching, and print the final output.
Then run it and show me what the agent did, step by step. Remind me in a
comment that this is for understanding only and a real Worker should
never connect to the Neon MCP server.
راقب ما يحدث: الوكيل يتّصل، والحزمة تسحب أدوات Neon، والنموذج يختار list_projects بنفسه، فتحصل على جواب بالإنجليزية. لقد رأيت للتو التوصيل نفسه الذي سيستخدمه عامل الجزء الرابع لديك، موجَّهاً فقط إلى خادم لا ينبغي له استخدامه في الإنتاج، وهو بالضبط لماذا ترمي هذا السكربت.
جرّب مع الذكاء الاصطناعي
Explain, in plain language and without writing code, how you would
connect one OpenAI Agents SDK agent to TWO MCP servers at once: the
Neon MCP server (remote) and a local filesystem MCP server for reading
project files. Cover:
1. Which transport each server would use, and why.
2. How the model decides which server's tool to call.
3. Which tools you'd put behind human approval, and why.
4. One thing that could go wrong with two servers connected, and how
you'd notice it.
المفهوم 14: خوادم MCP المخصصة، متى تكتب خادمك الخاص ومتى لا
خادم Neon MCP عام: يستطيع فعل أي شيء تستطيعه واجهة Neon. وتلك قوّته للتطوير وضعفه لزمن التشغيل. الخادم المخصص يعكس المقايضة: سطح ضيّق، بلا run_sql عام الغرض، فقط العمليات المحدّدة التي يحتاجها عاملك فعلاً.
شجرة القرار، بترتيب الأولوية.

المنطق نفسه في جدول سريع المسح:
| تريد كشف... | استخدم هذا | لماذا |
|---|---|---|
| دالة واحدة بمدخل واحد، يستخدمها وكيل واحد | @function_tool | لا حاجة لعبء البروتوكول. استدعاء دالة محلي يكفي. |
| عدة دوال متماسكة بإحكام مع كود وكيلك | @function_tool | إن تشاركت الحالة مع الوكيل وعاشت في المستودع نفسه، فهي جزء من الوكيل. |
| قدرة سيستخدمها عدة وكلاء (أو عدة عمليات نشر) | خادم MCP مخصص | البروتوكول هو ما يجعلها قابلة لإعادة الاستخدام. |
| قدرة تحتاج أن تتجاوز عمر عملية الوكيل | خادم MCP مخصص | اتصالات طويلة الأمد، ومهام خلفية، ومستهلكو طوابير. |
| وظيفة يقدّمها بائع (Neon، وَGitHub، وَLinear) | خادم MCP من البائع | لا تعِد بناء ما يأتون به. |
| عمليات حسّاسة تحتاج نطاقاً ضيّقاً | خادم MCP مخصص | عرّف بالضبط الأدوات التي تحتاجها؛ لا شيء غيرها. |
شكل خادم MCP المخصص أبسط مما يبدو. إنه برنامج صغير يعلن حفنة من الأدوات المسمّاة. لكل أداة وصف بإنجليزية واضحة (النوع نفسه من نصّ التحفيز الذي يحمله SKILL.md) يخبر النموذج متى يمدّ يده إليها، وقائمة قصيرة من المدخلات المُصنَّفة كي يعرف النموذج ما يمرّره. هذا كل شيء: بضع أدوات ضيّقة موصوفة جيداً ولا شيء غيرها. لا run_sql عام، ولا مخرج هروب.
ولا تكتب ذلك البرنامج بيدك. بالطريقة نفسها التي ثبّتت بها مهارات وتركت وكيلك يقوم بالعمل، توجد مهارة mcp-builder تحوّل وصف نطاق إلى خادم عامل ومختبَر. حُكمك يذهب إلى النطاق، أي الأدوات توجد، وما المسموح لكلٍّ فعله، وأيها لا توجد عمداً، لا إلى السباكة. وتدفّق المطالبة يبدو هكذا:
/mcp-builder Let's design a custom MCP server called "customer-data"
on the streamable-HTTP transport, stateless flavor (each call an
independent request, no open session, so it scales). Plan the
implementation first, then build it.
Scope: exactly three tools, nothing else.
- lookup_customer(customer_id): return id, email, tier, open-ticket count
- find_similar_resolved_tickets(description, limit): semantic search over
past resolved tickets
- issue_refund(order_id, amount_cents, reason): issue a refund (amount in
integer cents, never a float) AND write an audit row in the same transaction
No general SQL tool. Each tool gets a clear description so the model
knows when to call it. Start a fresh project with uv, walk me through
the plan before writing code, then build and verify it.
يجهّز الوكيل مشروع uv جديداً، ويخطّط الأدوات، ويبني الخادم، ويتحقّق أنه يعمل. وما إن يوجد، تصله بالطريقتين نفسيهما اللتين رأيت بهما خوادم MCP موصولة: إلى وكيل البرمجة العام لديك (Claude Code أو OpenCode، كي تختبره يدوياً) وإلى عامل OpenAI Agents SDK لديك (كي يستطيع العامل استخدامه فعلاً). القرار 6 من الجزء الرابع يمشي بهذا البناء من البداية إلى النهاية.
ثلاثة أشياء يمنحك إياها هذا الخادم ولا يمنحها @function_tool.
-
عزل العمليات. يعمل خادم MCP في عمليته الخاصة (عملية فرعية ل stdio، خدمة منفصلة ل streamable HTTP). تعطّل في الخادم لا يعطّل الوكيل؛ وتسرّب ذاكرة في الخادم لا يتسرّب في الوكيل.
-
النطاق. يكشف الخادم فقط حفنة الأدوات التي تعرّفها (خادم
customer-dataفي المثال العملي له ثلاث). لاrun_sql. لا "نفّذ كوداً اعتباطياً". ولا يستطيع النموذج الإفلات من هذا النطاق لأن البروتوكول لا يكشف أي شيء آخر. هذا دفاع حقيقي في العمق: حتى لو قرّر النموذج فعل شيء أحمق، فمساحة فعله محصورة في تلك الدوال القليلة. -
قابلية إعادة الاستخدام عبر الوكلاء. وكيل ثانٍ (عامل مبيعات، أو عامل تقارير) يستطيع التحدّث إلى خادم
customer-dataنفسه. النطاق نفسه، والبروتوكول نفسه، وحدود الثقة نفسها. تصير القدرة قطعة بنية تحتية مشتركة بدل نسخ-ولصق بين الوكلاء.
المقايضة حقيقية. خوادم MCP المخصصة تضيف تعقيداً تشغيلياً: عملية أخرى تنشرها، ومجموعة سجلات أخرى، وقفزة شبكية أخرى (إن كانت بعيدة)، وإصدار آخر تديره. لا تكتب واحداً لدالة واحدة يستخدمها وكيل واحد. اكتب واحداً حين تكون القدرة ستُعاد استخدامها، أو حين يهمّ النطاق، أو حين يشتري لك العزل أماناً.
PRIMM، التنبّؤ. تصمّم عامل دعم العملاء. تحتاج: (1) بحثاً دلالياً على التذاكر السابقة المحلولة؛ (2) كتابة صفّ تدقيق لاسترداد؛ (3) قراءة الطقس الحالي (يُستخدم في مهارة ترحيب واحدة تقول "صباح الخير من كراتشي المشمسة")؛ (4) استدعاء بوابة الدفع لإصدار استرداد. لكلٍّ، تنبّأ:
@function_tool، أم خادم MCP مخصص، أم خادم MCP من البائع (مثلاً خادم Stripe، إن وُجد)؟
تكشف الأجوبة الإطار:
- خادم MCP مخصص (
customer-data). يُعاد استخدامه عبر الوكلاء؛ بيانات حسّاسة؛ أدوات محدودة النطاق أفضل منrun_sqlواسع. - خادم MCP مخصص (
customer-data) أو@function_tool. كلاهما يعمل؛ إن كان العامل هو الكاتب الوحيد، فأداة دالة تكفي. إن كان عدة عمّال سيكتبون صفوف تدقيق، فخادم MCP. @function_tool. وكيل واحد، ودالة صغيرة واحدة، ولا سطح أمان يُدافَع عنه. لا تبنِ خادماً لها.- خادم MCP من البائع (Stripe MCP) إن وُجد، وإلا
@function_toolيستدعي واجهة Stripe. لا تغلّف واجهات طرف ثالث في خادم MCP خاص بك ما لم تحتج إضافة سياسة فوقها.
الإطار واضح ما إن تتتبّعه: قيمة MCP ترتفع بقيمة الحدود التي ينشئها. الحدود التي لا تحتاجها عبء.
جرّب مع الذكاء الاصطناعي
الصق هذا في وكيل البرمجة لديك. يطبّق شجرة القرار على عامل دعم العملاء الذي تبنيه فعلاً، فكل خيار خيار تستطيع شحنه، لا تخميناً عن بنية تحتية لا تملكها.
Here are five capabilities I'm thinking of adding to my customer-support
Worker. For each, walk the Concept 14 decision tree with me and recommend
one: a @function_tool, my custom customer-data MCP server, or a vendor
MCP server (if a credible one exists). Justify each choice with ONE of
the three properties (isolation, scope, reusability), or say plainly why
no boundary is worth building.
1. Look up a customer by email (the gap Decision 8 leaves open).
2. Issue the real refund through Stripe (actual money, third-party API).
3. Send the drafted reply as an email through our mail provider.
4. Convert a UTC timestamp to the customer's local time for a greeting.
5. Let a second Worker (a sales assistant) reuse the customer lookups.
Then push back on me: which TWO of these would you deliberately NOT put
behind a custom MCP server, and what does that say about when the
boundary earns its cost?
المفهوم 15: MCP تحت الحِمل: وسائط النقل، والتجميع، وما يحدث عند التوسّع
عرض توضيحي بوكيل واحد وخادم واحد يعمل ببساطة. أما الحركة الحقيقية، عدة محادثات في الدقيقة، فتضيف ثلاثة ضغوط. لست بحاجة للتصرّف حيالها لأول عامل، لكن معرفة وجودها توفّر عليك ظهيرة محيّرة لاحقاً. لكلٍّ إصلاح بسيط.
- القناة بين الوكيل والخادم. عملية فرعية محلية (stdio) مناسبة ما دام كل شيء يعمل على جهاز واحد. وفي اللحظة التي يتشارك فيها أكثر من وكيل الخادم، أو ينتقل الخادم إلى عتاده الخاص، انتقل إلى النقل البعيد (streamable HTTP). ذلك تغيير نشر، لا إعادة كتابة.
- لا تدفع كلفة الإعداد نفسها مراراً وتكراراً. ثلاث عادات صغيرة تحوّل التكاليف المتكرّرة إلى تكاليف لمرة واحدة: اتّصل بكل خادم مرة واحدة حين يقلع العامل وأبقِ ذلك الاتصال مفتوحاً، بدل إعادة الاتصال في كل طلب؛ ودع الوكيل يتذكّر قائمة أدوات الخادم بدل إعادة سؤال "ماذا تستطيع أن تفعل؟" كل تشغيل (حدّثها حين تغيّر الأدوات)؛ وأبقِ تجمّعاً جاهزاً من اتصالات قاعدة البيانات داخل الخادم، كي لا ينتظر استعلام فتح اتصال جديد في كل مرة. مأخذ واحد يصطدم به عامل طويل العمر على Postgres يتقلّص إلى الصفر أو مُجمَّع (Neon): يغلق المُجمِّع الاتصالات الخاملة، فإن حظرت العملية (مطالبة طرفية
input()تجمّد حلقة أحداث asyncio)، تفشل الكتابة التالية بخطأ "connection was closed in the middle of operation". شغّل المطالبات الحاجبة خارج الحلقة (asyncio.to_thread) واجعل التجمّع يعيد الاستحواذ مرة واحدة عند ذلك الخطأ. - ضع سقفاً على كل شيء، وأبقِ التتبّع كاملاً. حُدّ كم خطوة يجوز لطلب واحد أن يأخذها، وأعِد محاولة استدعاء أداة فاشل مرتين قبل الاستسلام، وحُدّ معدّل الخادم كي لا تُغرقه دفعة واحدة. وتأكّد أن تتبّعك يتبع الاستدعاء عبر حدود MCP: حين يستدعي العامل أداة، تريد أن يظهر عمل قاعدة بيانات الخادم نفسه في الصورة نفسها. وإلا فإن استعلاماً بطيئاً داخل الخادم يكون غير مرئي من الخارج، وستطارد الكمون في المكان الخطأ.
القُرص الأعمق (حدود تزامن لكل مستأجر، وضبط نقل دقيق) أبعد مما يحتاجه أول عامل. وهذه الثلاثة هي التي تلدغ أولاً.
فحص سريع. صحيح أم خطأ: (أ) نقل خادم من نقل SSE القديم إلى streamable HTTP يفرض عليك إعادة كتابة أدوات الخادم. (ب) ترك الوكيل يخزّن قائمة أدوات الخادم مؤقتاً آمن في الإنتاج، ما دمت تحدّث المخزَّن المؤقت بعد تغيير الأدوات. (ج) كشف خمس قدرات بوصفها أدوات MCP يكلّف النموذج دائماً ميزانية سياق أكثر من كشف الخمس نفسها بوصفها أدوات دالة محلية. الأجوبة: (أ) خطأ في الغالب: الأدوات بلا تغيير؛ الخادم فقط يحتاج التحدّث بالنقل الأحدث، وهو ما يفعله معظم الحديثة أصلاً. (ب) صحيح: ذلك هو النمط المقصود. (ج) خطأ: للنموذج، الأداة أداة. خمسة أوصاف أدوات تكلّف نحو الشيء نفسه أياً كان الجانب الذي تعيش فيه.
جرّب مع الذكاء الاصطناعي
My customer-support Worker is in production. It runs 80 conversations/minute
at peak. Each conversation makes 2-4 MCP tool calls on average. I'm seeing
intermittent latency spikes: most calls return in 200ms, but a small
percentage take 5-15 seconds.
Walk me through five places I'd investigate, in order of priority:
1. The agent-side MCP client connection management.
2. The transport choice between agent and MCP server.
3. The MCP server's internal connection pool to Postgres.
4. Postgres-side query performance (slow queries blocking the pool).
5. Network or DNS issues between agent and MCP server.
For each, name the specific signal I'd look for and the rough fix.
الجزء الرابع: المثال العملي، عامل دعم العملاء
بناء واحد واقعي يستخدم كل مفهوم أعلاه. تبدأ بوكيل محادثة بسيط (مطالبة واحدة، نحو دقيقة)، ثم تنمّي ذلك العامل نفسه إلى عامل دعم عملاء، قطعةً قطعة. كل قرار يضيف قطعة، مصدر الحقيقة، ثم المهارات، ثم طبقة MCP، ثم أثر التدقيق، وتعيد تشغيل العامل في كل مرة فترى القطعة الجديدة تعمل قبل المضيّ. ثمانية قرارات تبني العامل؛ والتاسع يضع إنساناً أمام الفعل الوحيد الذي يحرّك المال.
الخطوة 0: أقِم وكيل المحادثة (مطالبة واحدة، ~دقيقة). كي يبدأ الجميع القرار 1 من المكان نفسه. (أنجزت بناء وكلاء الذكاء الاصطناعي؟ افتح ذلك المشروع بدلاً منه، فهو العامل نفسه، واقفز إلى القرار 1.)
In this digital-fte folder, build me a small terminal chat agent with the
OpenAI Agents SDK: a uv project, a gpt-5-class model, on a local sandbox.
Check the current SDK docs for the API. Get it answering "hi", then stop,
we grow it in the steps below.
يُنشئ: ملف العامل (مثلاً worker.py) إضافة إلى مشروع uv الخاص به.
فحص. ترسل "hi" فيجيب. تلك هي خطّ البداية؛ والقرار 1 يعلّمه البنية الجديدة عبر
AGENTS.md.
الموجز
طوّر وكيل المحادثة البسيط من الخطوة 0 إلى عامل دعم عملاء بحيث:
- يحمّل ثلاث مهارات عند الطلب:
summarize-ticket، وَfind-similar-cases، وَescalate-with-context. - يقرأ من مصدر حقيقة في Neon Postgres ويكتب إليه بالجداول الخمسة من المفهوم 7 (أدوار المحادثة تعيش في Session الخاصة ب SDK على قاعدة البيانات نفسها).
- يستخدم pgvector لبحث دلالي على مكتبة صغيرة من الحالات السابقة المحلولة.
- يتحدّث إلى Postgres ل بيانات الأعمال في زمن التشغيل عبر خادم MCP مخصص محدود النطاق (
customer-data)، لا خادم Neon MCP أبداً ولاasyncpgمباشر في كود الوكيل. - يكتب صفّ تدقيق لكل فعل ذي معنى (كل مهارة تُستدعى، وكل كتابة في قاعدة البيانات، وكل استرداد يُنظَر فيه) عبر اتصاله المباشر الخاص، المسار الوحيد الذي يتجاوز عمداً حدود MCP، كي لا يُجوَّع أثر التدقيق من النظام الذي يدقّقه.
اختبار "التحقق في النهاية": عميل يرسل "I haven't received my refund from order #4429, it's been two weeks." يجد العامل ثلاث حالات سابقة متشابهة عبر البحث المتّجه، ويصوغ رداً يستشهد بحلّ أكثر الحالات تشابهاً، ويكتب صفّ تدقيق لما فعله (وفي نشر حقيقي، يصعّد إن كان العميل من فئة Pro). أما حلّ العميل أو الطلب بالضبط من الرسالة فيحتاج أداة بحث تضيفها لاحقاً؛ والقرار 8 يبيّن أين تلك الفجوة.
كيف تقرأ المطالبات التالية. تنمّي هذا العامل بمطالبة وكيل البرمجة لديك مهمة صغيرة واحدة في كل مرة، وكل قرار ينتهي بالطريقة نفسها: القطعة الجديدة موصولة بالعامل الواحد وتشغّله، فترى القطعة تعمل قبل أن يبني القرار التالي عليها. لن تكتب SQL، أو Python، أو إعدادات: الوكيل يكتبها، وأنت توجّه وتفحص. وكيلك قرأ أصلاً
AGENTS.mdحين فتح المجلد، فهو يعرف المشروع؛ ومطالباتك تبقى قصيرة. عادتان:
- خطوة واحدة، مهمة واحدة. الصق مطالبة تلك الخطوة ولا شيء غيرها. ولأي شيء يكتب كوداً حقيقياً، تقول المطالبة "خطّط أولاً": اقرأ الخطة، وادفع بالاعتراض، ووافق، ثم دعه يبني.
- افحص قبل الخطوة التالية. كل خطوة تنتهي ب فحص: سؤال واحد بإنجليزية واضحة تطرحه ("أرني X"). لا تمضِ حتى يجتاز، وإلا ستكون أربع خطوات في العمق قبل أن تكتشف أن الخطوة الأولى كانت خاطئة.
القرار 1: حدّث ملف القواعد بالبنية الجديدة
أين أنت: وكيل محادثة بسيط يجيب "hi"؛ هذا القرار يضيف قواعد البنية الثلاث إلى AGENTS.md؛ وفي النهاية سترى تلك القواعد في فرق الملف.
وكيلك يعرف أصلاً هذا المشروع من AGENTS.md. وما لا يعرفه بعد هو القواعد القليلة التي تضيفها بنية هذه الدورة، فتكتبها في AGENTS.md الآن، وتستطيع كل مطالبة لاحقة أن تبقى قصيرة. مهمة واحدة.
الخطوة 1: أضف القواعد الجديدة إلى AGENTS.md.
Add a short "Rules" section to AGENTS.md so a fresh session follows these:
- business data is read and written only through the customer-data MCP
server, never raw SQL from the running worker
- the audit log uses its own direct database connection, and each action
and its audit row are committed together
- embeddings use the same model to store and to search
Show me the diff before you write it.
يُعدّل: AGENTS.md.
فحص. اقرأ الفرق. تلك القواعد الثلاث موجودة، بلغة واضحة، الأولى خاصةً: إنها ما يمنع النموذج من الالتفاف بهدوء حول حدود MCP لاحقاً. إن لطّف الوكيل واحدة أو أسقطها، فأعِد المطالبة.
لماذا. ملف قواعد ضعيف يفشل بصمت بعد أسابيع، حين يأخذ النموذج الاختصار الذي قُصد بالقاعدة منعه. كتابته الآن هو ما يبقي كل مطالبة بعد هذه قصيرة.
القرار 2: خطّط المخطط ومجموعة المهارات
أين أنت: ملف AGENTS.md يذكر البنية لكن بلا تصميم لها بعد؛ هذا القرار يضيف خطة مكتوبة مراجَعة؛ وفي النهاية سترى خطة markdown دفعت عليها بالاعتراض ووافقت.
تنهي هذا القرار بخطة مكتوبة راجعتها، قبل أن يوجد سطر كود واحد. مهمة واحدة. اضغط Shift+Tab مرتين لدخول وضع التخطيط (في OpenCode، اضغط Tab للتبديل إلى وكيل التخطيط): يستطيع النموذج قراءة مشروعك لكن لا يستطيع تعديل أي شيء.
الخطوة 1: احصل على الخطة.
Plan the customer-support Worker evolution of this project. The
foundation (OpenAI Agents SDK, your sandbox runtime, sessions, streaming,
guardrails) stays. We're adding:
1. Three Skills: summarize-ticket, find-similar-cases, escalate-with-context.
For each, propose: the description, the operational shape (script-driven
or instruction-driven), and what reference files it needs.
2. The five-table schema from Part 2 Concept 7, plus any tables specific
to a customer-support domain (probably: customers, orders, tickets, refunds).
3. The custom MCP server (customer-data), with exactly the runtime tools
our agent will need. Propose the tool list and signatures. No run_sql.
4. The audit-logging plan: what writes an audit row, what doesn't.
Output the plan as a markdown file at plans/customer-support-worker-plan.md.
Do not write code yet.
For reference the part 2 here: https://agentfactory.panaversity.org/docs/digital-fte-crash-course
يُنشئ: plans/customer-support-worker-plan.md.
فحص. اقرأ الخطة وادفع بالاعتراض على الأمرين اللذين تخطئ فيهما المسودة الأولى عادةً: أوصاف المهارات الغامضة ("Summarizes tickets"، وصف لا ينطلق صحيحاً، المفهوم 3) ومدخلات أدوات MCP المفرطة الاتساع ("query: string"، وهو
run_sqlمتنكّراً؛ ينبغي أن يأخذlookup_customerقيمةcustomer_id، لا نصاً حراً تبني منه SQL). لا توافق على الخطة حتى يُحكَم الأمران.
لماذا الخطة أولاً. كلا وضعَي الفشل يكلّف ساعات بعد بنائهما ودقائق لإصلاحهما في خطة markdown. هذا أرخص مكان في الجزء كله لتكون مخطئاً.
أنت على وشك أن تمسّ قاعدة البيانات للمرة الأولى، وسترى كثيراً من SQL عبر القرارات 3 إلى 8. أنت لا تكتبه ولا تشغّله بيدك. ثلاثة مكوّنات تملكه، وهناك خادما MCP مختلفان يقومان بمهمتين مختلفتين.
| مسار SQL / البيانات | مَن يكتبه | مَن يشغّله | متى |
|---|---|---|---|
| المخطط + عمليات الترحيل (هذا القرار) | تصفه أنت؛ والوكيل يسوّده | خادم Neon MCP (أداة تطوير توصلها بوكيلك) | مرة واحدة، عند الإعداد |
| استعلامات التحقق (فحوص "يكتمل عندما") | مُبيَّنة في الدرس | خادم Neon MCP ب run_sql، تقوده أنت بإنجليزية واضحة | لتأكيد أن خطوة نجحت |
| SQL الأعمال في زمن التشغيل: عمليات بحث، بحث متّجه، استردادات (D6) | يولّده mcp-builder | خادم customer-data MCP الذي تبنيه | كل تفاعل عميل |
| كتابات التدقيق (D7) | كود نظام التدقيق الفرعي | تجمّع asyncpg منفصل (بلا MCP) | كل فعل |
خادما MCP، لا يُخلَط بينهما أبداً. خادم Neon MCP (الذي صادقت عليه في خطوة الإعداد أعلاه) أداة تطوير: تستخدمه لتجهيز قاعدة البيانات والتحقق منها بإنجليزية واضحة، ولا تستخدمه في زمن التشغيل أبداً. أما خادم customer-data MCP فهو الخادم المحدود النطاق الذي تبنيه في القرار 6؛ العامل قيد التشغيل يتحدّث إلى ذلك، وإليه وحده، لبيانات الأعمال. المفهوم 12 يشرح لماذا run_sql عام الغرض في الإنتاج ثغرة حقن تعليمات.
القراءة والكتابة والإسقاط ليست سلطات متساوية. أدوات العامل قيد التشغيل تنقسم بحسب الخطر:
- القراءة (
lookup_customer، وَfind_similar_resolved_tickets، يُبنَيان في D6): تعمل بحرّية، بلا بوابة. القراءات رخيصة السماح بها. - الكتابة (
issue_refund، يُبنى في D6): الأداة الوحيدة التي تحرّك المال. تبوّبها خلف موافقة بشرية في القرار 9، بعد أن يعمل العامل من البداية إلى النهاية، كي يوقّع إنسان قبل أن يمرّ أي استرداد. كتابات التدقيق للإلحاق فقط: تُدرَج، ولا تُحدَّث ولا تُحذَف أبداً. - الإسقاط / تغيير المخطط (
CREATE/DROP TABLE، وَDDL): غير قابلة للاستدعاء في زمن التشغيل إطلاقاً. الخادم المخصص لا يكشف أداة DDL أبداً، فلا شيء يُوافَق عليه. تغييرات المخطط تحدث فقط في زمن التطوير (هذا القرار)، عبر خادم Neon MCP، على فرع مؤقت قبل أن تمسّmain.
القاعدة العامة: القراءات تعمل بحرّية، والكتابات مبوَّبة، والتغييرات البنيوية لا تصل الإنتاج عبر الوكيل أبداً.
القرار 3: جهّز Neon وشغّل ترحيل المخطط
الطبقة المجانية في Neon تغطّي عاملاً واحداً عند الحجم الذي يفترضه الجزء الخامس (~200 محادثة/يوم). خطّط على 0 دولار/شهر هنا. حدود الخطة المجانية هي 0.5 غيغابايت تخزين و100 ساعة حوسبة لكل مشروع (تسعير Neon)؛ وفوق ذلك، تكون طبقة Launch ادفع-بقدر-ما-تستخدم (نحو 0.11 دولار/ساعة-CU + 0.35 دولار/غيغابايت-شهر)، وعامل مثال عملي يبقى عادةً تحت 25 دولاراً/شهر. انظر جدول شكل التكلفة في الجزء الخامس للتفصيل الكامل.
أين أنت: خطة موافَق عليها لكن بلا قاعدة بيانات؛ هذا القرار يضيف قاعدة بيانات Neon حيّة بمخططك وجلسة دائمة؛ وفي النهاية سترى تسعة جداول في Postgres وعاملاً يتذكّر أدواراً سابقة.
تنهي هذا القرار بقاعدة بيانات Neon حيّة تحمل مخططك، إضافة إلى Session تثبّت أدوار المحادثة فيها. أربع خطوات صغيرة، وتفحص كلاً قبل التالية، لأن خطوة قاعدة بيانات معطوبة غير مرئية حتى يقرأ شيء أسفلها لا شيء. اضغط Shift+Tab للخروج من وضع التخطيط وتأكّد أن خادم Neon MCP موصول (المفهوم 12). الوكيل يشغّل كل هذا عبر أدوات Neon MCP؛ ولا تفتح أنت لوحة تحكم قاعدة بيانات أبداً.
الخطوة 1: أنشئ المشروع.
Create a fresh Neon project called "chat-agent" and give me the
connection string for its main branch.
فحص. اطلب من الوكيل تأكيد وجود المشروع ولصق سلسلة اتصال
mainمجدداً. (يمكنك أيضاً رؤيتها في لوحة تحكم Neon.) لا تتابع دون سلسلة اتصال في يدك.
الخطوة 2: شغّل pgvector.
Enable the pgvector extension on the chat-agent database.
فحص. "أكّد أن امتداد
vectorصار الآن مدرَجاً على قاعدة البيانات." إن لم يكن، فلن يعمل أي شيء أسفله يخزّن تضمينات، فتوقّف هنا حتى يكون كذلك.
الخطوة 3: طبّق المخطط، فرعاً-أولاً.
Apply our schema to chat-agent: the five-table core from Concept 7
(conversations, documents, embeddings, audit_log, capability_invocations)
plus four domain tables, customers, orders, tickets, refunds. Build the
audit_log and capability_invocations columns EXACTLY as Concept 7 prints
them: audit_log keeps its `target` column and the closed `action` CHECK
set, capability_invocations keeps its `status` CHECK set, so Decision 8's
replay query matches the schema you built. Test it on a temporary branch
first, then merge to main. Plan the DDL first; I'll approve before you merge.
فحص. "عُدّ الجداول في مخطط public، أتوقّع تسعة، وأكّد أن فهرس embeddings موجود." تسعة جداول تعني أن الترحيل هبط. إن كانت أقلّ، فالدمج لم يُطبَّق نظيفاً: اجعل الوكيل يعيد التشغيل على فرع جديد. (هذه بالضبط حالة استخدام التطوير من المفهوم 12: عمل المخطط بإنجليزية واضحة، مختبَراً على فرع، مدموجاً إلى
mainفقط بعد "امضِ قُدماً".)
تقريباً ما ينبغي أن تراه:
table_count = 9
embeddings index: present
الخطوة 4: امنح العامل جلسته، وأثبت أنه يتذكّر.
Write the connection string to .env as NEON_DATABASE_URL, then give the
worker a SQLAlchemySession on that database so it remembers across turns.
Install what the session needs (the sqlalchemy extra, asyncpg, pgvector,
and greenlet), and use the postgresql+asyncpg:// form of the URL for it.
يُعدّل: ملف العامل (يضيف Session)؛ ويكتب NEON_DATABASE_URL إلى .env.
فحص. شغّل محادثة من دورين: أخبر العامل باسمك ورقم طلب، ثم في دور ثانٍ اطلب منه تكرارهما. يستردّ كليهما، تلك هي Session تؤدّي عملها، لا مجرد صفّ يجلس في جدول. ثم اسأل: "show me those turns in the
agent_messagestable." رؤيتهما في Postgres تثبت أن الحالة تعيش الآن في مصدر الحقيقة، لا في الذاكرة فقط. (أمران يفوّتهما الوكيل غالباً: امتداد[sqlalchemy]لا يسحبgreenlet، فيلزمuv add greenlet؛ ومحرّك async يحتاج صيغةpostgresql+asyncpg://للرابط، لاpostgresql://المجرّدة.SQLAlchemySessionينشئagent_sessionsوَagent_messagesنيابة عنك.)
القرار 4: عرّف وأثبت أول مهارة، summarize-ticket، ثم صِلها
أين أنت: عامل يتذكّر لكن بلا قدرة محمولة؛ هذا القرار يضيف ثلاث مهارات على القرص ويصلها بالعامل؛ وفي النهاية سترى واحدة تنطلق في تشغيل حقيقي.
تنهي هذا القرار بثلاث مهارات على القرص، الأولى مُثبَتة في مقابل معايير تضعها، وقدرة Skills موصولة بالعامل فتشاهدها تنطلق. وإليك النقلة عن كيف يكتب الناس المهارات عادةً: أنت لا تؤلّف المهارة بيدك وتفحصها بعينك. تخبر skill-creator متى ينبغي أن تنطلق المهارة وكيف تبدو النتيجة الجيدة، فيبني المهارة ويختبرها ويُحكِمها في مقابل تلك المعايير. تعريف النجاح والحكم على النتائج هو العمل الذي يقوم به خبير المجال في العالم الحقيقي؛ أما التأليف تحته فعمل الأداة.
الخطوة 1: تأكّد أن skill-creator متاحة. ثبّتها أصلاً (مع mcp-builder وَneon-postgres) في تجهيز القاعدة، فهي تجلس في .claude/skills/ ولا تعيد تثبيتها هنا. أعِد إضافتها فقط إن اختفت لسبب ما:
npx skills add https://github.com/anthropics/skills --skill skill-creator --agent claude-code -y
فحص.
skill-creatorموجودة في.claude/skills/. (تثبيت واحد خدم الأداتين: OpenCode يقرأ.claude/skills/احتياطياً، فلم يكن هناك قطّ تثبيت--agent opencodeمنفصل لتشغيله.)
الخطوة 2: عرّف ما تفعله المهارة ومتى تنطلق. يسألك skill-creator عن الأمرين اللذين تستطيع أنت وحدك تقريرهما، المُحفِّز والناتج. أعطه كليهما مقدّماً، بلغة واضحة، ودعه يسوّد.
Use skill-creator to build a summarize-ticket skill. Here is the spec.
Output: turn one support ticket into a five-section handoff (Customer
Context, Issue, Resolution Steps Taken, Current Status, Recommended Next
Action). It SHOULD fire on phrasings like "write a handoff note for #4471",
"TL;DR this thread", and "where does this stand before I escalate",
including ones that never say "summarize". It should NOT fire on drafting a
customer reply, triaging a batch, or reporting on ticket volume. Draft the
skill from that, then we'll test it.
يُنشئ: .claude/skills/summarize-ticket/.
فحص. توجد مسوّدة تحت
.claude/skills/summarize-ticket/، ويعكس وصفها قائمة الانطلاق/عدم-الانطلاق الخاصة بك، لا "summarizes tickets" عامة. ذلك الوصف هو المدخل الوحيد الذي يقرّر ما إذا كانت المهارة ستعمل أصلاً (المفهوم 3)؛ سلّمته بوصفه معايير قابلة للاختبار بدل تخمين الصياغة.
الخطوة 3: دع skill-creator يختبرها ويُحكِمها. هذا الجزء الذي يحلّ محلّ فحص الوصف بالعين. يحوّل skill-creator قائمة الانطلاق/عدم-الانطلاق إلى تقييمات تحفيز، ويشغّلها، ويحسّن الوصف حتى تنطلق المهارة حين ينبغي وتبقى صامتة حين لا ينبغي.
Test summarize-ticket against the fire and don't-fire cases I gave you:
turn them into trigger evals, run them, and tighten the description until
it passes. Show me which cases pass and which fail, before and after.
فحص. تقرأ نتائج التقييم، لا الوصف الخام: المهارة تنطلق على صياغات التسليم وَTL;DR والحالة وتبقى صامتة على الأشباه القريبة (صياغة رد، فرز دفعة). جدول النجاح/الفشل ذاك هو النسخة الصارمة من حدس المفهوم 3 "احذف الكلمة المفتاحية وانظر هل ما زال يقول متى ينطلق". النموذج يقرّر ما إذا كان سيشغّل هذه المهارة من وصفها وحده، فجعل ذلك الجدول أخضر هو اللعبة كلها.
كلتا الأداتين، انضباط واحد. في Claude Code، يشغّل skill-creator هذا بوصفه حلقة آلية: يقسّم حالاتك إلى مجموعة تدريب ومجموعة محجوزة، ويشغّل كلاً بضع مرات للحصول على معدّل تحفيز موثوق، ويُحسِّن عبر عدة جولات، مُبقياً الوصف الذي يسجّل الأفضل على الحالات التي لم يتدرّب عليها. وفي OpenCode تشغّل الحلقة نفسها يدوياً: عرّف الحالات، واختبر، وأحكِم، وكرّر. الأتمتة تختلف؛ وانضباط إثبات المُحفِّز في مقابل صياغات حقيقية متطابق.
الخطوة 4: عرّف المهارتين الأخريين بالطريقة نفسها. الحركة نفسها: عرّف متى تنطلق كلٌّ وما تنتج، ودع skill-creator يبنيها. لا تحتاج حلقة الاختبار الكاملة على الثلاث جميعاً؛ تشغيلها مرة على summarize-ticket علّمك الدورة. أعطه المُحفِّز وشكل الناتج لكلٍّ؛ والأوصاف التي يستقرّ عليها ينبغي أن تُقرأ كالتي أدناه. العامل يحتاج الثلاث جميعاً.
# .claude/skills/find-similar-cases/SKILL.md (frontmatter only)
---
name: find-similar-cases
description: Searches the resolved-tickets library for tickets semantically similar to a customer's described issue, returning the top 3-5 with their resolutions, ranked by how closely each matches. Use when the user describes a problem, complaint, or symptom and you need to check whether the team has handled something similar before. Calls the find_similar_resolved_tickets MCP tool. Always run this BEFORE drafting a response, so the response can reference proven prior resolutions rather than inventing a new approach.
---
المتن يمشي بهذه الخطوات:
- استخرج وصف المشكلة من السياق.
- استدعِ
find_similar_resolved_ticketsبlimit=5. - اعرض الأعلى ثلاثة بقيم مسافاتها في جدول markdown.
- أشِر صراحةً إلى المطابقات منخفضة الثقة (مسافة فوق نحو 0.3، حيث الأقلّ يعني الأكثر تشابهاً) بوصفها "no strong prior precedent found."
التعليمة "always run this BEFORE drafting" تقوم بعمل حقيقي؛ بدونها، يصوغ النموذج أحياناً رداً من معارفه السابقة ولا ينظر قطّ في المكتبة.
# .claude/skills/escalate-with-context/SKILL.md (frontmatter only)
---
name: escalate-with-context
description: Packages a customer conversation for handoff to a tier-2 support agent. Produces a structured escalation note with customer profile, issue summary, what was already tried, why escalation is recommended, and the suggested specialist team. Use when (a) the customer is on the Pro or Enterprise tier AND the issue is unresolved after one round of investigation, (b) the customer's sentiment is clearly negative, (c) the issue involves billing >$500 or a refund decision, or (d) the user explicitly asks for a human.
---
المتن يستدعي summarize-ticket أولاً للحصول على السياق المُهيكَل، ثم يكتب مذكّرة تصعيد من ستة أقسام (سياق العميل، والمشكلة، والحلول المُجرَّبة، وإشارات المشاعر، والفريق المُوصى به، وَSLA المقترح). شروط التحفيز الأربعة الصريحة في الوصف هي ما يمنع هذه المهارة من الانطلاق المفرط؛ فعامل بمنطق تصعيد غامض يصعّد كل شيء، وهو ما يبطل الغرض.
فحص. كلا الوصفين يسمّيان مُحفِّزات صريحة محدّدة، لا "use when relevant."
escalate-with-contextخاصةً: شروطه الأربعة هي ما يمنعه من الانطلاق على كل رسالة. المهارات الثلاث جميعاً تعيش الآن في.claude/skills/.
يُنشئ: .claude/skills/find-similar-cases/ وَ.claude/skills/escalate-with-context/.
الخطوة 5: صِل قدرة Skills بالعامل، وشاهد واحدة تنطلق. المهارات الثلاث على القرص؛ والآن على العامل نفسه أن يحمّلها. امنحه قدرة Skills فوق قدراته الافتراضية، ثم شغّله.
Give the worker the Skills capability pointed at .claude/skills, on top of
its default capabilities, and run it from the project root with: "write a
handoff note for ticket #4471, refund delayed two weeks, customer Sam."
Show me the run so I can see the skill load.
يُعدّل: ملف العامل (يضيف قدرة Skills).
فحص. يُظهِر التشغيل استدعاء
load_skillلsummarize-ticketويعود الرد بأقسامه الخمسة: تلك هي المهارة تنطلق داخل عاملك أنت، لا مجرد جالسة على القرص. وإن كتب العامل بدلاً من ذلك ملخّصاً بحرّية ولم يظهرload_skill، فالمسار حُلّ خطأً: المهارات تُحمَّل من مسار نسبيّ لمكان تشغيل العامل، فشغّله من جذر المشروع ب.claude/skillsنسبيّ، لا مطلق. (على macOS مسار مطلق تحت/tmpيحمّل صفر مهارات بصمت، بلا خطأ إطلاقاً، وهي أكثر طريقة محيّرة قد يفشل بها هذا.) واحد آخر: أنت تضيف Skills إلى القدرات الافتراضية، لا تستبدلها، وإلا يفقد العامل نظام الملفات والصدفة اللذين يتّكئ عليهما.
تقريباً ما ينبغي أن تراه في التشغيل:
tool call: load_skill(name="summarize-ticket")
reply: Customer Context / Issue / Resolution Steps Taken / Current Status / Recommended Next Action
لماذا تصلها الآن. هذه هي اللحظة التي تكفّ فيها المهارات عن كونها ملفات وتصير قدرة: الرسالة التالية التي تذكر تذكرة تُطلِق هذه المهارة من وصفها وحده. المهارتان الأخريان تتّكئان على أدوات MCP التي تبنيها تالياً، ف summarize-ticket، التي تقف وحدها، هي الصادقة لإثباتها هنا.
القرار 5: ابنِ خط أنابيب التضمين وابذر مكتبة المستندات
مجموعة بذرة من بضع عشرات من التذاكر المحلولة بنحو 300 رمز لكلٍّ تُضمَّن ب جزء من السنت بسعر text-embedding-3-small البالغ 0.02 دولار لكل مليون رمز إدخال. التضمين المستمرّ للتذاكر والمحادثات الجديدة يبقى عادةً تحت 3 دولارات/شهر عند حجم المثال العملي. الرافعة هي ميزانية الاستدلال، لا ميزانية التضمين.
أين أنت: مخطط بجداول فارغة ومهارات لا شيء لها لتبحث فيه؛ هذا القرار يضيف مكتبة مبذورة ومُضمَّنة من التذاكر السابقة المحلولة؛ وفي النهاية سترى بحثاً عن التشابه يعيد مطابقات مرتّبة.
تنهي هذا القرار بمكتبة صغيرة من التذاكر السابقة المحلولة، مُضمَّنة وقابلة للبحث. خطوتان.
الخطوة 1: ولّد مكتبة البذرة في الكود. "مكتبة" العامل مجموعة من التذاكر السابقة المحلولة: صغيرة بما يكفي لتعمل بسرعة، ومتنوّعة بما يكفي ليكون لدى البحث ما يميّز بينه. أنت لا تكتبها بيدك، ولا تملأ ملف CSV؛ الوكيل يولّدها.
Have the worker's own SDK generate a dozen-plus varied resolved tickets as
structured data (a Pydantic model is the clean way): each with a customer
email, a one-line summary, and the resolution. Vary the issues across
refunds, logins, duplicate charges, and shipping, so semantic search has
something to tell apart. Write the generator and run it; don't hand me a CSV.
يُنشئ: سكربت مولّد التذاكر.
فحص. أكثر من اثنتي عشرة تذكرة مولّدة عبر مشكلات مختلفة حقاً (استردادات، تسجيلات دخول، رسوم، شحن)، لا إعادة صياغة لثلاث. أنت لم تكتب صفّاً بيدك قط، وذلك هو المغزى: بيانات بذرة العامل شيء يستطيع العامل نفسه إنتاجه.
الخطوة 2: ابذر وضمّن. كل تذكرة مولّدة تحمل customer_email، وهو ما يتيح للباذر إيجاد-أو-إنشاء صفّ customers قبل إدراج التذكرة (المفتاح الأجنبي tickets.customer_id هو NOT NULL). ثم:
Seed the generated resolved tickets so the Worker can search them later.
For each one: find-or-create the customer by email, insert a resolved
ticket, store the case text as a documents row tagged source='past_case'
with the ticket id at metadata->>'ticket_id' (there is no ticket_id column
on documents), then embed that text with
text-embedding-3-small and link the embedding to the document. Write one
audit_log row for the whole seed run. Plan first.
يُنشئ: سكربت البذر والتضمين.
ذلك الشكل ليس اعتباطياً، وهو الجزء الذي لا يستطيع الوكيل تخمينه: find_similar_resolved_tickets في القرار 6 يبحث بربط embeddings ب documents (حيث source='past_case') ب tickets. إن لم تضع البذرة الصفوف بهذه الطريقة، فإن البحث في القرار 8 يعيد لا شيء بصمت ولن تعرف لماذا. الوكيل يكتب الباذر الفعلي؛ وأنت تحدّد الشكل الذي عليه إنتاجه. قاعدتان تؤكّدهما في النتيجة، كلتاهما من المفهوم 9 وكلتاهما أصلاً في AGENTS.md لديك: ضمّن ب النموذج نفسه الذي ستستعلم به لاحقاً، وسجّل pgvector على الاتصال (وإلا تُكتَب المتّجهات هراءً).
فحص. اطلب من الوكيل قراءة النتيجة مجدداً: "Count the documents tagged as past cases (should match the number of tickets you generated), count the embeddings (should match too), confirm only one embedding model is present, and run one similarity search to show the closest match to 'refund delayed two weeks' comes back ranked." شكلا فشل: إن أبلغ عن نموذجَي تضمين، فالبذرة خلطت النماذج في منتصف الطريق، أعِد الضبط وأعِد التشغيل؛ وإن عادت الأعداد أصفاراً، فالباذر ابتلع خطأً، اجعله يقرأ مجدداً صفّ
audit_logالذي كتبه لتشغيل البذرة (وهو بالضبط لماذا يكتب الباذر واحداً). لا تنتقل إلى القرار 6 حتى يعيد بحث التشابه نتائج مرتّبة.
تقريباً ما ينبغي أن يعيده بحث التشابه:
query: "refund delayed two weeks"
1. "refund not received after 14 days" distance 0.08
2. "duplicate charge, awaiting reversal" distance 0.24
لماذا هذا اتصال مباشر، لا MCP. سكربت البذر بنية تحتية: يعمل مرة واحدة، يدوياً، بواسطتك، لا شيء يفعله العامل بنفسه. حدود MCP لما يفعله الوكيل تلقائياً؛ وسكربت البذر شيء تفعله أنت. لا تضع حدوداً بينك وبين قاعدة بياناتك حين تكون أنت من على لوحة المفاتيح.
القرار 6: عرّف، وابنِ، وصِل خادم customer-data MCP
خادم MCP المخصص يعمل بوصفه خدمة صغيرة بجانب عاملك؛ ومُشارَكاً المضيف نفسه لا يضيف تكلفة استضافة ذات شأن (تظهر سطر حوسبة فقط إن دفعته إلى عتاد منفصل). وحيث تظهر الفاتورة فعلاً هو في الاستدلال: كل استدعاء lookup_customer أو find_similar_resolved_tickets يضيف ما يساوي رحلة ذهاب وإياب من الرموز إلى دور النموذج التالي. المفهوم 15 يغطّي جانب الكمون وحجم التجمّع من MCP-تحت-الحِمل.
أين أنت: مكتبة مبذورة لا يستطيع العامل بلوغها بعد في زمن التشغيل؛ هذا القرار يضيف خادم customer-data MCP المحدود النطاق ويصله؛ وفي النهاية سترى العامل يستدعي إحدى أدواته على رسالة حقيقية.
تنهي هذا القرار بخادم customer-data المحدود النطاق يعمل وموصولاً بعاملك، أدواته الثلاث قابلة للاستدعاء من تشغيل حقيقي. إنه الشكل نفسه كمهارات القرار 4: تعرّف ما على الموصِّل فعله وكم يبقى ضيّقاً، وَmcp-builder يبنيه، وتثبته باستخدامه. أنت توجّه النطاق؛ ولا تكتب أي قوالب FastMCP بيدك. (تبويب الأداة الخطرة الوحيدة، issue_refund، يأتي في القرار 9، بعد أن يعمل كل شيء.)
الخطوة 1: تأكّد أن mcp-builder متاحة. مثل skill-creator، ثبّتها في تجهيز القاعدة، فهي هنا أصلاً. أعِد إضافتها فقط إن اختفت:
npx skills add https://github.com/anthropics/skills --skill mcp-builder --agent claude-code -y
فحص.
mcp-builderموجودة في.claude/skills/.
الخطوة 2: عرّف عقد الأدوات والنطاق. هذه نسخة الموصِّل من تعريف معايير مهارة: تقول بالضبط أي الأدوات توجد، وما يأخذه كلٌّ، وكم يبقى الخادم ضيّقاً (لا SQL عام)، وَmcp-builder يخطّطه. ابنِه على streamable HTTP، نكهة عديمة الحالة (افتراضي المفهوم 11): كل استدعاء طلب مستقلّ، فالخادم خدمة حقيقية قابلة للعَنوَنة يبلغها العامل ب URL، وتستطيع تشغيل أكثر من نسخة إن نمت الحركة. (بناء محلي بحت بعامل واحد يمكن أن يستخدم stdio؛ والخدمة عديمة الحالة تطابق ما ستشحنه فعلاً.)
/mcp-builder Plan a custom MCP server called "customer-data" on the
streamable-HTTP transport, stateless flavor, with exactly three scoped
tools and no general SQL tool:
- lookup_customer(customer_id): return id, email, tier, open-ticket count.
Tier lives in customers.metadata->>'tier' (COALESCE to 'standard'); there
is no tier column.
- find_similar_resolved_tickets(description, limit): semantic search over
past resolved cases. Embed the description with text-embedding-3-small
(the SAME model the seed used) and register pgvector on the connection.
The search joins embeddings -> documents -> tickets, where the
documents->tickets link is documents.metadata->>'ticket_id' (there is no
ticket_id column on documents).
- issue_refund(order_id, amount_cents, reason): insert the refund (amount in
integer cents), set the order to refunded, AND write the audit_log row,
all in ONE transaction.
Give each tool a clear description so the model knows when to call it.
Show me the plan before any code.
فحص. اقرأ الخطة قبل أي كود: ثلاث أدوات بالضبط، بلا أداة SQL عامة، وَ
issue_refundيكتب الاسترداد، وتغيير حالة الطلب، وصفّ التدقيق في معاملة واحدة. ادفع بالاعتراض إن نقص أيٌّ منها. (مأخذ Neon تسلّمه للوكيل فقط إن نقلت المخطط عنpublicالافتراضي: أهّل أسماء الجداول بالمخطط، لأن نقطة Neon المُجمَّعة تعيد ضبطsearch_pathعند تحرير الاتصال، فلن يصمدSET search_path. على ترحيل الدورة الافتراضي يعمل ببساطة.) مأخذ Neon واحد ينطبق دائماً، هنا وفي القرار 7: النقطة المُجمَّعة (PgBouncer، وضع المعاملة) تكسر العبارات المُجهَّزة في asyncpg، فيجب أن يمرّر كلٌّ من تجمّع هذا الخادم وتجمّع التدقيقstatement_cache_size=0إلىasyncpg.create_pool(...)، وإلا يخطئ أول استعلام.
الخطوة 3: ابنِه، ودع mcp-builder يختبر الأدوات. ما إن تصحّ الخطة: "Build the server exactly as we planned, three tools and no more, then start it and confirm it boots cleanly. Don't add tools I didn't ask for." يستطيع mcp-builder المضيّ خطوة أبعد وتوليد تقييمات، مهام واقعية على الأدوات تلبيتها من البداية إلى النهاية، وهي نسخة الموصِّل من تقييم التحفيز الذي شغّلته على المهارة. للدورة، الاختبار الحاسم هو الخطوة التالية، استدعاء أداة من العامل، فإقلاع نظيف هنا يكفي للمضيّ.
يُنشئ: خادم customer-data-mcp/.
فحص. اقرأ وصف كل أداة في الخادم المبني: إنه ما يقرؤه النموذج ليقرّر متى يستدعي الأداة (الدور نفسه الذي يؤدّيه وصف
SKILL.md)، والغامض ينطلق في الوقت الخطأ. ثم أكّد الشيء الذي يخطئ فيه الوكيل خطأً دقيقاً غالباً: متنissue_refundيقوم بالكتابات الثلاث في معاملة واحدة. معظم هذه الانضباطات تعيش أيضاً فيAGENTS.mdلديك، فوكيل دقيق يطبّقها؛ أنت تؤكّد أنها صمدت.
خادم customer-data خدمة streamable HTTP، فيجب أن يكون قيد التشغيل قبل أن يستطيع العامل بلوغه. من هنا فصاعداً، التشغيلات الحيّة (هذا القرار، والقراران 8 و9) تحتاج طرفيّتين: شغّل الخادم في واحدة، وشغّل العامل في الأخرى، الخادم أولاً. أوقف الخادم وتفشل استدعاءات أدوات العامل بخطأ اتصال، لا بجواب خاطئ.
الخطوة 4: صِله بالعامل واستدعِ أداة. صِل الخادم بالعامل الذي يملك أصلاً جلسته ومهاراته، وأثبت أن أداة تعمل فعلاً. هذه نسخة الموصِّل من مشاهدة المهارة تنطلق في القرار 4:
Register the customer-data server with the worker as a remote
streamable-HTTP server at its URL, alongside the Session and Skills it
already has. Check the current SDK docs for the exact registration API.
يُعدّل: ملف العامل (يسجّل خادم customer-data).
فحص. إنها خدمة streamable HTTP، فشغّل الخادم أولاً، ثم شغّل العامل على رسالة حقيقية: "Start the customer-data server, then run the worker on 'I'm Sam, and I haven't had my refund for order #4429 in two weeks.'" ينبغي أن يستدعي العامل
find_similar_resolved_ticketsويعود بحالات سابقة مرتّبة، لا نتيجة فارغة ولا جواباً مُختلَقاً. تلك هي قناة MCP تعمل: بلغ العامل بيانات الأعمال عبر الخادم المحدود النطاق، وعبره وحده. رايتان حمراوان: أداة بنمطrun_sqlعام في القائمة تعني أن العامل لا يزال موصولاً بخادم Neon MCP في زمن التشغيل، أخرجها (المفهوم 12)؛ ونتيجة فارغة من البحث تعني أن بذرة القرار 5 لم تهبط بالشكل الذي يقرؤه الربط (embeddingsبdocumentsحيثsource='past_case'بtickets). إن لم يقلع الخادم نفسه، فاجعل الوكيل يقرأ سجلاته (ملاحظة استيراد بدء التشغيل في المفهوم 13 هي السبب المعتاد).
لماذا خادم مخصص، لا مجرد asyncpg في كود الوكيل. أسباب المفهوم 14 الثلاثة، بترتيب أهميتها هنا: النطاق (الوكيل يستطيع فعل ثلاثة أشياء بالضبط بقاعدة البيانات، لا أي شيء يسمح به SQL)، والعزل (الخادم يعمل في عمليته الخاصة بتجمّعه الخاص الذي لا يستطيع الوكيل استنزافه)، وقابلية إعادة الاستخدام (عامل ثانٍ يحتاج lookup_customer يتحدّث إلى هذا الخادم نفسه). ذلك السطح الضيّق هو حجّة الأمان كلها، ولهذا فإن فحص الخطوة 3 عن الحدود، لا عن السباكة.
القرار 7: صِل تسجيل التدقيق في كل مكان
أين أنت: عامل يتصرّف لكنه يسجّل كتابة الاسترداد الواحدة فقط؛ هذا القرار يضيف أفعال الوكيل نفسه إلى أثر التدقيق؛ وفي النهاية سترى أثر message_received / skill_activated / capability_invoked / message_sent لمحادثة واحدة.
هذا أحد القرارين اللذين يصطدم فيهما أول بناء بخطأ عادةً؛ والنداءات أدناه تسمّي كلاً منه قبل أن تلتقيه، فاقرأها أولاً.
تنهي هذا القرار بأفعال الوكيل نفسه مُسجَّلة في audit_log. خادم MCP يسجّل شيئاً واحداً أصلاً، issue_refund يكتب صفّ تدقيقه داخل معاملة الاسترداد (القرار 6)؛ والباقي هو كتابات جانب الوكيل: استدعاءات المهارات، واستدعاءات النموذج، واستدعاءات الأدوات، وحجوب حواجز الأمان. مهمة واحدة، باستخدام مساعد log_capability من المفهوم 10.
الخطوة 1: صِل مساعد التدقيق عند كل حدّ.
Wire the audit helper around the agent's own actions, at three points:
the start and end of each skill invocation, after each MCP tool call,
and around any guardrail trip. Use the separate audit connection (its
own pool), not the customer-data MCP boundary. Plan first.
يُعدّل: ملف العامل (يضيف توصيل التدقيق عند كل حدّ).
"الحدود" الثلاثة أعلاه لا تقابل ثلاثة خطّافات مطابقة، والتوصيل الساذج يُعطِّل التشغيل. الواقع:
- لا يوجد خطّاف مهارة. في وضع Skills الكسول الذي تستخدمه هذه الدورة، تُنشَّط المهارة باستدعاء النموذج ل أداة
load_skill، فراقب بدء/نهاية المهارة فيon_tool_start/on_tool_endحيثtool.name == "load_skill". واستدعاءات أدوات MCP تصل عبرon_tool_endنفسه. - حجوب حواجز الأمان استثناءات مرفوعة، لا خطّاف. التقط
InputGuardrailTripwireTriggered(ومتغيّرات الإخراج/الأداة) بtry/exceptحولRunner.run، واكتب صفّguardrail_trippedهناك. resultفيon_tool_endمُصنَّفstrلكنه يسلّمك الكائن الخام للأداة (نموذج Pydantic أو dict). تقطيعه أو عمليات نصّ عليه ترفع خطأً، واستثناء غير مُعالَج داخل خطّاف يقتل الدور كله (يظهر بوصفهUserError: Error running tool ...محيّراً). أرغمه بstr(...)و غلّف متن الخطّاف بtry/exceptكي لا يقدر خطأ تدقيق على إجهاض دور المستخدم أبداً.on_tool_endيُطلَق أيضاً حين تفشل أداة، مسلّماً إياك نتيجة"Error executing tool ...". اكتشف ذلك (فحص سلسلة فرعية، لاstartswith) وسجّلstatus="error"، وإلا يُسجَّل استرداد فاشل بوصفه نجاحاً.
اكتب صفّ conversations أولاً. audit_log.conversation_id مفتاح أجنبي إلى conversations(session_id). إن أشار صفّ تدقيق إلى جلسة بلا صفّ conversations بعد، ينتهك المفتاح الأجنبي ويتراجع عن المعاملة كلها، بما فيها الاسترداد الذي كان يسجّله. أدرِج-أو-حدّث صفّ conversations عند message_received، قبل أن يشير إليه أي صفّ تدقيق (القرار 3 ينشئ الجدول لكنه لا يقول قطّ متى يُكتَب الصفّ: إنه هنا).
حاجز إدخال بجلسة يرى المحضر كله. لا الرسالة الجديدة فقط: التاريخ المُهيَّأ الكامل إضافة إلى الدور الجديد. فكلمة مُعلَّمة من أي دور سابق تُسقِط كل دور لاحق (يُحجَب "say hello" بريء لأن رمز اختبار لا يزال في التاريخ). افحص فقط آخر عنصر role: user، لا الإدخال كله.
فحص. شغّل محادثة قابلة للرمي واحدة، ثم: "Using the Neon tools, find the most recent conversation and show me every
audit_logrow for it, in order." ينبغي أن ترى على الأقلmessage_received، وَskill_activated(العامل لديه مهاراته منذ القرار 4)، وَcapability_invokedواحداً لاستدعاء MCP، وَmessage_sent. شكلا فشل: إن رأيت فقط صفوف خادم MCP نفسه (capability_invoked، وَrefund_issued) ولا شيء من جانب الوكيل، فالمساعد موصول لكنه لا ينطلق قط، اجعل الوكيل يؤكّد أنه يعمل من داخل حلقة البثّ، لا مرة واحدة عند بدء التشغيل؛ وإن رأيت صفر صفوف، فاتصال التدقيق لا يبلغ قاعدة البيانات، اجعله يفحص تجمّع التدقيق في مقابل رابط قاعدة بياناتك.
تقريباً ما ينبغي أن تراه (محادثة واحدة، بالترتيب):
message_received
skill_activated
capability_invoked
message_sent
لماذا تجمّع التدقيق منفصل. يستخدم اتصاله الخاص، لا تجمّع customer-data MCP، لسببين: التدقيق يجب أن ينجح حتى حين يكون تجمّع البيانات مشبعاً، وكتابات التدقيق ينبغي ألا تنافس كتابات الأعمال على الاتصالات. نظام تدقيق فرعي يمكن أن يُجوَّعه النظام الذي يدقّقه ليس نظام تدقيق فرعياً. الميكانيكا صغيرة (المفهوم 7 يأتي بالجداول، والمفهوم 10 يأتي بالمساعد)؛ والانضباط هو استدعاؤه عند كل حدّ، باتّساق. (متطابق في OpenCode: إنه Python خالص.)
القرار 8: تحقّق من العامل كله على سيناريو واحد
أين أنت: كل طبقة موصولة ومفحوصة على حدة؛ هذا القرار لا يضيف جديداً، بل يثبتها تعمل معاً على سيناريو واحد ويعيد تشغيله من السجل؛ وفي النهاية سترى أثراً واحداً مرتّباً يعبر كل الطبقات.
الآن العامل لديه طبقاته الثلاث موصولة وكلٌّ مفحوصة وحدها: Session (القرار 3)، وَSkills (القرار 4)، وخادم MCP (القرار 6)، مع التدقيق تحتها (القرار 7). هذا القرار يثبت أنها تعمل معاً على سيناريو حقيقي واحد، ثم يعيد تشغيله من سجل التدقيق وحده.
الخطوة 1: شغّل السيناريو واقرأ أثره. اجعل وكيلك يشغّل العامل في مقابل الرسالة الواحدة التي تمرّن المكدّس كله (الخادم في طرفيّة، والعامل في أخرى، الخادم أولاً؛ انظر القرار 6):
Run the Worker and send it this customer message, then show me the
audit_log rows that conversation produced, in order:
"I haven't received my refund from order #4429, it's been two weeks."
ينبغي أن ترى هذه الصفوف خلال ثوانٍ قليلة:
action=message_received: تصل الرسالة، ويُنشأ صفّ المحادثة.action=skill_activated(فقط إن حُمِّلت مهارة): العامل قد يحمّل مهارة (find-similar-casesأوsummarize-ticket) لمعالجة الطلب. ويستطيع النموذج أيضاً بلوغfind_similar_resolved_ticketsمباشرة دون تحميل مهارة أولاً، فيكون هذا الصفّ غائباً ببساطة ويذهب الأثر مباشرة إلىcapability_invoked. كلاهما بناء صحيح، فلا تعامل غيابskill_activatedبوصفه خللاً.action=capability_invoked, target=mcp:find_similar_resolved_tickets: تقود المهارة بحثاً متّجهاً عبر خادم MCP، ويقرأ العامل أقرب حلّ سابق ليصوغ منه.action=message_sent: الرد المصوغ، مُسجَّلاً.
خامس مشروط، action=capability_invoked, target=mcp:lookup_customer، يظهر فقط إن كان لدى العامل أصلاً معرّف عميل. الدور الأول عادةً لا يملكه (أعطى العميل رقم طلب وبريداً إلكترونياً، لا UUID)، فيُتخطّى حتى يحلّ شيء أعلاه العميل: المصادقة، أو المنسّق، أو أداة lookup_customer_by_email تضيفها لاحقاً. وذلك مقبول؛ الرد لا يزال يستطيع الاستشهاد بالحالة السابقة.
فحص. الصفوف الأساسية موجودة وبالترتيب (مع
skill_activatedفقط إن حُمِّلت مهارة)، وتعبر الطبقات في أثر واحد: أداة MCP عملت في مقابل مصدر الحقيقة، وسُجِّلت المحادثة، وربما نُشِّطت مهارة. تلك هي العامل كله يعمل معاً. إن نقصcapability_invokedأوmessage_sent، فارجع إلى القرار الذي وصله وأعِد تشغيل فحص ذلك القرار.
message_received، وَskill_activated، وَmessage_sent يكتبها توصيل التدقيق من جانب الوكيل في القرار 7؛ وصفوف capability_invoked تأتي من ذلك التوصيل نفسه حول كل استدعاء MCP. خادم MCP يكتب صفّه الخاص فقط حين تغيّر أداة بيانات (صفّ refund_issued داخل issue_refund). فسيناريو للقراءة فقط كهذا يترك صفوف جانب الوكيل إضافة إلى قراءات capability_invoked، ولا صفوف كتابة أعمال حتى يحدث استرداد فعلاً، في القرار 9.
الآن وقد صارت المهارات تعمل داخل العامل، فإن scripts/ المهارة كود قابل للتنفيذ في صندوق الحماية. UnixLocalSandboxClient لا يعطي عزلاً؛ وَDocker، أو E2B، أو Cloudflare، أو Modal تحتويه. عامِل صلاحية الكتابة إلى مكتبة مهاراتك مثل صلاحية النشر، واعزل صندوق الحماية قبل أن تحمّل مهارات لم تكتبها.
Memory، وما ليست بهقائمة القدرات نفسها تأخذ Memory() بجانب Skills() (كلاهما من agents.sandbox.capabilities). تستحقّ المعرفة بدقّة، لأنها تبدو كالشيء الذي بنيته للتو وليست به. Memory() تتيح للعامل أن يتعلّم من عملياته السابقة: تقطّر محادثة كل تشغيل إلى ملفات مساحة عمل (ملف MEMORY.md وملخّص) حين تُغلَق جلسة صندوق الحماية، وتقرؤها التشغيلات اللاحقة، فيستكشف العامل أقلّ ويكرّر تصحيحات أقلّ. ذلك هو استدعاء المفهوم 3 "هل رأينا سؤالاً كهذا من قبل؟"، مُتولّى بواسطة زمن التشغيل، فلا تبنيه بيدك.
وما ليست به هو السجل الدائم للأعمال. ذاكرة صندوق الحماية قائمة على الملفات، وتقلّم أقدم مدخلاتها حسب الحداثة، وهي في مرحلة بيتا؛ صندوق حماية جديد يبدأ فارغاً، والعامل مُخبَر بأن يعاملها بوصفها إرشاداً، لا تخزيناً موثوقاً. جداول Neon لديك العكس في كل وجه: دائمة، وكاملة، ومستقرّة، وقابلة للاستعلام في SQL. فأنت تريد كليهما، لمهمتين مختلفتين. Memory() تجعل العامل أذكى عبر التشغيلات؛ ومصدر الحقيقة يجعل عمله دائماً، وقابلاً للإثبات، وقابلاً للبيع: الأصل الذي تملكه. الصفحات الأربع تحت Sandbox agents في وثائق SDK هي المصدر لهذه الطبقة كلها؛ وملف AGENTS.md المرافق يربط الأربع.
الخطوة 2: شغّل استعلام إعادة التشغيل. هذا هو الدليل الذي كان من أجله طبقة التدقيق كلها. اطلب من الوكيل سحب أثر المحادثة التي شغّلتها للتو:
Using the Neon tools, take the most recent conversation and show me its full
audit_logtrace, in order: created_at, action, target, payload, result.
فحص. بقراءة ذلك الناتج، تستطيع إعادة بناء سطراً سطراً ما فعله الوكيل ولماذا، دون إعادة تشغيل النموذج. إن لم تستطع، إن حدثت خطوة ليست في السجل، أو ادّعى صفّ فعلاً لا تعكسه جداول الأعمال، فهناك خلل توصيل. أصلحه قبل أن تعدّ العامل منجَزاً.
تقريباً كيف ينبغي أن تُقرأ إعادة التشغيل:
created_at action target result
10:02:11 message_received conversation:abc ok
10:02:12 capability_invoked mcp:find_similar_resolved_tickets ok
10:02:14 message_sent conversation:abc ok
لماذا هذا السيناريو. يمرّن كل قطعة بنيوية تضيفها هذه الدورة، في مرور واحد: مهارة تُنشَّط، وأداة مدعومة ب MCP تشغّل بحثاً دلالياً في مقابل مصدر الحقيقة، وأثر التدقيق يسجّل المسار كله، قابلاً للإعادة في SQL. لا شيء من ذلك كان موجوداً في وكيل المحادثة البسيط الذي بدأت به. وما لا يفعله بعد هو تحريك المال؛ ذلك هو الفعل الواحد الذي تضع إنساناً أمامه تالياً.
القرار 9: حصّن الفعل الواحد الذي يحرّك المال
أين أنت: عامل يعمل من البداية إلى النهاية لكنه يصدر استردادات بلا فحص؛ هذا القرار يضيف بوابة موافقة بشرية على issue_refund؛ وفي النهاية سترى استرداداً يتوقّف للتوقيع، ثم يمرّ عند الموافقة ويتوقّف عند الرفض.
هذا القرار الآخر الذي يصطدم فيه أول بناء بخطأ عادةً؛ والنداءات أدناه تسمّي كلاً منه قبل أن تلتقيه، فاقرأها أولاً.
العامل يعمل من البداية إلى النهاية. الآن أضف الشيء الوحيد الذي تركته عمداً: إنسان أمام issue_refund، الأداة الوحيدة التي تحرّك المال. تبني هذا أخيراً، عن قصد، لأن بوابة الموافقة لا تكون ذات معنى إلا حين يعمل الشيء الذي تحرسه فعلاً.
الخطوة 1: بوّب أداة الاسترداد.
Gate issue_refund behind human approval: register the customer-data server
so that tool needs sign-off before it runs, and leave lookup_customer and
find_similar_resolved_tickets un-gated. Check the current SDK docs for the
exact approval API.
يُعدّل: ملف العامل (يبوّب issue_refund على تسجيل الخادم).
فحص. أداتا القراءة لا تزالان تعملان دون مساس؛ وَ
issue_refundوحده مبوَّب. تعيش البوابة على كيف يُسجَّل الخادم، لا داخل الأداة. (داخل Claude Code أو OpenCode، مطالبة الإذن الخاصة بالعميل هي البوابة نفسها؛ وفي العامل المستقلّ هي إعداد الموافقة على تسجيل الخادم.)
الخطوة 2: شغّل استرداداً وشاهده يتوقّف. (الخادم في طرفيّة، والعامل في أخرى، الخادم أولاً؛ انظر القرار 6.)
Run the worker on a message that should lead to a refund on order #4429,
and show me what happens when it tries to issue it.
فحص. التشغيل يتوقّف بدل إصدار الاسترداد: العامل يبلّغ بأنه ينتظر الموافقة على
issue_refund(بمصطلحات SDK، يعود التشغيل بمقاطعة لا بجواب نهائي)، ولم يُكتَب شيء إلى جدولrefundsبعد. ذلك التوقّف هو نموذج السلطة يعمل: اقترح النموذج فعلاً، فأوقفه النظام عند الحدّ.
البوابة لا تنخرط إلا حين يستدعي النموذج issue_refund فعلاً. تعليمة نظام حذرة (مثل "only issue a refund once approved") قد تجعل النموذج يظلّ يطلب الموافقة نصاً ولا يستدعي الأداة قطّ، فلا يتوقّف شيء ولا يحدث استرداد، وهو ما يبدو بوابة معطوبة وليس كذلك. لإرغام البوابة على إظهار نفسها، ادفع الاستدعاء صراحةً: "Supervisor approved the refund for order #4429. Call issue_refund now: 2999 cents, reason 'arrived damaged'. Invoke the tool, don't ask again." بوابة SDK هي الحاجز الصلب على التنفيذ؛ لا تستطيع أن تجعل النموذج يمرّ عبر أداة من الأساس.
الخطوة 3: وافق مرة، ثم ارفض مرة. أثبت نصفَي البوابة:
Approve the pending refund and let the run finish, then show me the refunds
table and the audit_log row. Then run the same scenario again, reject it,
and show me that no refund was written.
فحص. عند الموافقة: يظهر صفّ الاسترداد، وينقلب الطلب إلى refunded، وَ
issue_refundيكتب صفّrefund_issuedالتدقيقي، كلّه في المعاملة الواحدة. عند الرفض: لا صفّ استرداد، ويُظهِر الأثر أن الفعل رُفِض. مأخذ واحد تسلّمه للوكيل، لأنه الفرق بين "يعمل" و"يبدو أنه ينبغي أن يعمل": استئناف تشغيل موافَق عليه حلقة، لا استدعاء واحداً. يمكن أن يحمل تشغيل أكثر من موافقة مُعلَّقة، فيظلّ الوكيل يستأنف ما دام التشغيل لا يزال فيه موافقات تنتظر (وافق أو ارفض كلاً، ثم استأنف)، لا مرة واحدة فقط. استأنف مرة واحدة وقد يعود إليك جواب فارغ والاسترداد لا يزال غير مكتوب.
تقريباً ما ينبغي أن ينتجه كل نصف:
approve -> refunds: 1 new row | orders.status = refunded | audit: refund_issued
reject -> refunds: no new row | audit: action declined
لماذا هذا أخيراً. بوابة موافقة تُضاف قبل أن يعمل العامل مسرحية لا تُختبَر: لا تستطيع تمييز بوابة عاملة من معطوبة حين لا يجري شيء عبرها. وحين تُضاف هنا، على عامل شاهدته يبحث ويصوغ ويدقّق، تستطيع إثبات نصفيها: الموافقة تمرّر الاسترداد، والرفض يوقفه، وسجل التدقيق يسجّل أيّهما. تلك هي نموذج السلطة كله، الوكيل يقترح والإنسان يقرّر.
الفحص أعلاه يفترض إنساناً حاضراً. إن جاء التوقيع بعد ساعة، في عملية أخرى، فالتشغيل المتوقّف يجب أن يُسلسَل (ب RunState الخاص ب SDK)، ويُخزَّن، ويُستأنَف حين يصل القرار. وموطنه الدائم جدول run_states صغير (صفّ واحد لكل توقّف: الحالة المُسلسَلة إضافة إلى awaiting/approved/rejected)، لا audit_log (للإلحاق فقط) ولا عمود على conversations (محادثة واحدة قد تتوقّف أكثر من مرة). استدعاءات السلسلة-والاستئناف جزء من سطح SDK المتحرّك، فأكّدها عبر Context7.
ماذا حدث للتوّ
تسعة قرارات، ووكيل المحادثة البسيط من الخطوة 0 صار له الآن أساس عامل. انظر إلى ما تغيّر:
- انتقلت القدرة خارج الكود. ثلاث مهارات تجلس في
.claude/skills/، مُتحكَّم في إصداراتها، قابلة للمشاركة عبر الوكلاء. - انتقلت المتاجر الدائمة خارج العملية. مخطط Postgres حقيقي (نواة الجداول الخمسة إضافة إلى طبقة مجال للعملاء، والطلبات، والتذاكر، والاستردادات) يحمل الآن مصدر حقيقة العامل ومكتبة المراجع التي يبحث فيها ب pgvector، بينما تبقي Session الخاصة ب SDK حالة محادثة العامل على قاعدة البيانات نفسها.
- وصول الأعمال في زمن التشغيل مُوسَّط. يبلغ الوكيل بيانات الأعمال في Postgres فقط عبر خادم MCP محدود النطاق يكشف ثلاث أدوات بالضبط؛ وكل قراءة وكتابة أعمال تعبر تلك الحدود الواحدة. ونظام التدقيق الفرعي هو الاستثناء المتعمَّد الوحيد، على اتصاله المباشر الخاص، كي لا يُجوَّعه الحدّ الذي يدقّقه.
- كل فعل يترك أثراً. سجل التدقيق يستطيع إعادة تشغيل أثر التفكير الكامل لأي محادثة، بعد أسابيع أو أشهر، في SQL.
- للفعل الخطر مالك. الأداة الوحيدة التي تحرّك المال تتوقّف لإنسان قبل أن تعمل؛ الموافقة تمرّرها، والرفض يوقفها، وفي الحالتين يسجّل سجل التدقيق القرار. تلك هي نموذج السلطة الذي يحتاجه عامل قبل أن يأتمنه أحد على أفعال حقيقية.
حزمة OpenAI Agents SDK لا تزال هناك. وصندوق الحماية لا يزال حوسبتك، والبثّ، وحواجز الأمان، والتتبّع التي بدأ بها الوكيل كلها لا تزال هناك. وما تغيّر هو البنية فوقها: المهارات تحمل القدرات، ومصدر الحقيقة يحمل الحقيقة، وَMCP يصلها، ويبقى إنسان في الحلقة على الأفعال التي تهمّ.
تلك هي أساس عامل. وما ليست به بعد هو دائم العمل، أو مبادِراً، أو جزءاً من قوة عاملة مُدارة. تلك هي النقلات التي تضيفها الدورات التالية.
القرار 10 (تحدٍّ اختياري): اجعل الموافقة المتوقّفة تنجو من إعادة التشغيل
في القرار 9 كان المُوافِق جالساً عند الطرفية، فكان [y/N] كافياً. أما الموافقات الحقيقية فنادراً ما تعمل هكذا: المدير الذي يوقّع على استرداد قد يجيب بعد ساعة، من تطبيق آخر، على جهاز آخر. عاملك لا يستطيع التعامل مع ذلك بعد. حين يتوقّف استرداد، يعيش التشغيل المتوقّف فقط في ذاكرة العامل، فإن أُغلقت العملية قبل أن يجيب الإنسان، ضاع الاسترداد المُعلَّق.
لقد نقلت أصلاً ثلاثة أنواع من الحالة إلى Postgres: أدوار المحادثة، وسجلات الأعمال ومكتبة المراجع، وأثر التدقيق. التشغيل المتوقّف هو النوع الوحيد الذي لا يزال محبوساً في الذاكرة. هذا التتويج الاختياري ينقله إلى قاعدة البيانات أيضاً، كي يمكن الموافقة على توقّف لاحقاً، من أي مكان. إنه تحدٍّ مُقيَّم، لا بناء مُوجَّه: كل خطوة تعطيك الفكرة والمطالبة، وتترك التوصيل لوكيلك.
الهدف: الموافقة على استرداد متوقّف أو رفضه من عملية مختلفة عن التي بدأته.
الخطوة 1: امنح كل تشغيل متوقّف موطناً في قاعدة البيانات. التوقّف يحتاج صفّه الخاص: أي محادثة وأداة ينتمي إليهما، والتشغيل المحفوظ نفسه، وحالة تنتقل من awaiting إلى approved، أو rejected، أو resumed. إنه جدوله الخاص، لا سجل التدقيق (ذاك تاريخ منتهٍ) ولا عمود على conversations (محادثة واحدة قد تتوقّف أكثر من مرة).
Add a run_states table that stores one paused run per row: the conversation
and tool it belongs to, the saved run, and a status that defaults to
"awaiting" and can become approved, rejected, or resumed. Plan the DDL first;
I'll approve before you apply it on a branch.
فحص. يوجد جدول
run_statesوتوقّف جديد يتخلّف إلىawaiting. أنت لم تكتب ال SQL قط: قلت ما الجدول من أجله، فكتبه وكيلك، بالطريقة نفسها التي هبط بها المخطط في القرار 3.
الخطوة 2: حين يتوقّف استرداد، احفظه وامضِ. الآن ينتظر العامل عند الطرفية؛ وبدلاً من ذلك ينبغي أن يسجّل التوقّف ويحرّر نفسه للدور التالي.
When a run comes back waiting for approval instead of with a final answer, do
not block on input. Save the paused run as a run_states row marked "awaiting"
and return, so the worker is free for the next turn. One turn is one request
that either finishes or parks. Check the current Agents SDK docs for the exact
"save the paused run" call before you write it.
فحص. دور استرداد يعود الآن سريعاً، تاركاً صفّ
awaitingخلفه، ولا شيء يحجب انتظاراً لإنسان.
الخطوة 3: وافق أو ارفض من أمر منفصل. ينتقل القرار خارج حلقة المحادثة إلى نقطة دخول صغيرة خاصة به، كي يستطيع العمل في عملية أخرى بالكامل.
Build a small "decide" command, separate from the chat loop: it lists the
awaiting rows, takes my approve or reject on one, then reloads that saved run
and finishes it. Keep resuming in a loop while the run still has approvals
pending, since resuming once can come back empty with the refund unwritten
(the loop gotcha from Decision 9). Confirm the reload call through Context7.
فحص. الموافقة على صفّ من أمر decide تقود ذلك الاسترداد إلى الاكتمال؛ ورفضه لا يكتب استرداداً ويسجّل الرفض.
الخطوة 4: اجعل الاسترداد آمناً لإعادة المحاولة. في إعداد موزّع، إعادة محاولة شبكية قد تُطلِق الاسترداد الموافَق عليه نفسه مرتين.
Make issue_refund idempotent: dedupe on the order plus a request id, so the
same approved refund cannot run twice.
فحص. استأنف الموافقة نفسها مرتين عن قصد: تحصل على صفّ
refundsواحد بالضبط، لا اثنين.
الخطوة 5: دور نشط واحد لكل محادثة. دوران على المحادثة نفسها في وقت واحد سيُفسِد جلستها.
Add a per-conversation lock (a Postgres advisory lock on the session id, or a
status guard) so only one turn is active per conversation at a time.
فحص. دور ثانٍ على المحادثة نفسها ينتظر أو يُرفَض، بدل أن يسابق الأول.
الاستدعاءات التي تحفظ تشغيلاً متوقّفاً وتعيد تحميله لاحقاً جزء من سطح SDK بيتا الذي يتحوّل بين الإصدارات. انضباط الدورة ينطبق على تحدّيها الخاص: الصق استدعاءَي الحفظ وإعادة التحميل بالضبط من وثائق Agents SDK الحالية أو Context7 بدل استذكارهما. الفكرة، أن تشغيلاً متوقّفاً يصير صفّاً تلتقطه لاحقاً، مستقرّة؛ أما أسماء الطرق فليست كذلك.
يكتمل عندما:
- تبدأ استرداداً في عملية واحدة؛ فتخرج والتشغيل مُركَن في
run_states(الحالةawaiting)، وبلا صفّrefundsبعد.- في عملية ثانية، توافق عليه؛ فيلتزم الاسترداد (صفّ استرداد، وينقلب الطلب، وصفّ تدقيق
refund_issued)، ويصير الصفّ المُركَنresumed.- مسار الرفض يترك صفر كتابات أعمال وصفّ تدقيق
refund_blocked.- الموافقة على التشغيل المُركَن نفسه مرتين لا تُصدِر استرداداً ثانياً.
- الحلقة كلها قابلة لإعادة التشغيل من
audit_logإضافة إلىrun_statesدون إعادة تشغيل النموذج.
امتداد (موزّع بالكامل). ضع خادم customer-data خلف URL حقيقي بمصادقة ووجّه العامل إليه؛ وبدّل صندوق الحماية المحلي بمُستضاف دون تغيير الوكيل نفسه (بدّل العميل، وأبقِ الوكيل)؛ وانقل أسرارك خارج ملف .env إلى مدير أسرار. العامل نفسه، قادر الآن على العمل عبر الأجهزة.
الحالة في قاعدة البيانات ضرورية لكنها غير كافية: آخر شيء ذي حالة لينتقل هو التشغيل المتوقّف نفسه، وما إن يعيش في run_states حتى يكفّ عاملك عن كونه مربوطاً بعملية واحدة.
الجزء الخامس: أين تنتهي هذه الدورة
شكل تكلفة عامل: كيف تقدّرها
لا مجاميع بالدولار هنا عن قصد: أسعار الرمز وحدود الطبقة المجانية تتغيّر شهرياً، فأي رقم مطبوع سيكون متقادماً وقت قراءتك له، والرقم المتقادم أسوأ من لا رقم. والذي يدوم هو الطريقة. وإليها، مع حركة المثال العملي نفسها بوصفها المدخلات التي تضعها: 200 محادثة/يوم، نحو 10 أدوار لكلٍّ، نحو 8 آلاف رمز إدخال لكل دور.
سطر واحد هو الفاتورة كلها تقريباً؛ والثلاثة الأخرى أخطاء تقريب. اعمل عليها بالترتيب.
1. استدلال النموذج. حجم رموزك الشهري مضروباً في سعر نموذجك للرمز. الحجم يأتي من حركتك أنت:
input tokens/month ≈ conversations/day × turns/conversation × tokens/turn × 30
للمثال: 200 × 10 × 8,000 × 30 ≈ 480M input tokens/month. اضرب ذلك في سعر إدخال نموذجك (من صفحة تسعيره)، ثم أضف رموز الإخراج بالطريقة نفسها (أقلّ منها بكثير، لكن بسعر أعلى للرمز). تلك الضربة الواحدة هي فاتورتك.
أكبر رافعة عليها هي التخزين المؤقت للتعليمات. ملف AGENTS.md لديك، وتعليمة النظام، وبيانات Skills الوصفية متطابقة في كل دور، فحين يخزّن المزوّد ذلك البادئة المستقرّة مؤقتاً، تُفوتَر تلك الرموز بجزء من السعر العادي. إبقاء البادئة مستقرّة (لا تُكثِر تغيير AGENTS.md في منتصف النهار) هو أعلى حركة تكلفة قيمة لديك. توجيه الأدوار السهلة إلى نموذج أصغر والصعبة فقط إلى نموذج رائد هو الثانية.
2. التضمينات. الرموز المُضمَّنة × سعر نموذج التضمين. تضمّن مجموعة البذرة مرة وتذاكر جديدة مع وصولها؛ وبسعر نموذج تضمين صغير ذلك سنتات، لا دولارات، ما لم تعِد تضمين تواريخ محادثات كاملة باستمرار. صفحة التسعير نفسها.
3. Postgres (Neon). غالباً 0 دولار: الطبقة المجانية تغطّي عاملاً واحداً منخفض الحجم، والتقلّص إلى الصفر يعني أن الساعات الخاملة لا تكلّف شيئاً. تدفع فقط بعد تجاوز حدود التخزين / ساعات الحوسبة المجانية، وعندها يكون تخزيناً إضافة إلى حوسبة نشطة، كلاهما على صفحة تسعير Neon.
4. حوسبة صندوق الحماية. 0 دولار هنا، لأن المثال العملي يعمل على UnixLocalSandboxClient، جهازك أنت. في الإنتاج تكون دقائق-حاوية حيثما تنشر (Docker، أو Cloudflare، أو E2B، أو Modal): طول الجلسة × التزامن × سعر ذلك المزوّد.
الطريقة كلها في سطر واحد: احسب حجم رموزك الشهري من أرقام محادثاتك أنت، واضرب في سعر اليوم للرمز، واقرأ الأسطر الثلاثة الأخرى عن صفحات التسعير. التوسّع إلى عمّال كثيرين لا يغيّر الصيغة، بل يضرب سطر الاستدلال في عدد العمّال ومدى انشغالهم؛ وأسطر البنية التحتية تبقى مسطّحة تقريباً، فالفاتورة التي تنمو هي فاتورة النموذج، والعادتان أعلاه (بادئة مستقرّة مُخزَّنة، ونموذج أرخص للأدوار السهلة) هما ما يبقيها في حدّها.
دليل الاستبدال: البنية ثابتة، والمنتجات ليست كذلك
تسمّي هذه الدورة بائعين محدّدين في كل طبقة (OpenAI Agents SDK، وصندوق الحماية المحلي للحزمة، وَNeon، وتضمينات OpenAI، وحزمة MCP ل Python). ذلك لأن مثالاً تعليمياً يحتاج أجوبة ملموسة، لا "استخدم أي زمن تشغيل LLM يحلو لك". لكن البنية تعمل مع أي بديل متوافق. خمسة استبدالات يتوقّعها تصميم الدورة صراحةً:
- مضيف Postgres: Neon ← Supabase، وَAWS RDS، ومُستضاف ذاتياً. أي شيء ب
pgvectorيعمل. تفقد التفريع والتقلّص إلى الصفر (هذان خاصّان ب Neon)، لكن مخطط الجداول الخمسة، وخط أنابيب التضمين، وانضباط أثر التدقيق، ونمط خادم MCP المخصص كلها قابلة للنقل بايتاً ببايت. التغيير الوحيد سلسلة الاتصال وربما إعداد SSL. - تخزين المتّجهات: pgvector ← Pinecone، وَWeaviate، وَQdrant. إن رفضت حجّة "قاعدة بيانات واحدة للعلائقي والمتّجه" من المفهوم 6، فبدّل جدول
embeddingsبعميل قاعدة بيانات متّجهة. الكلفة: متجران تبقيهما متّسقين (المفهوم 6 يحاجّ بأن ذلك نادراً ما يستحقّ). الفائدة: استدعاء أفضل عند أحجام ضخمة جداً (أكثر من 10 ملايين متّجه)، وبساطة تشغيلية لخدمة مُدارة. - نموذج التضمين: OpenAI ← Cohere، وَVoyage، وَBGE-small (محلي). غيّر ثابتاً واحداً (
EMBEDDING_MODEL) وبُعد عمود واحد (VECTOR(n)). شغّل إعادة تضمين لمرة واحدة للبيانات الموجودة. خط أنابيب المفهوم 9 لا يتغيّر. - صندوق الحماية: المحلي ← Cloudflare، وَE2B، وَModal، وَDaytona، أو Docker الخاص بك. أي شيء بحدود عمليات معزولة وإعادة تشغيل نظيفة يعمل. زمن تشغيل
SandboxAgentمحايد تجاه الخلفية؛ والمثال العملي يعمل علىUnixLocalSandboxClient، والإنتاج يبدّل إلى أيٍّ من هذه.scripts/المهارات تُنفَّذ بالطريقة نفسها. ومخطط حدود الثقة من الدورة السابقة لا يزال ينطبق. - زمن تشغيل الوكيل: OpenAI Agents SDK ← LangGraph، وَCrewAI، وَPydantic AI، أو حلقتك الخاصة. حدود MCP هي ما ينجو؛ كل إطار وكيل حديث له عميل MCP. والمهارات تعمل في أي وكيل يستطيع تحميل ملفات
SKILL.md(Claude Code، وَOpenCode، وَGoose، وأكثر فأكثر Cursor/Windsurf). وانضباط أثر التدقيق Python محايد تجاه الإطار.
ما لا يُبدَّل بسهولة. بروتوكول MCP نفسه، ومواصفة صيغة Skills، وعادة أثر التدقيق. هذه هي الأجزاء التي تحملها عبر المنتجات؛ والمنتجات هي الأجزاء التي تبدّلها. الشكل البنيوي نفسه تحت، وتطبيقات قابلة للاستبدال فوق.
كلمة عن "ثابت" و"مملوك". كلاهما إرشاد يستحقّ الرهان عليه، لا حقيقة مستقرّة. "ثابت" يسمّي أفضل المعايير المفتوحة المتاحة في 2026: MCP عمره نحو ثمانية عشر شهراً ومواصفة Skills أحدث، ويوماً ما قد تكون القناة أو صيغة القدرة نفسها هي ما يُستبدَل، لا فقط المنتجات الموصولة بها. الرهان على بروتوكولات مفتوحة بدل مملوكة هو كيف تتقادم بأناقة، لكن عامِل البنية بوصفها دائمة-بالتصميم، لا أبدية. و"مملوك" يعني فعلاً مملوك-بالتركيب: هذا العامل يعمل على سحابة Neon، ونماذج بائع، وعميل وكيل برمجة، ومهارات مسحوبة من مستودعات طرف ثالث. ما تملكه هو حرّية تبديل أي واحد منها دون إعادة كتابة الباقي. ذلك حقيقي ويساوي الكثير، وهو أقلّ مما توحي به الكلمة الكاملة. امتلك الحدود، لا الأساس.
ما لا تغطّيه هذه الدورة (بعد)
لديك الآن عامل يستوفي اثنين من الثوابت السبعة التي تضعها الأطروحة. تحديداً: يعمل على محرّك (الثابت 4، من الدورة السابقة)، ويعمل في مقابل مصدر حقيقة (الثابت 5، من هذه الدورة). الثوابت الخمسة الأخرى هي ما تتطلّبه الشركات الإنتاجية المعتمِدة على الذكاء الاصطناعي، وما تغطّيه الدورات اللاحقة. كلٌّ نقطة واحدة هنا، لا قسماً.
- الثابت 1: الإنسان هو الأصيل. مواصفات مُؤلَّفة، وبوابات موافقة، وإعلانات ميزانية. بنية تحديد القصد وامتلاك النتائج، مُغطّاة في الجزء السادس من الكتاب.
- الثابت 2: كل إنسان يحتاج وكيلاً. وكيل شخصي عند الحافّة يحمل سياقك، ويمثّل حُكمك، ويوسّط العمل إلى القوة العاملة. الأطروحة تسمّي OpenClaw بوصفه التحقيق الحالي.
- الثابت 3: القوة العاملة تحتاج مديراً. منسّق يوزّع العمل، ويفرض الميزانيات، ويدقّق التنفيذ، ويكشف التوظيف بوصفه قدرة قابلة للاستدعاء. الأطروحة تسمّي Paperclip.
- الثابت 6: القوة العاملة قابلة للتوسّع تحت السياسة. طبقة وصفية يولّد فيها وكيل مُفوَّض تعليمة، ويجهّز زمن تشغيل، ويسجّل عاملاً جديداً، دون إيقاظ إنسان. Claude Managed Agents تحقيق واحد.
- الثابت 7: القوة العاملة تعمل على جهاز عصبي. مُطلِقات (جداول، وخطّافات ويب، واستدعاءات API واردة) توقظ الوكيل تحت غلاف السلطة. Inngest (دوال دائمة ومهام خلفية) تحقيق واحد لأحداث القوة العاملة العامة؛ وَClaude Code Routines هو المسار الخاص بوكيل البرمجة.
كيف تتقن هذا فعلاً
قراءة هذه الدورة المكثفة لا تجعلك بارعاً في بناء العمّال. استخدامها يفعل. المسار يبدو كما في الدورة السابقة: تبدأ يدوياً، وتحسّ بالاحتكاك، وتدع كل قطعة احتكاك تعلّمك أي مفهوم تنتمي إليه.
الخريطة لهذه الدورة:
- "لماذا لا تنطلق مهارتي حين ينبغي؟" ← جودة الوصف (المفهوم 3). أعِد الكتابة. اختبر باختراع خمس طرق مختلفة قد يصوغ بها مستخدم المُحفِّز.
- "لماذا يخترع الوكيل بيانات لا تملكها قاعدة البيانات؟" ← الوكيل لا يستدعي خادم MCP فعلاً. افحص الأثر؛ افحص تسجيل
mcp_servers=[...]. - "لماذا سجل تدقيقي ناقص؟" ← كتابة التدقيق ليست في مسار الكود نفسه كالفعل (المفهوم 10). انقلها بجوار الفعل، في المعاملة نفسها.
- "لماذا نتائج pgvector لديّ غير ذات صلة؟" ← إما أن التقطيع خاطئ (المفهوم 9)، أو أن نموذج التضمين وقت الإدراج لا يطابق نموذج التضمين وقت الاستعلام. أعِد التضمين.
- "لماذا خادم MCP لديّ بطيء تحت الحِمل؟" ← تجمّع الاتصالات داخل الخادم صغير جداً، أو قائمة الأدوات غير مُخزَّنة مؤقتاً على العميل. المفهوم 15.
- "لماذا يبدو خادم Neon MCP مخيفاً في الإنتاج؟" ← لأن وثائق Neon نفسها تقول إنه ليس للإنتاج. اكتب خادم MCP مخصصاً (المفهوم 14). الأول يأخذ 30 دقيقة؛ والثاني يأخذ 10.
ابنِ البنية قطعةً واحدة في كل مرة. لا تحاول إضافة المهارات، ومصدر الحقيقة، وَMCP كلها في عطلة نهاية أسبوع واحدة. ابدأ من وكيل المحادثة في الخطوة 0. أضف مصدر حقيقة أولاً (القرارات 3–5) وشاهد تجربة تنقيحك تتغيّر. أضف مهارة (القرار 4) وشاهد كيف يقرّر النموذج استخدامها. أضف حدود MCP أخيراً (القرار 6). كل خطوة تعلّمها الخاص؛ وفعل الثلاثة دفعة واحدة جدار.
عائد المحمولية حقيقي: المهارات، والمخططات، وخوادم MCP التي تكتبها هنا كلها تنتقل إلى منتجات أخرى. دليل الاستبدال يسرد البدائل لكل طبقة.
النقلة في ما تصرف وقتك عليه
بعد القرار 4، يتغيّر شكل عملك. تصير كتابة الكود إحاطةً للوكيل؛ وتصير مراجعة الوصف (حقل ملف إعداد تتصفّحه عادةً) الحرفة الأساسية. وصف صرفت 30 دقيقة في تسويده وصقله يقوم بعمل بنيوي أكثر من 200 سطر كود خادم MCP الذي ولّده الوكيل تحته، لأن الوصف هو سطح التوجيه الذي يقرؤه النموذج كل دور.
نقلتان عمليتان. أولاً، تتوقّف عن سؤال "كيف أطبّق هذا؟" وتبدأ بسؤال "ما الطرق الخمس المختلفة التي قد يصوغ بها مستخدم حقيقي المُحفِّز؟" الكود لاحق؛ إن كان الوصف خاطئاً، لا يبلغ الوكيل الكود قط وجودة الكود غير ذات صلة. ثانياً، المراجعة تحلّ محلّ التأليف بوصفها المهارة الأساسية. الوكيل يسوّد؛ وأنت تقرّر ما إذا كانت المسوّدة تعمل في حالات التحفيز التي كتبت الوصف من أجلها. أصعب جزء هو مقاومة الرغبة في إعادة الكتابة حين تستطيع حلّه بنفسك في ثلاث دقائق: الانضباط نفسه الذي يمنعك من تجاوز حدود MCP.
مرجع سريع
المفاهيم الخمسة عشر في سطر لكلٍّ
- مهارة الوكيل مجلد. SKILL.md إضافة إلى scripts/references/assets اختيارية.
- الكشف التدريجي. بيانات وصفية عند بدء التشغيل ← متن كامل عند التنشيط ← مراجع عند الطلب.
- ملف SKILL.md هو بيانات وصفية + متن. الاسم، والوصف، وبيانات وصفية اختيارية، ثم تعليمات تشغيلية.
- المهارات تنتقل بوصفها ملفات. SKILL.md نفسه يعمل في Claude Code وOpenCode دون تعديل.
- ركّب مهارات صغيرة عبر تسليم نظام الملفات حين يهمّ العزل أكثر من بساطة التنسيق.
- Postgres + pgvector يتفوّق على قاعدة بيانات متّجهة منفصلة لأغلب أحمال عمل الوكلاء. Neon تضيف التفريع، والتقلّص إلى الصفر، وخادم MCP.
- خمسة جداول هي الحدّ الأدنى للمخطط التشغيلي: conversations، وdocuments، وembeddings، وaudit_log، وcapability_invocations؛ أدوار المحادثة تعيش في Session الخاصة ب SDK (
SQLAlchemySessionعلى قاعدة البيانات نفسها). - أساسيات pgvector:
VECTOR(1536)+ مسافة جيب التمام<=>+ فهرس HNSW. استخدم نموذج التضمين نفسه على الطرفين. - خط أنابيب التضمين: قطّع عند الحدود الدلالية (~400 رمز مع تداخل)، وضمّن دفعةً، وخزّن مع بيانات النموذج الوصفية.
- التدقيق ليس تسجيلاً. كل فعل ذي معنى يكتب صفّاً في المعاملة نفسها للفعل الذي يسجّله.
- MCP بروتوكول، لا خدمة. ثلاث أوّليات (أدوات، وموارد، وتعليمات)، وثلاثة وسائط نقل (stdio، وstreamable HTTP، وSSE القديم).
- خادم Neon MCP للتطوير. تصميم المخطط، وعمليات الترحيل القائمة على الفروع. لا لزمن تشغيل الإنتاج.
- حزمة OpenAI Agents SDK لها عميل MCP مدمج.
from agents.mcp import MCPServerStdio, MCPServerStreamableHttp. استخدمasync with. خزّنlist_toolsمؤقتاً في الإنتاج. - خوادم MCP المخصصة تستحقّ ثمنها عبر النطاق، والعزل، وقابلية إعادة الاستخدام. لا تكتب واحداً لدالة واحدة يستخدمها وكيل واحد.
- MCP تحت الحِمل: streamable HTTP للبعيد، وخزّن الأدوات مؤقتاً، وأعِد استخدام الاتصالات، وجمّع داخل الخادم، وانشر سياق التتبّع عبر
_meta.
حين يبدو شيء خاطئاً
Skill not firing when it should
→ Description too vague. Rewrite with "Use when..." and specific keywords (Concept 3).
Skill firing when it shouldn't
→ Description too broad. Add explicit constraints in the description.
pgvector returning irrelevant results
→ Embedding model mismatch (insert vs. query). Verify the model column in
the embeddings table. Re-embed if needed.
MCP tool not appearing in agent
→ Server not registered, or list_tools cache stale. Check mcp_servers=[...]
and try cache_tools_list=False temporarily.
Audit log has gaps
→ Action and audit write are in different code paths. Move them next to
each other, ideally same transaction.
Agent timing out on Postgres operations under load
→ MCP server's connection pool too small. Check asyncpg.create_pool(max_size=...).
MCP server hangs on startup with torch / sentence-transformers / large imports
→ Default client_session_timeout_seconds=5 is too short for servers that
load ML models at import. Bump to 60. See Concept 13's callout.
CREATE TABLE fails: relation "notes" already exists
→ You're pointing at a database that already has tables. Use a fresh
database or Neon project; the Quick Win's build prompt makes a fresh one.
Non-OpenAI key getting 401 against api.openai.com
→ Set OPENAI_BASE_URL to your provider's OpenAI-compatible endpoint
(e.g., https://api.deepseek.com/v1) before running the agent.
Agent fails partway with a 401 / auth / BadRequestError
→ Wrong key, wrong provider, or expired key. Have your agent confirm
OPENAI_API_KEY is set and test a model call before the full run; it
fails in one second instead of four files deep.
Neon MCP server returning errors in production agent code
→ You're using it wrong. Neon's docs are explicit: development only.
Write a custom MCP server instead (Concept 14, ~30 minutes).
بطاقات الدراسة
فحص المعرفة
فحص ذاتي سريع ومُبوَّب على الأفكار التي مررت بها للتوّ.