Признаки для расстановки ролей и оценка надёжности разделения

На записях с микрофоном на столе голоса участников для модели почти
неразличимы: перебор четырёх моделей отпечатков и смена алгоритма
кластеризации баланс улучшают, но роли всё равно скачут.

Поэтому сервис теперь отдаёт то, на что можно опереться: акустику каждой
реплики (громкость, доля высоких, центроид - они связаны с расстоянием
до микрофона) и метрику separation_quality с флагом speakers_reliable.
Ниже 0.35 разметка по говорящим случайна, и роли должна определять LLM
по смыслу реплик.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vladimir Bryzgalov
2026-08-15 23:21:56 +05:00
co-authored by Claude Opus 5
parent 4de6b14680
commit e2db164f0b
4 changed files with 140 additions and 1 deletions
+50
View File
@@ -158,3 +158,53 @@ class TestBenchmarkEndpoint:
with TestClient(main.app) as c:
assert c.post("/v1/benchmark", files={"file": ("a.wav", b"x")}).status_code == 401
class TestSeparationQuality:
"""Метрика нужна, чтобы было видно, когда разметке по голосам верить нельзя."""
def test_distinct_voices_score_high(self):
import numpy as np
from app.acoustics import separation_quality
emb = np.vstack([np.random.RandomState(0).randn(8, 64),
np.random.RandomState(1).randn(8, 64) + 6])
labels = np.array([0] * 8 + [1] * 8)
assert separation_quality(emb, labels) > 0.4
def test_indistinguishable_voices_score_low(self):
import numpy as np
from app.acoustics import separation_quality
emb = np.random.RandomState(2).randn(16, 64)
labels = np.array([0] * 8 + [1] * 8)
assert separation_quality(emb, labels) < 0.3
def test_too_few_segments_returns_zero(self):
import numpy as np
from app.acoustics import separation_quality
assert separation_quality(np.random.randn(2, 64), np.array([0, 1])) == 0.0
def test_acoustics_reflect_high_frequency_content(self):
import numpy as np
from app.acoustics import segment_acoustics
sr = 16000
t = np.arange(sr) / sr
low = np.sin(2 * np.pi * 300 * t).astype(np.float32)
high = np.sin(2 * np.pi * 5000 * t).astype(np.float32)
assert segment_acoustics(high)["hf_ratio"] > segment_acoustics(low)["hf_ratio"]
def test_acoustics_reflect_loudness(self):
import numpy as np
from app.acoustics import segment_acoustics
loud = (np.random.RandomState(0).randn(16000) * 0.3).astype(np.float32)
quiet = loud * 0.1
assert segment_acoustics(loud)["loudness_db"] > segment_acoustics(quiet)["loudness_db"]