Files
ai-device/intelligent_cabin/archive/tests/test_router_decisions.py
2026-06-11 16:28:00 +08:00

196 lines
7.4 KiB
Python

from __future__ import annotations
import unittest
from app.schemas.debug import IntentCandidate, MatcherStageDebug
from app.schemas.intent import IntentDefinition
from app.services.intent_registry import IntentRegistry
from app.services.router import IntentMatchResult, MultiStageIntentMatcher
class _FakeMatcher:
def __init__(self, stage_debug: MatcherStageDebug) -> None:
self._stage_debug = stage_debug
def match(self, text: str) -> IntentMatchResult:
_ = text
return IntentMatchResult(intent=None, stage_debug=self._stage_debug)
def _intent(intent_id: str) -> IntentDefinition:
return IntentDefinition(
intent_id=intent_id,
plugin_id=f"mock.{intent_id}",
domain="test",
keywords=[],
examples=[],
)
class RouterDecisionTests(unittest.TestCase):
def setUp(self) -> None:
self.registry = IntentRegistry([_intent("alpha"), _intent("beta"), _intent("gamma")])
def test_execute_when_bert_classifier_is_clear(self) -> None:
matcher = MultiStageIntentMatcher(
registry=self.registry,
matchers=[
_FakeMatcher(
MatcherStageDebug(
stage="classifier",
accepted=True,
selected_intent="alpha",
score=0.92,
reason="classifier selected best candidate",
backend="joint-bert-local",
candidates=[
IntentCandidate(intent_id="alpha", score=0.92, reason="classifier", model_name="joint-bert-local"),
IntentCandidate(intent_id="beta", score=0.21, reason="classifier", model_name="joint-bert-local"),
],
)
),
],
)
result = matcher.match("alpha")
self.assertEqual(result.debug.decision, "execute")
self.assertEqual(result.intent.intent_id if result.intent else None, "alpha")
def test_clarify_when_bert_top_candidates_are_too_close(self) -> None:
matcher = MultiStageIntentMatcher(
registry=self.registry,
matchers=[
_FakeMatcher(
MatcherStageDebug(
stage="classifier",
accepted=True,
selected_intent="alpha",
score=0.22,
reason="classifier selected best candidate",
backend="bert-local",
metadata={"threshold": 0.2},
candidates=[
IntentCandidate(intent_id="alpha", score=0.31, reason="classifier", model_name="bert-local"),
IntentCandidate(intent_id="beta", score=0.28, reason="classifier", model_name="bert-local"),
],
)
),
],
route_to_cloud_threshold=0.2,
)
result = matcher.match("ambiguous request")
self.assertEqual(result.debug.decision, "clarify")
self.assertIsNone(result.intent)
self.assertEqual(result.debug.confidence_grade, "medium")
def test_route_to_cloud_when_bert_signal_is_weak_but_known(self) -> None:
matcher = MultiStageIntentMatcher(
registry=self.registry,
matchers=[
_FakeMatcher(
MatcherStageDebug(
stage="classifier",
accepted=False,
selected_intent="alpha",
score=0.29,
reason="classifier below execute threshold",
backend="joint-bert-local",
candidates=[
IntentCandidate(intent_id="alpha", score=0.29, reason="classifier", model_name="joint-bert-local"),
IntentCandidate(intent_id="beta", score=0.14, reason="classifier", model_name="joint-bert-local"),
],
)
),
],
)
result = matcher.match("weak symbolic request")
self.assertEqual(result.debug.decision, "route_to_cloud")
self.assertIsNone(result.intent)
def test_reject_when_no_branch_has_usable_signal(self) -> None:
matcher = MultiStageIntentMatcher(
registry=self.registry,
matchers=[
_FakeMatcher(
MatcherStageDebug(
stage="classifier",
accepted=False,
score=0.12,
reason="classifier below threshold",
backend="bert-local",
metadata={"threshold": 0.2},
candidates=[],
)
),
],
)
result = matcher.match("unknown request")
self.assertEqual(result.debug.decision, "reject")
self.assertTrue(result.debug.unknown_detected)
self.assertIsNone(result.intent)
def test_route_to_cloud_for_low_confidence_classifier_only_bert_signal(self) -> None:
matcher = MultiStageIntentMatcher(
registry=self.registry,
matchers=[
_FakeMatcher(
MatcherStageDebug(
stage="classifier",
accepted=True,
selected_intent="alpha",
score=0.31,
reason="classifier selected best candidate",
backend="bert-local",
metadata={"threshold": 0.0, "top_margin": 0.04},
candidates=[
IntentCandidate(intent_id="alpha", score=0.31, reason="classifier", model_name="bert-local"),
IntentCandidate(intent_id="beta", score=0.27, reason="classifier", model_name="bert-local"),
],
)
),
],
)
result = matcher.match("bert only weak request")
self.assertEqual(result.debug.decision, "route_to_cloud")
self.assertIsNone(result.intent)
def test_execute_for_high_confidence_classifier_only_bert_signal(self) -> None:
matcher = MultiStageIntentMatcher(
registry=self.registry,
matchers=[
_FakeMatcher(
MatcherStageDebug(
stage="classifier",
accepted=True,
selected_intent="alpha",
score=0.92,
reason="classifier selected best candidate",
backend="bert-local",
metadata={"threshold": 0.0, "top_margin": 0.63},
candidates=[
IntentCandidate(intent_id="alpha", score=0.92, reason="classifier", model_name="bert-local"),
IntentCandidate(intent_id="beta", score=0.29, reason="classifier", model_name="bert-local"),
],
)
),
],
)
result = matcher.match("bert only strong request")
self.assertEqual(result.debug.decision, "execute")
self.assertEqual(result.intent.intent_id if result.intent else None, "alpha")
if __name__ == "__main__":
unittest.main()