from __future__ import annotations import unittest from app.schemas.intent import IntentDefinition from app.services.classifier import JointBertIntentClassifier from app.services.planner import HeuristicWorkflowPlanner from app.services.router import JointBertSlotExtractor class FakeJointNLU: def __init__(self) -> None: self._predictions = { "把空调调到22度": { "intent_id": "cabin_set_ac", "intent_score": 0.93, "candidates": [("cabin_set_ac", 0.93), ("cabin_ac_on", 0.04)], "slots": {"temperature": 22}, }, "导航去公司,然后把空调调到22度": { "intent_id": "cabin_nav_to", "intent_score": 0.88, "candidates": [("cabin_nav_to", 0.88), ("cabin_set_ac", 0.72)], "slots": {"destination": "公司"}, }, } self._slot_predictions = { ("把空调调到22度", "cabin_set_ac"): {"temperature": 22}, ("导航去公司", "cabin_nav_to"): {"destination": "公司"}, ("把空调调到22度", "cabin_set_ac"): {"temperature": 22}, } def warmup(self, sample_text: str = "打开车窗") -> bool: _ = sample_text return True def predict(self, text: str, intents: list[IntentDefinition]): from app.services.joint_nlu import JointCandidate, JointNluResult raw = self._predictions[text] candidates = [JointCandidate(intent_id=intent_id, score=score) for intent_id, score in raw["candidates"]] return JointNluResult( intent_id=raw["intent_id"], intent_score=raw["intent_score"], candidates=candidates, slots=dict(raw["slots"]), ) def extract_slots(self, text: str, intent: IntentDefinition): return dict(self._slot_predictions.get((text, intent.intent_id), {})) def extract_slots_by_intent_id(self, text: str, intent_id: str, required_slots=None): _ = required_slots return dict(self._slot_predictions.get((text, intent_id), {})) class JointNLUIntegrationTests(unittest.TestCase): def setUp(self) -> None: self.intents = [ IntentDefinition(intent_id="cabin_set_ac", plugin_id="x", domain="cabin", required_slots=["temperature"]), IntentDefinition(intent_id="cabin_nav_to", plugin_id="x", domain="cabin", required_slots=["destination"]), IntentDefinition(intent_id="cabin_ac_on", plugin_id="x", domain="cabin"), ] self.fake_nlu = FakeJointNLU() def test_joint_classifier_uses_joint_nlu_intent_head(self) -> None: classifier = JointBertIntentClassifier(self.fake_nlu, threshold=0.3, top_k=2) result = classifier.predict("把空调调到22度", self.intents) self.assertIsNotNone(result.intent) self.assertEqual(result.intent.intent_id, "cabin_set_ac") self.assertEqual(result.raw_candidates[0]["intent_id"], "cabin_set_ac") def test_joint_slot_extractor_uses_joint_nlu_slots(self) -> None: extractor = JointBertSlotExtractor(self.fake_nlu) slots = extractor.extract("把空调调到22度", self.intents[0]) self.assertEqual(slots, {"temperature": 22}) def test_planner_prefers_joint_nlu_slots_for_each_clause(self) -> None: planner = HeuristicWorkflowPlanner(joint_nlu=self.fake_nlu) result = planner.plan("导航去公司,然后把空调调到22度", self.intents) self.assertTrue(result.accepted) self.assertEqual(result.steps[0].slots, {"destination": "公司"}) self.assertEqual(result.steps[1].slots, {"temperature": 22}) if __name__ == "__main__": unittest.main()