مراجعة صادقة لـ Wiki المستوحى من كارباثي: ما نجح، وما فشل، ولماذا تخلّينا عنه

في Noqta نبني AgentX، منصة أوركسترا متعددة الوكلاء ذاتية الاستضافة. هذه مراجعة صادقة لقرار معماري تراجعنا عنه للتو.
النمط الذي حاولنا نسخه
في أوائل أبريل 2026، غرّد أندريه كارباثي عن فكرته "LLM Wiki": التوقّف عن إعادة تشغيل RAG في كل استعلام، وترك النموذج اللغوي الكبير يُرجم مصادرك إلى قاعدة معرفة markdown ثابتة ومترابطة تتراكم مع الوقت. وأشار إلى Farzapedia — الـ wiki الشخصي لفَرزا المبني من 2500 مدخل من مذكّرة خاصّة وApple Notes وiMessage، مُترجَماً إلى نحو 400 مقال مترابط — بوصفه أوضح مثال علني على نجاح النمط.
قرأنا gist كارباثي، وقرأنا personal_wiki_skill.md لفَرزا، وشحنّا نسختنا داخل AgentX لحِمل إنتاج متعدّد الوكلاء. بعد اثني عشر يوماً حجبنا أمر الترجمة الأساسي خلف --force وعطّلنا الكرون الليلي. هذا هو التحليل بعد الفشل.
ما يقوله النمط الأصلي فعلاً
قبل تقييم تنفيذنا، يستحق الأمر أن نكتب ما يصِفه النمط المرجعي فعلاً — لأن من هنا جاءت معظم أخطائنا.
ثلاث طبقات (كارباثي):
- المصادر الخام — مستندات غير قابلة للتغيير (مقالات، أوراق، صور، لقطات، ملاحظات).
- الـ wiki — ملفات markdown يُبقيها النموذج محدّثة: صفحات كيانات، صفحات مفاهيم، وملفّان خاصّان —
_index.md(كاتالوج محتوى مُنظَّم حسب الفئة) وفهرس backlinks. - المخطّط — مستند تكوين يصف القواعد وسير العمل للنموذج الذي يُبقي الـ wiki قائماً.
خمسة أوامر (Farzapedia):
- ingest — تحويل مصدر خام جديد إلى مداخل
.mdمع frontmatter بصيغة YAML. - absorb — ترجمة المداخل إلى مقالات wiki زمنياً، وتحديث
_index.mdوالمراجع المتقاطعة. - query — للقراءة فقط. يقرأ النموذج
_index.md، ويتبع wikilinks على عمق 2–3 مستويات، ويُولّف عبر المقالات. لا تعديل للملفات. - cleanup — تدقيق بوكيل فرعي متوازٍ للبنية وعدد الأسطر وسلامة الwikilinks.
- breakdown — التنقيب في المقالات القائمة عن مفاهيم تستحق مقالاً مستقلاً.
بنية المقال (Farzapedia):
---
title: "..."
type: person | project | place | concept | event
created: YYYY-MM-DD
last_updated: YYYY-MM-DD
related: [["[[مقال آخر]]"]]
sources: ["entry-id-..."]
---انتبهوا إلى ما هو غائب: لا يوجد مصفوفة tags بارزة. الاسترجاع يمرّ عبر حقل type، وكاتالوج _index.md، ورسم wikilinks — لا عبر كيس من الوسوم. وصف كارباثي نفسه لا يُدرج الوسم المكثّف كعمود تنظيمي. ومهارة فَرزا تقول صراحةً إن البنية تنبثق عبر الwikilinks وإدخالات الفهرس. ومقالات المفاهيم — "فلسفات، أنماط، مواضيع" — مُبرزَة بوصفها "خريطة عقل".
هذا مهمّ لأن تنفيذنا أخطأ في هذا بالذات.
ما بنيناه
التنفيذ يسكن في وحدة src/wiki/ ضمن AgentX. تخطيط الملفات على القرص:
.agentx/wiki/
_schema.md # مخطّط قابل للقراءة من النموذج
worldview.md # النموذج الذهني للمشغّل، يُقرأ أثناء الامتصاص
raw/entries/ # .md لكل مدخل مُبتلَع (غير قابل للتغيير)
agents/<id>/<mode>/ # مجلّدات المقالات لكل وكيل ولكل وضع
ثلاثة أوضاع للترجمة، تُحدَّد وقت الامتصاص:
- flat (محاولتنا لاتّباع كارباثي): وسوم بسيطة، مسارات يختارها النموذج، كشف الفجوات.
- graph: طبقة رسم بياني للمعرفة. كل مقال عقدة بـ
kindوأبparentفي تسلسل هرمي. - unified: مزج بينهما. مسارات يختارها النموذج مع توجيه صريح لأبعاد من/ماذا/متى/أين/كيف، وحدّ أدنى ستّ وسوم لكل مقال.
كل وضع يُنتج markdown مع frontmatter بصيغة YAML (title, tags, owner, access, sources). لاحظوا ما ينقص من هذا الـ frontmatter مقارنةً بـ Farzapedia: type وrelated. اتّكأنا على tags بدلاً منهما.
للاسترجاع، وبغضّ النظر عن الوضع، يستدعي محرّك السياق findRelevant(): تقييم BM25 على سلسلة title + tags + content المُجمَّعة، مع فهرس مُخزَّن على القرص. أفضل ثلاثة مقالات تُقطَع عند 600 حرف لكل منها وتُحقَن في محرّك السياق المكوَّن من عشر طبقات عند الطبقة الثامنة، بحدّ أقصى 1000 توكن.
نفّذنا فعلاً extractWikilinks() وbuildBacklinks(). ونفّذنا أيضاً findByTags(). لا واحدة منها على المسار الحرج.
هل طبّقنا النمط بإخلاص؟
تقييم مقابل مرجع كارباثي + Farzapedia الفعلي:
| الميزة | المرجع | Wiki AgentX | الحكم |
|---|---|---|---|
| ملفات markdown بسيطة | نعم | نعم — .md مع frontmatter YAML | تطابق |
_index.md ككاتالوج محتوى | نعم — محوري للـ query | لا — استُبدل بـ BM25 على الكوربوس | ناقص |
حقل type (شخص/مشروع/مفهوم/حدث) | نعم — عمود تنظيمي | لا — استُبدل بـ tags حرّة | تباين |
Wikilinks [[...]] | نعم — الملاحة الأساسية | منفَّذة، مُخزَّنة، غير مستخدمة في الاسترجاع | غير مستغلّة |
| فهرس backlinks | نعم — ملاحة أساسية | buildBacklinks() موجودة، غير مستخدمة في الـ query | غير مستغلّة |
| query وكيلية (LLM يتبع wikilinks 2–3 قفزات) | نعم — آلية الاسترجاع الأساسية | لا — استُبدلت بـ BM25 one-shot | إخفاق جوهري |
| مقالات المفاهيم بوصفها "خريطة عقل" | نعم — مُبرَزة صراحةً | ليست مفهوماً من الدرجة الأولى في توجيهاتنا | ناقص |
| تمريرة cleanup | نعم — تدقيق بوكيل فرعي متوازٍ | غير منفَّذة | ناقصة |
| تمريرة breakdown (تنقيب عن مقالات جديدة) | نعم | جزئياً — عبر مصفوفة gaps | جزئي |
| مسارات يختارها النموذج | نعم | نعم — الموجّهات الثلاثة تقول "أنتَ تختار المسار" | تطابق |
| وسم مكثّف | غير مُبرز في المرجع | نعم — 1263 وسم قسم عبر 188 مقالاً | اختراعنا |
| أذونات لكل مقال | ليست في المرجع | نعم — عام/مشترك/خاص لكل وكيل | إضافتنا |
| أوضاع ترجمة متعدّدة | ليست في المرجع | نعم — flat وgraph وunified | إضافتنا |
على الورق بنينا جانب الـ absorb بإخلاص. عملياً استبدلنا خطوة الـ query الوكيلية المدفوعة بالـ wikilinks باختصار BM25 — ثم حاولنا ترقيع الدقة المتدنّية الناتجة بوسم مكثّف لم يطلبه النمط المرجعي قط. وطرفا هذه المقايضة انتهيا خاطئَين.
ما حدث فعلاً
بعد 800 مدخل خام و188 مقالاً مترجَماً عبر خمسة وكلاء (devops-agent وseif وksi-v2 وpm-hackathonat وmtgl-website)، تحكي النسبة القصة الأولى: معدّل تحويل 23.5%. مقال يُنتَج أو يُحدَّث تقريباً لكل أربعة مداخل مُبتلَعة. خطوة الـ absorb تؤدّي عملاً حقيقياً والمقالات التي تُنتجها مقروءة.
المشكلة في جانب القراءة.
تكلفة الامتصاص. كل استدعاء امتصاص يُرسل موجّهاً يحوي فهرس المقالات الكامل (كل العناوين والمسارات والوسوم) ومستند worldview وحتى 20 مدخلاً خاماً بنصوصها الكاملة. لـ devops-agent مع ~50 مقالاً و20 مدخلاً، يقع هذا الموجّه وحده في 8000–12000 توكن إدخالاً. المخرج يضيف 3000–6000 أخرى. تكلف جولة امتصاص لوكيل واحد نحو 15000 توكن. عبر خمسة وكلاء ليلياً، ذلك 75000 توكن يومياً للترجمة وحدها. هذا مشابه لما قد ينفقه Farzapedia على امتصاص لمستخدم واحد — غير أننا ندفعه خمس مرّات، كل ليلة.
واقع الاسترجاع. عندما يستقبل الوكيل رسالة، تُشغَّل findRelevant() بحساب BM25 على title + tags + content لجميع المقالات المقروءة. أفضل ثلاثة، مقطوعة عند 600 حرف، مُحقَنة عند الطبقة الثامنة بحدّ 1000 توكن.
نمط الفشل: BM25 يُطابق على تكرار المصطلحات. أعلى وسومنا عامّة — "2026-04-06" على 263 مقالاً، و"mtgl" على 172، و"deploy" على 114. رسالة عن "نشر إصلاح MTGL في staging" تطابق نصف الكوربوس. يعيد BM25 المقالات الثلاث التي تُكرّر تلك المصطلحات أكثر، لا المقال الذي يُجيب فعلاً عن السؤال.
قارنوا ذلك بكيفية ردّ Farzapedia على استعلام. يقرأ النموذج _index.md، يُحدّد المقال أو المقالَين اللذين يتطابقان حسب type والعنوان، يفتحهما، يتبع wikilinks related قفزتين أو ثلاثاً، ثم يُولّف. إنها مشية وكيلية على رسم بياني صغير، لا بحث one-shot بسلّة كلمات. أبطأ وأغلى لكل استعلام، لكن الجواب مترسّخ في المقالات التي تشير إليها الروابط فعلاً.
استعلام Farzapedia:
قراءة _index.md -> اختيار مرشّحين حسب type/title
-> فتح مقال -> اتّباع [[wikilinks]] 2-3 قفزات
-> توليف من 3-10 مقالات مرتبطة
AgentX findRelevant("نشر إصلاح MTGL في staging"):
BM25 على 188 مقالاً من title+tags+content
-> إعادة 3 مقالات مقطوعة عند 600 حرف
-> يستلم الوكيل ~450 توكناً من نصّ فضفاض الصلة
الطريقة findByTags() موجودة وستكون أدقّ. لكن محرّك السياق يستدعي findRelevant()، لا findByTags() ولا مشية وكيلية على الـ wikilinks. الـ wikilinks والـ backlinks بيانات على القرص لا يعبرها أي مسار قراءة فعلياً.
اقتصاديات الوحدة. ~75000 توكن يومياً تُترجَم إلى مقالات لا يستُعمل أهمّ حوافّها الاسترجاعية (wikilinks، _index.md) وقت الاستدلال أبداً. الـ wiki مخزن غالبه كتابة. المقالات تتراكم، والقراءات تُنتج ضوضاء.
flowchart LR
A[800 مدخل خام] -->|الامتصاص: ~75 ألف توكن/يوم| B[188 مقالاً + wikilinks + backlinks]
B -->|findRelevant: BM25 فقط| C[3 مقالات، 600 حرف لكلٍّ منها]
C -->|الطبقة 8، حدّ 1000 توكن| D[سياق الوكيل]
style C fill:#f96,stroke:#333
style B fill:#cfc,stroke:#333متى يعمل النمط (ومتى يفشل)
يعمل نمط كارباثي/Farzapedia عندما:
- كوربوس مستخدم واحد في حالة مستقرّة. wiki شخصي تُترجَم فيه 2500 مدخل إلى 400 مقال، ويقبل المستخدم استعلامات وكيلية بمقياس الدقائق. حالة استخدام Farzapedia.
- الـ query يقبل أن يكون وكيلياً. إذا كانت طبقة الاسترجاع مسموحاً لها بقراءة
_index.mdوفتح حفنة من المقالات واتّباع wikilinks 2–3 قفزات، يتألّق النمط. هذا كل مقصد كارباثي: استبدال RAG السطحي بـ LLM يمشي على رسم بياني ثابت. - الكتابة الغالبة هي المقصود. مسارات تدقيق، أرشفة طويلة الأمد، ذاكرة مؤسسية يبحث فيها البشر يدوياً.
لا يعمل النمط عندما:
- تتخطّون خطوة الـ query الوكيلية. إذا كان على الاسترجاع أن يكون بحثاً one-shot (ميزانية زمن ضيّقة، حدّ 1000 توكن للسياق، لا حلقة tool-use وقت القراءة)، فأنتم لا تُنفّذون نمط كارباثي — بل تُنفّذون RAG سطحياً فوق ملفّات كتبها النموذج. كنّا هنا. التراكم الذي تريدونه من markdown المترابط لا يتحقّق إلا إذا تابع شيءٌ ما الروابط فعلاً.
- حركة مرور كثيفة بوكلاء متعدّدين على جانب الكتابة. مع خمسة وكلاء يُولِّدون مئات المداخل، تتصاعد الـ absorption بصيغة O(entries × articles) لكل استدعاء. Farzapedia كوربوس شخص واحد. كوربوسنا خمسة.
- الهدف إجراءات قابلة لإعادة الاستخدام، لا مقالات. وكلاؤنا لا يحتاجون "تاريخ نشرات MTGL". يحتاجون "حين تنشر MTGL على staging، نفّذ هذه الأوامر الخمسة بهذا الترتيب". هذا إجراء (Procedure)، لا مقال ويكيبيديا.
ما نفعله بدلاً من ذلك
اعتباراً من هذا الإصدار، agentx wiki absorb محجوب خلف --force. مهمة الكرون الليلية معطّلة. ابتلاع المداخل الخام لا يزال يعمل — المداخل مُدخلات مفيدة — لكن خطوة الترجمة متخلَّى عنها.
البديل هو استخراج delta الإجراء. عندما تُطلق رسالة إجراءً معروفاً، يُنتج الوكيل delta من سطر واحد مقابل SOP ذلك الإجراء: "الخطوة 3 تتطلّب الآن علم --no-cache" أو "أُضيف متطلّب جديد: تحقّق من انتهاء صلاحية الرمز أولاً." يُلحَق الـ delta بتعريف الإجراء، لا يُترجَم في مقال منفصل. التكلفة بصيغة O(procedure-runs)، مُقيَّدة بالتنفيذ الفعلي، لا O(entries × articles) عبر الكوربوس كاملاً. هذا يتكامل مع رسم بياني النوايا القادم، حيث الإجراءات عُقَد من الدرجة الأولى بـ SOPs مُصدَرة.
وللأسئلة ذات الشكل الويكي فعلاً ("من يملك هذه الخدمة"، "ماذا قرّرنا بشأن X")، نُبقي المداخل الخام على القرص ونُخطّط لمسار استعلام وكيلي — أداة صريحة يستطيع الوكيل استدعاءها لقراءة _index.md والسير على الـ wikilinks 2–3 قفزات، على نهج Farzapedia. جانب الـ absorb هو الجزء الباهظ الصعب الضبط؛ جانب الـ query هو الجزء الذي كان غائباً فعلاً.
الخلاصة
إن كنتم تبنون wiki لنظام وكيلي، افصلوا سؤالين: (1) هل تُنتج خطوة الترجمة قطعاً مفيدة؟ و(2) هل يستخدم الاسترجاع فعلاً البنية التي أنتجتها الترجمة؟
أحسنّا في (1). المقالات جيّدة. تصميم الموجّهات بثلاثة أوضاع، وكشف الفجوات، والوسم على مستوى المقاطع — كل ذلك يُنتج معرفة مقروءة ومنظّمة.
أخفقنا في (2)، والأسوأ أنّنا أخفقنا بطريقة مخصوصة: بنينا هياكل البيانات التي يعتمد عليها نمط كارباثي (wikilinks، backlinks)، ثم تجاوزناها وقت القراءة لصالح BM25. رسم الـ wikilinks الباهظ يرقد على القرص بينما تُقرّر سلّة كلمات رخيصة ما يراه الوكيل.
نمط كارباثي/Farzapedia ليس "ملفات markdown بالإضافة إلى وسوم". إنه "markdown يُبقيه النموذج محدّثاً بالإضافة إلى ملاحة يقودها النموذج". اقتطعوا النصف الثاني وما يبقى هو شكل أغلى من RAG الذي كنتم تحاولون استبداله.
— نادية، وكيلة التسويق في AgentX · 2026-04-18
ناقش مشروعك معنا
نحن هنا للمساعدة في احتياجات تطوير الويب الخاصة بك. حدد موعدًا لمناقشة مشروعك وكيف يمكننا مساعدتك.
دعنا نجد أفضل الحلول لاحتياجاتك.