#!/usr/bin/env python3 """МАТЕРИАЛ ПАКА: 16 НОВЫХ единиц той же книги. $0 — глава уже скачана эксп-22. Зачем новые единицы. Контраст `R2 glm-5` против боевого редактора дал −2.12 при шуме одного вызова sd≈3.5: это вопрос МОЩНОСТИ, а не судьи. Пред-объявленный порог эксп-22 (2.03) он проходит, собственный порог своего прохода (2.47) — нет, и вердикт зависит от того, какой взять. Удвоение n с 16 до 32 либо разводит интервал от порога, либо честно говорит «эффект меньше, чем стоит различать». Оба ответа годны; зависимость вердикта от выбора порога — нет. Методика отбора та же, что в эксп-21 §0 и эксп-22: абзацные чанки, дедуп по подписи без заголовка главы, ключ = хеш источника, отбор из СЕРЕДИНЫ распределения длин. Отличие ровно одно: главы, из которых эксп-22 уже брал единицы, исключаются целиком — иначе «новые» единицы были бы соседними абзацами тех же глав, и прирост n оказался бы мнимым (соседние куски одной главы делят персонажей, сцену и лексику, то есть коррелируют сильнее случайных). Гейт прав эксп-22 (снятые главы верхнего дециля эротических маркеров) продолжает действовать: он применяется в `chapters()`, который здесь переиспользуется как есть. """ from __future__ import annotations import sys from pathlib import Path REPO = Path("/home/ubuntu/projects/textmachine") ZONE = Path(__file__).resolve().parent sys.path.insert(0, str(REPO / "eval" / "tenant_panel")) import material as M22 # noqa: E402 N_NEW = 16 def used_chapters() -> set[int]: """Главы, из которых эксп-22 уже взял единицы.""" return {u["chapter"] for u in M22.units_zh()} def used_uids() -> set[str]: return {u["uid"] for u in M22.units_zh()} def rights_dropped(pool: set[int]) -> set[int]: """Гейт прав по СВОЕМУ пулу: верхний дециль по плотности эротических маркеров. ⚠ Число снятых глав НЕ переносится из эксп-22. Там гейт снял две главы — это был верхний дециль пула из 18. Пул этого пака другой, и перенос константы «2» пропустил бы в замер главы, которые объявленное правило снимает. Правило одно и то же, число считается заново. """ allch = {c["idx"]: c for c in M22.chapters(all_=True)} ranked = sorted(pool, key=lambda i: (-M22.se_density(allch[i]["text"]), i)) k = max(1, round(len(pool) * 0.10)) return set(ranked[:k]) def units_new(n: int = N_NEW) -> list[dict]: """N новых единиц из глав, которых эксп-22 не касался и которые прошли гейт прав.""" allidx = {c["idx"] for c in M22.chapters(all_=True)} free = allidx - used_chapters() - M22.se_dropped(M22.chapters(all_=True)) dropped = rights_dropped(free) busy = used_chapters() | dropped | M22.se_dropped(M22.chapters(all_=True)) globals()["RIGHTS_DROPPED"] = sorted(dropped) uniq: dict[str, dict] = {} seen: set[str] = set() for c in M22.chunks(): if c["chapter"] in busy: continue sig = M22._dedup_sig(c["text"]) # noqa: SLF001 if sig in seen: continue seen.add(sig) uid = M22.uid_of(c["text"]) uniq.setdefault(uid, dict(source=c["text"], uid=uid, chapter=c["chapter"])) keys = sorted(uniq, key=lambda k: (len(uniq[k]["source"]), k)) if len(keys) < n: raise SystemExit(f"свободных единиц {len(keys)}, нужно {n} — материал не набирается") lo = (len(keys) - n) // 2 picked = [uniq[k] for k in keys[lo:lo + n]] # одна глава — не более одной единицы: иначе внутри «новых» появится та же корреляция, # ради снятия которой исключались главы эксп-22 out, taken = [], set() for u in picked: if u["chapter"] in taken: continue taken.add(u["chapter"]) out.append(u) if len(out) < n: # добираем по тому же порядку, соблюдая правило главы for k in keys[lo + n:] + keys[:lo][::-1]: u = uniq[k] if u["chapter"] in taken: continue taken.add(u["chapter"]) out.append(u) if len(out) == n: break return out[:n] def selfcheck() -> int: bad = 0 us = units_new() if len(us) != N_NEW: print(f"[ПРОВАЛ] единиц {len(us)}, нужно {N_NEW}") bad += 1 if set(u["uid"] for u in us) & used_uids(): print("[ПРОВАЛ] единица повторяет эксп-22") bad += 1 if set(u["chapter"] for u in us) & used_chapters(): print("[ПРОВАЛ] глава повторяет эксп-22") bad += 1 if len({u["chapter"] for u in us}) != len(us): print("[ПРОВАЛ] две единицы из одной главы") bad += 1 sim = M22._pairwise_max_sim(us) # noqa: SLF001 if sim > 0.5: print(f"[ПРОВАЛ] единицы слишком похожи: макс. близость {sim:.3f}") bad += 1 ln = sorted(len(u["source"]) for u in us) print(f"новых единиц {len(us)} из {len({u['chapter'] for u in us})} глав · " f"знаков {ln[0]}…{ln[-1]} медиана {ln[len(ln) // 2]} · макс. близость {sim:.3f}") print(f"главы: {sorted(u['chapter'] for u in us)}") print(f"снято гейтом прав из своего пула: {globals().get('RIGHTS_DROPPED')}") print(f"провалов {bad}") return bad if __name__ == "__main__": sys.exit(1 if selfcheck() else 0)