feat(plagiarism): показывать тематически близкие источники, а не только нарушения
All checks were successful
Deploy / test (push) Successful in 4m15s
Deploy / deploy (push) Successful in 37s

Кандидаты уровня 3 (FAISS), которые прошли порог семантической схожести,
но LLM не подтвердила заимствование, раньше молча отбрасывались. Теперь
это отдельный блок "recommendations" в отчёте — не плагиат, но источники,
полезные для раскрытия темы.
This commit is contained in:
jze9
2026-08-25 14:16:09 +05:00
parent 12eb954838
commit 53d0de3d71
6 changed files with 155 additions and 10 deletions

View File

@@ -1,4 +1,4 @@
import { AlertTriangle, CheckCircle, Info } from 'lucide-react';
import { AlertTriangle, CheckCircle, Info, Lightbulb } from 'lucide-react';
import { clsx } from 'clsx';
import type { PlagiarismResultData } from '../types';
@@ -173,6 +173,49 @@ export function PlagiarismReport({ data }: PlagiarismReportProps) {
<p className="text-base font-medium">Совпадений не обнаружено</p>
</div>
)}
{/* Рекомендации по теме — тематически близкие работы, не заимствование */}
{!!data.recommendations?.length && (
<div>
<h3 className="flex items-center gap-1.5 text-base font-semibold text-gray-900 mb-1">
<Lightbulb className="w-4 h-4 text-blue-500" />
Похожие по теме работы ({data.recommendations.length})
</h3>
<p className="text-sm text-gray-500 mb-3">
Не заимствование — семантический поиск нашёл близкие по смыслу источники.
Могут пригодиться для раскрытия темы и списка литературы.
</p>
<div className="space-y-3">
{data.recommendations.map((rec, i) => (
<div key={i} className="border border-blue-100 bg-blue-50/40 rounded-xl overflow-hidden">
<div className="flex items-center gap-3 px-4 py-2.5 border-b border-blue-100">
<span className="text-sm font-medium text-gray-700 flex-1 truncate">
{rec.source_title}
</span>
<span className="text-xs font-medium px-2 py-0.5 rounded-full bg-blue-100 text-blue-700">
{rec.similarity.toFixed(0)}% по смыслу
</span>
</div>
<div className="px-4 py-3">
<p className="text-sm text-gray-700 italic leading-relaxed line-clamp-2">
«{rec.fragment}»
</p>
{rec.source_url && (
<a
href={rec.source_url}
target="_blank"
rel="noopener noreferrer"
className="text-xs text-brand-600 hover:underline mt-1.5 inline-block"
>
Открыть источник →
</a>
)}
</div>
</div>
))}
</div>
</div>
)}
</div>
);
}

View File

@@ -62,6 +62,16 @@ export interface PlagiarismMatch {
cited?: boolean;
}
export interface PlagiarismRecommendation {
fragment: string;
position_start: number;
position_end: number;
similarity: number;
source_title: string;
source_url: string | null;
source_db: string;
}
export interface PlagiarismResultData {
overall_similarity: number;
// Доля БЕЗ похожих на корректную цитату фрагментов — «настоящий» плагиат.
@@ -77,6 +87,10 @@ export interface PlagiarismResultData {
fuzzy: number;
semantic_llm: number;
};
// Тематически близкие работы, которые LLM не подтвердила как заимствование —
// не плагиат, но кандидаты для раскрытия/развития темы. Опционально: старые
// задачи в БД посчитаны без этого поля.
recommendations?: PlagiarismRecommendation[];
}
// ─── Библиография ─────────────────────────────────────────────────────────────