حدود النظام
درس 9 من 10
9حدود النظام
الصورة الكبيرةالنظام الجيد يعرف متى يقول «لا أعرف». والنظام الآمن لا يسترجع أصلًا ما لا يحقّ للمستخدم رؤيته. هذان قراران مختلفان: الأول عتبة تختارها بالقياس، والثاني شرط في استعلام قاعدة البيانات، لا جملة في القالب ترجو النموذج أن يطيعها.
ماذا تشاهد في الرسم: كلما رفعت العتبة نزل الخط السفلي، فيقلّ الاختراع خارج النطاق، لكن الخط العلوي ينزل أيضًا بعد 0.55 فيرفض النظام أسئلة مشروعة. لا نقطة تجعل الخطأين صفرًا معًا، فأنت تختار أي خطأ أقل ضررًا بمستخدميك.
خارطة هذا الجزء
المفاهيم بالترتيب، اضغط أيًّا منها للقفز إليه:
طريقان إلى نفس الفكرة
موظف الاستقبال الجيد يقول «لا أعرف، اسأل القسم الفلاني» بدل أن يخترع جوابًا. النظام الذكي كذلك: إن لم يجد المصدر، يرفض بأدب. وإن كان الموظف مسموحًا له بالحديث عن قسم واحد، فهذا يُفرض في نظام الملفات الذي يفتحه، لا بتوصية شفهية.
الرفض يُبنى على عتبة: إن كانت أعلى نتيجة تشابه أقل من حد، أو لم يجد الحَكَم دعمًا في المقاطع، يرد النظام بأنه لا يعرف. والنطاق يُفرض في الاستعلام نفسه بمرشح WHERE على البيانات الوصفية (القسم، والصلاحية)، لا بتعليمات في رسالة النظام.
قبل أن تبدأ
🧲البحث المتجهي
تعرف أن لكل نتيجة درجة تشابه، وأن pgvector يسمح بشرط WHERE في نفس الاستعلام.
📐التقييم أولًا
عندك أسئلة داخل النطاق وخارجه، وهي بالضبط ما تُختار منه العتبة.
الفكرة الجوهرية
النظام الجيد يعرف متى يقول «لا أعرف»، والآمن لا يسترجع ما لا يحقّ للمستخدم رؤيته. تختار في هذا الدرس عتبة الرفض، ثم تضع الصلاحيات في الاستعلام، ثم تكتبها في pgvector، ثم تصوغ رفضًا لا يسرّب شيئًا.
1عتبة الرفض
يرفض النظام حين تكون أعلى درجة استرجاع تحت عتبة. والعتبة رقم تختاره من بياناتك: أسئلة تعرف أنها داخل النطاق وأخرى خارجه، وأعلى درجة لكل منها.
# top retrieval score for questions we labelled by hand
in_scope = [0.81, 0.77, 0.74, 0.69, 0.66, 0.72, 0.58, 0.79, 0.63, 0.71, 0.55, 0.68]
out_scope = [0.41, 0.52, 0.38, 0.47, 0.60, 0.35, 0.44, 0.50, 0.29, 0.46]
print("threshold answers-in-scope wrongly-answers-out-of-scope")
for t in [0.45, 0.50, 0.55, 0.60, 0.65]:
kept = sum(s >= t for s in in_scope)
leaked = sum(s >= t for s in out_scope)
print(f" {t:.2f} {kept:2}/{len(in_scope)} {leaked}/{len(out_scope)}")
عند 0.45 يجيب النظام عن كل الأسئلة الصحيحة، ويجيب أيضًا عن نصف الأسئلة الخارجة عن نطاقه، أي يخترع. عند 0.55 يجيب عن الكل ويخطئ في سؤال خارجي واحد. وعند 0.65 لا يخطئ خارجيًّا أبدًا لكنه يرفض ثلاثة أسئلة مشروعة. لا توجد عتبة مثالية: أنت تختار أي خطأ أقل ضررًا لمستخدميك، وتكتب السبب.
2الصلاحيات شرط في الاستعلام
اعتبر كل ما يدخل نافذة السياق مرئيًّا للمستخدم. لا جملة في القالب تمنع النموذج بثقة من ذكر ما رآه.
انظر إلى الرسم: الصلاحيات تُطبَّق قبل الترتيب، فلا يصل إلى النموذج إلا ما يحقّ للمستخدم رؤيته.
مهندس يسأل «ما راتبي ورصيد إجازتي؟». مستند سلّم الرواتب الأعلى تشابهًا، لكنه للموارد البشرية فقط.
docs = [
{"id": 1, "text": "Salary scale for grade 7", "access": {"hr"}},
{"id": 2, "text": "Annual leave is 21 working days", "access": {"all"}},
{"id": 3, "text": "Disciplinary case of employee 4471", "access": {"hr", "legal"}},
{"id": 4, "text": "Travel allowance paid in 10 days", "access": {"all"}},
]
scores = {1: 0.83, 2: 0.80, 3: 0.78, 4: 0.40} # similarity to "what is my salary and leave?"
def retrieve_prompt_only(k=2):
return [d["id"] for d in sorted(docs, key=lambda d: -scores[d["id"]])][:k]
def retrieve_scoped(user_groups, k=2):
allowed = [d for d in docs if d["access"] & (user_groups | {"all"})]
return [d["id"] for d in sorted(allowed, key=lambda d: -scores[d["id"]])][:k]
print("filter in the prompt (hope the model obeys):", retrieve_prompt_only())
print("filter in the query, for an engineer: ", retrieve_scoped({"engineering"}))
print("filter in the query, for HR: ", retrieve_scoped({"hr"}))
بالترشيح في القالب، وصل مستند الرواتب (1) إلى سياق المهندس، والآن كل شيء معلّق على أن يطيع النموذج «لا تذكر المستندات المحظورة». بالترشيح في الاستعلام، لم يرَ النموذج المستند أصلًا، وموظف الموارد البشرية يراه كما يجب. ولاحظ أن ملف القضية التأديبية (3) لم يظهر لأيٍّ منهما هنا لأنه لم يكن ضمن أعلى نتيجتين، لكنه كان سيظهر للمهندس بسؤال آخر لولا الشرط.
3الشرط في pgvector
في قاعدة حقيقية يصير الشرط سطرًا في WHERE بجانب البحث المتّجهي. ويمكن أن يحمل أكثر من الصلاحيات، مثل تاريخ انتهاء السياسة.
SELECT doc_id, body, 1 - (embedding <=> $1) AS similarity FROM chunks WHERE access && $2::text[] -- $2: the user's groups plus 'all', from the session, never from the prompt AND (valid_until IS NULL OR valid_until > now()) ORDER BY embedding <=> $1 LIMIT 5;
مجموعات المستخدم تأتي من جلسته الموثّقة في الخادم، لا من شيء كتبه في السؤال. والشرط الثاني يمنع استرجاع سياسات منتهية، وهو نوع آخر من «النطاق». اكتب اختبارًا آليًّا: مستخدم بلا صلاحية يسأل سؤالًا يطابق مستندًا محظورًا، والنتائج يجب ألا تحتويه.
4التسرّب ورسالة الرفض
التسرّب وصول معلومة من مستند محظور إلى المستخدم عبر الإجابة، حتى لو لم يُعرض المستند نفسه. ورسالة الرفض نفسها قد تسرّب إن قالت إن المستند موجود.
انظر إلى الرسم: المستند المحظور لا يُسترجع أصلًا، فتنخفض أعلى درجة، ويرى المستخدم الرفض العادي نفسه.
كيف يعمل، خطوة بخطوة: كيف يمرّ سؤال عبر حدود النظام
تأخذ مجموعات المستخدم من جلسته الموثّقة على الخادم، لا من نص سؤاله.
تسترجع بشرط WHERE على الصلاحيات وتاريخ الصلاحية قبل الترتيب، فلا يصل المحظور للنموذج أصلًا.
إن كانت أعلى درجة تحت العتبة التي اخترتها، ترفض قبل استدعاء النموذج.
رسالة الرفض واحدة للغياب ولنقص الصلاحية، تقترح أين يسأل ولا تكشف وجود مستند.
مثال محلول
متى يرفض النظام؟ حين لا يجد مستندًا قريبًا بما يكفي. لكن «قريب بما يكفي» رقم، ويُختار من بياناتك: خذ أسئلة تعرف أنها داخل النطاق وأخرى خارجه، وانظر إلى أعلى درجة تشابه لكل منها.
جرّب بلا كود
خرافة مقابل حقيقة
| الخرافة | الحقيقة |
|---|---|
| جملة «لا تكشف الرواتب» في القالب تحمي البيانات. | هي طلب لا ضمان. ما يدخل السياق قد يخرج في الإجابة. الحماية الوحيدة المضمونة ألا يُسترجع المستند لهذا المستخدم. |
| هناك عتبة رفض تصلح لكل نظام. | العتبة تُختار من بياناتك، ومن نوع الخطأ الأخطر عليك: في الأسئلة الطبية الرفض أفضل من إجابة واثقة خاطئة. |
| رسالة «لا تملك صلاحية» رسالة رفض صادقة ومفيدة. | هي نفسها تسرّب: أخبرت المستخدم أن المستند موجود. الرفض للغياب ولنقص الصلاحية يجب أن يبدوا متطابقين. |
جرّب بنفسك
عتبة 0.45 تحضر عن كل الأسئلة الصحيحة ونصف الخارجية. عتبة 0.65 لا تخطئ خارجيًّا وترفض 3 من 12 صحيحة. أيّهما تختار لنظام يجيب عن أسئلة طبية؟
العتبة قرار عن أي خطأ أقل ضررًا. في المجالات الحسّاسة، رفض سؤال مشروع مع توجيه المستخدم لمختص أهون بكثير من إجابة خاطئة بثقة.
من أين تأتي مجموعات صلاحية المستخدم في استعلام الاسترجاع؟
أي شيء يكتبه المستخدم أو يقرّره النموذج قابل للتلاعب. الصلاحيات تأتي من نفس مصدر الحقيقة الذي يستخدمه باقي تطبيقك.
بطاقات مراجعة سريعة
WHERE)، فلا يصل للنموذج إلا ما يحقّ للمستخدم رؤيته. لا تُطبَّق بجملة في القالب.تحقق من فهمك
لماذا لا تكفي جملة «لا تكشف معلومات الرواتب لغير الموارد البشرية» في رسالة النظام؟
لأنها طلب لا ضمان. النموذج قد يُخدع بصياغة ذكية، أو يلخّص السياق كاملًا عند الطلب، أو يذكر رقمًا ضمن إجابة عن شيء آخر. وإن تغيّر النموذج تغيّر مدى طاعته. الشرط في الاستعلام يعمل بنفس الطريقة مع أي نموذج، ويُختبر آليًّا.
لماذا أضاف استعلام pgvector شرط valid_until بجانب الصلاحيات؟
حتى لا تُسترجع سياسات منتهية الصلاحية. هذا نوع آخر من النطاق: المستند موجود ومسموح، لكنه لم يعد صحيحًا، فلا يجب أن يصل للنموذج.
مصادر هذا الدرس
مرتّبة بالنوع. ابدأ بالتوثيق الرسمي إذا تبي المرجع الدقيق، وبالمقال أو الفيديو إذا تبي الشرح أولًا.
خلّصت الدرس؟
سجّل الدخول ليُحفظ تقدّمك.