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

@@ -139,3 +139,56 @@ def test_no_full_text_means_nothing_marked_cited():
out = aggregate_results([_m("A", 0)], [], [], total_fragments=2) # full_text не передан
assert out["matches"][0]["cited"] is False
assert out["overall_similarity"] == out["uncited_similarity"]
# ─── aggregate_results: recommendations ──────────────────────────────────────
def _cand(title: str, similarity: float, url: str | None = None) -> dict:
return {
"fragment": "фрагмент",
"position_start": 0,
"position_end": 8,
"similarity": similarity,
"source_title": title,
"source_url": url,
"source_db": "openalex",
}
def test_no_related_candidates_means_empty_recommendations():
out = aggregate_results([], [], [], total_fragments=10)
assert out["recommendations"] == []
def test_related_candidates_become_recommendations_sorted_by_similarity():
out = aggregate_results(
[], [], [], total_fragments=10,
related_candidates=[_cand("Low", 60.0), _cand("High", 90.0)],
)
titles = [r["source_title"] for r in out["recommendations"]]
assert titles == ["High", "Low"]
def test_recommendations_dedup_keeps_best_per_source():
out = aggregate_results(
[], [], [], total_fragments=10,
related_candidates=[_cand("A", 60.0, "url-a"), _cand("A", 85.0, "url-a")],
)
assert len(out["recommendations"]) == 1
assert out["recommendations"][0]["similarity"] == 85.0
def test_recommendations_capped_at_ten():
candidates = [_cand(f"S{i}", float(i), f"url-{i}") for i in range(15)]
out = aggregate_results([], [], [], total_fragments=10, related_candidates=candidates)
assert len(out["recommendations"]) == 10
def test_source_already_flagged_as_match_excluded_from_recommendations():
semantic = [{**_cand("A", 90.0, "url-a"), "method": "semantic+llm"}]
out = aggregate_results(
[], [], semantic, total_fragments=10,
related_candidates=[_cand("A", 60.0, "url-a"), _cand("B", 70.0, "url-b")],
)
titles = [r["source_title"] for r in out["recommendations"]]
assert titles == ["B"]