feat: 完善 N7 智能座舱交互与控制链路

This commit is contained in:
vee
2026-09-07 15:34:59 +08:00
parent 32d179143f
commit c96970f068
100 changed files with 30088 additions and 1174 deletions
+256 -4
View File
@@ -5,6 +5,7 @@ import unittest
from pathlib import Path
from unittest.mock import MagicMock, patch
from cognitive_agent.application.codex_assistant import CodexAssistantService
from cognitive_agent.application.domain_runtime import ConfiguredDomainRuntime
from cognitive_agent.application.mode_profile import ModeProfileRegistry
from cognitive_agent.application.seat_service import REPOSITORY_ROOT, SeatDemoService
@@ -791,6 +792,12 @@ operations:
},
},
)
saved_public_mode = next(
mode
for mode in self.service.mode_profiles.public_modes("denza_n7")
if mode["id"] == "nap"
)
self.assertEqual(saved_public_mode["behavior"], "cyclic")
with patch.object(
self.service,
@@ -824,7 +831,7 @@ operations:
}
self.assertIn("roof.set_shade_motion", replayed_capabilities)
def test_build_named_mode_executes_and_persists_in_one_turn(self) -> None:
def test_build_named_mode_returns_draft_before_explicit_save(self) -> None:
mode_root = Path(self.temporary_directory.name) / "modes"
self.service.mode_profiles = ModeProfileRegistry(mode_root)
model_output = {
@@ -864,9 +871,41 @@ operations:
)
self.assertEqual(built["status"], "succeeded")
self.assertEqual(built["mode_profile"]["name"], "K歌模式")
self.assertIsNone(built["mode_profile"])
self.assertEqual(
built["orchestration_route"], "mode_profile_create_preview"
)
draft = built["mode_profile_draft"]
self.assertEqual(draft["name"], "K歌模式")
self.assertEqual(draft["operation_count"], 2)
self.assertEqual(set(draft["domains"]), {"ambient_light", "climate"})
self.assertEqual(built["mode_creation"]["source_status"], "succeeded")
self.assertEqual(built["mode_creation"]["operation_count"], 2)
self.assertNotIn(
"K歌模式",
{
mode["name"]
for mode in self.service.runtime_info()["modes"]
},
)
self.assertEqual(
self.service.mode_profiles.match_text(
"进入K歌模式", vehicle_id="denza_n7"
),
[],
)
saved = self.service.create_mode_profile_card(
draft["name"],
draft["instruction"],
draft["source_request_id"],
draft["vehicle_id"],
)
self.assertEqual(saved["status"], "succeeded")
self.assertEqual(saved["mode_profile"]["name"], "K歌模式")
self.assertEqual(saved["mode_profile"]["operation_count"], 2)
self.assertTrue(saved["mode_profile"]["command_flow"]["plans"])
self.assertIn(
"K歌模式",
{
@@ -878,7 +917,7 @@ operations:
"进入K歌模式", vehicle_id="denza_n7"
)
self.assertEqual([profile.name for profile in matched], ["K歌模式"])
self.assertTrue(Path(built["mode_profile"]["source"]).is_file())
self.assertTrue(Path(matched[0].source).is_file())
def test_build_named_mode_is_always_a_dry_run_configuration_workflow(self) -> None:
mode_root = Path(self.temporary_directory.name) / "hardware-modes"
@@ -916,7 +955,208 @@ operations:
self.assertEqual(built["status"], "succeeded")
self.assertEqual(built["execution_mode"], "dry_run")
self.assertFalse(built["hardware_execution_allowed"])
self.assertEqual(built["mode_profile"]["name"], "K歌模式")
self.assertIsNone(built["mode_profile"])
self.assertEqual(built["mode_profile_draft"]["name"], "K歌模式")
self.assertEqual(
built["orchestration_route"], "mode_profile_create_preview"
)
self.assertNotIn(
"K歌模式",
{
mode["name"]
for mode in self.service.runtime_info()["modes"]
},
)
def test_build_named_mode_uses_the_user_supplied_scene_requirements(self) -> None:
mode_root = Path(self.temporary_directory.name) / "described-modes"
self.service.mode_profiles = ModeProfileRegistry(mode_root)
model_output = {
"detected_domains": ["climate"],
"requests": [
{
"domain": "climate",
"capability": "climate.set_temperature",
"target_scope": "vehicle",
"operation": "set",
"parameters": [{"name": "temperature_c", "value": 24}],
"reason": "保持助眠温度。",
}
],
"ambiguities": [],
"decision_summary": ["已按用户描述构建助眠场景。"],
"confidence": 0.95,
}
with patch.object(
self.service.openai,
"interpret",
return_value=model_output,
) as interpret:
built = self.service.process(
"创建宝宝睡眠模式:营造安静的助眠氛围",
"openai",
"conv-build-described-mode",
vehicle_id="denza_n7",
)
self.assertEqual(built["status"], "succeeded")
self.assertIsNone(built["mode_profile"])
self.assertEqual(built["mode_profile_draft"]["name"], "宝宝睡眠模式")
self.assertEqual(
built["mode_profile_draft"]["instruction"], "营造安静的助眠氛围"
)
self.assertEqual(
built["orchestration_route"], "mode_profile_create_preview"
)
self.assertEqual(interpret.call_args.args[0], "营造安静的助眠氛围")
def test_movie_mode_preview_and_confirmed_save_keep_all_nine_actions(
self,
) -> None:
user_root = Path(self.temporary_directory.name) / "movie-mode-user"
self.service.mode_profiles = ModeProfileRegistry(
REPOSITORY_ROOT / "config" / "modes",
user_root=user_root,
)
utterance = (
"构建一个观影模式,遮阳帘关闭,车窗关闭,"
"副驾座椅调整为零重力模式,灯光全部关闭"
)
built = self.service.preview_native_can(
utterance,
provider="local",
conversation_id="conv-build-movie-mode",
vehicle_id="denza_n7",
)
self.assertEqual(built["status"], "succeeded")
self.assertEqual(
built["orchestration_route"], "mode_profile_create_preview"
)
self.assertEqual(built["unresolved_clauses"], [])
self.assertIsNone(built["mode_profile"])
draft = built["mode_profile_draft"]
self.assertEqual(draft["name"], "观影模式")
self.assertEqual(draft["operation_count"], 9)
self.assertEqual(
set(draft["domains"]),
{"seat", "roof", "window", "interior_light", "ambient_light"},
)
self.assertNotIn(
"观影模式",
{mode["name"] for mode in self.service.runtime_info()["modes"]},
)
intent_actions = built["intent_ir"]["spec"]["actions"]
capabilities = [action["capability"] for action in intent_actions]
self.assertEqual(len(capabilities), 9)
self.assertEqual(
set(capabilities),
{
"seat.set_longitudinal_position",
"seat.set_cushion_tilt",
"seat.set_backrest_recline",
"seat.set_legrest_height",
"roof.set_shade_motion",
"window.set_motion",
"interior_light.set_indoor_power",
"interior_light.set_reading_power",
"ambient_light.set_power",
},
)
window_action = next(
action
for action in intent_actions
if action["capability"] == "window.set_motion"
)
self.assertEqual(window_action["target"]["zone"], "front_left")
saved = self.service.create_mode_profile_card(
draft["name"],
draft["instruction"],
draft["source_request_id"],
draft["vehicle_id"],
)
self.assertEqual(saved["status"], "succeeded")
profile = saved["mode_profile"]
self.assertEqual(profile["name"], "观影模式")
self.assertEqual(profile["origin"], "user")
self.assertEqual(profile["operation_count"], 9)
self.assertEqual(
sum(
len(config["spec"]["operations"])
for config in profile["command_flow"]["function_configs"].values()
),
9,
)
self.assertEqual(
set(profile["command_flow"]["plans"]),
set(profile["command_flow"]["function_configs"]),
)
persisted = self.service.mode_profiles.get_profile(
profile["id"], "denza_n7"
)
self.assertTrue(Path(persisted.source).is_file())
executed = self.service.execute_mode_profile_card(
profile["id"],
conversation_id="conv-execute-movie-mode",
execution_mode="dry_run",
vehicle_id="denza_n7",
)
self.assertEqual(executed["status"], "succeeded")
self.assertEqual(executed["orchestration_route"], "saved_mode_command_flow")
self.assertEqual(len(executed["execution_report"]["spec"]["results"]), 9)
def test_movie_mode_assistant_route_uses_real_builder_without_codex(self) -> None:
user_root = Path(self.temporary_directory.name) / "assistant-movie-mode-user"
self.service.mode_profiles = ModeProfileRegistry(
REPOSITORY_ROOT / "config" / "modes",
user_root=user_root,
)
utterance = (
"构建一个观影模式,遮阳帘关闭,车窗关闭,"
"副驾座椅调整为零重力模式,灯光全部关闭"
)
codex_client = MagicMock()
codex_client.available = False
assistant = CodexAssistantService(codex_client)
events: list[dict[str, object]] = []
def execute_control(instruction: str, progress: object) -> dict[str, object]:
callback = progress if callable(progress) else None
return self.service.preview_native_can(
instruction,
provider="local",
conversation_id="conv-assistant-movie-mode",
execution_mode="hardware",
vehicle_id="denza_n7",
on_commands_validated=callback,
)
result = assistant.stream(
utterance,
conversation_id="conv-assistant-movie-mode",
principal="integration-test",
history=[],
emit=lambda event: events.append(event) or True,
execute_control=execute_control,
)
self.assertEqual(result["route"], "vehicle_control")
self.assertTrue(result["success"])
self.assertEqual(result["result"]["execution_mode"], "dry_run")
self.assertEqual(result["result"]["mode_profile_draft"]["name"], "观影模式")
self.assertEqual(result["result"]["mode_profile_draft"]["operation_count"], 9)
self.assertEqual(len(result["result"]["actions"]), 9)
self.assertEqual(events[0]["engine"], "n7web-mode-builder")
self.assertEqual(events[-1]["route"], "vehicle_control")
self.assertIn("观影模式", str(events[-1]["answer"]))
self.assertFalse(any(user_root.glob("*.yaml")))
codex_client.start_turn.assert_not_called()
def test_create_mode_language_does_not_match_ordinary_mode_execution(self) -> None:
self.assertEqual(
@@ -927,6 +1167,18 @@ operations:
self.service._parse_mode_profile_create_name("新增冥想空间模式"),
"冥想空间模式",
)
self.assertEqual(
self.service._parse_mode_profile_create_name(
"创建宝宝睡眠模式:副驾半躺、空调24度、关闭阅读灯"
),
"宝宝睡眠模式",
)
self.assertEqual(
self.service._parse_mode_profile_create_name(
"请帮我创建一个 宝宝睡眠模式:副驾半躺"
),
"宝宝睡眠模式",
)
self.assertIsNone(
self.service._parse_mode_profile_create_name("进入K歌模式")
)