feat: harden seat intent and expand HIL coverage

This commit is contained in:
vee
2026-08-02 05:20:38 +08:00
parent 4aded5c21d
commit 55d0d400cb
62 changed files with 53112 additions and 1748 deletions
+178 -13
View File
@@ -3,6 +3,7 @@
from __future__ import annotations
import json
import os
import re
import sys
import time
@@ -22,9 +23,11 @@ if str(SEAT_SOURCE) not in sys.path:
from seat_domain import ( # noqa: E402
HardwareSeatExecutor,
SIMULATED_HARDWARE_FAULTS,
SeatCanMatrix,
SeatCompiler,
T2GSeatCompiler,
SimulatedHardwareSeatExecutor,
VirtualSeatExecutor,
build_function_config,
build_function_preset,
@@ -120,6 +123,10 @@ class SeatDemoService:
)
self.compiler = SeatCompiler(self.domain_root / "can" / "signal-mapping.yaml")
self.executor = VirtualSeatExecutor(self.matrix)
self.simulated_hardware_executor = SimulatedHardwareSeatExecutor(self.matrix)
self.simulated_hardware_enabled = os.getenv(
"COGNITIVE_AGENT_SIMULATED_HARDWARE_ENABLED", "false"
).strip().lower() in {"1", "true", "yes", "on"}
self.hardware_matrix = SeatCanMatrix(
[
self.domain_root / "can" / "seat-thermal-hardware-matrix.yaml",
@@ -131,6 +138,10 @@ class SeatDemoService:
repository_root / "platforms" / "t2g_seat_bench" / "calibration" / "seat.yaml",
)
self.hardware_settings = SeatHardwareSettings.from_env(repository_root)
# Shared by the short-lived hardware executor instances. A process must
# establish a fresh physical four-axis reference before trusting stored
# Hall counts, and a firmware replacement invalidates it again.
self._seat_position_memory_state = {"synchronized": False}
self.openai = OpenAIIntentInterpreter()
self.domain_runtimes = {
domain: ConfiguredDomainRuntime(repository_root, domain)
@@ -171,11 +182,21 @@ class SeatDemoService:
"domain": "cabin",
"platform": "reference_vehicle",
"execution_mode": "dry_run",
"execution_modes": ["dry_run", "hardware"],
"execution_modes": ["dry_run", "simulated_hardware", "hardware"],
"hardware_execution_allowed": self.hardware_settings.configured,
"hardware": self.hardware_settings.public_info(),
"seat_position_memory_synchronized": bool(
self._seat_position_memory_state.get("synchronized", False)
),
"simulated_hardware": {
"enabled": self.simulated_hardware_enabled,
"default_fault": "normal",
"fault_scenarios": list(SIMULATED_HARDWARE_FAULTS),
"physical_can_opened": False,
},
"openai_available": self.openai.available,
"openai_model": self.openai.model,
"openai_responses_endpoint": self.openai.responses_endpoint,
"conversation_memory": True,
"default_vehicle_id": default_vehicle.id,
"vehicle": default_vehicle.public_dict(),
@@ -272,6 +293,10 @@ class SeatDemoService:
auto_home=self.hardware_settings.auto_home_enabled,
home_timeout_s=self.hardware_settings.home_timeout_s,
firmware_guard=self.hardware_settings.firmware_guard(),
synchronize_position_before_first_motion=(
self.hardware_settings.position_sync_enabled
),
position_sync_state=self._seat_position_memory_state,
)
def process(
@@ -282,6 +307,7 @@ class SeatDemoService:
execution_mode: str = "dry_run",
approval_token: str | None = None,
vehicle_id: str | None = None,
simulation_fault: str = "normal",
) -> dict[str, Any]:
utterance = utterance.strip()
if not utterance:
@@ -290,8 +316,21 @@ class SeatDemoService:
raise ValueError("utterance must be at most 1000 characters")
if provider not in {"auto", "openai", "local"}:
raise ValueError("provider must be auto, openai, or local")
if execution_mode not in {"dry_run", "hardware"}:
raise ValueError("execution_mode must be dry_run or hardware")
if execution_mode not in {"dry_run", "simulated_hardware", "hardware"}:
raise ValueError(
"execution_mode must be dry_run, simulated_hardware, or hardware"
)
simulated_hardware_request = execution_mode == "simulated_hardware"
if simulated_hardware_request and not self.simulated_hardware_enabled:
raise HardwareSafetyError(
"simulated hardware is disabled; set "
"COGNITIVE_AGENT_SIMULATED_HARDWARE_ENABLED=true"
)
if simulation_fault not in SIMULATED_HARDWARE_FAULTS:
raise ValueError(
"simulation_fault must be one of "
+ ", ".join(SIMULATED_HARDWARE_FAULTS)
)
mode_save_name = self._parse_mode_profile_save_name(utterance)
mode_restore_name = self._parse_mode_profile_restore_name(utterance)
mode_update = self._parse_mode_profile_update(utterance)
@@ -329,6 +368,8 @@ class SeatDemoService:
"hardware_execution_allowed": (
hardware_request and not control_plane_request
),
"simulated_hardware": simulated_hardware_request,
"simulation_fault": simulation_fault if simulated_hardware_request else None,
"vehicle": vehicle_profile.public_dict(),
"matched_modes": [
profile.public_dict() for profile in matched_modes
@@ -483,6 +524,8 @@ class SeatDemoService:
result,
traces,
hardware_request=hardware_request,
simulated_hardware_request=simulated_hardware_request,
simulation_fault=simulation_fault,
vehicle_profile=vehicle_profile,
)
@@ -763,11 +806,19 @@ class SeatDemoService:
)
+ "板端 Position_Valid 将在发送绝对移动命令前实时检查。"
if hardware_request
else "平台、矩阵和策略均限制为 dry-run;硬件发送路径未启用。"
else (
"模拟台架隔离门禁已通过;仅生成故障注入反馈,物理 CAN、ZLG 与 IAR 路径均未打开。"
if simulated_hardware_request
else "平台、矩阵和策略均限制为 dry-run;硬件发送路径未启用。"
)
),
safety_started,
{
"hardware_execution_allowed": hardware_request,
"simulated_hardware": simulated_hardware_request,
"simulation_fault": (
simulation_fault if simulated_hardware_request else None
),
"position_calibration_gate": (
"runtime_can_preflight" if hardware_request else "not_applicable"
),
@@ -788,6 +839,13 @@ class SeatDemoService:
execution_report = hardware_executor.execute(
command_plan, function_config, context["devices"]
)
elif simulated_hardware_request:
execution_report = self.simulated_hardware_executor.execute(
command_plan,
function_config,
context["devices"],
fault_scenario=simulation_fault,
)
else:
execution_report = self.executor.execute(
command_plan, function_config, context["devices"]
@@ -845,7 +903,15 @@ class SeatDemoService:
else hardware_failure_summary
)
if hardware_request
else "虚拟 CAN 已编码命令帧并生成模拟回读。"
else (
"模拟台架已生成隔离的 HIL 反馈,未访问物理 CAN。"
if simulated_hardware_request and execution_succeeded
else (
f"模拟台架故障已触发:{failed_execution['message']}"
if simulated_hardware_request and failed_execution
else "虚拟 CAN 已编码命令帧并生成模拟回读。"
)
)
)
),
execute_started,
@@ -861,6 +927,14 @@ class SeatDemoService:
},
)
if execution_succeeded:
execution_notices = [
str(item.get("telemetry", {}).get("user_notice"))
for item in execution_results
if item.get("telemetry", {}).get("user_notice")
]
for notice in execution_notices:
if notice not in parsed["decision_summary"]:
parsed["decision_summary"].append(notice)
state_actions = (
self._hardware_feedback_actions(
parsed["actions"], function_config, execution_report
@@ -950,7 +1024,15 @@ class SeatDemoService:
else "未获得满足目标的真实硬件反馈。"
)
if hardware_request
else "虚构反馈帧中的最终状态与请求目标一致。"
else (
"模拟台架反馈与请求目标一致,物理 CAN 始终关闭。"
if simulated_hardware_request and execution_succeeded
else (
f"模拟台架故障反馈:{failed_execution['message']}"
if simulated_hardware_request and failed_execution
else "虚构反馈帧中的最终状态与请求目标一致。"
)
)
)
),
time.perf_counter(),
@@ -958,6 +1040,9 @@ class SeatDemoService:
"command_frame": telemetry.get("command_frame", {}).get("data_hex"),
"feedback_frame": telemetry.get("feedback_frame", {}).get("data_hex"),
"simulated": not hardware_request,
"simulation_fault": (
simulation_fault if simulated_hardware_request else None
),
},
)
self._persist(result)
@@ -1026,9 +1111,16 @@ class SeatDemoService:
action["position_valid"] = bool(
telemetry["position_valid"]
)
if telemetry.get("motion_mode") == "relative_timed_duty":
if telemetry.get("motion_mode") in {
"relative_timed_duty",
"timed_user_request",
}:
action["position_estimated"] = True
action["feedback_basis"] = "relative_hall_confirmed_open_loop"
action["feedback_basis"] = (
"timed_endpoint_or_hall_confirmed_open_loop"
if telemetry.get("motion_mode") == "timed_user_request"
else "relative_hall_confirmed_open_loop"
)
continue
raw = telemetry.get("feedback_value")
if raw is not None:
@@ -1141,6 +1233,35 @@ class SeatDemoService:
return parsed
if self._contains_seat_preset_request(parsed):
return parsed
if len(matched_modes) > 1:
rejected = deepcopy(parsed)
for key in (
"actions",
"domain_actions",
"preset_actions",
"preset_events",
"deferred_requests",
"model_requests",
):
rejected[key] = []
rejected["ambiguities"] = [
{
"field": "mode_profile",
"question": "一条请求只能执行一个座椅功能模式,请确认要执行的模式。",
}
]
rejected["decision_summary"] = [
"同一条请求命中多个功能模式,已停止生成控制指令以避免模式冲突。"
]
return rejected
safety_ambiguities = [
item
for item in parsed.get("ambiguities", [])
if item.get("field") != "capability"
]
if safety_ambiguities:
return parsed
mode_requests = self.mode_profiles.requests_for_profiles(matched_modes)
detected_domains = []
@@ -1969,6 +2090,8 @@ class SeatDemoService:
traces: list[dict[str, Any]],
*,
hardware_request: bool = False,
simulated_hardware_request: bool = False,
simulation_fault: str = "normal",
vehicle_profile: VehicleProfile | None = None,
) -> dict[str, Any]:
"""Run isolated adapters, with only the passenger seat eligible for HIL."""
@@ -2165,11 +2288,19 @@ class SeatDemoService:
(
"副驾座椅已通过 T2G HIL 安全门,其他座舱领域保持虚拟 CAN。"
if hardware_request
else "所有领域均为 dry-run;矩阵缺少实车完整性信息,硬件发送保持禁用。"
else (
"模拟台架仅对座椅结果注入故障;全部领域的物理发送路径保持关闭。"
if simulated_hardware_request
else "所有领域均为 dry-run;矩阵缺少实车完整性信息,硬件发送保持禁用。"
)
),
time.perf_counter(),
{
"hardware_execution_allowed": hardware_request,
"simulated_hardware": simulated_hardware_request,
"simulation_fault": (
simulation_fault if simulated_hardware_request else None
),
"hardware_domains": ["seat"] if hardware_request else [],
"virtual_domains": [
domain
@@ -2190,6 +2321,13 @@ class SeatDemoService:
reports[domain] = self._new_hardware_executor().execute(
plans[domain], config, context["devices"]
)
elif simulated_hardware_request:
reports[domain] = self.simulated_hardware_executor.execute(
plans[domain],
config,
context["devices"],
fault_scenario=simulation_fault,
)
else:
reports[domain] = self.executor.execute(
plans[domain], config, context["devices"]
@@ -2212,8 +2350,9 @@ class SeatDemoService:
if hardware_request
else None
)
simulated_seat = simulated_hardware_request and "seat" in reports
report_spec: dict[str, Any] = {
"mode": "hil" if hardware_request else "dry_run",
"mode": "hil" if hardware_request or simulated_seat else "dry_run",
"status": "succeeded" if execution_succeeded else "failed",
"started_at": execution_started_at,
"finished_at": _now(),
@@ -2229,14 +2368,27 @@ class SeatDemoService:
"name": (
"Cabin orchestration mixed HIL execution report"
if hardware_request
else "Cabin orchestration virtual execution report"
else (
"Cabin orchestration simulated hardware execution report"
if simulated_seat
else "Cabin orchestration virtual execution report"
)
),
"version": "1.0.0",
"request_id": request_id,
"labels": {
"simulation": "mixed" if hardware_request else "true",
"seat_feedback": (
"physical_can" if hardware_request else "simulated"
"physical_can"
if hardware_request
else (
"simulated_hardware"
if simulated_seat
else "simulated"
)
),
"fault_scenario": (
simulation_fault if simulated_seat else "none"
),
"orchestration": "cross_domain"
if len(configs) > 1
@@ -2272,7 +2424,20 @@ class SeatDemoService:
else (
f"副驾座椅真实执行失败:{failed_execution['message']}"
if hardware_request and failed_execution
else f"已虚拟执行 {len(report_results)} 个领域操作并汇总反馈。"
else (
f"模拟台架已执行 {len(report_results)} 个领域操作,物理 CAN 未打开。"
if simulated_seat and execution_succeeded
else (
f"模拟台架故障已触发:{failed_execution['message']}"
if simulated_seat and failed_execution
else (
"本次没有座椅动作,故障注入未应用;"
f"已虚拟执行 {len(report_results)} 个领域操作。"
if simulated_hardware_request
else f"已虚拟执行 {len(report_results)} 个领域操作并汇总反馈。"
)
)
)
)
),
execute_started,