واجهة برمجة تطبيقات مهلوسة.
الكود البرمجي المولد بالذكاء الاصطناعي الذي يستدعي دوالاً أو أساليب أو معاملات أو مفاتيح إعداد أو حزم تبدو معقولة ولكنها غير موجودة في الإصدار الفعلي للمكتبة التي تعتمد عليها.
##العلامات والأعراض
يكتشف المراجع واجهة برمجة تطبيقات مهلوسة عندما يقرأ الكود بسلاسة ويبدو وكأنه استخدام نموذجي للمكتبة، ومع ذلك لا يمكن العثور على الاستدعاء أو الخيار أو الاستيراد المحدد في الواجهة الحقيقية لتلك المكتبة. العلامات المميزة:
- أسلوب أو خيار يكون اسمه مريحاً ومناسباً جداً — يقوم بالضبط بما طلبته في نص التوجيه، مع اسم يدمج بين واجهتي برمجة تطبيقات حقيقيتين (مثل
findLastWhere، وincludesAll، وparseDateSafe). - اختراع معاملات في دالة حقيقية (المتغير الخطير — غالباً ما يجمع الكود بنجاح ويفشل فقط في وقت التشغيل، أو يتجاهل الخيار بصمت).
- استيراد (
import) حزمة غير موجودة فيpackage.json/requirements.txt، أو استيراد مسمى لا تصدره الوحدة البرمجية أبداً. - أشكال واجهة برمجة تطبيقات تخلط بين الإصدارات: كاستدعاء توقيع الإصدار v2 على عميل من الإصدار v5، أو أسلوب تم حذفه أو تغيير اسمه منذ عدة إصدارات.
- تعليقات مدمجة واثقة تؤكد أن الاستدعاء صحيح (مثل "// تعيد الرسوم المستردة").
// مولد بالذكاء الاصطناعي — سلس، ومعقول، وخاطئ
import { formatRelative } from 'date-fns';
// roundingMethod مخترع هنا — لا تقبل formatRelative مثل هذا الخيار (فهو خاص بـ formatDistanceStrict)، لذا يتم تجاهله بصمت
const label = formatRelative(date, new Date(), { roundingMethod: 'floor' });
// findLastWhere غير موجود في المصفوفة (Array) — خطأ نوع في وقت التشغيل (TypeError)
const lastActive = items.findLastWhere(i => i.active);
// تابع SDK مخترع: لا يحتوي stripe-node على refunds.createForCharge — الاستدعاء الحقيقي هو stripe.refunds.create({ charge, amount })
await stripe.refunds.createForCharge(chargeId, { amount: 500 });
طريقة استدلال سريعة: إذا لم تتمكن من الإشارة إلى توثيق الأسلوب أو تعريف النوع لاستدعاء من جهة خارجية في أقل من دقيقة، فتعامل معه على أنه مهلوس حتى يثبت العكس.
##أسباب المشكلة
لماذا تنتجها النماذج؟
- معقولية الرمز التالي، وليس البحث الحقيقي. يتنبأ نموذج اللغة الكبيرة بالتكملة الأكثر احتمالاً إحصائياً، والتي تعتبر عملياً متوسط كل واجهة برمجة تطبيقات مشابهة رآها. هذا المتوسط هو غالباً أسلوب ينبغي أن يكون موجوداً — فيصدر النموذج "الاسم الأكثر ملاءمة وسهولة" بدلاً من الاسم الحقيقي. في دراسة حول ترشيح واجهات برمجة التطبيقات لـ Java 8 (JRE)، تبيّن أن 58.1% - 84.1% من واجهات برمجة التطبيقات التي رشحتها ثلاثة نماذج غير موجودة في المكتبة المحددة، وكانت معظم الأخطاء أسماء أساليب غير موجودة (arXiv 2409.15228).
- خلط الإصدارات. تخلط مجموعات بيانات التدريب بين إصدارات متعددة لمكتبة ما، وبالتالي يدمج النموذج توقيعات الإصدار v2 والإصدار v5 في توقيع واحد لا يطابق أياً منهما. تعارضات المعرفة بواجهة برمجة التطبيقات، مثل استدعاء معامل غير موجود، فئة موثقة من الهلوسات على مستوى المستودع (Zhang et al., ISSTA 2025)، ويشير الباحثون إلى أن بعض الهلوسات قد تجتاز الفحوص الساكنة وجميع حالات الاختبار.
- تاريخ انتهاء التدريب (Training-cutoff staleness). يستخدم النموذج بثقة واجهات برمجة تطبيقات تم تغيير اسمها أو إهمالها أو إزالتها منذ تاريخ انتهاء تدريبه، ويلجأ إلى ما ظهر أكثر في بيانات تدريبه — وقد يكون ذلك إصداراً من المكتبة أقدم من الإصدار المثبت لديك.
- غياب سياق المستودع/التبعية. بدون ملف
package.jsonأو المصدر الفعلي للموديول، يبتكر النموذج دوالاً مساعدة "يشعر" أنها تنتمي إلى تقنياتك البرمجية. - التملق / التلهف للإجابة. نادراً ما يقول المساعد "لست متأكداً من وجود هذا الأسلوب" — بل ينتج كوداً واثقاً يبدو قابلاً للتشغيل، مما يقلل من شكوك المراجعين.
- الحتمية تجعل الهلوسة قابلة للاستغلال. هلوسة الحزم ليست ضوضاء عشوائية: عبر 16 نموذجاً و576,000 عينة كود (2.23 مليون إشارة إلى حزم)، تبين أن 19.7% من الإشارات إلى الحزم كانت مهلوسة (205,474 اسماً فريداً غير موجود)، وتكررت 58% من الحزم المهلوسة أكثر من مرة عبر 10 إعادات تشغيل للتوجيه نفسه (Spracklen et al., USENIX Security 2025؛ ملخص SecurityWeek).
لماذا يسبب ضرراً؟
- الصحة. المعاملات المخترعة والخيارات المتجاهلة بصمت تنتج سلوكاً خاطئاً في مسارات كود نادراً ما تغطيها الاختبارات؛ ويظهر الفشل في الإنتاج الفعلي، وليس في وقت التجميع.
- الأمان / سلسلة التوريد. اسم الحزمة المهلوس هو هدف جاهز للتسجيل: ينشر المهاجمون برمجيات خبيثة تحت الاسم المتوقع، وبالتالي فإن المطور التالي الذي يقبل اقتراح الذكاء الاصطناعي سيقوم بتثبيت برمجيات خبيثة — وهو ما يُعرف بهجوم slopsquatting (مصطلح صاغه سيث لارسون من مؤسسة بايثون). يعيد متغير التقادم تقديم واجهات برمجة تطبيقات مهملة أو تحتوي على ثغرات أمنية (CVEs) بصمت.
- عبء المراجعة. يلقي الكود الذي يبدو معقولاً بعبء التحقق من كل استدعاء غير مألوف مقابل الوثائق الرسمية على عاتق المراجعين؛ والأسلوب السلس في الكتابة يقلل من احتمالية إجراء هذا التحقق.
- تراكم الديون التقنية. غالباً ما يقوم المطورون بلصق الكود المهلوس "شبه الجاهز" ويرقعون حوله بدلاً من إصلاح الاستدعاء الجذري — مما يغذي الاتجاه الأوسع في عصر الذكاء الاصطناعي المتمثل في ارتفاع النسخ/اللصق وانخفاض إعادة الهيكلة (GitClear 2025: انخفضت الأسطر المنقولة (المعاد هيكلتها) من 25% (2021) إلى أقل من 10% (2024)، وارتفعت الأسطر المنسوخة/الملصقة من 8.3% إلى 12.3%، وتضاعفت كتل التكرار ذات الخمسة أسطر فأكثر نحو 8 مرات في 2024).
##المعالجة
تكتيكات المراجعة وصياغة سير العمل
- اجعل النموذج يعتمد على الواجهة البرمجية الحقيقية. الصق ملفات تعريف الأنواع الفعلية، أو صفحة الوثائق ذات الصلة، أو كود المصدر للإصدار المثبت في سياق النموذج، أو استخدم أداة استرجاع الوثائق (من نوع Context7 أو RAG). أخبر النموذج بالإصدارات الدقيقة للتبعيات من ملف القفل الخاص بك.
- اطلب استشهادات وتوثيقاً. اطلب من النموذج تسمية الوثيقة الرسمية أو توقيع النوع لكل استدعاء لجهة خارجية يستخدمه. قاعدة ذهبية للممارسين: يجب تتبع كل استدعاء أسلوب لمكتبة خارجية والرجوع لوثائقه الرسمية قبل الموافقة على طلب السحب (PR).
- اجعله يشغل الكود في حلقة التقييم. اطلب نجاح تشغيل
tsc/mypy/pylint/ عملية بناء الكود ومجموعة الاختبارات، واطلب من الوكيل البرمجي تنفيذnpm install/pip installفعلياً حتى تفشل الحزم المهلوسة سريعاً قبل أن تصل إلى مرحلة المراجعة. - اطلب منه إعادة الاستخدام، وليس الاختراع. قدم له نتائج
grepللموديلات الحالية ووجهه لاستدعاء الدوال المساعدة المتوفرة (مثلأعد استخدام الدوال المساعدة في utils/http.ts) بدلاً من استحضار دوال جديدة — وهذا يواجه أيضاً رائحة تكرار الدوال المساعدة المعاد اختراعها. - بوابة القيود على التبعيات. استخدم ملف القفل + قائمة سماح للتثبيت وماسحاً أمنياً لسلسلة التوريد (Socket/Snyk) قبل إضافة أي حزمة جديدة، حتى لا تتسلل الأسماء المستغلة عبر هجوم slopsquatting.
إعادة هيكلة الكود
استبدل الاستدعاء المخترع بالاستدعاء الحقيقي المتحقق منه. إذا كنت تريد فعلاً التسهيل الذي تخيله النموذج، فقم بتطبيقه مرة واحدة مقابل واجهة برمجة التطبيقات الحقيقية خلف غلاف يُنشأ بتقنية استخراج الدالة بدلاً من نشر الاستدعاء الزائف في أماكن متعددة.
// قبل — تابع SDK مخترع + قيمة خيار غير صالحة
async function refundLast(chargeId: string) {
// stripe.refunds.createForCharge غير موجود، و'requested' ليست سبب استرداد صالحاً
return stripe.refunds.createForCharge(chargeId, { amount: 500, reason: 'requested' });
}
// بعد — متحقق منه مقابل وثائق/أنواع Stripe SDK المثبتة،
// وتم تغليف "التسهيل" مرة واحدة حتى لا يتكرر استدعاء واجهة برمجة التطبيقات الحقيقية
async function refundCharge(chargeId: string, amountCents: number) {
return stripe.refunds.create({
charge: chargeId,
amount: amountCents,
reason: 'requested_by_customer',
});
}
بالنسبة لمتغير التقادم، تعامل مع استدعاء مهمل مميز كمهمة ترقية حقيقية: انتقل إلى واجهة برمجة التطبيقات الحالية وثبت إصدارها، بدلاً من مجرد كتم التحذير.
الحدود القياسية. تلتقط مصححات الأنواع وأدوات الحلول المجموعة الفرعية القابلة للحل (الكائنات المحددة بالنوع، الاستيرادات غير المحلولة). أما الجزء الديناميكي/غير المحدد بالنوع — مثل الخيارات المخترعة على متغيرات من نوع any، أو مفاتيح الإعداد النصية، أو حقول بيانات REST — فليس له مكتشف تلقائي موثوق ويجب التحقق منه بواسطة مطور بشري مقابل الوثائق الحقيقية.
##أدوات الكشف
- eslint-plugin-import import/no-unresolved — استيراد غير محلول (Unresolved import)
- eslint-plugin-import import/named — تصدير مسمى غير موجود (Non-existent named export)
- Pylint no-member (E1101) — الوصول إلى عضو غير معرف (Access to undefined member)
- mypy attr-defined — الخاصية/الأسلوب غير معرف على النوع