Discourse AI: طلب Gemini المحجوب عبر promptFeedback.blockReason يُعامل كإجابة فارغة ولا يُترجم أبدًا

الوصف

عندما ترفض واجهة برمجة تطبيقات Gemini من Google طلبًا عبر promptFeedback.blockReason، يعالج Discourse AI الاستجابة على أنها رد ناجح لكنه فارغ. في الترجمة بالذكاء الاصطناعي، لهذا الأمر تأثيران: أولًا، لا يحصل الموضوع أو المنشور أبدًا على لغة مكتشفة، وبالتالي لا يتم ترجمته أبدًا، وثانيًا، يرسل ملء البيانات (backfill) نفس الطلب المحظور مرة أخرى في التشغيلات اللاحقة. لا يوجد أي مؤشر للإداري على أن حظرًا قد حدث.

تم رصد المشكلة على موقع مضاف ذاتيًا يعمل بإصدار release/2026.7 (2026.7.3، الترتيب f1caa6321287f918644fba9ff8943579fdcf027a) مع إضافة discourse-ai المدمجة. كان الطلب يتعلق باكتشاف لغة عنوان موضوع، باستخدام وكيل مكتشف اللغة المدمج على gemini-3.5-flash-lite. كان العنوان عبارة قصيرة وبنّاءة باللغة السويدية. تم ترجمة نفس العنوان بواسطة gemini-3.8-flash دون أي حظر.

أجابت Google بترميز HTTP 200 وحالة تدفق واحدة بدون مرشحين (candidates):

data: {"promptFeedback": {"blockReason": "PROHIBITED_CONTENT"},"usageMetadata": {"promptTokenCount": 417,"totalTokenCount": 417,"promptTokensDetails": [{"modality": "TEXT","tokenCount": 417}],"serviceTier": "standard"},"modelVersion": "gemini-3.5-flash-lite","responseId": "..."}

يحمل الطلب بالفعل BLOCK_NONE للفئات الأربع للسلامة التي يضبطها Discourse AI، لذا فإن هذا الحظر ليس من النوع الذي تؤثر فيه تلك الإعدادات. يسجّل سجل التدقيق (audit log) response_status بقيمة 200.

السبب الجذري

  1. DiscourseAi::Completions::Endpoints::Gemini#decode_chunk يقرأ فقط candidates من كل حالة تدفق. لا يتم فحص promptFeedback أبدًا، لذا ينتج عن الطلب المحظور نتيجة فارغة بدون خطأ.
  2. DiscourseAi::Translation::LanguageDetector#detect يعيد nil عندما لا يطابق الرد نمط وسم اللغة، وهو ما يحدث في حالة الرد الفارغ. يحتفظ الموضوع أو المنشور بـ locale فارغًا (NULL).
  3. Jobs::TopicsLocaleDetectionBackfill يختار المواضيع التي تكون locale فيها فارغة (NULL) في كل تشغيل ولا يوجد حد لمحاولات الكشف، لذا فبمطابقة الكود، يتم تكرار الطلب المحظور كل خمس دقائق طالما أن الموضوع ضمن نافذة ملء البيانات. المنشورات محدودة بمحاولتي كشف في اليوم الواحد بسبب حصة إعادة التوطين (relocalize quota)، وتُعاد تجربتها يوميًا.
  4. مرشحو الترجمة يتطلبون لغة مكتشفة، لذا لا يتم ترجمة العنصر أبدًا.

الكود نفسه موجود على الفرع main حتى تاريخ 2026-10-03.

الإصلاح المقترح

  1. اكتشاف promptFeedback.blockReason في نقطة نهاية Gemini ورفع أو تسجيل خطأ واضح ومميز يحدد السبب.
  2. تسجيل الفشل ضد الموضوع أو المنشور حتى يتوقف ملء البيانات عن تكرار طلب سيُحظر مرة أخرى، أو تطبيق حد للمحاولات على كشف المواضيع كما هو موجود بالفعل للمنشورات.
  3. عرض العناصر المحظورة في صفحة إدارة الترجمة، حتى يتمكن الإداري من ضبط اللغة يدويًا.

إعادة إنتاج المشكلة

يعتمد الحظر على مرشح Google ولا يمكن إجباره عبر إدخال معروف. يمكن العثور على المكالمات المتأثرة في سجل التدقيق:

SELECT id, created_at, language_model, post_id, topic_id, response_status
FROM ai_api_audit_logs
WHERE feature_name = 'translation'
  AND raw_response_payload LIKE '%blockReason%'
ORDER BY created_at

حل مؤقت

يُتيح ضبط لغة الموضوع يدويًا متابعة الترجمة. على هذا الموقع، لم يقم نموذج الترجمة بحظر النص نفسه.