diff --git a/services/worker-gpu/app/tasks/plagiarism.py b/services/worker-gpu/app/tasks/plagiarism.py index 7132f99..14fc74f 100644 --- a/services/worker-gpu/app/tasks/plagiarism.py +++ b/services/worker-gpu/app/tasks/plagiarism.py @@ -121,6 +121,10 @@ def check_plagiarism( if not doc_meta: continue + # user_submission — чужие непубличные загрузки (и свои же прошлые + # прогоны того же файла), не легитимный источник для сравнения. + if doc_meta["source"] == "user_submission": + continue llm_result = {"is_paraphrase": False, "confidence": 0.0, "reason": ""} if source_text: diff --git a/services/worker-indexer/app/config.py b/services/worker-indexer/app/config.py index dce4e51..09f7a88 100644 --- a/services/worker-indexer/app/config.py +++ b/services/worker-indexer/app/config.py @@ -61,10 +61,12 @@ class Settings(BaseSettings): # Автоматически добавлять проверенные работы студентов в базу для сравнения # (как в коммерческих системах — Антиплагиат.ру/Turnitin ловят списывание у - # предыдущих потоков именно так). Без этого каждая работа лежит в StagedWork - # и ждёт ручного одобрения админом — сейчас это дефолт для роста корпуса; - # выключить, если нужна модерация перед публикацией. - AUTO_APPROVE_SUBMISSIONS: bool = True + # предыдущих потоков именно так). Выключено по умолчанию: без ручной + # модерации студент, перепроверивший тот же файл дважды, получал 100% + # "плагиата" — против собственной же более ранней загрузки. L1/L2/L3 теперь + # и так исключают source=user_submission из сравнения, так что включать это + # обратно есть смысл только вместе с реальной защитой от self/cross-match. + AUTO_APPROVE_SUBMISSIONS: bool = False # App ENVIRONMENT: str = "development" diff --git a/services/worker-indexer/app/tasks/index.py b/services/worker-indexer/app/tasks/index.py index 12b5412..ba6130e 100644 --- a/services/worker-indexer/app/tasks/index.py +++ b/services/worker-indexer/app/tasks/index.py @@ -108,13 +108,20 @@ def extract_and_check( continue frag_hashes = list(frag_fp) - # Источник, разделяющий больше всего отпечатков с этим фрагментом + # Источник, разделяющий больше всего отпечатков с этим фрагментом. + # user_submission исключены: это чужие непубличные загрузки (и + # свои же прошлые прогоны того же файла) — сравнение с ними даёт + # ложные 100%-совпадения, а не реальный плагиат из источника. row = session.execute( select( Fingerprint.doc_id, func.count(Fingerprint.id).label("cnt"), ) - .where(Fingerprint.hash_value.in_(frag_hashes)) + .join(Document, Document.id == Fingerprint.doc_id) + .where( + Fingerprint.hash_value.in_(frag_hashes), + Document.source != "user_submission", + ) .group_by(Fingerprint.doc_id) .order_by(func.count(Fingerprint.id).desc()) .limit(1) @@ -164,7 +171,7 @@ def extract_and_check( try: doc_id = int(key.split(":")[-1]) doc = session.get(Document, doc_id) - if not doc: + if not doc or doc.source == "user_submission": continue level2_matches.append({