Files
Cognitive_Agent/src/cognitive_agent/application/seat_service.py
T

6490 lines
262 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Application service for the stage-one seat single-domain closed loop."""
from __future__ import annotations
import json
import os
import re
import sys
import time
import uuid
from collections.abc import Callable
from concurrent.futures import ThreadPoolExecutor
from copy import deepcopy
from datetime import UTC, datetime
from pathlib import Path
from typing import Any
import yaml
REPOSITORY_ROOT = Path(__file__).resolve().parents[3]
SEAT_SOURCE = REPOSITORY_ROOT / "domains" / "seat" / "src"
if str(SEAT_SOURCE) not in sys.path:
sys.path.insert(0, str(SEAT_SOURCE))
from seat_domain import ( # noqa: E402
HardwareSeatExecutor,
SIMULATED_HARDWARE_FAULTS,
SeatCanMatrix,
SeatCompiler,
T2GSeatCompiler,
SimulatedHardwareSeatExecutor,
VirtualSeatExecutor,
build_function_config,
build_function_preset,
build_intent_ir,
parse_local_intent,
validate_semantics,
)
from cognitive_agent.application.assistant_plan_guard import ( # noqa: E402
guard_assistant_plan,
)
from cognitive_agent.application.capability_registry import CapabilityRegistry # noqa: E402
from cognitive_agent.application.conversation_state import ConversationStateStore # noqa: E402
from cognitive_agent.application.domain_runtime import ConfiguredDomainRuntime # noqa: E402
from cognitive_agent.application.mode_profile import ( # noqa: E402
ModeProfile,
ModeProfileRegistry,
)
from cognitive_agent.application.model_plan_adapter import ( # noqa: E402
ModelPlanAdapter,
APPLY_PRESET_CAPABILITY,
DYNAMIC_MODE_STOP_CAPABILITY,
SAVE_PRESET_CAPABILITY,
SEAT_CAPABILITIES,
SEAT_MOTION_CAPABILITIES,
)
from cognitive_agent.application.mqtt_ota_seat_executor import ( # noqa: E402
MqttNativeSeatExecutor,
)
from cognitive_agent.application.native_can_intent import ( # noqa: E402
DRIVER_SEAT_MASSAGE_CONTINUATION_PATTERN,
merge_native_can_intent,
)
from cognitive_agent.application.direct_can_control import ( # noqa: E402
build_direct_can_intent,
)
from cognitive_agent.application.native_can_relative import ( # noqa: E402
NativeCanRelativeResolver,
)
from cognitive_agent.application.semantic_ir import ( # noqa: E402
apply_language_safety_guards,
resolve_explicit_seat_scopes,
)
from cognitive_agent.application.native_can_feedback import ( # noqa: E402
VehicleSafetyPreflightError,
authoritative_physical_ch1_feedback_state,
decode_feedback,
observe_vehicle_safety,
require_stationary_vehicle,
)
from cognitive_agent.application.native_can_runtime import ( # noqa: E402
NativeCanExecutor,
NativeCanRuntime,
UNVERIFIED_NATIVE_CAPABILITIES,
)
from cognitive_agent.application.seat_calibration import position_pct_to_angle # noqa: E402
from cognitive_agent.application.vehicle_profile import ( # noqa: E402
VehicleProfile,
VehicleProfileRegistry,
)
from cognitive_agent.application.vehicle_state_monitor import ( # noqa: E402
OBSERVABLE_IDS,
REQUIRED_BODY_IDS,
REQUIRED_SEAT_IDS,
VehicleStateMonitor,
)
from cognitive_agent.application.zcan_seat_executor import ( # noqa: E402
ZcanBridgeSeatExecutor,
)
from cognitive_agent.infrastructure.llm.openai_responses import ( # noqa: E402
OpenAIIntentError,
OpenAIIntentInterpreter,
REASONING_EFFORTS,
)
from cognitive_agent.infrastructure.can.hardware import ( # noqa: E402
HardwareSafetyError,
PHYSICAL_BENCH_COORDINATOR,
SeatHardwareSettings,
VehicleCanSettings,
VehicleMqttSettings,
)
from cognitive_agent.infrastructure.can.gateway import ( # noqa: E402
ZlgCanBridgeProcess,
ZlgChannelConfig,
)
from cognitive_agent.infrastructure.can.mqtt_transport import ( # noqa: E402
MqttCanTransport,
)
from cognitive_agent.infrastructure.can.access import ( # noqa: E402
PHYSICAL_BENCH_ACCESS,
)
from cognitive_agent.runtime.contracts import ContractValidator # noqa: E402
def _now() -> str:
return datetime.now(UTC).isoformat().replace("+00:00", "Z")
def _clean_vehicle_number(value: float | None) -> int | float | None:
if value is None:
return None
return int(value) if float(value).is_integer() else round(float(value), 1)
CONVERSATION_ID_PATTERN = re.compile(r"^conv-[a-z0-9-]{6,80}$")
MODE_EDITOR_REQUEST_ID_PATTERN = re.compile(r"^req-[a-z0-9-]{6,100}$")
SEAT_SCOPE_PATTERN = re.compile(r"\bseats?\b|座椅|坐椅", re.IGNORECASE)
DRIVER_SEAT_SCOPE_PATTERN = re.compile(
r"\bdriver\b|\bfront[-_ ]?left\b|\bleft[-_ ]?front\b|"
r"主驾|主驾驶(?:座|座椅|席)?|驾驶位|驾驶员|驾驶席|驾驶座(?:椅)?|"
r"司机位|司机座(?:椅)?|左前",
re.IGNORECASE,
)
PASSENGER_SEAT_SCOPE_PATTERN = re.compile(
r"\bpassenger\b|\bfront[-_ ]?right\b|\bright[-_ ]?front\b|副驾|乘客|乘员|右前",
re.IGNORECASE,
)
WIRED_CAN_TRANSPORT = "wired_can"
WIRELESS_MQTT_TRANSPORT = "wireless_mqtt"
VEHICLE_CONTROL_TRANSPORTS = frozenset(
{WIRED_CAN_TRANSPORT, WIRELESS_MQTT_TRANSPORT}
)
def _wireless_mqtt_publish_preflight() -> dict[str, Any]:
"""Describe the non-physical gate used before an approved MQTT publish."""
return {
"transport": WIRELESS_MQTT_TRANSPORT,
"status": "delegated",
"policy": "mqtt_whitelist_publish",
"control_gate": "capability_parameter_and_frame_whitelist",
"required_vehicle_frames": [],
}
EXECUTION_VERIFICATION_STATUSES = frozenset(
{"verified_success", "submitted_unverified", "failed_or_blocked"}
)
def _operation_verification_status(item: dict[str, Any]) -> str:
"""Classify one operation without conflating Tx and physical feedback."""
telemetry = item.get("telemetry", {})
explicit = str(telemetry.get("verification_status") or "")
if explicit in EXECUTION_VERIFICATION_STATUSES:
return explicit
if item.get("status") != "succeeded":
return "failed_or_blocked"
if (
telemetry.get("physical_effect_confirmed") is True
or telemetry.get("feedback_frame") is not None
or telemetry.get("hardware_feedback") is True
or telemetry.get("firmware_verified") is True
):
return "verified_success"
if (
telemetry.get("command_frame") is not None
or telemetry.get("delivery_status") in {"submitted", "queued", "published"}
):
return "submitted_unverified"
# Dry-run and control-plane operations are complete at their own evidence
# level and do not claim that a physical actuator moved.
return "verified_success"
def _annotate_execution_report(report: dict[str, Any]) -> tuple[str, bool]:
"""Add task-level evidence and normalize submission success.
Returns ``(verification_status, partially_succeeded)``. Support steps such
as firmware identity checks do not turn a failed requested operation into a
partially successful user task.
"""
spec = report.setdefault("spec", {})
results = [item for item in spec.get("results", []) if isinstance(item, dict)]
operation_results = [
item
for item in results
if item.get("ref") not in {"seat-controller-firmware"}
] or results
statuses_by_ref: dict[str, list[str]] = {}
for item in operation_results:
statuses_by_ref.setdefault(str(item.get("ref") or "operation"), []).append(
_operation_verification_status(item)
)
statuses = [
(
"failed_or_blocked"
if "failed_or_blocked" in values
else "submitted_unverified"
if "submitted_unverified" in values
else "verified_success"
)
for values in statuses_by_ref.values()
]
failed = sum(status == "failed_or_blocked" for status in statuses)
accepted = len(statuses) - failed
partially_succeeded = bool(failed and accepted)
verification_status = (
"failed_or_blocked"
if failed
else "submitted_unverified"
if any(status == "submitted_unverified" for status in statuses)
else "verified_success"
)
spec["verification_status"] = verification_status
spec["status"] = (
"partially_succeeded"
if partially_succeeded
else "failed"
if failed
else "succeeded"
)
return verification_status, partially_succeeded
def _has_mixed_driver_passenger_seat_scope(utterance: str) -> bool:
"""Detect one request that crosses the CH1-driver and CH0-passenger boundary."""
return bool(
SEAT_SCOPE_PATTERN.search(utterance)
and DRIVER_SEAT_SCOPE_PATTERN.search(utterance)
and PASSENGER_SEAT_SCOPE_PATTERN.search(utterance)
)
SAVE_MODE_PROFILE_NAME = re.compile(
r"(?:固化为|保存为长期|保存成长期|长期保存为|记为长期|命名为)"
r"(?P<name>[^,。,.!!\s]{1,64}?模式)"
)
CREATE_MODE_PROFILE_NAME = re.compile(
r"^\s*(?:(?:请|麻烦)\s*(?:帮我|帮忙|给我)?\s*|"
r"(?:帮我|帮忙|给我)\s*)?"
r"(?:构建|创建|新建|新增|生成|添加)\s*(?:一下)?\s*"
r"(?:一个|个|一套|套)?\s*"
r"(?:名为|叫做|叫)?\s*[“\"'「『]?"
r"(?P<name>[^,。,.!!::\s“”\"'「」『』]{1,64}?模式)"
r"[”\"'」』]?"
r"\s*(?:配置)?(?:并(?:添加|加入)(?:到)?功能模式(?:栏)?)?"
r"(?:[,,::]\s*(?P<instruction>[\s\S]{1,800}))?[。.!!]?\s*$"
)
# ``、`` is used both as a command separator (``车窗关闭、空调24度``)
# and as an enumeration separator (``主驾、副驾车窗关闭``). Treat it as a
# boundary only when both sides already look like self-contained controls;
# otherwise keep it intact for the target/feature parsers below.
CONTROL_CLAUSE_SUBJECT_PATTERN = re.compile(
r"座椅|座位|靠背|椅背|坐盆|坐垫|腿托|脚托|滑轨|"
r"通风|加热|按摩|零重力|半躺|女王副驾|休闲躺椅|"
r"遮阳帘|天幕帘|天幕|车窗|窗户|"
r"灯光|室内灯|车内灯|阅读灯|氛围灯|"
r"空调|压缩机|温度|风量|风速|内循环|外循环|"
r"车门|门锁|后备箱|前大灯|近光灯|远光灯|雾灯|转向灯|双闪|"
r"出风口"
)
CONTROL_CLAUSE_PREDICATE_PATTERN = re.compile(
r"打开|开启|启动|关闭|关掉|关上|停止|暂停|取消|"
r"调整|调节|调到|调成|调为|调至|设置|设为|设到|切换|"
r"移动|移到|移至|前移|后移|上移|下移|升高|降低|抬起|放倒|"
r"调直|立直|竖直|归零|复位|锁上|上锁|落锁|解锁|"
r"零重力|半躺|最大|最小|最高|最低|最前|最后|到底|"
r"(?:\d+(?:\.\d+)?|[零一二三四五六七八九十百两半]+)"
r"(?:%|%|度|℃|档|级|秒|毫秒)"
)
UPDATE_MODE_PROFILE_PREFIX = re.compile(
r"^(?:请(?:帮我)?|帮我)?(?:修改一下|修改|调整|更改|更新|改一下)"
r"(?P<name>[^,。,.!!\s]{1,64}?模式)"
r"(?:[,。,.!!\s]*(?P<change>.*))?$"
)
UPDATE_MODE_PROFILE_BA = re.compile(
r"^(?:请(?:帮我)?|帮我)?(?:把|将)"
r"(?P<name>[^,。,.!!\s]{1,64}?模式)"
r"的(?P<change>.+)$"
)
RESTORE_MODE_PROFILE_PREFIX = re.compile(
r"^(?:请(?:帮我)?|帮我)?(?:恢复|还原|重置|初始化)"
r"(?P<name>[^,。,.!!\s]{1,64}?模式)"
r"(?:的)?(?:初始配置|初始参数|默认配置|原始配置|出厂配置)?$"
)
RESTORE_MODE_PROFILE_SUFFIX = re.compile(
r"^(?:请(?:帮我)?|帮我)?(?:把|将)?"
r"(?P<name>[^,。,.!!\s]{1,64}?模式)"
r"(?:恢复|还原|重置|初始化)(?:到|为)?"
r"(?:初始配置|初始参数|默认配置|原始配置|出厂配置)?$"
)
SEAT_AXIS_TO_CAPABILITY = {
axis: capability for capability, axis in SEAT_MOTION_CAPABILITIES.items()
}
SEAT_FUNCTION_TO_CAPABILITY = {
function: capability for capability, function in SEAT_CAPABILITIES.items()
}
DYNAMIC_MODE_STOP_WORDS = ("停止", "关闭", "结束", "取消", "停下", "退出")
DYNAMIC_MODE_WORDS = (
"动态模式",
"动态座椅",
"木马模式",
"木马",
"摇摇马",
"骑木马",
"rocking horse",
"动态摇篮",
"摇篮模式",
)
class SeatDemoService:
def __init__(self, repository_root: Path = REPOSITORY_ROOT):
self.root = repository_root
self.domain_root = repository_root / "domains" / "seat"
self.contracts = ContractValidator(repository_root)
self.matrix = SeatCanMatrix(
[
self.domain_root / "can" / "seat-thermal-matrix.yaml",
self.domain_root / "can" / "seat-motion-virtual-matrix.yaml",
]
)
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",
self.domain_root / "can" / "seat-motion-hardware-matrix.yaml",
]
)
self.hardware_compiler = T2GSeatCompiler(
self.domain_root / "can" / "signal-mapping.hardware.yaml",
repository_root / "platforms" / "t2g_seat_bench" / "calibration" / "seat.yaml",
)
self.hardware_settings = SeatHardwareSettings.from_env(repository_root)
self.vehicle_can_settings = VehicleCanSettings.from_env(repository_root)
self.vehicle_mqtt_settings = VehicleMqttSettings.from_env()
self._vehicle_state_monitor = (
VehicleStateMonitor(self.vehicle_can_settings)
if self.vehicle_can_settings.backend == "zlgcan-linux"
and self.vehicle_can_settings.state_configured
and not self.vehicle_can_settings.configured
else None
)
self._vehicle_mqtt_transport = MqttCanTransport(
self.vehicle_mqtt_settings
)
self.native_can_runtime = NativeCanRuntime(repository_root)
self.native_can_relative_resolver = NativeCanRelativeResolver(repository_root)
self.hardware_execution = PHYSICAL_BENCH_COORDINATOR
self.hardware_access = PHYSICAL_BENCH_ACCESS
# 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)
for domain in ("climate", "roof", "ambient_light")
}
self.capability_registry = CapabilityRegistry(
repository_root, {"seat", *self.domain_runtimes}
)
self.capability_context = self.capability_registry.snapshot()
self.semantic_capability_metadata = {
capability["name"]: {
"risk_level": capability.get("risk_level", "high"),
"confirmation_policy": capability.get(
"confirmation_policy", "on_ambiguity"
),
}
for domain in self.capability_context["domains"]
for capability in domain["capabilities"]
}
self.model_plan_adapter = ModelPlanAdapter(self.capability_context)
self.vehicle_profiles = VehicleProfileRegistry(repository_root / "config" / "vehicles")
configured_mode_root = os.getenv(
"COGNITIVE_AGENT_MODE_PROFILE_ROOT", ""
).strip()
self.mode_profile_user_root = (
Path(configured_mode_root).expanduser()
if configured_mode_root
else repository_root / "artifacts" / "mode-profiles"
)
if not self.mode_profile_user_root.is_absolute():
self.mode_profile_user_root = repository_root / self.mode_profile_user_root
self.mode_profiles = ModeProfileRegistry(
repository_root / "config" / "modes",
user_root=self.mode_profile_user_root,
)
self._artifact_root = repository_root / "artifacts" / "runs"
self.conversations = ConversationStateStore(
preset_root=repository_root / "artifacts" / "user-presets"
)
@property
def artifact_root(self) -> Path:
return self._artifact_root
@artifact_root.setter
def artifact_root(self, value: Path) -> None:
self._artifact_root = Path(value)
if hasattr(self, "conversations"):
self.conversations.preset_root = self._artifact_root / "user-presets"
@property
def driver_only_ch1_execution_active(self) -> bool:
"""Return whether deployment policy authorizes only the CH1 vehicle path."""
return bool(
not self.hardware_settings.enabled
and self.vehicle_can_settings.configured
and self.vehicle_can_settings.channel_is_allowed
and self.vehicle_can_settings.channel == 1
)
def _driver_only_vehicle_execution_active(self, transport: str) -> bool:
if self.hardware_settings.enabled:
return False
if transport == WIRELESS_MQTT_TRANSPORT:
return self.vehicle_mqtt_settings.configured
return self.driver_only_ch1_execution_active
@staticmethod
def _validate_control_transport(transport: str) -> str:
normalized = str(transport).strip().lower()
if normalized not in VEHICLE_CONTROL_TRANSPORTS:
raise ValueError(
"control_transport must be wired_can or wireless_mqtt"
)
return normalized
def runtime_info(self) -> dict[str, Any]:
domain_runtime_info = [
runtime.runtime_info() for runtime in self.domain_runtimes.values()
]
default_vehicle = self.vehicle_profiles.default()
hardware_info = self.hardware_settings.public_info()
hardware_info.update(self.hardware_execution.public_info())
hardware_info.update(self.hardware_access.public_info())
vehicle_can_info = self.vehicle_can_settings.public_info()
if self._vehicle_state_monitor is not None:
state_sync_info = self._vehicle_state_monitor.public_info()
if self.hardware_settings.configured:
# Keep the slower legacy cadence when the same page also polls
# the physical passenger-seat path through the shared adapter.
state_sync_info["poll_interval_ms"] = 5_000
vehicle_can_info["state_sync"] = state_sync_info
elif self.vehicle_can_settings.state_configured:
vehicle_can_info["state_sync"] = {
"mode": "on_demand_shared_adapter",
"state": "ready",
"read_only": True,
"poll_interval_ms": 5_000,
"snapshot_freshness_ms": 5_000,
"capture_sequence": None,
"observed_frame_ids": [],
"last_error": None,
}
vehicle_mqtt_info = self._vehicle_mqtt_transport.public_info()
return {
"service": "cabin-cognitive-orchestrator",
"domain": "cabin",
"platform": "reference_vehicle",
"execution_mode": os.getenv(
"COGNITIVE_AGENT_EXECUTION_MODE", "dry_run"
).strip(),
"execution_modes": ["dry_run", "simulated_hardware", "hardware"],
"hardware_execution_allowed": (
self.hardware_settings.configured
or self.vehicle_can_settings.configured
or self.vehicle_mqtt_settings.configured
),
"hardware": hardware_info,
"vehicle_can": vehicle_can_info,
"vehicle_mqtt": vehicle_mqtt_info,
"vehicle_transports": {
"default": WIRED_CAN_TRANSPORT,
"selection_scope": "browser_request",
"options": [
{
"id": WIRED_CAN_TRANSPORT,
"label": "有线控制",
**vehicle_can_info,
"target_policy": (
"dual_channel_wired_can0_passenger_can1_vehicle"
if self.hardware_settings.configured
and self.vehicle_can_settings.configured
else vehicle_can_info.get("target_policy")
),
"channels": {
"seat": 0,
"vehicle": self.vehicle_can_settings.channel,
},
},
{
"id": WIRELESS_MQTT_TRANSPORT,
"label": "无线控制",
**vehicle_mqtt_info,
},
],
},
"vehicle_safety_policy": {
"required_channel": 1,
"required_gear": "P",
"maximum_speed_kmh": 0.05,
"fresh_feedback_ids": ["0x342", "0x4EB"],
"preflight": "read_only_before_every_physical_request",
},
"multi_channel_can": {
"adapter_ownership": "single_process",
"seat_channel": (
self.hardware_settings.channel
if self.hardware_settings.enabled
else None
),
"vehicle_channel": self.vehicle_can_settings.channel,
"channels_must_be_distinct": bool(
self.hardware_settings.enabled
and self.vehicle_can_settings.enabled
),
"active_policy": (
"dual_channel_wired_can0_passenger_can1_vehicle"
if self.hardware_settings.configured
and self.vehicle_can_settings.configured
else "ch1_body_can_driver_only"
if self.vehicle_can_settings.configured
else "ch1_body_can_read_only"
if self.vehicle_can_settings.state_configured
else "ch0_passenger_only"
if self.hardware_settings.configured
else "virtual_only"
),
"seat_roles": {
"passenger": {"zone": "front_right", "channel": 0},
"driver": {"zone": "front_left", "channel": 1},
},
},
"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_models": self.openai.model_catalog,
"openai_reasoning_effort_default": self.openai.reasoning_effort,
"openai_reasoning_efforts": list(REASONING_EFFORTS),
"openai_responses_endpoint": self.openai.responses_endpoint,
"conversation_memory": True,
"default_vehicle_id": default_vehicle.id,
"vehicle": default_vehicle.public_dict(),
"vehicles": self.vehicle_profiles.public_profiles(),
"modes": self.mode_profiles.public_modes(default_vehicle.id),
"mode_profile_storage": "persistent_yaml",
"mode_profile_user_root": str(self.mode_profile_user_root),
"preset_storage": "conversation_durable",
"inferred_level_range": [1, 3],
"motion_position_range_pct": [0, 100],
"capability_domains": [
{
"domain": item["domain"],
"availability": item["availability"],
}
for item in self.capability_context["domains"]
],
"capabilities": [
capability
for domain in self.capability_context["domains"]
for capability in domain["capabilities"]
],
"matrix": {
"messages": len(self.matrix.data["messages"])
+ sum(item["matrix"]["messages"] for item in domain_runtime_info),
"signals": sum(len(item["signals"]) for item in self.matrix.data["messages"])
+ sum(item["matrix"]["signals"] for item in domain_runtime_info),
"source": {
"seat": self.matrix.data["source"],
**{
item["domain"]: item["matrix"]["source"]
for item in domain_runtime_info
},
},
"limitations": self.matrix.data["limitations"]
+ [
limitation
for item in domain_runtime_info
for limitation in item["matrix"]["limitations"]
],
},
"domain_runtimes": domain_runtime_info,
}
def delete_user_mode_profile(
self,
mode_id: str,
vehicle_id: str | None = None,
) -> dict[str, Any]:
resolved_vehicle_id = vehicle_id or self.vehicle_profiles.default().id
profile = self.mode_profiles.delete_user_mode(mode_id, resolved_vehicle_id)
return {
"status": "succeeded",
"deleted_mode_profile": profile.public_dict(),
"vehicle_id": resolved_vehicle_id,
}
def get_mode_profile_card(
self,
mode_id: str,
vehicle_id: str | None = None,
) -> ModeProfile:
resolved_vehicle_id = vehicle_id or self.vehicle_profiles.default().id
return self.mode_profiles.get_profile(mode_id, resolved_vehicle_id)
def execute_mode_profile_card(
self,
mode_id: str,
*,
conversation_id: str | None = None,
execution_mode: str = "dry_run",
approval_token: str | None = None,
vehicle_id: str | None = None,
simulation_fault: str = "normal",
hardware_client_id: str | None = None,
hardware_access_bypass: bool = True,
control_transport: str = WIRED_CAN_TRANSPORT,
) -> dict[str, Any]:
"""Execute a persisted mode by ID without interpreting natural language."""
resolved_vehicle_id = vehicle_id or self.vehicle_profiles.default().id
profile = self.mode_profiles.get_profile(mode_id, resolved_vehicle_id)
if not profile.command_flow or not profile.command_flow.get("plans"):
raise ValueError("该模式尚未生成可直接执行的控制指令流。")
result = self.process(
profile.instruction or f"进入{profile.name}",
provider="local",
conversation_id=conversation_id,
execution_mode=execution_mode,
approval_token=approval_token,
vehicle_id=resolved_vehicle_id,
simulation_fault=simulation_fault,
hardware_client_id=hardware_client_id,
hardware_access_bypass=hardware_access_bypass,
control_transport=control_transport,
_direct_mode_profile_id=profile.id,
)
public_profile = profile.public_dict()
result["provider_used"] = "saved_mode_command_flow"
result["utterance"] = profile.name
result["matched_modes"] = [public_profile]
result["mode_profile"] = public_profile
result["orchestration_route"] = "saved_mode_command_flow"
result["decision_summary"] = [
f"直接载入已保存的模式“{profile.name}”,未再次解析自然语言。",
*list(result.get("decision_summary") or []),
]
self._persist(result)
return result
def save_mode_profile_card(
self,
mode_id: str,
name: str,
instruction: str,
source_request_id: str,
vehicle_id: str | None = None,
) -> dict[str, Any]:
"""Persist one edited mode from a server-generated control flow."""
return self._persist_mode_profile_card(
mode_id,
name,
instruction,
source_request_id,
vehicle_id,
)
def create_mode_profile_card(
self,
name: str,
instruction: str,
source_request_id: str,
vehicle_id: str | None = None,
) -> dict[str, Any]:
"""Create one user mode from a server-generated control flow."""
return self._persist_mode_profile_card(
None,
name,
instruction,
source_request_id,
vehicle_id,
)
def _persist_mode_profile_card(
self,
mode_id: str | None,
name: str,
instruction: str,
source_request_id: str,
vehicle_id: str | None = None,
) -> dict[str, Any]:
"""Validate and persist a created or edited mode command flow."""
resolved_vehicle_id = vehicle_id or self.vehicle_profiles.default().id
clean_mode_id = str(mode_id or "").strip()
clean_name = str(name).strip()
clean_instruction = str(instruction).strip()
clean_request_id = str(source_request_id).strip()
if not clean_name:
raise ValueError("模式名称不能为空。")
if len(clean_name) > 64:
raise ValueError("模式名称不能超过 64 个字符。")
if not clean_instruction:
raise ValueError("自然语言指令不能为空。")
if len(clean_instruction) > 1000:
raise ValueError("自然语言指令不能超过 1000 个字符。")
if not MODE_EDITOR_REQUEST_ID_PATTERN.fullmatch(clean_request_id):
raise ValueError("控制指令流来源请求编号无效。")
visible_profiles = self.mode_profiles._profiles_for_vehicle( # noqa: SLF001
resolved_vehicle_id
)
profile = (
next(
(item for item in visible_profiles if item.id == clean_mode_id),
None,
)
if clean_mode_id
else None
)
if clean_mode_id and profile is None:
raise KeyError(clean_mode_id)
normalized_name = re.sub(r"\s+", "", clean_name).casefold()
if any(
(profile is None or item.id != profile.id)
and re.sub(r"\s+", "", item.name).casefold() == normalized_name
for item in visible_profiles
):
raise ValueError("该模式名称已被其他模式使用。")
result_path = self.artifact_root / clean_request_id / "result.json"
if not result_path.is_file():
raise ValueError("未找到“生成指令”按钮产生的控制指令流,请重新生成。")
try:
source_result = json.loads(result_path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
raise ValueError("控制指令流来源结果无法读取,请重新生成。") from exc
if not isinstance(source_result, dict) or source_result.get(
"request_id"
) != clean_request_id:
raise ValueError("控制指令流来源结果不完整,请重新生成。")
if source_result.get("status") != "succeeded":
raise ValueError("控制指令流没有完整通过校验,请重新生成。")
source_vehicle = source_result.get("vehicle")
source_vehicle_id = (
str(source_vehicle.get("id") or "").strip()
if isinstance(source_vehicle, dict)
else ""
)
if source_vehicle_id and source_vehicle_id != resolved_vehicle_id:
raise ValueError("控制指令流不属于当前车型,请重新生成。")
source_draft = source_result.get("mode_profile_draft")
if isinstance(source_draft, dict):
draft_request_id = str(source_draft.get("source_request_id") or "").strip()
draft_vehicle_id = str(source_draft.get("vehicle_id") or "").strip()
draft_instruction = str(source_draft.get("instruction") or "").strip()
draft_name = str(source_draft.get("name") or "").strip()
if draft_request_id != clean_request_id:
raise ValueError("功能模式草稿与控制指令流不匹配,请重新生成。")
if draft_vehicle_id and draft_vehicle_id != resolved_vehicle_id:
raise ValueError("功能模式草稿不属于当前车型,请重新生成。")
if draft_instruction and draft_instruction != clean_instruction:
raise ValueError("自然语言指令已修改,请重新生成模式预览。")
if draft_name and draft_name != clean_name:
raise ValueError("功能模式名称已修改,请重新生成模式预览。")
recorded_instruction = str(source_result.get("utterance", "")).strip()
if not recorded_instruction:
source_spans = {
str(item.get("source_span", "")).strip()
for item in (
(source_result.get("intent_ir") or {}).get("spec", {}).get(
"requests", []
)
)
if isinstance(item, dict) and str(item.get("source_span", "")).strip()
}
if len(source_spans) == 1:
recorded_instruction = next(iter(source_spans))
if recorded_instruction and recorded_instruction != clean_instruction:
raise ValueError("自然语言指令已修改,请重新点击“生成指令”。")
plans = source_result.get("command_plans") or {}
configs = source_result.get("function_configs") or {}
if not isinstance(plans, dict) or not isinstance(configs, dict):
raise ValueError("解析结果中没有可永久化的控制指令流。")
executable_plan_keys = {
key
for key, plan in plans.items()
if isinstance(plan, dict)
and isinstance(plan.get("spec"), dict)
and isinstance(plan["spec"].get("commands"), list)
and plan["spec"]["commands"]
}
if not executable_plan_keys:
raise ValueError("请先点击“生成指令”并成功生成控制指令流。")
operations: list[dict[str, Any]] = []
signatures: set[tuple[str, str, str, str]] = set()
for key in plans:
if key not in executable_plan_keys:
continue
config = configs.get(key)
if not isinstance(config, dict):
continue
spec = config.get("spec") or {}
domain = str(spec.get("domain", "")).strip()
for operation in spec.get("operations", []):
if not isinstance(operation, dict):
continue
capability = str(operation.get("capability", "")).strip()
operation_target = operation.get("target") or {}
target = str(
operation_target.get("instance")
or operation_target.get("zone")
or ""
).strip()
parameters = operation.get("parameters") or {}
if not domain or not capability or not target or not isinstance(
parameters, dict
):
continue
signature = (
domain,
capability,
target,
json.dumps(parameters, ensure_ascii=False, sort_keys=True),
)
if signature in signatures:
continue
signatures.add(signature)
operations.append(
{
"domain": domain,
"capability": capability,
"target_scope": target,
"operation": "set",
"parameters": deepcopy(parameters),
"reason": f"由模式编辑器请求 {clean_request_id} 固化。",
}
)
if not operations:
raise ValueError("控制指令流没有关联到可复用的模式操作。")
command_flow = {
"schema": "cognitive-agent-mode-command-flow/v1",
"source_request_id": clean_request_id,
"generated_at": _now(),
"execution_mode": source_result.get("execution_mode"),
"control_transport": source_result.get("control_transport"),
"function_configs": {
key: deepcopy(configs[key])
for key in plans
if key in executable_plan_keys and key in configs
},
"plans": {key: deepcopy(plans[key]) for key in plans if key in executable_plan_keys},
# A mode card stores only the validated, deterministic plan. Live
# delivery evidence belongs to each later execution, not to the
# generation workflow.
"execution_steps": [],
}
saved = self.mode_profiles.save_mode(
clean_name,
operations,
mode_id=profile.id if profile is not None else None,
aliases=list(profile.aliases) if profile is not None else [],
vehicle_id=resolved_vehicle_id,
description=(
profile.description
if profile is not None
else "由自然语言生成的自定义模式。"
),
icon=profile.icon if profile is not None else "bookmark-plus",
instruction=clean_instruction,
command_flow=command_flow,
timeline=profile.timeline if profile is not None else None,
)
public_profile = next(
(
item
for item in self.mode_profiles.public_modes(resolved_vehicle_id)
if item["id"] == saved.id
),
saved.public_dict(),
)
return {
"status": "succeeded",
"mode_profile": public_profile,
"vehicle_id": resolved_vehicle_id,
"storage": "persistent_yaml",
"source_request_id": clean_request_id,
}
def synchronize_hardware_state(
self,
conversation_id: str,
vehicle_id: str | None = None,
*,
control_transport: str = WIRED_CAN_TRANSPORT,
hardware_client_id: str | None = None,
hardware_access_bypass: bool = True,
) -> dict[str, Any]:
"""Read the bench only when no physical execution currently owns it."""
control_transport = self._validate_control_transport(control_transport)
mqtt_selected = control_transport == WIRELESS_MQTT_TRANSPORT
if hardware_access_bypass:
# Wireless CAN0 is a thread-safe view over the persistent MQTT
# subscription. HardwareSeatExecutor still serializes this seat
# read with seat actuation through its adapter lock, while an
# absent CAN0 stream must not block the independent cached CAN1
# dashboard or make control requests fail with hardware_busy.
if mqtt_selected:
return self._synchronize_hardware_state_once(
conversation_id,
vehicle_id,
control_transport=control_transport,
)
with self.hardware_execution.reserve():
return self._synchronize_hardware_state_once(
conversation_id,
vehicle_id,
control_transport=control_transport,
)
authorized_client_id = self.hardware_access.ensure_authorized(hardware_client_id)
if mqtt_selected:
with self.hardware_access.use(authorized_client_id):
return self._synchronize_hardware_state_once(
conversation_id,
vehicle_id,
control_transport=control_transport,
)
with self.hardware_execution.reserve():
with self.hardware_access.use(authorized_client_id):
return self._synchronize_hardware_state_once(
conversation_id,
vehicle_id,
control_transport=control_transport,
)
def _synchronize_hardware_state_once(
self,
conversation_id: str,
vehicle_id: str | None = None,
*,
control_transport: str = WIRED_CAN_TRANSPORT,
) -> dict[str, Any]:
"""Read the bench and project it into the current passenger conversation."""
if not CONVERSATION_ID_PATTERN.fullmatch(conversation_id):
raise ValueError(
"conversation_id must start with conv- and contain only lowercase letters, numbers, or hyphens"
)
control_transport = self._validate_control_transport(control_transport)
mqtt_selected = control_transport == WIRELESS_MQTT_TRANSPORT
wired_state_bridge = None
if mqtt_selected:
if not self.vehicle_mqtt_settings.state_configured:
raise HardwareSafetyError("the MQTT CAN0 seat relay is not configured")
bus_factory = lambda: self._vehicle_mqtt_transport.bus(
channel=0,
replay_latest=True,
)
else:
if not self.hardware_settings.configured:
raise HardwareSafetyError(
"the passenger-seat CAN0 hardware runtime is not configured"
)
if (
self.hardware_settings.native_passenger_can0
and self.vehicle_can_settings.adapter_configured
):
wired_state_bridge = self.vehicle_can_settings.bridge(
[
ZlgChannelConfig(
channel=0,
frame_format="classic_can",
arbitration_bitrate=self.hardware_settings.bitrate,
data_bitrate=self.vehicle_can_settings.data_bitrate,
can_fd_iso=True,
termination_enabled=(
self.hardware_settings.termination_enabled
),
)
],
monitor_only=True,
)
bus_factory = lambda: wired_state_bridge.channel_bus(0)
else:
bus_factory = None
vehicle_profile = self.vehicle_profiles.get(vehicle_id)
try:
physical_state = self._new_hardware_executor(
bus_factory,
remote_transport=mqtt_selected,
).read_physical_state()
finally:
if wired_state_bridge is not None:
wired_state_bridge.close()
if mqtt_selected:
physical_state["source"] = "wireless_mqtt_ch0"
context = self.conversations.synchronize_physical_passenger(
conversation_id,
physical_state,
)
context = self._context_for_vehicle(context, vehicle_profile)
return {
"status": "succeeded",
"conversation_id": conversation_id,
"conversation_turn": context["turn_count"],
"source": "wireless_mqtt_ch0" if mqtt_selected else "physical_can",
"control_transport": control_transport,
"captured_at": physical_state.get("captured_at"),
"complete": bool(physical_state.get("complete")),
"missing": list(physical_state.get("missing", [])),
"physical_state": physical_state,
"actuator_state": context["devices"],
}
def synchronize_vehicle_state(
self,
conversation_id: str,
vehicle_id: str | None = None,
*,
control_transport: str = WIRED_CAN_TRANSPORT,
hardware_client_id: str | None = None,
hardware_access_bypass: bool = True,
) -> dict[str, Any]:
"""Passively receive CH1 driver-seat feedback without transmitting CAN."""
control_transport = self._validate_control_transport(control_transport)
mqtt_selected = control_transport == WIRELESS_MQTT_TRANSPORT
if hardware_access_bypass:
# The MQTT snapshot is already decoded and protected by the
# transport condition lock. It is independent from physical
# execution ownership and should remain readable during CAN0
# waits or an in-flight command.
if mqtt_selected:
return self._synchronize_vehicle_state_once(
conversation_id,
vehicle_id,
control_transport=control_transport,
)
with self.hardware_execution.reserve():
return self._synchronize_vehicle_state_once(
conversation_id,
vehicle_id,
control_transport=control_transport,
)
authorized_client_id = self.hardware_access.ensure_authorized(hardware_client_id)
if mqtt_selected:
with self.hardware_access.use(authorized_client_id):
return self._synchronize_vehicle_state_once(
conversation_id,
vehicle_id,
control_transport=control_transport,
)
with self.hardware_execution.reserve():
with self.hardware_access.use(authorized_client_id):
return self._synchronize_vehicle_state_once(
conversation_id,
vehicle_id,
control_transport=control_transport,
)
def start(self) -> None:
if self._vehicle_state_monitor is not None:
self._vehicle_state_monitor.start()
self._vehicle_mqtt_transport.start()
def close(self) -> None:
if self._vehicle_state_monitor is not None:
self._vehicle_state_monitor.close()
self._vehicle_mqtt_transport.close()
def _synchronize_vehicle_state_once(
self,
conversation_id: str,
vehicle_id: str | None = None,
*,
control_transport: str = WIRED_CAN_TRANSPORT,
) -> dict[str, Any]:
if not CONVERSATION_ID_PATTERN.fullmatch(conversation_id):
raise ValueError(
"conversation_id must start with conv- and contain only lowercase letters, numbers, or hyphens"
)
control_transport = self._validate_control_transport(control_transport)
mqtt_selected = control_transport == WIRELESS_MQTT_TRANSPORT
if mqtt_selected:
if not self.vehicle_mqtt_settings.state_configured:
raise HardwareSafetyError("the MQTT vehicle CAN relay is not configured")
elif not self.vehicle_can_settings.state_configured:
raise HardwareSafetyError("the CH1 vehicle CAN runtime is not configured")
vehicle_profile = self.vehicle_profiles.get(vehicle_id)
shared_runtime_enabled = (
self.vehicle_can_settings.backend != "zlgcan-linux"
and self.vehicle_can_settings.configured
and os.getenv(
"COGNITIVE_AGENT_SHARED_CAN_RUNTIME_ENABLED",
os.getenv(
"COGNITIVE_AGENT_SEAT_SHARED_ZCANPRO_BRIDGE_ENABLED", "true"
),
).strip().lower() in {"1", "true", "yes", "on"}
)
bridge = None
capture_sequence: int | None = None
if mqtt_selected:
snapshot = self._vehicle_mqtt_transport.snapshot()
feedback_states = list(snapshot.feedback_states)
observed_ids = set(snapshot.observed_ids)
captured_at = snapshot.captured_at
capture_sequence = snapshot.capture_sequence
elif self._vehicle_state_monitor is not None:
snapshot = self._vehicle_state_monitor.snapshot()
feedback_states = list(snapshot.feedback_states)
observed_ids = set(snapshot.observed_ids)
captured_at = snapshot.captured_at
capture_sequence = snapshot.capture_sequence
elif shared_runtime_enabled:
from cognitive_agent.application.self_repair.service import BRIDGE_MANAGER
if not hasattr(BRIDGE_MANAGER, "channel_bus_factory"):
raise HardwareSafetyError(
"CH1 feedback requires the standalone multi-channel CAN runtime"
)
bus = BRIDGE_MANAGER.channel_bus_factory(
self.vehicle_can_settings.channel
)()
else:
bridge = self.vehicle_can_settings.bridge(
[self.vehicle_can_settings.channel_config()],
monitor_only=True,
)
bus = bridge.channel_bus(self.vehicle_can_settings.channel)
if not mqtt_selected and self._vehicle_state_monitor is None:
feedback_states = []
observed_ids = set()
captured_at = None
# Keep driver-seat completeness compatible with the existing endpoint,
# while passively collecting every recognized CH1 body-state frame for
# the whole-vehicle dashboard during the same bounded receive window.
deadline = (
time.monotonic()
+ self.vehicle_can_settings.feedback_timeout_ms / 1000
)
try:
while time.monotonic() < deadline and observed_ids != OBSERVABLE_IDS:
remaining_ms = max(1, int((deadline - time.monotonic()) * 1000))
frame = bus.receive(remaining_ms, include_fd=True)
if frame is None:
break
if frame.channel != self.vehicle_can_settings.channel:
continue
if frame.arbitration_id not in OBSERVABLE_IDS or len(frame.data) < 8:
continue
decoded = decode_feedback(frame)
if not any(
key in decoded
for key in ("actuator_state", "domain_state", "vehicle_safety")
):
# 0x4EB is multiplexed. Only subtype 0x07 carries the
# verified speed signal, so unrelated subframes must not
# make the body-state snapshot look complete.
continue
frame_id = str(decoded.get("frame_id", ""))
decoded.update(
{
"valid": True,
"feedback_frame_id": frame_id,
"captured_at": _now(),
"verification": {
"authoritative": True,
"channel": 1,
"simulated": False,
"read_only": True,
"frame_id": frame_id,
"payload_size": len(frame.data),
},
}
)
feedback_states.append(decoded)
observed_ids.add(frame.arbitration_id)
captured_at = str(decoded["captured_at"])
finally:
if bridge is not None:
bridge.close()
else:
bus.close()
context = self.conversations.synchronize_physical_ch1_feedback(
conversation_id,
feedback_states,
)
context = self._context_for_vehicle(context, vehicle_profile)
missing = [
f"0x{frame_id:03X}"
for frame_id in sorted(REQUIRED_SEAT_IDS - observed_ids)
]
body_state_missing = [
f"0x{frame_id:03X}"
for frame_id in sorted(REQUIRED_BODY_IDS - observed_ids)
]
return {
"status": "succeeded",
"conversation_id": conversation_id,
"conversation_turn": context["turn_count"],
"source": (
"wireless_mqtt_ch1" if mqtt_selected else "physical_can_ch1"
),
"control_transport": control_transport,
"read_only": True,
"captured_at": captured_at or _now(),
"capture_sequence": capture_sequence,
"complete": not missing,
"missing": missing,
"body_state_complete": not body_state_missing,
"body_state_missing": body_state_missing,
"observed_frame_ids": [f"0x{frame_id:03X}" for frame_id in sorted(observed_ids)],
"actuator_state": context["devices"],
"domain_state": context["domain_devices"],
}
def _new_hardware_executor(
self,
bus_factory: Any | None = None,
*,
remote_transport: bool = False,
) -> HardwareSeatExecutor | MqttNativeSeatExecutor | ZcanBridgeSeatExecutor:
shared_bridge_enabled = os.getenv(
"COGNITIVE_AGENT_SHARED_CAN_RUNTIME_ENABLED",
os.getenv("COGNITIVE_AGENT_SEAT_SHARED_ZCANPRO_BRIDGE_ENABLED", "true"),
).strip().lower() in {"1", "true", "yes", "on"}
if (
bus_factory is None
and shared_bridge_enabled
and self.hardware_settings.backend != "zlgcan-linux"
):
# Import lazily so the ordinary dry-run service does not initialize
# any self-repair runtime state. Both modules then share the one
# process that is allowed to own the Windows ZLG adapter.
from cognitive_agent.application.self_repair.service import (
BRIDGE_MANAGER,
PENDING_DIR,
REQUIRED_BRIDGE_VERSION,
STATUS_PATH,
)
manager = BRIDGE_MANAGER
manager_snapshot = manager.snapshot()
if (
manager_snapshot.get("backend") == "standalone_zlg"
or Path(str(manager_snapshot.get("zcan_exe") or "")).is_file()
):
return ZcanBridgeSeatExecutor(
self.hardware_matrix,
manager,
STATUS_PATH,
PENDING_DIR,
required_version=REQUIRED_BRIDGE_VERSION,
auto_home=self.hardware_settings.auto_home_enabled,
)
resolved_bus_factory = bus_factory or self.hardware_settings.bus_factory()
executor = HardwareSeatExecutor(
self.hardware_matrix,
resolved_bus_factory,
thermal_timeout_ms=self.hardware_settings.thermal_timeout_ms,
ack_timeout_ms=self.hardware_settings.ack_timeout_ms,
axis_maximum_x100={
capability: int(
self.hardware_compiler.protocol["normalized_position_max"]
)
for capability in self.hardware_compiler.axes
},
motor_max_hall={
int(axis["motor_selector"]): int(axis["default_max_hall_count"])
for axis in self.hardware_compiler.axes.values()
},
stop_all_motor_selector=int(
self.hardware_compiler.protocol["stop_all_motor_selector"]
),
auto_home=(
self.hardware_settings.auto_home_enabled
if not remote_transport
else False
),
home_timeout_s=self.hardware_settings.home_timeout_s,
firmware_guard=(
None
if remote_transport
or self.hardware_settings.native_passenger_can0
else self.hardware_settings.firmware_guard(
resolved_bus_factory,
allow_programming=not remote_transport,
)
),
synchronize_position_before_first_motion=(
self.hardware_settings.position_sync_enabled
if not remote_transport
else False
),
position_sync_state=self._seat_position_memory_state,
require_thermal_feedback=not remote_transport,
feedback_required=not remote_transport,
)
if remote_transport:
return MqttNativeSeatExecutor(executor, resolved_bus_factory)
return executor
def _new_linux_wired_dual_channel_bridge(self) -> ZlgCanBridgeProcess:
"""Open CAN0 passenger classic CAN and CAN1 body CAN FD under one owner."""
if not self.hardware_settings.native_passenger_can0:
raise HardwareSafetyError(
"the shared Linux dual-channel bridge requires native passenger CAN0"
)
if not self.vehicle_can_settings.configured:
raise HardwareSafetyError(
"the shared Linux dual-channel bridge requires configured vehicle CAN1"
)
if (
self.hardware_settings.device_type
!= self.vehicle_can_settings.device_type
or self.hardware_settings.device_index
!= self.vehicle_can_settings.device_index
or self.hardware_settings.zlg_dll.resolve()
!= self.vehicle_can_settings.zlg_dll.resolve()
):
raise HardwareSafetyError(
"CAN0 and CAN1 must use the same Linux ZLG device and SDK library"
)
return self.vehicle_can_settings.bridge(
[
ZlgChannelConfig(
channel=0,
frame_format="classic_can",
arbitration_bitrate=self.hardware_settings.bitrate,
data_bitrate=self.vehicle_can_settings.data_bitrate,
can_fd_iso=True,
termination_enabled=(
self.hardware_settings.termination_enabled
),
),
self.vehicle_can_settings.channel_config(),
]
)
def preview_native_can(
self,
utterance: str,
provider: str = "auto",
conversation_id: str | None = None,
execution_mode: str = "dry_run",
approval_token: str | None = None,
vehicle_id: str | None = None,
simulation_fault: str = "normal",
*,
hardware_client_id: str | None = None,
hardware_access_bypass: bool = True,
reasoning_effort: str | None = None,
control_transport: str = WIRED_CAN_TRANSPORT,
on_commands_validated: Callable[[dict[str, Any]], None] | None = None,
assistant_plan: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""Validate and execute deterministic controls before hosted parsing."""
utterance = utterance.strip()
if not utterance:
raise ValueError("utterance must not be empty")
if provider not in {"auto", "openai", "local"}:
raise ValueError("provider must be auto, openai, or local")
conversation_id = conversation_id or f"conv-{uuid.uuid4().hex}"
if not CONVERSATION_ID_PATTERN.fullmatch(conversation_id):
raise ValueError(
"conversation_id must start with conv- and contain only lowercase letters, numbers, or hyphens"
)
vehicle_profile = self.vehicle_profiles.get(vehicle_id)
control_plane_request = any(
matcher(utterance) is not None
for matcher in (
self._parse_mode_profile_save_name,
self._parse_mode_profile_create_name,
self._parse_mode_profile_restore_name,
self._parse_mode_profile_update,
)
)
if control_plane_request:
result = self.process(
utterance,
provider=provider,
conversation_id=conversation_id,
execution_mode=execution_mode,
approval_token=approval_token,
vehicle_id=vehicle_profile.id,
simulation_fault=simulation_fault,
hardware_client_id=hardware_client_id,
hardware_access_bypass=hardware_access_bypass,
reasoning_effort=reasoning_effort,
control_transport=control_transport,
_on_commands_validated=on_commands_validated,
_assistant_plan=assistant_plan,
)
result.update(
{
"phase": "control_plane_complete",
"preview_request_id": result["request_id"],
"unresolved_clauses": list(
result.get("unresolved_clauses") or []
),
"supplement_required": False,
}
)
self._persist(result)
return result
context = self._context_for_vehicle(
self.conversations.snapshot(conversation_id),
vehicle_profile,
)
clause_results = self._local_clause_results(utterance, context)
unresolved_clauses = [
clause
for clause, _, status in clause_results
if status != "complete"
]
started = time.perf_counter()
def publish_local_commands(validated: dict[str, Any]) -> None:
if on_commands_validated is None:
return
event = deepcopy(validated)
event.update(
{
"phase": "deterministic_commands_validated",
"provider_requested": provider,
"unresolved_clauses": unresolved_clauses,
"supplement_required": bool(
provider != "local" and unresolved_clauses
),
}
)
on_commands_validated(event)
result = self.process(
utterance,
provider="local",
conversation_id=conversation_id,
execution_mode=execution_mode,
approval_token=approval_token,
vehicle_id=vehicle_profile.id,
simulation_fault=simulation_fault,
hardware_client_id=hardware_client_id,
hardware_access_bypass=hardware_access_bypass,
reasoning_effort=reasoning_effort,
control_transport=control_transport,
_local_preview=True,
_allow_empty_control_result=True,
_on_commands_validated=publish_local_commands,
_assistant_plan=assistant_plan,
)
elapsed_ms = round((time.perf_counter() - started) * 1000, 3)
supplement_required = bool(
provider != "local" and unresolved_clauses
)
result.update(
{
"phase": "deterministic_preview",
"provider_requested": provider,
"preview_request_id": result["request_id"],
"preview_elapsed_ms": elapsed_ms,
"unresolved_clauses": unresolved_clauses,
"supplement_required": supplement_required,
}
)
result["decision_summary"] = [
*result.get("decision_summary", []),
(
"本地规则阶段已完成白名单校验并先行处理 "
f"{sum(len((config.get('spec') or {}).get('operations', [])) for config in result.get('function_configs', {}).values())} 条 CAN 控制指令。"
),
]
self._persist(result)
manifest = {
"version": 1,
"preview_request_id": result["request_id"],
"utterance": utterance,
"provider": provider,
"conversation_id": conversation_id,
"execution_mode": execution_mode,
"vehicle_id": vehicle_profile.id,
"simulation_fault": simulation_fault,
"hardware_client_id": hardware_client_id,
"reasoning_effort": reasoning_effort,
"control_transport": control_transport,
"unresolved_clauses": unresolved_clauses,
"preview_control_signatures": result.get(
"parsed_control_signatures", []
),
}
self._atomic_write(
self.artifact_root / result["request_id"] / "preview.json",
json.dumps(manifest, ensure_ascii=False, indent=2) + "\n",
)
return result
def supplement_native_can(
self,
preview_request_id: str,
*,
provider: str | None = None,
approval_token: str | None = None,
hardware_client_id: str | None = None,
hardware_access_bypass: bool = True,
reasoning_effort: str | None = None,
on_commands_validated: Callable[[dict[str, Any]], None] | None = None,
) -> dict[str, Any]:
"""Parse only unresolved clauses and merge them with the local result."""
if not MODE_EDITOR_REQUEST_ID_PATTERN.fullmatch(preview_request_id):
raise ValueError("invalid preview request id")
preview_dir = self.artifact_root / preview_request_id
try:
manifest = json.loads(
(preview_dir / "preview.json").read_text(encoding="utf-8")
)
preview = json.loads(
(preview_dir / "result.json").read_text(encoding="utf-8")
)
except (OSError, json.JSONDecodeError) as exc:
raise ValueError("native CAN preview not found") from exc
if (
not isinstance(manifest, dict)
or manifest.get("version") != 1
or manifest.get("preview_request_id") != preview_request_id
or not isinstance(preview, dict)
or preview.get("request_id") != preview_request_id
):
raise ValueError("invalid native CAN preview manifest")
selected_provider = str(manifest.get("provider") or "auto")
if provider is not None and provider != selected_provider:
raise ValueError("supplement provider does not match preview")
selected_effort = manifest.get("reasoning_effort")
if reasoning_effort is not None and reasoning_effort != selected_effort:
raise ValueError("supplement reasoning effort does not match preview")
expected_client_id = manifest.get("hardware_client_id")
if hardware_client_id != expected_client_id:
raise ValueError("supplement hardware client does not match preview")
unresolved_clauses = [
str(item).strip()
for item in manifest.get("unresolved_clauses", [])
if str(item).strip()
]
if not unresolved_clauses:
raise ValueError("native CAN preview has no unresolved clauses")
excluded_signatures = {
tuple(item)
for item in manifest.get("preview_control_signatures", [])
if isinstance(item, list)
}
def publish_cloud_commands(validated: dict[str, Any]) -> None:
if on_commands_validated is None:
return
event = self._merge_native_can_phases(preview, validated)
event.update(
{
"phase": "supplement_commands_validated",
"preview_request_id": preview_request_id,
"supplement_of": preview_request_id,
"supplement_required": False,
}
)
on_commands_validated(event)
supplement = self.process(
",".join(unresolved_clauses),
provider=selected_provider,
conversation_id=str(manifest["conversation_id"]),
execution_mode=str(manifest.get("execution_mode") or "dry_run"),
approval_token=approval_token,
vehicle_id=str(manifest["vehicle_id"]),
simulation_fault=str(manifest.get("simulation_fault") or "normal"),
hardware_client_id=hardware_client_id,
hardware_access_bypass=hardware_access_bypass,
reasoning_effort=(
str(selected_effort) if selected_effort is not None else None
),
control_transport=str(
manifest.get("control_transport") or WIRED_CAN_TRANSPORT
),
_excluded_control_signatures=excluded_signatures,
_model_parse_source="cloud_supplement",
_allow_empty_control_result=True,
_on_commands_validated=publish_cloud_commands,
)
merged = self._merge_native_can_phases(preview, supplement)
merged.update(
{
"phase": "two_stage_complete",
"utterance": str(
preview.get("utterance")
if preview.get("orchestration_route") == "unified_can_infrared"
else manifest["utterance"]
),
"provider_requested": selected_provider,
"preview_request_id": preview_request_id,
"supplement_of": preview_request_id,
"supplement_request_id": supplement["request_id"],
"supplement_required": False,
"unresolved_clauses": unresolved_clauses,
}
)
self._persist(merged)
return merged
@staticmethod
def _merge_native_can_phases(
preview: dict[str, Any],
supplement: dict[str, Any],
) -> dict[str, Any]:
"""Create one durable result without collapsing same-domain phases."""
merged = deepcopy(supplement)
def phase_documents(
local_documents: dict[str, Any] | None,
cloud_documents: dict[str, Any] | None,
) -> dict[str, Any]:
return {
**{
f"local_{key}": deepcopy(value)
for key, value in (local_documents or {}).items()
},
**{
f"cloud_{key}": deepcopy(value)
for key, value in (cloud_documents or {}).items()
},
}
configs = phase_documents(
preview.get("function_configs"),
supplement.get("function_configs"),
)
plans = phase_documents(
preview.get("command_plans"),
supplement.get("command_plans"),
)
merged["function_configs"] = configs
merged["command_plans"] = plans
merged["function_config"] = next(iter(configs.values()), None)
merged["command_plan"] = next(iter(plans.values()), None)
merged["decision_summary"] = [
*preview.get("decision_summary", []),
*supplement.get("decision_summary", []),
]
merged["trace_events"] = [
*preview.get("trace_events", []),
*supplement.get("trace_events", []),
]
merged["validation"] = [
*preview.get("validation", []),
*supplement.get("validation", []),
]
merged["parsed_control_signatures"] = [
*preview.get("parsed_control_signatures", []),
*supplement.get("parsed_control_signatures", []),
]
merged["phase_execution_reports"] = {
"local_rule": deepcopy(preview.get("execution_report")),
"cloud_supplement": deepcopy(supplement.get("execution_report")),
**deepcopy(preview.get("phase_execution_reports") or {}),
}
merged["operation_results"] = [
*deepcopy(preview.get("operation_results") or []),
*deepcopy(supplement.get("operation_results") or []),
]
if preview.get("orchestration_route") == "unified_can_infrared":
merged["orchestration_route"] = "unified_can_infrared"
for key in (
"infrared_actions",
"peripheral",
"control_transports",
):
merged[key] = deepcopy(preview.get(key))
merged["provider_used"] = (
f"{supplement.get('provider_used') or 'cloud_supplement'}"
"+infrared_local_rules"
)
if supplement.get("execution_report") is None:
merged["execution_report"] = deepcopy(preview.get("execution_report"))
successful = {"succeeded", "partially_succeeded", "no_action"}
preview_ok = preview.get("status") in successful
supplement_ok = supplement.get("status") in successful
has_any_commands = bool(configs and plans)
if preview_ok and supplement_ok:
merged["status"] = "succeeded" if has_any_commands else "no_action"
elif preview_ok and preview.get("function_configs"):
merged["status"] = "partially_succeeded"
merged["local_preview"] = {
"request_id": preview.get("request_id"),
"elapsed_ms": preview.get("preview_elapsed_ms"),
"command_count": sum(
len((config.get("spec") or {}).get("operations", []))
for config in preview.get("function_configs", {}).values()
),
}
return merged
def process_can_commands(
self,
commands: object,
conversation_id: str | None = None,
execution_mode: str = "dry_run",
approval_token: str | None = None,
vehicle_id: str | None = None,
simulation_fault: str = "normal",
*,
hardware_client_id: str | None = None,
hardware_access_bypass: bool = True,
control_transport: str = WIRED_CAN_TRANSPORT,
) -> dict[str, Any]:
"""Compile and execute an exact UI command batch without intent parsing."""
resolved_conversation_id = conversation_id or f"conv-{uuid.uuid4().hex}"
if not CONVERSATION_ID_PATTERN.fullmatch(resolved_conversation_id):
raise ValueError(
"conversation_id must start with conv- and contain only lowercase letters, numbers, or hyphens"
)
vehicle_profile = self.vehicle_profiles.get(vehicle_id)
context = self._context_for_vehicle(
self.conversations.snapshot(resolved_conversation_id),
vehicle_profile,
)
direct_can_batch = build_direct_can_intent(commands, context)
normalized_commands, _parsed = direct_can_batch
return self.process(
";".join(normalized_commands),
provider="local",
conversation_id=resolved_conversation_id,
execution_mode=execution_mode,
approval_token=approval_token,
vehicle_id=vehicle_id,
simulation_fault=simulation_fault,
hardware_client_id=hardware_client_id,
hardware_access_bypass=hardware_access_bypass,
control_transport=control_transport,
_direct_can_batch=direct_can_batch,
)
def _adapt_speech_understanding(
self,
understanding: dict[str, Any],
context: dict[str, Any],
vehicle_profile: Any,
) -> dict[str, Any]:
"""Revalidate a server-owned speech result into the normal control IR.
Speech is allowed to skip a second natural-language parse, but it never
skips the authoritative capability catalog or ModelPlanAdapter. Native
CAN actions and configured-domain/seat actions are routed through the
same structures used by the rest of one-sentence orchestration.
"""
if understanding.get("execution_eligible") is not True:
raise ValueError("speech understanding is not execution eligible")
commands = understanding.get("commands")
if not isinstance(commands, list) or not commands:
raise ValueError("speech understanding has no executable commands")
native_actions: list[dict[str, Any]] = []
model_requests: list[dict[str, Any]] = []
detected_domains: list[str] = []
def add_payload(payload: object, source_text: str) -> None:
if not isinstance(payload, dict):
raise ValueError("speech command has no validated executor payload")
domain = str(payload.get("domain", ""))
capability = str(payload.get("capability", ""))
target = str(payload.get("target", ""))
operation = str(payload.get("operation", "set"))
parameters = payload.get("parameters")
if not domain or not capability or not target or not isinstance(parameters, dict):
raise ValueError("speech executor payload is incomplete")
definitions = tuple(
definition
for definition in self.capability_registry.find(capability)
if definition.domain == domain
)
if len(definitions) != 1:
raise ValueError(
f"speech capability is not uniquely registered: {domain}.{capability}"
)
if domain not in detected_domains:
detected_domains.append(domain)
# Catalog availability describes where a capability is declared,
# not necessarily which executor owns it. Roof, climate and
# ambient capabilities are declared in domain catalogs but some
# are byte-exact NativeCanRuntime operations. Keep this routing in
# lockstep with CapabilityResolver._executor_binding().
if capability in self.native_can_runtime.supported_capabilities:
native_actions.append(
{
"domain": domain,
"capability": capability,
"target": target,
"operation": operation,
"parameters": deepcopy(parameters),
"parse_source": "speech_semantic",
"source_span": source_text,
"reason": "语音语义层已按真实能力目录解析并由服务端重新校验。",
}
)
return
model_requests.append(
{
"domain": domain,
"capability": capability,
"target_scope": target,
"operation": operation,
"parameters": [
{"name": name, "value": value}
for name, value in parameters.items()
],
"reason": "语音语义层已按真实能力目录解析并由服务端重新校验。",
}
)
for command in commands:
if not isinstance(command, dict):
raise ValueError("speech command must be an object")
if (
command.get("status") != "active"
or command.get("negated") is True
or command.get("resolution_status") != "executable"
or command.get("validation_issues")
):
raise ValueError("speech command failed execution invariants")
source_text = str(command.get("source_text") or understanding.get("raw_text") or "")
payload = command.get("executor_payload")
if command.get("mode_profile_id"):
if not isinstance(payload, dict) or not isinstance(payload.get("operations"), list):
raise ValueError("speech mode command has no validated operations")
for operation_payload in payload["operations"]:
add_payload(operation_payload, source_text)
else:
add_payload(payload, source_text)
plan = {
"detected_domains": detected_domains,
"requests": model_requests,
"ambiguities": [],
"decision_summary": [
"复用服务端语音理解结果;未对 cleanText 进行第二次自然语言解析。"
],
"confidence": min(
float(command.get("confidence", 0.0))
for command in commands
if isinstance(command, dict)
),
}
parsed = self.model_plan_adapter.adapt(
plan,
context=context,
vehicle_profile=vehicle_profile,
utterance=str(understanding.get("raw_text") or ""),
)
parsed["native_can_actions"] = native_actions
parsed.setdefault("semantic_constraints", [])
parsed.setdefault("unresolved_spans", [])
if parsed.get("ambiguities") or parsed.get("deferred_requests"):
raise ValueError("speech command no longer passes capability resolution")
mapped_count = (
len(parsed.get("actions", ()))
+ len(parsed.get("domain_actions", ()))
+ len(parsed.get("native_can_actions", ()))
)
expected_count = len(model_requests) + len(native_actions)
if mapped_count != expected_count:
raise ValueError("speech command mapping lost a validated capability mutation")
return parsed
def process(
self,
utterance: str,
provider: str = "auto",
conversation_id: str | None = None,
execution_mode: str = "dry_run",
approval_token: str | None = None,
vehicle_id: str | None = None,
simulation_fault: str = "normal",
*,
hardware_client_id: str | None = None,
hardware_access_bypass: bool = True,
reasoning_effort: str | None = None,
control_transport: str = WIRED_CAN_TRANSPORT,
_local_preview: bool = False,
_excluded_control_signatures: set[tuple[Any, ...]] | None = None,
_model_parse_source: str = "cloud_model",
_allow_empty_control_result: bool = False,
_on_commands_validated: Callable[[dict[str, Any]], None] | None = None,
_direct_can_batch: tuple[list[str], dict[str, Any]] | None = None,
_direct_mode_profile_id: str | None = None,
_speech_understanding: dict[str, Any] | None = None,
_assistant_plan: dict[str, Any] | None = None,
) -> dict[str, Any]:
control_transport = self._validate_control_transport(control_transport)
# Creating a reusable scene is a configuration workflow. Synthesize
# and validate it in dry-run even when the console currently targets
# the real bench, otherwise virtual domains such as climate or ambient
# light would be rejected before the profile can be persisted.
if self._parse_mode_profile_create_name(utterance.strip()) is not None:
execution_mode = "dry_run"
if execution_mode != "hardware":
return self._process_once(
utterance,
provider,
conversation_id,
execution_mode,
approval_token,
vehicle_id,
simulation_fault,
reasoning_effort,
control_transport,
_local_preview,
_excluded_control_signatures,
_model_parse_source,
_allow_empty_control_result,
_on_commands_validated,
_direct_can_batch,
_direct_mode_profile_id,
_speech_understanding,
assistant_plan=_assistant_plan,
)
if control_transport == WIRELESS_MQTT_TRANSPORT:
# The head unit owns both physical CAN channels in wireless mode.
# Local T2G/ZLG hardware access, firmware profiles and the local
# physical-bench lease are unrelated to MQTT relay execution.
return self._process_once(
utterance,
provider,
conversation_id,
execution_mode,
approval_token,
vehicle_id,
simulation_fault,
reasoning_effort,
control_transport,
_local_preview,
_excluded_control_signatures,
_model_parse_source,
_allow_empty_control_result,
_on_commands_validated,
_direct_can_batch,
_direct_mode_profile_id,
_speech_understanding,
assistant_plan=_assistant_plan,
)
if hardware_access_bypass or self._driver_only_vehicle_execution_active(
control_transport
):
with self.hardware_execution.reserve():
return self._process_once(
utterance,
provider,
conversation_id,
execution_mode,
approval_token,
vehicle_id,
simulation_fault,
reasoning_effort,
control_transport,
_local_preview,
_excluded_control_signatures,
_model_parse_source,
_allow_empty_control_result,
_on_commands_validated,
_direct_can_batch,
_direct_mode_profile_id,
_speech_understanding,
assistant_plan=_assistant_plan,
)
authorized_client_id = self.hardware_access.ensure_authorized(hardware_client_id)
with self.hardware_execution.reserve():
with self.hardware_access.use(authorized_client_id):
return self._process_once(
utterance,
provider,
conversation_id,
execution_mode,
approval_token,
vehicle_id,
simulation_fault,
reasoning_effort,
control_transport,
_local_preview,
_excluded_control_signatures,
_model_parse_source,
_allow_empty_control_result,
_on_commands_validated,
_direct_can_batch,
_direct_mode_profile_id,
_speech_understanding,
assistant_plan=_assistant_plan,
)
def _process_once(
self,
utterance: str,
provider: str = "auto",
conversation_id: str | None = None,
execution_mode: str = "dry_run",
approval_token: str | None = None,
vehicle_id: str | None = None,
simulation_fault: str = "normal",
reasoning_effort: str | None = None,
control_transport: str = WIRED_CAN_TRANSPORT,
local_preview: bool = False,
excluded_control_signatures: set[tuple[Any, ...]] | None = None,
model_parse_source: str = "cloud_model",
allow_empty_control_result: bool = False,
on_commands_validated: Callable[[dict[str, Any]], None] | None = None,
direct_can_batch: tuple[list[str], dict[str, Any]] | None = None,
direct_mode_profile_id: str | None = None,
speech_understanding: dict[str, Any] | None = None,
assistant_plan: dict[str, Any] | None = None,
) -> dict[str, Any]:
control_transport = self._validate_control_transport(control_transport)
utterance = utterance.strip()
if not utterance:
raise ValueError("utterance must not be empty")
if len(utterance) > 1000:
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")
effective_reasoning_effort = (
self.openai.reasoning_effort
if reasoning_effort is None
else self.openai.validate_reasoning_effort(reasoning_effort)
)
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_create_name = self._parse_mode_profile_create_name(utterance)
mode_restore_name = self._parse_mode_profile_restore_name(utterance)
mode_update = self._parse_mode_profile_update(utterance)
control_plane_request = (
mode_save_name is not None
or mode_restore_name is not None
or mode_update is not None
)
hardware_request = execution_mode == "hardware"
if hardware_request and _has_mixed_driver_passenger_seat_scope(utterance):
raise HardwareSafetyError(
"hardware mode refuses a mixed CH1 driver/front-left and "
"CH0 passenger/front-right seat request"
)
conversation_id = conversation_id or f"conv-{uuid.uuid4().hex}"
if not CONVERSATION_ID_PATTERN.fullmatch(conversation_id):
raise ValueError("conversation_id must start with conv- and contain only lowercase letters, numbers, or hyphens")
vehicle_profile = self.vehicle_profiles.get(vehicle_id)
direct_mode_profile = (
self.mode_profiles.get_profile(
direct_mode_profile_id,
vehicle_profile.id,
)
if direct_mode_profile_id
else None
)
if mode_create_name is not None:
return self._process_mode_profile_create(
utterance,
provider,
conversation_id,
execution_mode,
approval_token,
vehicle_profile,
simulation_fault,
mode_create_name,
control_transport,
assistant_plan=assistant_plan,
)
request_id = f"req-{datetime.now(UTC).strftime('%Y%m%d%H%M%S')}-{uuid.uuid4().hex[:6]}"
context = self._context_for_vehicle(
self.conversations.snapshot(conversation_id),
vehicle_profile,
)
context["mode_profiles"] = self.mode_profiles.public_modes(vehicle_profile.id)
matched_modes = (
[direct_mode_profile]
if direct_mode_profile is not None
else []
if direct_can_batch is not None or speech_understanding is not None
else self.mode_profiles.match_text(
utterance,
vehicle_id=vehicle_profile.id,
)
)
traces: list[dict[str, Any]] = []
result: dict[str, Any] = {
"request_id": request_id,
"utterance": utterance,
"conversation_id": conversation_id,
"conversation_turn": context["turn_count"],
"status": "processing",
"provider_requested": provider,
"reasoning_effort_requested": effective_reasoning_effort,
"reasoning_effort_used": None,
"execution_mode": execution_mode,
"control_transport": control_transport,
"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
],
"mode_profile": None,
"decision_summary": [],
"deferred_requests": [],
"model_requests": [],
"intent_ir": None,
"function_config": None,
"function_configs": {},
"scene_config": None,
"preset_config": None,
"validation": [],
"command_plan": None,
"command_plans": {},
"execution_report": None,
"actuator_state": context["devices"],
"domain_state": context["domain_devices"],
"saved_presets": [
{"name": item["name"], "target": item["target"]}
for item in context.get("presets", [])
],
"trace_events": traces,
"can_commands": (
list(direct_can_batch[0]) if direct_can_batch is not None else []
),
"speech_understanding": (
{
"utterance_id": speech_understanding.get("utterance_id"),
"raw_text": speech_understanding.get("raw_text"),
"clean_text": speech_understanding.get("clean_text"),
"semantic_status": speech_understanding.get("semantic_status"),
}
if speech_understanding is not None
else None
),
}
started = time.perf_counter()
if mode_save_name is not None:
return self._process_mode_profile_save(
utterance,
request_id,
conversation_id,
context,
mode_save_name,
vehicle_profile,
result,
traces,
)
if mode_restore_name is not None:
return self._process_mode_profile_restore(
utterance,
request_id,
conversation_id,
mode_restore_name,
vehicle_profile,
result,
traces,
)
if mode_update is not None:
mode_name, change_text = mode_update
return self._process_mode_profile_update(
utterance,
request_id,
provider,
conversation_id,
context,
mode_name,
change_text,
vehicle_profile,
result,
traces,
)
if direct_mode_profile is not None:
mode_requests = self.mode_profiles.requests_for_profiles(
[direct_mode_profile]
)
detected_domains = list(
dict.fromkeys(
str(item.get("domain") or "")
for item in mode_requests
if str(item.get("domain") or "")
)
)
parsed = self.model_plan_adapter.adapt(
{
"detected_domains": detected_domains or ["seat"],
"requests": mode_requests,
"ambiguities": [],
"decision_summary": [
f"已载入模式“{direct_mode_profile.name}”保存的结构化操作。"
],
"confidence": 1.0,
},
context,
vehicle_profile,
)
saved_native_actions: list[dict[str, Any]] = []
for request in mode_requests:
parameters = {
str(item.get("name")): deepcopy(item.get("value"))
for item in request.get("parameters", [])
if isinstance(item, dict) and str(item.get("name") or "")
}
action = {
"domain": str(request.get("domain") or ""),
"capability": str(request.get("capability") or ""),
"target": str(request.get("target_scope") or "vehicle"),
"parameters": parameters,
"operation": str(request.get("operation") or "set"),
"reason": str(request.get("reason") or ""),
"execution_backend": "native_can",
"parse_source": "saved_mode_command_flow",
}
if self.native_can_runtime.supports(action):
saved_native_actions.append(action)
parsed["native_can_actions"] = [
*list(parsed.get("native_can_actions", [])),
*saved_native_actions,
]
provider_used = "saved_mode_command_flow"
provider_notice = None
elif direct_can_batch is not None:
parsed = deepcopy(direct_can_batch[1])
provider_used = "direct_can_control"
provider_notice = None
elif speech_understanding is not None:
parsed = self._adapt_speech_understanding(
speech_understanding,
context,
vehicle_profile,
)
parsed = self._mark_control_parse_source(
parsed,
"speech_semantic",
overwrite=True,
)
provider_used = "speech_semantic"
provider_notice = "使用服务端缓存并重新校验的语音结构化意图。"
else:
parsed, provider_used, provider_notice = self._interpret(
utterance,
provider,
context,
vehicle_profile,
reasoning_effort=reasoning_effort,
local_preview=local_preview,
model_parse_source=model_parse_source,
assistant_plan=assistant_plan,
)
dynamic_mode_stop = self._is_dynamic_mode_stop_request(utterance)
if dynamic_mode_stop:
parsed = self._apply_dynamic_mode_stop_request(parsed, utterance)
# Reuse the same conservative clause segmentation for the final
# native pass. Without this normalization a Chinese enumeration
# comma between independent controls can leak an early target
# (for example ``副驾``) into a later HVAC clause and can also
# hide otherwise valid window/temperature actions.
segmented_utterance = ",".join(
self._split_intent_clauses(utterance)
)
relative_resolution = (
self.native_can_relative_resolver.resolve(
segmented_utterance,
context,
)
if self.native_can_relative_resolver.matches(
segmented_utterance
)
else None
)
native_merge_utterance = segmented_utterance
if any(
request.get("capability") in {SAVE_PRESET_CAPABILITY, APPLY_PRESET_CAPABILITY}
for request in parsed.get("model_requests", [])
):
# These registered capabilities currently have one executable
# target (the passenger-seat posture store). Keep that
# adapter-resolved target from being discarded by the native
# parser's general unscoped-seat => driver default. The
# original utterance is still used by the language safety
# guards below.
native_merge_utterance = f"副驾座椅,{utterance}"
parsed = merge_native_can_intent(
parsed,
native_merge_utterance,
relative_actions=(
relative_resolution.actions if relative_resolution else None
),
relative_ambiguities=(
relative_resolution.ambiguities if relative_resolution else None
),
relative_claimed_controls=(
relative_resolution.claimed_controls if relative_resolution else None
),
)
if not dynamic_mode_stop:
# Apply repository-backed modes after the final native-CAN
# merge. Otherwise that merge can discard a valid passenger
# mode operation while enforcing its generic unscoped-seat
# CH1 default.
parsed = self._apply_mode_profiles(
parsed,
matched_modes,
context,
vehicle_profile,
utterance=utterance,
)
parsed = self._mark_control_parse_source(parsed, "local_rule")
if direct_mode_profile is not None:
parsed = self._mark_control_parse_source(
parsed,
"saved_mode_command_flow",
)
if local_preview:
# A preview may discard ordinary model-supplement questions, but
# target and safety conflicts are execution gates. Keeping these
# makes the assistant path no less strict than normal process().
parsed["ambiguities"] = [
item
for item in parsed.get("ambiguities", [])
if str(item.get("field", ""))
in {"target", "target_scope", "mode_profile", "risk", "safety", "utterance"}
or str(item.get("reason", "")).startswith(
("mode_target_", "safety_", "risk_", "mixed_")
)
]
parsed["unresolved_spans"] = [
item
for item in parsed.get("unresolved_spans", [])
if str(item.get("reason", "")).startswith(
("mode_target_", "safety_", "risk_", "mixed_")
)
]
parsed["model_requests"] = []
if excluded_control_signatures:
parsed = self._filter_excluded_controls(
parsed,
excluded_control_signatures,
)
parsed = apply_language_safety_guards(parsed, utterance)
parsed = self._partition_native_actions(
parsed,
promote_supported=(
direct_mode_profile is not None
or (
hardware_request
and (
self.vehicle_mqtt_settings.enabled
if control_transport == WIRELESS_MQTT_TRANSPORT
else self.vehicle_can_settings.enabled
)
)
),
)
result["parsed_control_signatures"] = [
list(self._control_signature(action))
for action in [
*parsed.get("actions", []),
*parsed.get("domain_actions", []),
*parsed.get("native_can_actions", []),
]
]
has_control = any(
parsed.get(key)
for key in (
"actions",
"domain_actions",
"native_can_actions",
"preset_actions",
)
)
if (
allow_empty_control_result
and not has_control
and not parsed.get("ambiguities")
):
result["provider_used"] = provider_used
result["decision_summary"] = [
*parsed.get("decision_summary", []),
"当前阶段没有新增的已验证 CAN 控制指令。",
]
result["deferred_requests"] = parsed.get("deferred_requests", [])
result["model_requests"] = parsed.get("model_requests", [])
result["intent_ir"] = build_intent_ir(
parsed,
utterance,
request_id,
provider_used,
self.semantic_capability_metadata,
)
result["validation"] = self.contracts.validate(result["intent_ir"])
result["status"] = "no_action"
self._trace(
traces,
request_id,
"intent_parse",
"succeeded",
"当前阶段未产生新的白名单控制动作。",
started,
{"provider": provider_used, "control_count": 0},
domain="orchestration",
)
self._persist(result)
return result
if hardware_request and not control_plane_request:
if (
control_transport == WIRELESS_MQTT_TRANSPORT
and parsed.get("domain_actions")
and (parsed.get("actions") or parsed.get("native_can_actions"))
):
parsed = self._defer_nonwhitelisted_wireless_actions(parsed)
if parsed.get("domain_actions"):
unsupported = sorted(
{
str(action.get("capability", "unknown"))
for action in parsed["domain_actions"]
}
)
raise HardwareSafetyError(
"hardware mode refuses virtual or unverified vehicle actions: "
+ ", ".join(unsupported)
)
if not parsed.get("actions") and not parsed.get("native_can_actions"):
raise HardwareSafetyError(
"hardware mode requires at least one verified seat or vehicle CAN operation"
)
if parsed.get("actions") and parsed.get("native_can_actions"):
if (
control_transport == WIRED_CAN_TRANSPORT
and self.hardware_settings.backend
not in {"zlgcan", "zlgcan-linux"}
):
raise HardwareSafetyError(
"linked seat/vehicle execution requires a shared ZLG backend"
)
if control_transport == WIRED_CAN_TRANSPORT:
try:
seat_channel = int(self.hardware_settings.channel, 0)
except ValueError as exc:
raise HardwareSafetyError(
"ZLG seat CAN channel must be an integer"
) from exc
if seat_channel == self.vehicle_can_settings.channel:
raise HardwareSafetyError(
"seat and vehicle CAN must use different adapter channels"
)
self._trace(
traces,
request_id,
"intent_parse",
"succeeded",
f"使用 {provider_used} 完成结构化意图抽取。",
started,
{
"provider": provider_used,
"confidence": parsed["confidence"],
"conversation_turn": context["turn_count"],
},
)
if provider_notice:
parsed["decision_summary"].append(provider_notice)
result["provider_used"] = provider_used
if "openai:" in provider_used:
result["reasoning_effort_used"] = effective_reasoning_effort
result["decision_summary"] = parsed["decision_summary"]
result["deferred_requests"] = parsed.get("deferred_requests", [])
result["model_requests"] = parsed.get("model_requests", [])
if (
parsed.get("semantic_constraints")
and not parsed.get("actions")
and not parsed.get("domain_actions")
and not parsed.get("native_can_actions")
and not parsed.get("ambiguities")
and not parsed.get("unresolved_spans")
):
intent_ir = build_intent_ir(
parsed,
utterance,
request_id,
provider_used,
self.semantic_capability_metadata,
)
result["intent_ir"] = intent_ir
result["validation"] = self.contracts.validate(intent_ir)
result["status"] = "no_action"
self._trace(
traces,
request_id,
"safety_check",
"succeeded",
"检测到禁止或保持约束;未生成、编译或发送任何 CAN 动作。",
time.perf_counter(),
{"constraint_count": len(intent_ir["spec"]["constraints"]), "execution_eligible": False},
domain="orchestration",
)
self._persist(result)
return result
context_started = time.perf_counter()
self._trace(
traces,
request_id,
"context_resolve",
"succeeded",
(
f"已将会话 {conversation_id} 的第 {context['turn_count']} 轮执行状态"
"、域能力注册表和长期模式配置作为模型上下文;"
"本地层仅注入已命中的长期模式动作。"
),
context_started,
{
"conversation_id": conversation_id,
"turn_count": context["turn_count"],
"last_target": context["last_target"],
"state_before": context["devices"],
"capability_domains": [
item["domain"] for item in self.capability_context["domains"]
],
"matched_modes": [profile.id for profile in matched_modes],
},
)
route_started = time.perf_counter()
detected_domains = parsed.get("detected_domains", ["seat"])
seat_only = detected_domains == ["seat"]
self._trace(
traces,
request_id,
"domain_route",
"succeeded",
(
"仅识别到 seat 领域,使用已验证的座椅单域闭环。"
if seat_only
else (
f"识别到 {','.join(detected_domains)} 领域,"
"按领域边界生成独立 FunctionConfig。"
)
),
route_started,
{
"domains": detected_domains,
"mode": "single_domain" if len(detected_domains) == 1 else "cross_domain",
},
)
if parsed.get("domain_actions") or parsed.get("native_can_actions") or any(
domain != "seat" for domain in detected_domains
):
return self._process_orchestrated(
utterance,
request_id,
provider_used,
conversation_id,
context,
parsed,
result,
traces,
hardware_request=hardware_request,
simulated_hardware_request=simulated_hardware_request,
simulation_fault=simulation_fault,
vehicle_profile=vehicle_profile,
control_transport=control_transport,
approval_token=approval_token,
on_commands_validated=on_commands_validated,
)
intent_ir = build_intent_ir(
parsed,
utterance,
request_id,
provider_used,
self.semantic_capability_metadata,
)
result["intent_ir"] = intent_ir
generation_started = time.perf_counter()
function_config = build_function_config(parsed, request_id, provider_used)
preset_config = None
if parsed.get("preset_actions"):
preset_action = parsed["preset_actions"][0]
preset_config = build_function_preset(
preset_action["target"],
preset_action["preset_name"],
context["devices"][preset_action["target"]],
request_id,
provider_used,
)
result["function_config"] = function_config
result["function_configs"] = {"seat": function_config} if function_config else {}
result["preset_config"] = preset_config
generated_document = function_config or preset_config
self._trace(
traces,
request_id,
"function_generate",
"succeeded" if generated_document else "waiting_confirmation",
(
"已生成座椅 FunctionConfig。"
if function_config
else (
"已从当前姿态生成 FunctionPreset。"
if preset_config
else "没有足够信息生成可执行配置。"
)
),
generation_started,
{
"operation_count": len(parsed["actions"]),
"preset_operation_count": (
len(preset_config["spec"]["operations"]) if preset_config else 0
),
},
)
schema_started = time.perf_counter()
validation = self.contracts.validate(intent_ir)
if function_config is not None:
validation.extend(self.contracts.validate(function_config))
if preset_config is not None:
validation.extend(self.contracts.validate(preset_config))
schema_errors = [issue for issue in validation if issue["severity"] == "error"]
self._trace(
traces,
request_id,
"schema_validate",
"failed" if schema_errors else "succeeded",
f"公共与领域 Schema 校验{'发现错误' if schema_errors else '通过'}。",
schema_started,
{"error_count": len(schema_errors)},
)
semantic_started = time.perf_counter()
semantic_issues = validate_semantics(parsed, function_config)
validation.extend(semantic_issues)
if hardware_request:
self._adapt_validation_for_hardware(validation)
semantic_errors = [issue for issue in semantic_issues if issue["severity"] == "error"]
self._trace(
traces,
request_id,
"domain_validate",
"failed" if semantic_errors else "succeeded",
"座椅语义校验阻止了不受支持的请求。" if semantic_errors else "座椅能力和目标映射校验通过。",
semantic_started,
{"error_count": len(semantic_errors), "warning_count": len(semantic_issues) - len(semantic_errors)},
)
result["validation"] = validation
all_errors = [issue for issue in validation if issue["severity"] == "error"]
if all_errors:
if any(issue["code"] == "AMBIGUOUS_INTENT" for issue in all_errors):
result["status"] = "needs_confirmation"
else:
result["status"] = "validation_failed"
self._persist(result)
return result
if parsed.get("deferred_requests") and not parsed.get("actions"):
result["status"] = "capability_unavailable"
self._persist(result)
return result
if preset_config is not None and not parsed.get("actions"):
preset_action = parsed["preset_actions"][0]
compile_started = time.perf_counter()
self._trace(
traces,
request_id,
"compile",
"succeeded",
"FunctionPreset 属于控制平面数据,不生成 CAN 命令。",
compile_started,
{
"preset_name": preset_action["preset_name"],
"command_count": 0,
},
)
safety_started = time.perf_counter()
self._trace(
traces,
request_id,
"safety_check",
"succeeded",
"预设仅保存已验证的五轴目标,不改变硬件发送边界。",
safety_started,
{"hardware_execution_allowed": False},
)
execute_started = time.perf_counter()
execution_report = {
"apiVersion": "cognitive-agent/v1alpha1",
"kind": "ExecutionReport",
"metadata": {
"id": f"report-{request_id}",
"name": "Seat posture preset save report",
"version": "1.0.0",
"request_id": request_id,
"labels": {"control_plane": "preset_store"},
},
"spec": {
"mode": "dry_run",
"status": "succeeded",
"started_at": _now(),
"finished_at": _now(),
"results": [
{
"ref": f"save-{preset_action['target']}-posture-preset",
"domain": "seat",
"status": "succeeded",
"message": (
f"已将 {preset_action['target']} 当前五轴姿态保存为"
f"“{preset_action['preset_name']}”。"
),
"telemetry": {
"operation": "save_posture_preset",
"target": preset_action["target"],
"preset_name": preset_action["preset_name"],
"captured_axis_count": len(
preset_config["spec"]["operations"]
),
"storage_scope": "conversation_durable",
"can_command_emitted": False,
},
}
],
},
}
report_issues = self.contracts.validate(execution_report)
validation.extend(report_issues)
result["execution_report"] = execution_report
if report_issues:
result["status"] = "execution_report_invalid"
self._persist(result)
return result
updated_context = self.conversations.save_preset(
conversation_id,
utterance,
preset_action["target"],
preset_action["preset_name"],
preset_config,
parsed["decision_summary"],
)
updated_context = self._context_for_vehicle(
updated_context,
vehicle_profile,
)
result["status"] = "succeeded"
result["conversation_turn"] = updated_context["turn_count"]
result["actuator_state"] = updated_context["devices"]
result["saved_presets"] = [
{"name": item["name"], "target": item["target"]}
for item in updated_context["presets"]
]
self._trace(
traces,
request_id,
"execute",
"succeeded",
"当前姿态已写入会话预设仓库;未生成 CAN 帧。",
execute_started,
{
"mode": "control_plane",
"preset_name": preset_action["preset_name"],
},
)
self._trace(
traces,
request_id,
"state_update",
"succeeded",
f"预设“{preset_action['preset_name']}”已持久化,可在后续指令中直接调用。",
time.perf_counter(),
{
"preset_count": len(updated_context["presets"]),
"storage_scope": "conversation_durable",
},
)
self._persist(result)
return result
compile_started = time.perf_counter()
try:
if hardware_request:
if control_transport != WIRELESS_MQTT_TRANSPORT:
self.hardware_settings.validate_function_config(function_config)
command_plan = self.hardware_compiler.compile(
function_config, context["devices"]
)
else:
command_plan = self.compiler.compile(function_config)
except ValueError as exc:
compile_issue = {
"severity": "error",
"code": "CAN_COMPILE_REJECTED",
"path": "spec.operations",
"message": str(exc),
}
validation.append(compile_issue)
result["validation"] = validation
result["status"] = "compile_failed"
self._trace(
traces,
request_id,
"compile",
"failed",
f"座椅 CAN 编译被安全约束阻止:{exc}",
compile_started,
{"error": str(exc), "hardware_request": hardware_request},
)
self._persist(result)
return result
plan_issues = self.contracts.validate(command_plan)
validation.extend(plan_issues)
result["command_plan"] = command_plan
result["command_plans"] = {"seat": command_plan}
self._trace(
traces,
request_id,
"compile",
"failed" if plan_issues else "succeeded",
"配置已确定性编译为座椅 CAN 语义信号计划。",
compile_started,
{
"messages": [
command["message"] for command in command_plan["spec"]["commands"]
],
"command_count": len(command_plan["spec"]["commands"]),
"signal_count": sum(
len(command["signals"])
for command in command_plan["spec"]["commands"]
),
},
)
if plan_issues:
result["status"] = "compile_failed"
self._persist(result)
return result
self._emit_validated_commands(result, on_commands_validated)
if hardware_request:
self._authorize_compiled_hardware_delivery(
parsed,
approval_token,
control_transport,
)
safety_started = time.perf_counter()
self._trace(
traces,
request_id,
"safety_check",
"succeeded",
(
"无线 MQTT CAN0 座椅语义白名单与固件原生 0x300 报文白名单已通过;"
"本地 T2G 固件 profile、USB-CAN 与物理台架连接不参与校验。"
if hardware_request
and control_transport == WIRELESS_MQTT_TRANSPORT
else (
"硬件启用、授权令牌、固件 profile 与目标座椅配置门禁已通过;"
if self.hardware_settings.approval_required
else "硬件启用、调试免令牌、固件 profile 与目标座椅配置门禁已通过;"
)
+ "板端 Position_Valid 将在发送绝对移动命令前实时检查。"
if hardware_request
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": (
"remote_native_can0_feedback"
if hardware_request
and control_transport == WIRELESS_MQTT_TRANSPORT
else "runtime_can_preflight"
if hardware_request
else "not_applicable"
),
"mapping_status": (
"mqtt_can0_native_firmware_whitelist"
if hardware_request
and control_transport == WIRELESS_MQTT_TRANSPORT
else "protocol_hil_verified_combined_firmware_hil_pending"
if hardware_request
else "draft"
),
"firmware_profiles": (
sorted(self.hardware_settings.profiles)
if hardware_request
and control_transport != WIRELESS_MQTT_TRANSPORT
else []
),
},
)
execute_started = time.perf_counter()
if hardware_request:
mqtt_selected = control_transport == WIRELESS_MQTT_TRANSPORT
vehicle_safety_preflight = None
wired_bridge = None
try:
if mqtt_selected:
vehicle_safety_preflight = _wireless_mqtt_publish_preflight()
hardware_executor = self._new_hardware_executor(
lambda: self._vehicle_mqtt_transport.bus(channel=0),
remote_transport=True,
)
elif (
self.hardware_settings.native_passenger_can0
and self.vehicle_can_settings.configured
):
wired_bridge = self._new_linux_wired_dual_channel_bridge()
vehicle_safety_preflight = observe_vehicle_safety(
wired_bridge.channel_bus(1),
timeout_ms=min(
5_000,
max(
1_000,
self.vehicle_can_settings.feedback_timeout_ms,
),
),
required_channel=1,
)
require_stationary_vehicle(vehicle_safety_preflight)
hardware_executor = self._new_hardware_executor(
lambda: wired_bridge.channel_bus(0)
)
else:
hardware_executor = self._new_hardware_executor()
execution_report = hardware_executor.execute(
command_plan, function_config, context["devices"]
)
except VehicleSafetyPreflightError as exc:
raise HardwareSafetyError(
"CH1 vehicle safety preflight refused passenger CAN0 execution: "
f"{exc}"
) from exc
finally:
if wired_bridge is not None:
wired_bridge.close()
if vehicle_safety_preflight is not None:
for item in execution_report["spec"].get("results", []):
item.setdefault("telemetry", {})[
"vehicle_safety_preflight"
] = vehicle_safety_preflight
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"]
)
execution_outcome, execution_partially_succeeded = (
_annotate_execution_report(execution_report)
)
report_issues = self.contracts.validate(execution_report)
validation.extend(report_issues)
result["execution_report"] = execution_report
result["execution_outcome"] = execution_outcome
execution_succeeded = (
not report_issues
and execution_report["spec"]["status"] == "succeeded"
)
if report_issues:
result["status"] = "execution_report_invalid"
elif execution_partially_succeeded:
result["status"] = "partially_succeeded"
elif not execution_succeeded:
result["status"] = "execution_failed"
elif parsed.get("deferred_requests"):
result["status"] = "partially_succeeded"
else:
result["status"] = "succeeded"
execution_results = execution_report["spec"]["results"]
stop_all_attempted = any(
item.get("telemetry", {}).get("stop_all_attempted")
for item in execution_results
)
firmware_gate_failed = any(
item.get("telemetry", {}).get("error_type")
== "FirmwareProgrammingError"
for item in execution_results
)
failed_execution = next(
(item for item in execution_results if item.get("status") == "failed"),
None,
)
failed_telemetry = (
failed_execution.get("telemetry", {}) if failed_execution else {}
)
hardware_failure_summary = (
f"真实 CAN 执行失败:{failed_execution['message']}"
if failed_execution
else "真实 CAN 执行失败,未获得满足目标的物理反馈。"
)
if stop_all_attempted:
hardware_failure_summary += " 移动路径已尝试发送 STOP_ALL。"
self._trace(
traces,
request_id,
"execute",
"succeeded" if execution_succeeded else "failed",
(
"真实 CAN 命令已提交;当前反馈窗口未确认车辆效果,状态以实时 CAN 回读为准。"
if hardware_request
and execution_succeeded
and execution_outcome == "submitted_unverified"
else "真实 CAN 命令已发送,并收到 IAR 固件的实际状态回读。"
if hardware_request and execution_succeeded
else (
(
"固件身份验证或自动烧录失败;未发送座椅控制帧。"
if firmware_gate_failed
else hardware_failure_summary
)
if hardware_request
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,
{
"mode": execution_report["spec"]["mode"],
"result_count": len(execution_report["spec"]["results"]),
"firmware_gate_failed": firmware_gate_failed,
"stop_all_attempted": stop_all_attempted,
"motor_result": failed_telemetry.get("motor_result_name"),
"failure_category": failed_telemetry.get("failure_category"),
"hall_before": failed_telemetry.get("hall_before"),
"hall_after": failed_telemetry.get("hall_after"),
},
)
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
)
if hardware_request
else parsed["actions"]
)
updated_context = self.conversations.apply_successful_actions(
conversation_id,
utterance,
state_actions,
parsed["decision_summary"],
)
physical_state = execution_report.get("spec", {}).get(
"physical_state"
)
if hardware_request and isinstance(physical_state, dict):
updated_context = self.conversations.synchronize_physical_passenger(
conversation_id,
physical_state,
)
updated_context = self._context_for_vehicle(
updated_context,
vehicle_profile,
)
result["conversation_turn"] = updated_context["turn_count"]
result["actuator_state"] = updated_context["devices"]
result["domain_state"] = updated_context["domain_devices"]
self._trace(
traces,
request_id,
"state_update",
"succeeded",
f"会话状态已更新为第 {updated_context['turn_count']} 轮,可用于下一条指令。",
time.perf_counter(),
{"state_after": updated_context["devices"]},
)
elif hardware_request:
physical_state = execution_report.get("spec", {}).get(
"physical_state"
)
if isinstance(physical_state, dict) and (
physical_state.get("position")
or physical_state.get("ventilation")
or physical_state.get("heating")
):
updated_context = self.conversations.synchronize_physical_passenger(
conversation_id,
physical_state,
)
updated_context = self._context_for_vehicle(
updated_context,
vehicle_profile,
)
result["actuator_state"] = updated_context["devices"]
result["domain_state"] = updated_context["domain_devices"]
self._trace(
traces,
request_id,
"state_sync",
"succeeded",
"真实执行未完成,但已按 CAN 物理回读同步副驾状态。",
time.perf_counter(),
{"state_after": updated_context["devices"]},
)
telemetry = next(
(
item.get("telemetry", {})
for item in execution_report["spec"]["results"]
if item.get("telemetry", {}).get("feedback_frame")
),
execution_report["spec"]["results"][0].get("telemetry", {}),
)
self._trace(
traces,
request_id,
"telemetry",
"succeeded" if execution_succeeded else "failed",
(
"IAR 固件反馈帧中的实际状态与请求目标一致。"
if hardware_request and execution_succeeded
else (
(
f"未获得满足目标的真实硬件反馈:"
f"{failed_execution['message']}"
if failed_execution
else "未获得满足目标的真实硬件反馈。"
)
if hardware_request
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(),
{
"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)
return result
@staticmethod
def _adapt_validation_for_hardware(
validation: list[dict[str, Any]],
) -> None:
"""Replace reference-vehicle dry-run warnings with HIL bench semantics."""
hardware_messages = {
"LEVEL_COMMAND_SIMULATED": (
"LEVEL_COMMAND_T2G_MAPPED",
"目标档位已映射到统一 T2G 固件的 0x29E 档位信号;"
"执行结果以 0x4FA 物理 CAN 回读为准。",
),
"SYNTHETIC_MOTION_CAN": (
"T2G_MOTION_PROTOCOL_MAPPED",
"参考车型的虚拟运动矩阵未用于本次 HIL;座椅移动通过统一 T2G 固件的 "
"0x300 命令和 0x301/0x302 物理反馈闭环执行。",
),
"DRIVER_MOTION_INTERLOCK_NOT_IMPLEMENTED": (
"BENCH_ONLY_NO_VEHICLE_INTERLOCKS",
"当前真实移动路径仅限独立 T2G 台架;尚未接入车速、挡位、乘员和防夹信号,"
"不得直接用于车载座椅系统。",
),
"COMBINED_THERMAL_SIMULATED": (
"COMBINED_THERMAL_T2G_MAPPED",
"加热与通风组合目标由统一 T2G 固件执行并以 0x4FA 物理反馈确认;"
"接入完整座椅总成前仍需补充热安全互锁验证。",
),
"VIRTUAL_EXECUTION_ONLY": (
"REFERENCE_MATRIX_OVERRIDDEN_BY_T2G_BENCH",
"参考车型矩阵元数据仍不完整;本次 HIL 仅使用仓库内已验证的 "
"T2G 统一固件协议、身份门禁和物理反馈闭环。",
),
}
for issue in validation:
replacement = hardware_messages.get(str(issue.get("code")))
if replacement is not None:
issue["code"], issue["message"] = replacement
@staticmethod
def _hardware_feedback_actions(
actions: list[dict[str, Any]],
function_config: dict[str, Any],
execution_report: dict[str, Any],
) -> list[dict[str, Any]]:
"""Project authoritative physical feedback into conversation/UI state."""
feedback_by_ref = {
item["ref"]: item.get("telemetry", {})
for item in execution_report["spec"]["results"]
if _operation_verification_status(item) == "verified_success"
}
updated: list[dict[str, Any]] = []
operations = function_config["spec"]["operations"]
for source_action, operation in zip(actions, operations, strict=True):
telemetry = feedback_by_ref.get(operation["id"])
if telemetry is None:
# Submission alone is not an authoritative vehicle-state
# update. The asynchronous CAN/MQTT state monitor will apply
# the change if and when the vehicle reports it.
continue
action = deepcopy(source_action)
if action.get("action_type") == "position":
if telemetry.get("effective_position_pct") is not None:
action["position_pct"] = int(
telemetry["effective_position_pct"]
)
if telemetry.get("position_valid") is not None:
action["position_valid"] = bool(
telemetry["position_valid"]
)
if telemetry.get("motion_mode") in {
"relative_timed_duty",
"timed_user_request",
}:
action["position_estimated"] = True
action["feedback_basis"] = (
"timed_endpoint_or_hall_confirmed_open_loop"
if telemetry.get("motion_mode") == "timed_user_request"
else "relative_hall_confirmed_open_loop"
)
updated.append(action)
continue
raw = telemetry.get("feedback_value")
if raw is not None:
raw_value = int(raw)
action["state"] = "off" if raw_value <= 1 else "on"
action["requested_level"] = max(0, raw_value - 1)
updated.append(action)
physical_state = execution_report.get("spec", {}).get("physical_state")
if isinstance(physical_state, dict):
positions = physical_state.get("position", {})
by_axis = {
action.get("axis"): action
for action in updated
if action.get("action_type") == "position"
}
for axis, feedback in positions.items():
if not isinstance(feedback, dict) or feedback.get("position_pct") is None:
continue
action = by_axis.get(axis)
if action is None:
action = {
"domain": "seat",
"target": "passenger",
"action_type": "position",
"axis": axis,
}
updated.append(action)
by_axis[axis] = action
action["position_pct"] = int(feedback["position_pct"])
if feedback.get("position_valid") is not None:
action["position_valid"] = bool(feedback["position_valid"])
action["feedback_basis"] = "physical_can_snapshot"
by_function = {
action.get("function"): action
for action in updated
if action.get("function") in {"ventilation", "heating"}
}
for feature in ("ventilation", "heating"):
feedback = physical_state.get(feature)
if not isinstance(feedback, dict):
continue
action = by_function.get(feature)
if action is None:
action = {
"domain": "seat",
"target": "passenger",
"function": feature,
}
updated.append(action)
by_function[feature] = action
action["state"] = (
"on" if feedback.get("state") == "on" else "off"
)
action["requested_level"] = int(feedback.get("level", 0))
for action in updated:
action["physical_target_alias"] = "t2g_primary_seat"
return updated
@staticmethod
def _physical_ch1_feedback_states(
reports: dict[str, dict[str, Any]],
) -> list[dict[str, Any]]:
"""Return only fully verified decoded CH1 feedback state patches."""
states: list[dict[str, Any]] = []
for report in reports.values():
for item in report.get("spec", {}).get("results", []):
feedback_state = authoritative_physical_ch1_feedback_state(item)
if feedback_state is not None:
states.append(feedback_state)
return states
@staticmethod
def _parse_mode_profile_save_name(utterance: str) -> str | None:
explicit = SAVE_MODE_PROFILE_NAME.search(utterance)
if explicit is not None:
return explicit.group("name").strip()
save_like = any(word in utterance for word in ("保存", "记住", "记为"))
mode_context = any(
word in utterance
for word in ("这个模式", "这个配置", "当前模式", "长期", "固化")
)
if not save_like or not mode_context:
return None
generic = re.search(
r"(?:保存为|保存成|记为|命名为)(?P<name>[^,。,.!!\s]{1,64}?模式)",
utterance,
)
return generic.group("name").strip() if generic is not None else None
@staticmethod
def _parse_mode_profile_create_name(utterance: str) -> str | None:
explicit = CREATE_MODE_PROFILE_NAME.search(utterance)
return explicit.group("name").strip() if explicit is not None else None
@staticmethod
def _parse_mode_profile_update(utterance: str) -> tuple[str, str] | None:
explicit = UPDATE_MODE_PROFILE_PREFIX.search(utterance)
if explicit is not None:
change_text = str(explicit.group("change") or "").strip()
return explicit.group("name").strip(), change_text
inline = UPDATE_MODE_PROFILE_BA.search(utterance)
if inline is None:
return None
change_text = inline.group("change").strip("的").strip()
if not any(word in change_text for word in ("改", "调", "设", "更新", "变")):
return None
return inline.group("name").strip(), change_text
@staticmethod
def _parse_mode_profile_restore_name(utterance: str) -> str | None:
explicit = RESTORE_MODE_PROFILE_PREFIX.search(utterance)
if explicit is not None:
return explicit.group("name").strip()
suffix = RESTORE_MODE_PROFILE_SUFFIX.search(utterance)
return suffix.group("name").strip() if suffix is not None else None
def _apply_mode_profiles(
self,
parsed: dict[str, Any],
matched_modes: list[ModeProfile],
context: dict[str, Any],
vehicle_profile: VehicleProfile,
*,
utterance: str,
) -> dict[str, Any]:
if not matched_modes:
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
target_conflict = self._explicit_mode_target_conflict(
matched_modes[0],
utterance,
)
if target_conflict is not None:
rejected = deepcopy(parsed)
for key in (
"actions",
"domain_actions",
"native_can_actions",
"preset_actions",
"preset_events",
"deferred_requests",
"model_requests",
):
rejected[key] = []
requested_scope, supported_scopes = target_conflict
rejected["ambiguities"] = [
*rejected.get("ambiguities", []),
{
"field": "mode_profile",
"question": (
f"该模式的座椅目标为 {', '.join(supported_scopes)},"
f"与用户指定的 {requested_scope} 不一致,请重新确认。"
),
"reason": "mode_target_scope_mismatch",
},
]
rejected["unresolved_spans"] = [
*rejected.get("unresolved_spans", []),
{
"text": utterance,
"reason": "mode_target_scope_mismatch",
},
]
rejected["decision_summary"] = [
*rejected.get("decision_summary", []),
"用户显式座位与功能模式登记目标不一致,已停止所有模式动作。",
]
return rejected
parsed = self._apply_implicit_mode_target_policy(
parsed,
matched_modes,
utterance,
)
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 = []
for request in mode_requests:
if request["domain"] not in detected_domains:
detected_domains.append(request["domain"])
mode_plan = {
"detected_domains": detected_domains or ["seat"],
"requests": mode_requests,
"ambiguities": [],
"decision_summary": [
"命中长期模式配置:"
+ "、".join(profile.name for profile in matched_modes)
+ "。"
],
"confidence": max(float(parsed.get("confidence", 0)), 0.97),
}
mode_parsed = self.model_plan_adapter.adapt(
mode_plan,
context,
vehicle_profile,
)
parsed_has_explicit_request = bool(
parsed.get("actions")
or parsed.get("domain_actions")
or parsed.get("preset_actions")
or parsed.get("preset_events")
or parsed.get("model_requests")
)
merged = deepcopy(parsed)
merged["actions"] = self._merge_control_actions(
mode_parsed.get("actions", []),
parsed.get("actions", []),
)
merged["domain_actions"] = self._merge_control_actions(
mode_parsed.get("domain_actions", []),
parsed.get("domain_actions", []),
)
for key in (
"preset_actions",
"preset_events",
"deferred_requests",
"ambiguities",
"model_requests",
):
parsed_values = parsed.get(key, [])
if key == "ambiguities" and not parsed_has_explicit_request:
# A local parser does not know arbitrary named modes and emits
# a generic unsupported-capability ambiguity. A unique,
# repository-backed mode match is authoritative for that case.
parsed_values = []
merged[key] = self._merge_unique(
mode_parsed.get(key, []),
parsed_values,
)
merged["decision_summary"] = [
*mode_parsed.get("decision_summary", []),
*parsed.get("decision_summary", []),
]
merged["detected_domains"] = self._merge_domains(
mode_parsed.get("detected_domains", []),
parsed.get("detected_domains", []),
)
merged["confidence"] = max(
float(mode_parsed.get("confidence", 0)),
float(parsed.get("confidence", 0)),
)
return merged
@staticmethod
def _explicit_mode_target_conflict(
profile: ModeProfile,
utterance: str,
) -> tuple[str, tuple[str, ...]] | None:
"""Reject a named mode when its persisted seat scope contradicts text."""
aliases = (profile.id, profile.name, *profile.aliases)
mode_clause = next(
(
clause
for clause in re.split(r"[,,;;。!?!?\n]+", utterance)
if any(
re.sub(r"\s+", "", alias.casefold())
in re.sub(r"\s+", "", clause.casefold())
for alias in aliases
if alias
)
),
utterance,
)
requested = resolve_explicit_seat_scopes(
mode_clause,
assume_seat_context=True,
)
if requested is None:
return None
normalized_scopes = {
"driver" if operation.target_scope == "front_left" else
"passenger" if operation.target_scope == "front_right" else
"rear" if operation.target_scope in {"rear", "rear_left", "rear_right"} else
operation.target_scope
for operation in profile.operations
if operation.domain == "seat"
}
if requested == normalized_scopes:
return None
return ",".join(sorted(requested)), tuple(sorted(normalized_scopes))
@staticmethod
def _apply_implicit_mode_target_policy(
parsed: dict[str, Any],
matched_modes: list[ModeProfile],
utterance: str,
) -> dict[str, Any]:
"""Let one matched mode own only an omitted seat target.
The native CH1 parser can add an implicit-driver ambiguity before or
after mode injection. A unique repository-backed mode already carries
authoritative target scopes on every operation, so that one tagged
default is redundant. Explicit scopes and all other safety questions
remain untouched.
"""
if len(matched_modes) != 1 or (
DRIVER_SEAT_SCOPE_PATTERN.search(utterance)
or PASSENGER_SEAT_SCOPE_PATTERN.search(utterance)
):
return parsed
ambiguities = list(parsed.get("ambiguities", []))
filtered = [
item
for item in ambiguities
if item.get("reason") != "implicit_driver_default_unresolved"
]
if len(filtered) == len(ambiguities):
return parsed
resolved = deepcopy(parsed)
resolved["ambiguities"] = filtered
return resolved
@staticmethod
def _is_dynamic_mode_stop_request(utterance: str) -> bool:
compact = re.sub(r"\s+", "", utterance)
if any(negation in compact for negation in ("不要停止", "无需停止", "别停止")):
return False
return any(word in compact for word in DYNAMIC_MODE_STOP_WORDS) and any(
word in compact for word in DYNAMIC_MODE_WORDS
)
def _apply_dynamic_mode_stop_request(
self,
parsed: dict[str, Any],
utterance: str,
) -> dict[str, Any]:
"""Make explicit stop language authoritative over a matched start profile."""
stopped = deepcopy(parsed)
stopped["actions"] = [
action
for action in stopped.get("actions", [])
if action.get("action_type") not in {"dynamic_mode", "dynamic_mode_stop"}
]
stopped["actions"].append(
{"action_type": "dynamic_mode_stop", "target": "passenger"}
)
stopped["model_requests"] = [
request
for request in stopped.get("model_requests", [])
if request.get("capability")
not in {"seat.start_dynamic_mode", DYNAMIC_MODE_STOP_CAPABILITY}
]
stopped["model_requests"].append(
{
"domain": "seat",
"capability": DYNAMIC_MODE_STOP_CAPABILITY,
"target_scope": "passenger",
"operation": "deactivate",
"parameters": [],
"reason": "用户明确要求停止动态座椅循环。",
"source_span": utterance,
}
)
stopped["ambiguities"] = [
item
for item in stopped.get("ambiguities", [])
if item.get("field") != "capability"
]
stopped["detected_domains"] = self._merge_domains(
["seat"], stopped.get("detected_domains", [])
)
stopped["decision_summary"] = [
"识别到停止动态座椅模式指令;不会重新启动同名长期模式。",
*stopped.get("decision_summary", []),
]
stopped["confidence"] = max(float(stopped.get("confidence", 0)), 0.99)
return stopped
@staticmethod
def _contains_seat_preset_request(parsed: dict[str, Any]) -> bool:
capabilities = {
request.get("capability")
for request in parsed.get("model_requests", [])
if isinstance(request, dict)
}
return bool(
parsed.get("preset_actions")
or parsed.get("preset_events")
or SAVE_PRESET_CAPABILITY in capabilities
or APPLY_PRESET_CAPABILITY in capabilities
)
@classmethod
def _merge_control_actions(
cls,
mode_items: list[dict[str, Any]],
parsed_items: list[dict[str, Any]],
) -> list[dict[str, Any]]:
parsed_controls = {
cls._control_signature(item)
for item in parsed_items
}
candidates = [
*parsed_items,
*(
item
for item in mode_items
if cls._control_signature(item) not in parsed_controls
),
]
return cls._merge_unique([], candidates)
@staticmethod
def _control_signature(action: dict[str, Any]) -> tuple[Any, ...]:
if action.get("domain"):
return (
action.get("domain"),
action.get("capability"),
action.get("target"),
)
if action.get("action_type") == "position":
return (
"seat",
"position",
action.get("target"),
action.get("axis"),
)
if action.get("action_type") == "dynamic_mode_stop":
return (
"seat",
"dynamic_mode_stop",
action.get("target"),
)
if action.get("action_type") == "dynamic_mode":
return (
"seat",
"dynamic_mode",
action.get("target"),
action.get("mode_id"),
)
return (
"seat",
"function",
action.get("target"),
action.get("function"),
)
@staticmethod
def _merge_unique(
first: list[dict[str, Any]],
second: list[dict[str, Any]],
) -> list[dict[str, Any]]:
merged = []
seen: set[str] = set()
for item in [*first, *second]:
signature = json.dumps(item, ensure_ascii=False, sort_keys=True)
if signature in seen:
continue
seen.add(signature)
merged.append(item)
return merged
@staticmethod
def _merge_domains(
first: list[str],
second: list[str],
) -> list[str]:
domains = []
for domain in [*first, *second]:
if domain not in domains:
domains.append(domain)
return domains or ["seat"]
def _process_mode_profile_update(
self,
utterance: str,
request_id: str,
provider: str,
conversation_id: str,
context: dict[str, Any],
mode_name: str,
change_text: str,
vehicle_profile: VehicleProfile,
result: dict[str, Any],
traces: list[dict[str, Any]],
) -> dict[str, Any]:
started = time.perf_counter()
result["provider_used"] = "mode_profile_registry"
result["decision_summary"] = [
f"识别为长期模式修改请求,目标模式为“{mode_name}”。"
]
self._trace(
traces,
request_id,
"intent_parse",
"succeeded",
"识别到长期模式配置修改请求;本次不调用 CAN 编译路径。",
started,
{"mode_name": mode_name, "change_text": change_text},
domain="mode",
)
profile = self._resolve_single_mode_profile(
mode_name,
vehicle_profile,
result,
traces,
request_id,
)
if profile is None:
self._persist(result)
return result
if not change_text:
result["status"] = "needs_confirmation"
result["validation"] = [
{
"severity": "error",
"code": "MODE_PROFILE_UPDATE_MISSING_CHANGE",
"path": "utterance",
"message": "请说明要把长期模式中的哪些参数改成什么目标值。",
}
]
self._persist(result)
return result
parsed, provider_used, provider_notice = self._interpret(
change_text,
provider,
context,
vehicle_profile,
)
result["provider_used"] = f"mode_profile_registry+{provider_used}"
result["decision_summary"].extend(parsed.get("decision_summary", []))
if provider_notice:
result["decision_summary"].append(provider_notice)
result["deferred_requests"] = parsed.get("deferred_requests", [])
result["model_requests"] = parsed.get("model_requests", [])
source_actions = [
*parsed.get("actions", []),
*parsed.get("domain_actions", []),
]
mode_operations = self._mode_operations_from_actions(source_actions)
if not mode_operations:
validation = [
{
"severity": "error",
"code": "NO_MODE_PROFILE_UPDATE_OPERATIONS",
"path": "spec.operations",
"message": "没有从修改说明中得到可写入长期模式库的可执行配置。",
}
]
validation.extend(
{
"severity": "error",
"code": "AMBIGUOUS_INTENT",
"path": ambiguity.get("field", "utterance"),
"message": ambiguity.get("question", "修改说明不完整。"),
}
for ambiguity in parsed.get("ambiguities", [])
)
result["validation"] = validation
result["status"] = (
"capability_unavailable"
if parsed.get("deferred_requests") and not parsed.get("ambiguities")
else "needs_confirmation"
)
self._persist(result)
return result
updated_profile = self.mode_profiles.update_mode(profile, mode_operations)
profile_public = updated_profile.public_dict()
source_path = self._display_path(Path(updated_profile.source))
backup_path = self._display_path(
Path(updated_profile.source).with_suffix(".yaml.initial.bak")
)
result["mode_profile"] = {
**profile_public,
"source": source_path,
"initial_backup_source": backup_path,
}
result["matched_modes"] = [profile_public]
result["model_requests"] = self.mode_profiles.requests_for_profiles(
[updated_profile]
)
result["decision_summary"].append(
f"已更新长期模式“{updated_profile.name}”,原始配置备份保留为初始化来源。"
)
self._trace(
traces,
request_id,
"function_generate",
"succeeded",
"已将修改后的可执行配置写回长期模式 YAML。",
time.perf_counter(),
{
"mode_profile_id": updated_profile.id,
"operation_count": len(updated_profile.operations),
"updated_operation_count": len(mode_operations),
"source": source_path,
"initial_backup_source": backup_path,
},
domain="mode",
)
execution_report = self._mode_profile_control_report(
request_id,
"update_mode_profile",
updated_profile,
f"已更新长期模式“{updated_profile.name}”。",
{
"updated_operation_count": len(mode_operations),
"source": source_path,
"initial_backup_source": backup_path,
},
)
validation = self.contracts.validate(execution_report)
result["validation"] = validation
result["execution_report"] = execution_report
if validation:
result["status"] = "execution_report_invalid"
self._persist(result)
return result
updated_context = self.conversations.record_mode_profile_event(
conversation_id,
utterance,
result["mode_profile"],
source_actions,
result["decision_summary"],
"mode_profile_update",
)
result["status"] = (
"partially_succeeded"
if parsed.get("deferred_requests")
else "succeeded"
)
result["conversation_turn"] = updated_context["turn_count"]
result["actuator_state"] = updated_context["devices"]
result["domain_state"] = updated_context["domain_devices"]
self._trace(
traces,
request_id,
"execute",
"succeeded",
"长期模式修改属于控制平面操作,未生成或发送 CAN 帧。",
time.perf_counter(),
{
"mode": "control_plane",
"mode_profile_id": updated_profile.id,
"can_command_emitted": False,
},
domain="mode",
)
self._trace(
traces,
request_id,
"state_update",
"succeeded",
f"长期模式“{updated_profile.name}”已更新,可被后续自然语言请求命中。",
time.perf_counter(),
{
"turn_count": updated_context["turn_count"],
"mode_profile_id": updated_profile.id,
},
domain="mode",
)
self._persist(result)
return result
def _process_mode_profile_restore(
self,
utterance: str,
request_id: str,
conversation_id: str,
mode_name: str,
vehicle_profile: VehicleProfile,
result: dict[str, Any],
traces: list[dict[str, Any]],
) -> dict[str, Any]:
started = time.perf_counter()
result["provider_used"] = "mode_profile_registry"
result["decision_summary"] = [
f"识别为长期模式初始化请求,目标模式为“{mode_name}”。"
]
self._trace(
traces,
request_id,
"intent_parse",
"succeeded",
"识别到长期模式初始化请求;本次不调用模型或 CAN 编译路径。",
started,
{"mode_name": mode_name},
domain="mode",
)
profile = self._resolve_single_mode_profile(
mode_name,
vehicle_profile,
result,
traces,
request_id,
)
if profile is None:
self._persist(result)
return result
try:
restored_profile = self.mode_profiles.restore_initial_mode(profile)
except ValueError as exc:
result["status"] = "needs_confirmation"
result["validation"] = [
{
"severity": "error",
"code": "MODE_PROFILE_INITIAL_BACKUP_MISSING",
"path": "mode_profile.initial_backup",
"message": str(exc),
}
]
self._persist(result)
return result
profile_public = restored_profile.public_dict()
source_path = self._display_path(Path(restored_profile.source))
backup_path = self._display_path(
Path(restored_profile.source).with_suffix(".yaml.initial.bak")
)
result["mode_profile"] = {
**profile_public,
"source": source_path,
"initial_backup_source": backup_path,
}
result["matched_modes"] = [profile_public]
result["model_requests"] = self.mode_profiles.requests_for_profiles(
[restored_profile]
)
result["decision_summary"].append(
f"已将长期模式“{restored_profile.name}”恢复为初始配置。"
)
self._trace(
traces,
request_id,
"function_generate",
"succeeded",
"已从初始备份恢复长期模式 YAML。",
time.perf_counter(),
{
"mode_profile_id": restored_profile.id,
"operation_count": len(restored_profile.operations),
"source": source_path,
"initial_backup_source": backup_path,
},
domain="mode",
)
execution_report = self._mode_profile_control_report(
request_id,
"restore_mode_profile",
restored_profile,
f"已恢复长期模式“{restored_profile.name}”。",
{
"source": source_path,
"initial_backup_source": backup_path,
},
)
validation = self.contracts.validate(execution_report)
result["validation"] = validation
result["execution_report"] = execution_report
if validation:
result["status"] = "execution_report_invalid"
self._persist(result)
return result
updated_context = self.conversations.record_mode_profile_event(
conversation_id,
utterance,
result["mode_profile"],
[],
result["decision_summary"],
"mode_profile_restore",
)
result["status"] = "succeeded"
result["conversation_turn"] = updated_context["turn_count"]
result["actuator_state"] = updated_context["devices"]
result["domain_state"] = updated_context["domain_devices"]
self._trace(
traces,
request_id,
"execute",
"succeeded",
"长期模式初始化属于控制平面操作,未生成或发送 CAN 帧。",
time.perf_counter(),
{
"mode": "control_plane",
"mode_profile_id": restored_profile.id,
"can_command_emitted": False,
},
domain="mode",
)
self._trace(
traces,
request_id,
"state_update",
"succeeded",
f"长期模式“{restored_profile.name}”已恢复,可被后续自然语言请求命中。",
time.perf_counter(),
{
"turn_count": updated_context["turn_count"],
"mode_profile_id": restored_profile.id,
},
domain="mode",
)
self._persist(result)
return result
def _resolve_single_mode_profile(
self,
mode_name: str,
vehicle_profile: VehicleProfile,
result: dict[str, Any],
traces: list[dict[str, Any]],
request_id: str,
) -> ModeProfile | None:
matches = self.mode_profiles.match_text(
mode_name,
vehicle_id=vehicle_profile.id,
)
result["matched_modes"] = [profile.public_dict() for profile in matches]
if len(matches) == 1:
return matches[0]
result["status"] = "needs_confirmation"
result["validation"] = [
{
"severity": "error",
"code": (
"MODE_PROFILE_NOT_FOUND"
if not matches
else "MODE_PROFILE_AMBIGUOUS"
),
"path": "utterance.mode_profile",
"message": (
f"未在车型 {vehicle_profile.id} 的长期模式库中找到“{mode_name}”。"
if not matches
else f"“{mode_name}”命中了多个长期模式,请指定更完整名称。"
),
}
]
self._trace(
traces,
request_id,
"context_resolve",
"failed",
result["validation"][0]["message"],
time.perf_counter(),
{
"mode_name": mode_name,
"vehicle_id": vehicle_profile.id,
"match_count": len(matches),
},
domain="mode",
)
return None
def _mode_profile_control_report(
self,
request_id: str,
operation: str,
profile: ModeProfile,
message: str,
telemetry: dict[str, Any],
) -> dict[str, Any]:
return {
"apiVersion": "cognitive-agent/v1alpha1",
"kind": "ExecutionReport",
"metadata": {
"id": f"report-{request_id}",
"name": "Mode profile control report",
"version": "1.0.0",
"request_id": request_id,
"labels": {"control_plane": "mode_profile_store"},
},
"spec": {
"mode": "dry_run",
"status": "succeeded",
"started_at": _now(),
"finished_at": _now(),
"results": [
{
"ref": f"{operation}-{profile.id}",
"domain": "mode",
"status": "succeeded",
"message": message,
"telemetry": {
"operation": operation,
"mode_profile_id": profile.id,
"operation_count": len(profile.operations),
"storage_scope": "repository_config",
"can_command_emitted": False,
**telemetry,
},
}
],
},
}
def _display_path(self, path: Path) -> str:
try:
return str(path.relative_to(self.root))
except ValueError:
return str(path)
@staticmethod
def _mode_feedback_text(value: object, *, limit: int = 180) -> str:
text = re.sub(r"[\x00-\x1f\x7f]+", " ", str(value or ""))
return re.sub(r"\s+", " ", text).strip(" ,,。;;::")[:limit]
@classmethod
def _mode_failure_issue(
cls,
clause: str,
parsed: dict[str, Any],
) -> dict[str, str]:
display_clause = cls._mode_feedback_text(
re.sub(r"^(?:副驾座椅|主驾座椅)[,,]", "", clause),
limit=120,
)
ambiguities = [
item
for item in parsed.get("ambiguities", [])
if isinstance(item, dict) and str(item.get("field") or "") != "capability"
]
ambiguity = ambiguities[0] if ambiguities else None
field = str(ambiguity.get("field") or "") if ambiguity else ""
question = cls._mode_feedback_text(
ambiguity.get("question") if ambiguity else "",
limit=220,
)
# Parser questions may contain both the diagnosis and a concrete
# correction. Split those parts so every public failure has the same
# reason/next-step contract.
reason_from_question = question
next_from_question = ""
for separator in (";请", "。请"):
if separator in question:
reason_from_question, suffix = question.split(separator, 1)
next_from_question = f"请{suffix}"
break
if reason_from_question and not re.search(
r"(?:CAN|CH[01]|JSON|schema|0x[0-9a-f]+|内部路径)",
reason_from_question,
re.IGNORECASE,
):
reason = f"设置“{display_clause}”未通过:{reason_from_question}"
else:
reason = (
f"设置“{display_clause}”未被识别为当前支持的座舱控制动作"
)
if next_from_question:
next_step = next_from_question
elif "temperature" in field or "温度" in display_clause:
next_step = (
"请只保留一个18至32℃的整数温度,例如“空调调整到24度”,然后重试"
)
elif field == "target" or "target_scope" in field:
next_step = (
"请改用当前支持的副驾座椅目标,并明确部件与位置,例如"
"“副驾腿托调整到80%”"
)
elif "position" in field or re.search(
r"座椅|靠背|椅背|坐垫|坐盆|腿托|脚托|滑轨",
display_clause,
):
next_step = (
"请按“副驾 + 座椅部件 + 方向或0到100%位置”重述,例如"
"“副驾腿托向上调整到最大位置”或“副驾腿托调整到80%”"
)
else:
next_step = (
"请按“设备 + 动作 + 明确目标”重述,例如“关闭所有车窗”、"
"“副驾靠背调整到80%”或“空调调整到24度”"
)
return {
"clause": display_clause,
"reason": reason,
"next_step": cls._mode_feedback_text(next_step, limit=260),
}
@classmethod
def _mode_creation_failure_from_clauses(
cls,
clause_results: list[tuple[str, dict[str, Any], str]],
unresolved_clauses: list[str],
) -> dict[str, Any]:
unresolved = set(unresolved_clauses)
issues = [
cls._mode_failure_issue(clause, parsed)
for clause, parsed, status in clause_results
if status != "complete" and clause in unresolved
][:3]
if not issues:
issues = [
cls._mode_failure_issue(clause, {})
for clause in unresolved_clauses[:3]
]
reason = ";".join(dict.fromkeys(item["reason"] for item in issues))
next_step = ";".join(
dict.fromkeys(item["next_step"] for item in issues)
)
return {
"code": "MODE_PROFILE_DRAFT_INCOMPLETE",
"reason": reason or "部分座舱设置没有转换为可保存的控制动作",
"next_step": next_step
or "请将未识别设置改写为明确的设备、动作和目标值后重试",
"unresolved_clauses": [item["clause"] for item in issues],
"issues": issues,
}
def _process_mode_profile_create(
self,
utterance: str,
provider: str,
conversation_id: str,
execution_mode: str,
approval_token: str | None,
vehicle_profile: VehicleProfile,
simulation_fault: str,
mode_name: str,
control_transport: str,
assistant_plan: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""Build and verify a named-mode draft for explicit user confirmation.
A request such as ``构建一个K歌模式`` used to be interpreted as an
ordinary temporary scene. Generate the scene through the normal
dry-run path, but leave persistence to the existing mode-card API so
the assistant can show the exact actions before the user saves them.
"""
create_match = CREATE_MODE_PROFILE_NAME.fullmatch(utterance)
requested_instruction = (
str(create_match.group("instruction") or "").strip(" ,,::。.!!")
if create_match is not None
else ""
)
source_utterance = requested_instruction or (
f"进入{mode_name},请根据该场景名称使用当前可控原子能力构建并执行场景"
)
source_result = self._process_once(
source_utterance,
provider,
conversation_id,
"dry_run",
approval_token,
vehicle_profile.id,
simulation_fault,
control_transport=control_transport,
assistant_plan=assistant_plan,
)
draft_context = self._context_for_vehicle(
self.conversations.snapshot(conversation_id),
vehicle_profile,
)
draft_context["mode_profiles"] = self.mode_profiles.public_modes(
vehicle_profile.id
)
local_clause_results = self._local_clause_results(
source_utterance,
draft_context,
)
unresolved_draft_clauses = [
clause
for clause, _parsed, status in local_clause_results
if status != "complete"
and not self.mode_profiles.match_text(
clause,
vehicle_id=vehicle_profile.id,
)
]
if provider == "local" and assistant_plan is None and unresolved_draft_clauses:
source_result["status"] = "needs_confirmation"
source_result["unresolved_clauses"] = unresolved_draft_clauses
source_result["mode_creation_failure"] = (
self._mode_creation_failure_from_clauses(
local_clause_results,
unresolved_draft_clauses,
)
)
source_result["supplement_required"] = False
source_result["validation"] = [
*list(source_result.get("validation", [])),
{
"severity": "error",
"code": "MODE_PROFILE_DRAFT_INCOMPLETE",
"path": "mode_profile_draft.instruction",
"message": (
"以下模式设置尚未完整解析:"
+ ";".join(unresolved_draft_clauses)
),
},
]
source_result["decision_summary"] = [
f"未能完整构建“{mode_name}”,因此没有生成可保存草稿。",
*list(source_result.get("decision_summary", [])),
"请补充为当前支持的明确设备动作后重试。",
]
self._persist(source_result)
return source_result
if source_result.get("status") != "succeeded":
source_result["mode_creation_failure"] = {
"code": "MODE_PROFILE_ACTION_VALIDATION_FAILED",
"reason": "场景动作未通过车辆能力或安全校验",
"next_step": (
"请检查每项设备目标是否在当前支持范围内;仍失败时可分条输入后重试"
),
"unresolved_clauses": [],
"issues": [],
}
source_result["decision_summary"] = [
f"未能完成“{mode_name}”的场景动作验证,因此没有创建长期模式。",
*list(source_result.get("decision_summary", [])),
]
self._persist(source_result)
return source_result
intent_ir = source_result.get("intent_ir")
intent_spec = intent_ir.get("spec") if isinstance(intent_ir, dict) else None
raw_intent_actions = (
intent_spec.get("actions", []) if isinstance(intent_spec, dict) else []
)
reusable_actions = [
item for item in raw_intent_actions if isinstance(item, dict)
] if isinstance(raw_intent_actions, list) else []
if not reusable_actions:
source_result["status"] = "needs_confirmation"
source_result["mode_creation_failure"] = {
"code": "MODE_PROFILE_DRAFT_EMPTY",
"reason": "没有识别到可保存的座舱控制动作",
"next_step": (
"请按“设备 + 动作 + 明确目标”补充,例如“关闭所有车窗,空调调整到24度”"
),
"unresolved_clauses": [],
"issues": [],
}
source_result["validation"] = [
{
"severity": "error",
"code": "MODE_PROFILE_DRAFT_EMPTY",
"path": "intent_ir.spec.actions",
"message": "没有生成可保存的功能模式动作,请补充更明确的座舱设置。",
}
]
source_result["decision_summary"] = [
f"未能为“{mode_name}”生成可保存的场景动作。",
*list(source_result.get("decision_summary", [])),
]
self._persist(source_result)
return source_result
domains = list(
dict.fromkeys(
str(item.get("domain") or "")
for item in reusable_actions
if str(item.get("domain") or "")
)
)
source_request_id = str(source_result.get("request_id") or "")
source_result["mode_profile_draft"] = {
"name": mode_name,
"instruction": source_utterance,
"source_request_id": source_request_id,
"vehicle_id": vehicle_profile.id,
"domains": domains,
"operation_count": len(reusable_actions),
}
source_result["mode_creation"] = {
"requested_utterance": utterance,
"source_utterance": source_utterance,
"source_request_id": source_request_id,
"source_status": source_result.get("status"),
"source_provider": source_result.get("provider_used"),
"operation_count": len(reusable_actions),
}
source_result["orchestration_route"] = "mode_profile_create_preview"
source_result["decision_summary"] = [
f"已构建并验证“{mode_name}”场景。",
*list(source_result.get("decision_summary", [])),
"请确认动作后保存为功能模式,或先进行一次模拟试运行。",
]
# Overwrite the persisted source result with the draft metadata. The
# mode-card endpoint later re-reads this server-owned artifact and
# refuses any client-side instruction or request-id substitution.
self._persist(source_result)
return source_result
def _process_mode_profile_save(
self,
utterance: str,
request_id: str,
conversation_id: str,
context: dict[str, Any],
mode_name: str,
vehicle_profile: VehicleProfile,
result: dict[str, Any],
traces: list[dict[str, Any]],
) -> dict[str, Any]:
started = time.perf_counter()
result["provider_used"] = "mode_profile_registry"
result["decision_summary"] = [
f"识别为长期模式固化请求,目标名称为“{mode_name}”。"
]
self._trace(
traces,
request_id,
"intent_parse",
"succeeded",
"识别到长期模式配置保存请求;不调用 CAN 编译路径。",
started,
{"mode_name": mode_name},
domain="mode",
)
source_actions, mode_operations = self._last_mode_source(context)
if not mode_operations:
result["status"] = "needs_confirmation"
result["validation"] = [
{
"severity": "error",
"code": "NO_RECENT_MODE_SOURCE",
"path": "conversation.recent_turns",
"message": "当前会话没有可固化为长期模式的最近成功动作。",
}
]
self._persist(result)
return result
profile = self.mode_profiles.save_mode(
mode_name,
mode_operations,
vehicle_id=vehicle_profile.id,
)
profile_public = profile.public_dict()
profile_path = Path(profile.source)
try:
source_path = str(profile_path.relative_to(self.root))
except ValueError:
source_path = str(profile_path)
result["mode_profile"] = {
**profile_public,
"source": source_path,
}
result["matched_modes"] = [profile_public]
result["decision_summary"].append(
f"已将最近一次成功动作固化为长期模式“{profile.name}”。"
)
result["model_requests"] = self.mode_profiles.requests_for_profiles([profile])
generation_started = time.perf_counter()
self._trace(
traces,
request_id,
"function_generate",
"succeeded",
"已写入长期模式 YAML;该配置将在后续请求中重新生成 FunctionConfig。",
generation_started,
{
"mode_profile_id": profile.id,
"operation_count": len(profile.operations),
"source": source_path,
},
domain="mode",
)
execution_report = {
"apiVersion": "cognitive-agent/v1alpha1",
"kind": "ExecutionReport",
"metadata": {
"id": f"report-{request_id}",
"name": "Mode profile save report",
"version": "1.0.0",
"request_id": request_id,
"labels": {"control_plane": "mode_profile_store"},
},
"spec": {
"mode": "dry_run",
"status": "succeeded",
"started_at": _now(),
"finished_at": _now(),
"results": [
{
"ref": f"save-{profile.id}",
"domain": "mode",
"status": "succeeded",
"message": f"已保存长期模式“{profile.name}”。",
"telemetry": {
"operation": "save_mode_profile",
"mode_profile_id": profile.id,
"operation_count": len(profile.operations),
"storage_scope": "repository_config",
"source": source_path,
"can_command_emitted": False,
},
}
],
},
}
validation = self.contracts.validate(execution_report)
result["validation"] = validation
result["execution_report"] = execution_report
if validation:
result["status"] = "execution_report_invalid"
self._persist(result)
return result
updated_context = self.conversations.save_mode_profile(
conversation_id,
utterance,
result["mode_profile"],
source_actions,
result["decision_summary"],
)
result["status"] = "succeeded"
result["conversation_turn"] = updated_context["turn_count"]
result["actuator_state"] = updated_context["devices"]
result["domain_state"] = updated_context["domain_devices"]
self._trace(
traces,
request_id,
"execute",
"succeeded",
"长期模式保存属于控制平面操作,未生成或发送 CAN 帧。",
time.perf_counter(),
{
"mode": "control_plane",
"mode_profile_id": profile.id,
"can_command_emitted": False,
},
domain="mode",
)
self._trace(
traces,
request_id,
"state_update",
"succeeded",
f"长期模式“{profile.name}”已登记,可被后续自然语言请求命中。",
time.perf_counter(),
{
"turn_count": updated_context["turn_count"],
"mode_profile_id": profile.id,
},
domain="mode",
)
self._persist(result)
return result
def _last_mode_source(
self,
context: dict[str, Any],
) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
for turn in reversed(context.get("recent_turns", [])):
actions = turn.get("actions", [])
if not isinstance(actions, list):
continue
operations = self._mode_operations_from_actions(actions)
if operations:
return deepcopy(actions), operations
return [], []
def _mode_operations_from_actions(
self,
actions: list[dict[str, Any]],
) -> list[dict[str, Any]]:
operations = []
for action in actions:
operation = self._mode_operation_from_action(action)
if operation is not None:
operations.append(operation)
return operations
def _mode_operation_from_action(
self,
action: dict[str, Any],
) -> dict[str, Any] | None:
domain = action.get("domain")
if domain in self.domain_runtimes:
return {
"domain": domain,
"capability": action["capability"],
"target_scope": action["target"],
"operation": action.get("operation", "set"),
"parameters": dict(action.get("parameters", {})),
"reason": action.get("reason", "用户固化的跨域模式动作。"),
}
if action.get("action_type") == "position":
capability = SEAT_AXIS_TO_CAPABILITY.get(str(action.get("axis")))
if capability is None:
return None
parameters = {"position_pct": action["position_pct"]}
if action.get("angle_deg") is not None:
parameters["angle_deg"] = action["angle_deg"]
return {
"domain": "seat",
"capability": capability,
"target_scope": action["target"],
"operation": "set",
"parameters": parameters,
"reason": "用户固化的座椅位置模式动作。",
}
if action.get("action_type") == "dynamic_mode_stop":
return {
"domain": "seat",
"capability": DYNAMIC_MODE_STOP_CAPABILITY,
"target_scope": action["target"],
"operation": "deactivate",
"parameters": {},
"reason": "用户固化的动态座椅停止动作。",
}
if action.get("action_type") == "dynamic_mode":
return {
"domain": "seat",
"capability": "seat.start_dynamic_mode",
"target_scope": action["target"],
"operation": "set",
"parameters": {"mode_id": int(action["mode_id"])},
"reason": "用户固化的固件原生动态座椅模式。",
}
function = action.get("function")
capability = SEAT_FUNCTION_TO_CAPABILITY.get(str(function))
if capability is None:
return None
parameters = {"state": action.get("state")}
if action.get("requested_level") is not None:
parameters["level"] = action["requested_level"]
return {
"domain": "seat",
"capability": capability,
"target_scope": action["target"],
"operation": "set",
"parameters": parameters,
"reason": "用户固化的座椅舒适功能模式动作。",
}
def _partition_native_actions(
self, parsed: dict[str, Any], *, promote_supported: bool = False
) -> dict[str, Any]:
"""Promote verified CAN_DEV capabilities and leave unsupported domains virtual."""
result = deepcopy(parsed)
candidate_native_actions = list(result.get("native_can_actions", []))
native_actions = [
action
for action in candidate_native_actions
if self.native_can_runtime.supports(action)
]
native_signatures = {
(
str(action.get("domain")),
str(action.get("capability")),
str(action.get("target", action.get("target_scope", "vehicle"))),
json.dumps(action.get("parameters", {}), sort_keys=True, ensure_ascii=False),
)
for action in candidate_native_actions
}
virtual_actions: list[dict[str, Any]] = []
demoted_signatures: set[tuple[str, str, str, str]] = set()
for action in candidate_native_actions:
if str(action.get("capability")) not in UNVERIFIED_NATIVE_CAPABILITIES:
continue
demoted = dict(action)
demoted["execution_backend"] = "configured_domain"
virtual_actions.append(demoted)
demoted_signatures.add(
(
str(action.get("domain")),
str(action.get("capability")),
str(action.get("target", action.get("target_scope", "vehicle"))),
json.dumps(action.get("parameters", {}), sort_keys=True, ensure_ascii=False),
)
)
for action in result.get("domain_actions", []):
signature = (
str(action.get("domain")),
str(action.get("capability")),
str(action.get("target", action.get("target_scope", "vehicle"))),
json.dumps(action.get("parameters", {}), sort_keys=True, ensure_ascii=False),
)
if signature in native_signatures:
continue
if promote_supported and self.native_can_runtime.supports(action):
promoted = dict(action)
promoted["execution_backend"] = "native_can"
native_actions.append(promoted)
else:
virtual_actions.append(action)
deduplicated: list[dict[str, Any]] = []
signatures: set[tuple[str, str, str, str]] = set()
for action in native_actions:
signature = (
str(action.get("domain")),
str(action.get("capability")),
str(action.get("target", action.get("target_scope", "vehicle"))),
json.dumps(action.get("parameters", {}), sort_keys=True, ensure_ascii=False),
)
if signature not in signatures:
signatures.add(signature)
deduplicated.append(action)
result["domain_actions"] = virtual_actions
result["native_can_actions"] = deduplicated
detected_domains = list(result.get("detected_domains", []))
if result.get("actions") and "seat" not in detected_domains:
detected_domains.insert(0, "seat")
for action in [*virtual_actions, *deduplicated]:
if action["domain"] not in detected_domains:
detected_domains.append(action["domain"])
result["detected_domains"] = detected_domains
deferred_requests = [
item
for item in result.get("deferred_requests", [])
if not (
item.get("execution_backend") == "native_can"
and (
self.native_can_runtime.supports(item)
or (
str(item.get("domain")),
str(item.get("capability")),
str(item.get("target", item.get("target_scope", "vehicle"))),
json.dumps(item.get("parameters", {}), sort_keys=True, ensure_ascii=False),
)
in demoted_signatures
)
)
]
for item in deferred_requests:
if item.get("capability") == "door.set_vehicle_lock":
item["execution_backend"] = "unsupported"
item["rejection_code"] = "CH1_DOOR_LOCK_CONTROL_UNSUPPORTED"
item["reason"] = (
"Current CH1 whole-vehicle door-lock control is unsupported; "
"lock feedback is observational only and no verified physical "
"command will be built."
)
result["deferred_requests"] = deferred_requests
return result
@staticmethod
def _defer_nonwhitelisted_wireless_actions(
parsed: dict[str, Any],
) -> dict[str, Any]:
"""Keep approved MQTT actions executable when a model adds virtual ones."""
result = deepcopy(parsed)
deferred_requests = list(result.get("deferred_requests", []))
existing = {
(
str(item.get("domain", "")),
str(item.get("capability", "")),
str(item.get("target", item.get("target_scope", "vehicle"))),
)
for item in deferred_requests
}
for action in result.get("domain_actions", []):
target = str(
action.get("target", action.get("target_scope", "vehicle"))
)
signature = (
str(action.get("domain", "unknown")),
str(action.get("capability", "unknown")),
target,
)
if signature in existing:
continue
parameters = dict(action.get("parameters", {}))
requested_value: Any = parameters
if len(parameters) == 1:
requested_value = next(iter(parameters.values()))
deferred_requests.append(
{
"domain": signature[0],
"capability": signature[1],
"target_scope": target,
"desired_change": str(action.get("operation", "set")),
"requested_value": requested_value,
"parameters": parameters,
"reason": (
"该模型动作不在无线 MQTT CAN 控制白名单中,已单独跳过;"
"同一句中的白名单动作继续发布。"
),
"execution_backend": "unsupported",
"rejection_code": "WIRELESS_MQTT_CONTROL_NOT_WHITELISTED",
}
)
existing.add(signature)
result["domain_actions"] = []
result["deferred_requests"] = deferred_requests
return result
@staticmethod
def _emit_validated_commands(
result: dict[str, Any],
callback: Callable[[dict[str, Any]], None] | None,
) -> None:
"""Expose fully compiled commands before physical execution begins."""
if callback is None:
return
event = deepcopy(result)
event["status"] = "processing"
event["phase"] = "commands_validated"
event["execution_report"] = None
callback(event)
def _authorize_compiled_hardware_delivery(
self,
parsed: dict[str, Any],
approval_token: str | None,
control_transport: str,
) -> None:
"""Authorize physical transports after commands are safe to display.
Parsing, schema validation and deterministic CAN compilation do not
transmit frames. Keeping transport readiness checks after the
``commands_validated`` callback lets every UI target show the same
parsed commands, while these gates still run before any executor can
open a CAN or MQTT transmission path.
"""
if parsed.get("actions"):
(
self.vehicle_mqtt_settings
if control_transport == WIRELESS_MQTT_TRANSPORT
else self.hardware_settings
).authorize(approval_token)
if parsed.get("native_can_actions"):
(
self.vehicle_mqtt_settings
if control_transport == WIRELESS_MQTT_TRANSPORT
else self.vehicle_can_settings
).authorize(approval_token)
def _process_orchestrated(
self,
utterance: str,
request_id: str,
provider_used: str,
conversation_id: str,
context: dict[str, Any],
parsed: dict[str, Any],
result: dict[str, Any],
traces: list[dict[str, Any]],
*,
hardware_request: bool = False,
simulated_hardware_request: bool = False,
simulation_fault: str = "normal",
vehicle_profile: VehicleProfile | None = None,
control_transport: str = WIRED_CAN_TRANSPORT,
approval_token: str | None = None,
on_commands_validated: Callable[[dict[str, Any]], None] | None = None,
) -> dict[str, Any]:
"""Run isolated adapters under the selected physical-bus policy."""
vehicle_profile = vehicle_profile or self.vehicle_profiles.default()
generation_started = time.perf_counter()
intent_ir = build_intent_ir(
parsed,
utterance,
request_id,
provider_used,
self.semantic_capability_metadata,
)
intent_actions = [
*parsed.get("domain_actions", []),
*parsed.get("native_can_actions", []),
]
for action in intent_actions:
if action in parsed.get("native_can_actions", []):
capability = self.native_can_runtime.capabilities[action["capability"]]
else:
capability = self.domain_runtimes[action["domain"]].capabilities[
action["capability"]
]
intent_ir["spec"]["requests"].append(
{
"domain": action["domain"],
"goal": (
f"{action['operation']} {action['capability']} "
f"for {action['target']}"
),
"target": {
"device": capability["target_devices"][0],
"zone": action["target"],
},
"parameters": {
"capability": action["capability"],
**action["parameters"],
},
"source_span": utterance,
}
)
result["intent_ir"] = intent_ir
if intent_ir["spec"]["requires_confirmation"]:
validation = self.contracts.validate(intent_ir)
validation.extend(
{
"severity": "error",
"code": "AMBIGUOUS_INTENT",
"path": item.get("field", "utterance"),
"message": item.get("question", "控制目标需要确认。"),
}
for item in intent_ir["spec"]["ambiguities"]
)
validation.extend(
{
"severity": "error",
"code": "UNRESOLVED_UTTERANCE_SPAN",
"path": "utterance",
"message": (
f"未解析原文“{item['text']}”:{item['reason']}。"
),
}
for item in intent_ir["spec"]["unresolved_spans"]
)
result["validation"] = validation
result["status"] = "needs_confirmation"
self._trace(
traces,
request_id,
"safety_check",
"failed",
"语义意图存在歧义或未覆盖片段;未进入 CAN 编译与执行。",
time.perf_counter(),
{
"ambiguity_count": len(intent_ir["spec"]["ambiguities"]),
"unresolved_span_count": len(intent_ir["spec"]["unresolved_spans"]),
"execution_eligible": False,
},
domain="orchestration",
)
self._persist(result)
return result
configs: dict[str, dict[str, Any]] = {}
if parsed.get("actions"):
seat_config = build_function_config(parsed, request_id, provider_used)
if seat_config is not None:
configs["seat"] = seat_config
domain_actions: dict[str, list[dict[str, Any]]] = {}
for action in parsed.get("domain_actions", []):
domain_actions.setdefault(action["domain"], []).append(action)
for domain in ("climate", "roof", "ambient_light"):
if domain in domain_actions:
configs[domain] = self.domain_runtimes[domain].build_function_config(
domain_actions[domain],
request_id,
provider_used,
)
native_config_keys: set[str] = set()
native_actions_by_domain: dict[str, list[dict[str, Any]]] = {}
for action in parsed.get("native_can_actions", []):
native_actions_by_domain.setdefault(action["domain"], []).append(action)
for domain, actions in native_actions_by_domain.items():
key = f"native_{domain}"
native_config_keys.add(key)
configs[key] = self.native_can_runtime.build_function_config(
domain,
actions,
request_id,
provider_used,
)
result["function_configs"] = configs
result["function_config"] = (
configs.get("seat") or next(iter(configs.values()), None)
)
self._trace(
traces,
request_id,
"function_generate",
"succeeded" if configs else "waiting_confirmation",
(
f"已按领域边界生成 {len(configs)} 份 FunctionConfig。"
if configs
else "没有足够的绝对目标生成可执行配置。"
),
generation_started,
{
"domains": list(configs),
"operation_count": sum(
len(config["spec"]["operations"]) for config in configs.values()
),
},
domain="orchestration",
)
validation_started = time.perf_counter()
validation = self.contracts.validate(intent_ir)
for config in configs.values():
validation.extend(self.contracts.validate(config))
if parsed.get("actions"):
validation.extend(validate_semantics(parsed, configs.get("seat")))
else:
for ambiguity in parsed.get("ambiguities", []):
validation.append(
{
"severity": "error",
"code": "AMBIGUOUS_INTENT",
"path": ambiguity["field"],
"message": ambiguity["question"],
}
)
if not native_config_keys:
validation.append(
{
"severity": "warning",
"code": "VIRTUAL_EXECUTION_ONLY",
"path": "platform.reference_vehicle",
"message": "当前三域矩阵仅允许虚拟执行,禁止实车下发。",
}
)
if not configs and not parsed.get("ambiguities") and not parsed.get(
"deferred_requests"
):
validation.append(
{
"severity": "error",
"code": "NO_EXECUTABLE_OPERATION",
"path": "spec.operations",
"message": "模型没有生成任何可执行领域操作。",
}
)
if hardware_request:
self._adapt_validation_for_hardware(validation)
result["validation"] = validation
errors = [issue for issue in validation if issue["severity"] == "error"]
self._trace(
traces,
request_id,
"schema_validate",
"failed" if errors else "succeeded",
f"公共和领域 Schema 校验{'发现错误' if errors else '通过'}。",
validation_started,
{"error_count": len(errors), "document_count": len(configs) + 1},
domain="orchestration",
)
if errors:
result["status"] = (
"needs_confirmation"
if any(issue["code"] == "AMBIGUOUS_INTENT" for issue in errors)
else "validation_failed"
)
self._persist(result)
return result
if parsed.get("deferred_requests") and not configs:
result["status"] = "capability_unavailable"
self._persist(result)
return result
compile_started = time.perf_counter()
plans: dict[str, dict[str, Any]] = {}
wireless_hardware_request = (
hardware_request
and control_transport == WIRELESS_MQTT_TRANSPORT
)
for domain, config in configs.items():
if domain == "seat":
if hardware_request:
if not wireless_hardware_request:
self.hardware_settings.validate_function_config(config)
plans[domain] = self.hardware_compiler.compile(
config,
context["devices"],
)
else:
plans[domain] = self.compiler.compile(config)
elif domain in native_config_keys:
plans[domain] = self.native_can_runtime.compile(config)
else:
runtime = self.domain_runtimes[domain]
plans[domain] = runtime.compile(config)
validation.extend(self.contracts.validate(plans[domain]))
result["command_plans"] = plans
result["command_plan"] = plans.get("seat") or next(
iter(plans.values()), None
)
scene_config = (
self._build_scene_config(request_id, configs, plans)
if len(configs) > 1
else None
)
result["scene_config"] = scene_config
if scene_config is not None:
validation.extend(self.contracts.validate(scene_config))
compile_errors = [
issue for issue in validation if issue["severity"] == "error"
]
self._trace(
traces,
request_id,
"compile",
"failed" if compile_errors else "succeeded",
(
f"{len(configs)} 份配置已编译为相互隔离的 CAN 命令计划"
+ (",并生成 SceneConfig。" if scene_config else "。")
),
compile_started,
{
"domains": list(plans),
"command_count": sum(
len(plan["spec"]["commands"]) for plan in plans.values()
),
"scene_generated": scene_config is not None,
},
domain="orchestration",
)
if compile_errors:
result["status"] = "compile_failed"
self._persist(result)
return result
self._emit_validated_commands(result, on_commands_validated)
if hardware_request:
self._authorize_compiled_hardware_delivery(
parsed,
approval_token,
control_transport,
)
self._trace(
traces,
request_id,
"safety_check",
"succeeded",
(
"所有动作已通过无线 MQTT 能力、参数及 CAN 帧白名单门禁。"
if (
hardware_request
and control_transport == WIRELESS_MQTT_TRANSPORT
)
else "所有动作已通过 CH1 车身 CAN 白名单和物理反馈门禁。"
if hardware_request and native_config_keys and "seat" not in configs
else "副驾 T2G 台架动作已通过独立硬件安全门。"
if hardware_request
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": (
[configs[key]["spec"]["domain"] for key in native_config_keys]
+ (["seat"] if "seat" in configs else [])
if hardware_request
else []
),
"virtual_domains": [
domain
for domain in configs
if not hardware_request
],
"domains": list(configs),
},
domain="orchestration",
)
execute_started = time.perf_counter()
execution_started_at = _now()
reports = self._execute_orchestrated_reports(
configs,
plans,
native_config_keys,
context,
hardware_request=hardware_request,
simulated_hardware_request=simulated_hardware_request,
simulation_fault=simulation_fault,
control_transport=control_transport,
)
for domain_report in reports.values():
_annotate_execution_report(domain_report)
report_results = [
item
for domain_report in reports.values()
for item in domain_report["spec"]["results"]
]
operation_results = [
item
for item in report_results
if item.get("ref") != "seat-controller-firmware"
]
operation_domains = {
str(item.get("domain")) for item in operation_results
}
successful_domains = {
domain
for domain in operation_domains
if all(
_operation_verification_status(item) != "failed_or_blocked"
for item in operation_results
if str(item.get("domain")) == domain
)
}
failed_domains = {
str(item.get("domain"))
for item in operation_results
if _operation_verification_status(item) == "failed_or_blocked"
}
execution_succeeded = not failed_domains
execution_partially_succeeded = bool(successful_domains and failed_domains)
execution_outcome = (
"failed_or_blocked"
if failed_domains
else "submitted_unverified"
if any(
_operation_verification_status(item) == "submitted_unverified"
for item in operation_results
)
else "verified_success"
)
verified_domains = {
domain
for domain in operation_domains
if all(
_operation_verification_status(item) == "verified_success"
for item in operation_results
if str(item.get("domain")) == domain
)
}
physical_state = (
reports.get("seat", {}).get("spec", {}).get("physical_state")
if hardware_request
else None
)
simulated_seat = simulated_hardware_request and "seat" in reports
report_spec: dict[str, Any] = {
"mode": "hil" if hardware_request or simulated_seat else "dry_run",
"status": (
"partially_succeeded"
if execution_partially_succeeded
else "succeeded"
if execution_succeeded
else "failed"
),
"verification_status": execution_outcome,
"started_at": execution_started_at,
"finished_at": _now(),
"results": report_results,
}
if isinstance(physical_state, dict):
report_spec["physical_state"] = physical_state
execution_report = {
"apiVersion": "cognitive-agent/v1alpha1",
"kind": "ExecutionReport",
"metadata": {
"id": f"report-{request_id}",
"name": (
"Cabin orchestration mixed HIL execution report"
if hardware_request
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": (
"wireless_mqtt_ch0"
if wireless_hardware_request and "seat" in configs
else "wireless_mqtt_ch1"
if wireless_hardware_request and native_config_keys
else "physical_can_ch1"
if hardware_request and native_config_keys and "seat" not in configs
else "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
else "single_domain",
},
},
"spec": report_spec,
}
report_issues = self.contracts.validate(execution_report)
validation.extend(report_issues)
result["execution_report"] = execution_report
result["execution_outcome"] = execution_outcome
result["validation"] = validation
if report_issues:
result["status"] = "execution_report_invalid"
elif execution_partially_succeeded:
result["status"] = "partially_succeeded"
elif not execution_succeeded:
result["status"] = "execution_failed"
elif parsed.get("deferred_requests"):
result["status"] = "partially_succeeded"
else:
result["status"] = "succeeded"
failed_execution = next(
(item for item in report_results if item.get("status") == "failed"),
None,
)
self._trace(
traces,
request_id,
"execute",
"succeeded" if execution_succeeded and not report_issues else "failed",
(
"无线 MQTT CAN0/CAN1 白名单控制帧已提交;"
"车辆实际状态以状态话题回读为准。"
if wireless_hardware_request and execution_succeeded
else "有线 CAN0/CAN1 白名单控制帧已提交;"
"当前反馈窗口未确认车辆效果,状态以实时 CAN 回读为准。"
if hardware_request
and execution_succeeded
and execution_outcome == "submitted_unverified"
else "CH1 车身 CAN 动作已按目标字段物理回读闭环执行。"
if hardware_request and native_config_keys and execution_succeeded
else "副驾座椅已按独立 T2G 物理 CAN 回读闭环执行。"
if hardware_request and execution_succeeded
else (
(
"跨域指令部分执行成功;成功域="
f"{','.join(sorted(successful_domains))},失败域="
f"{','.join(sorted(failed_domains))}:"
f"{failed_execution['message']}"
)
if execution_partially_succeeded and failed_execution
else f"真实 CAN 执行失败:{failed_execution['message']}"
if hardware_request and failed_execution
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,
{
"mode": execution_report["spec"]["mode"],
"simulation": execution_report["metadata"]["labels"]["simulation"],
"domains": list(configs),
"result_count": len(report_results),
"failed_domain": (
failed_execution.get("domain") if failed_execution else None
),
"successful_domains": sorted(successful_domains),
"failed_domains": sorted(failed_domains),
"partially_succeeded": execution_partially_succeeded,
"execution_outcome": execution_outcome,
},
domain="orchestration",
)
if (execution_succeeded or execution_partially_succeeded) and not report_issues:
seat_actions = (
parsed.get("actions", [])
if execution_succeeded or "seat" in successful_domains
else []
)
if hardware_request and seat_actions and "seat" in reports:
seat_actions = self._hardware_feedback_actions(
seat_actions,
configs["seat"],
reports["seat"],
)
all_actions = [
*seat_actions,
*[
action
for action in parsed.get("domain_actions", [])
if (
not hardware_request
and (
execution_succeeded
or str(action.get("domain")) in successful_domains
)
)
or (
hardware_request
and str(action.get("domain")) in verified_domains
)
],
*[
{
**action,
**(
{"feedback_source": "physical_can_ch1"}
if hardware_request
else {}
),
}
for action in parsed.get("native_can_actions", [])
if not hardware_request
and (
execution_succeeded
or str(action.get("domain")) in successful_domains
)
],
]
updated_context = self.conversations.apply_successful_actions(
conversation_id,
utterance,
all_actions,
parsed["decision_summary"],
)
if hardware_request and isinstance(physical_state, dict):
updated_context = self.conversations.synchronize_physical_passenger(
conversation_id,
physical_state,
)
if hardware_request:
physical_ch1_feedback = self._physical_ch1_feedback_states(reports)
if physical_ch1_feedback:
updated_context = self.conversations.synchronize_physical_ch1_feedback(
conversation_id,
physical_ch1_feedback,
)
updated_context = self._context_for_vehicle(
updated_context,
vehicle_profile,
)
result["conversation_turn"] = updated_context["turn_count"]
result["actuator_state"] = updated_context["devices"]
result["domain_state"] = updated_context["domain_devices"]
self._trace(
traces,
request_id,
"state_update",
"succeeded",
(
f"跨域会话状态已更新为第 {updated_context['turn_count']} 轮。"
if execution_succeeded
else "仅成功设备已写入会话状态;失败设备保持执行前状态。"
),
time.perf_counter(),
{
"seat_state": updated_context["devices"],
"domain_state": updated_context["domain_devices"],
"successful_domains": sorted(successful_domains),
"failed_domains": sorted(failed_domains),
},
domain="orchestration",
)
elif hardware_request and isinstance(physical_state, dict) and (
physical_state.get("position")
or physical_state.get("ventilation")
or physical_state.get("heating")
):
updated_context = self.conversations.synchronize_physical_passenger(
conversation_id,
physical_state,
)
updated_context = self._context_for_vehicle(
updated_context,
vehicle_profile,
)
result["actuator_state"] = updated_context["devices"]
result["domain_state"] = updated_context["domain_devices"]
self._trace(
traces,
request_id,
"state_sync",
"succeeded",
"跨域真实执行未完成,但已按 CAN 回读同步副驾物理状态。",
time.perf_counter(),
{"seat_state": updated_context["devices"].get("passenger", {})},
domain="orchestration",
)
self._persist(result)
return result
def _execute_orchestrated_reports(
self,
configs: dict[str, dict[str, Any]],
plans: dict[str, dict[str, Any]],
native_config_keys: set[str],
context: dict[str, Any],
*,
hardware_request: bool,
simulated_hardware_request: bool,
simulation_fault: str,
control_transport: str = WIRED_CAN_TRANSPORT,
) -> dict[str, dict[str, Any]]:
reports: dict[str, dict[str, Any]] = {}
bridge = None
seat_bus_factory = None
native_bus_factory = None
vehicle_safety_preflight: dict[str, Any] | None = None
mqtt_selected = control_transport == WIRELESS_MQTT_TRANSPORT
wired_native_passenger = bool(
not mqtt_selected
and "seat" in configs
and self.hardware_settings.native_passenger_can0
and self.vehicle_can_settings.configured
)
if hardware_request and (
native_config_keys
or (mqtt_selected and "seat" in configs)
or wired_native_passenger
):
shared_runtime_enabled = (
not mqtt_selected
and self.vehicle_can_settings.backend != "zlgcan-linux"
and os.getenv(
"COGNITIVE_AGENT_SHARED_CAN_RUNTIME_ENABLED",
os.getenv(
"COGNITIVE_AGENT_SEAT_SHARED_ZCANPRO_BRIDGE_ENABLED", "true"
),
).strip().lower() in {"1", "true", "yes", "on"}
)
if mqtt_selected:
native_bus_factory = lambda: self._vehicle_mqtt_transport.bus(channel=1)
if "seat" in configs:
seat_bus_factory = lambda: self._vehicle_mqtt_transport.bus(channel=0)
elif shared_runtime_enabled:
from cognitive_agent.application.self_repair.service import (
BRIDGE_MANAGER,
)
if not hasattr(BRIDGE_MANAGER, "channel_bus_factory"):
raise HardwareSafetyError(
"vehicle CAN requires the standalone multi-channel runtime; "
"the ZCANPRO compatibility backend owns CH0 only"
)
native_bus_factory = BRIDGE_MANAGER.channel_bus_factory(
self.vehicle_can_settings.channel
)
# Seat commands continue through the high-level request bridge
# on CH0; passing a raw bus factory here would bypass its one-
# owner queue and allow self-repair to race orchestration.
seat_bus_factory = None
elif wired_native_passenger:
bridge = self._new_linux_wired_dual_channel_bridge()
native_bus_factory = lambda: bridge.channel_bus(1)
seat_bus_factory = lambda: bridge.channel_bus(0)
else:
channel_configs = [self.vehicle_can_settings.channel_config()]
if "seat" in configs:
seat_channel = int(self.hardware_settings.channel, 0)
channel_configs.insert(
0,
ZlgChannelConfig(
channel=seat_channel,
frame_format="classic_can",
arbitration_bitrate=self.hardware_settings.bitrate,
data_bitrate=self.vehicle_can_settings.data_bitrate,
can_fd_iso=True,
),
)
bridge = self.vehicle_can_settings.bridge(channel_configs)
native_bus_factory = lambda: bridge.channel_bus(
self.vehicle_can_settings.channel
)
if "seat" in configs:
seat_channel = int(self.hardware_settings.channel, 0)
seat_bus_factory = lambda: bridge.channel_bus(seat_channel)
if mqtt_selected:
# The head unit is the physical CAN owner. Once an action has
# passed the capability/parameter compiler, MqttCanTransport
# applies the final channel, ID and frame-format allow-list at
# publish time. Missing 0x342/0x4EB state frames must not
# suppress an otherwise approved MQTT command.
vehicle_safety_preflight = _wireless_mqtt_publish_preflight()
else:
# The wired bridge is open but no control frame has been
# submitted yet. Require two independent, fresh CH1 status
# frames before entering any actuator executor; CH0 traffic is
# ignored by the observer.
try:
vehicle_safety_preflight = observe_vehicle_safety(
native_bus_factory(),
timeout_ms=min(
5_000,
max(1_000, self.vehicle_can_settings.feedback_timeout_ms),
),
required_channel=1,
)
require_stationary_vehicle(vehicle_safety_preflight)
except VehicleSafetyPreflightError as exc:
if bridge is not None:
bridge.close()
raise HardwareSafetyError(
"CH1 vehicle safety preflight refused physical execution: "
f"{exc}"
) from exc
except Exception:
if bridge is not None:
bridge.close()
raise
try:
def execute_native_configs() -> dict[str, dict[str, Any]]:
native_reports: dict[str, dict[str, Any]] = {}
for key in sorted(native_config_keys):
if not hardware_request:
native_reports[key] = self.native_can_runtime.execute_virtual(
plans[key], configs[key]
)
continue
executor = NativeCanExecutor(
native_bus_factory,
feedback_timeout_ms=(
self.vehicle_mqtt_settings.feedback_timeout_ms
if control_transport == WIRELESS_MQTT_TRANSPORT
else self.vehicle_can_settings.feedback_timeout_ms
),
feedback_required=not mqtt_selected,
)
native_reports[key] = executor.execute(
self.native_can_runtime,
plans[key],
configs[key],
)
native_reports[key]["spec"]["vehicle_safety_preflight"] = (
vehicle_safety_preflight
)
for item in native_reports[key]["spec"].get("results", []):
item.setdefault("telemetry", {})[
"vehicle_safety_preflight"
] = vehicle_safety_preflight
return native_reports
def execute_seat_config() -> dict[str, Any]:
config = configs["seat"]
if hardware_request:
seat_executor = (
self._new_hardware_executor(
seat_bus_factory,
remote_transport=mqtt_selected,
)
if seat_bus_factory is not None
else self._new_hardware_executor()
)
return seat_executor.execute(
plans["seat"], config, context["devices"]
)
if simulated_hardware_request:
return self.simulated_hardware_executor.execute(
plans["seat"],
config,
context["devices"],
fault_scenario=simulation_fault,
)
return self.executor.execute(
plans["seat"], config, context["devices"]
)
if hardware_request and native_config_keys and "seat" in configs:
# Both channel views share one adapter owner; executor threads let
# the two channels start within normal USB scheduling jitter.
with ThreadPoolExecutor(max_workers=2) as pool:
native_future = pool.submit(execute_native_configs)
seat_future = pool.submit(execute_seat_config)
reports.update(native_future.result())
reports["seat"] = seat_future.result()
else:
reports.update(execute_native_configs())
if "seat" in configs:
reports["seat"] = execute_seat_config()
for domain, config in configs.items():
if domain in native_config_keys or domain == "seat":
continue
reports[domain] = self.domain_runtimes[domain].execute(
plans[domain], config
)
finally:
if bridge is not None:
bridge.close()
return reports
@staticmethod
def _build_scene_config(
request_id: str,
configs: dict[str, dict[str, Any]],
plans: dict[str, dict[str, Any]],
) -> dict[str, Any]:
function_refs = []
steps = []
for domain in configs:
reference_id = f"{domain}_function"
function_refs.append(
{
"id": reference_id,
"domain": domain,
"config_ref": f"functions/{domain}.yaml",
"sha256": plans[domain]["spec"]["source"]["config_sha256"],
}
)
steps.append(
{
"id": f"execute_{domain}",
"function_ref": reference_id,
"start_offset_ms": 0,
"failure_policy": "abort" if domain == "seat" else "continue",
}
)
return {
"apiVersion": "cognitive-agent/v1alpha1",
"kind": "SceneConfig",
"metadata": {
"id": f"scene-{request_id}",
"name": "Generated cabin scene",
"version": "1.0.0",
"request_id": request_id,
"generated_at": _now(),
"labels": {"execution_scope": "virtual_only"},
},
"spec": {
"function_refs": function_refs,
"steps": steps,
"execution": {
"mode": "parallel",
"default_failure_policy": "continue",
"max_parallelism": min(4, len(configs)),
"scene_timeout_ms": 30000,
},
},
}
@staticmethod
def _split_intent_clauses(utterance: str) -> list[str]:
def looks_like_control(value: str) -> bool:
return bool(
CONTROL_CLAUSE_SUBJECT_PATTERN.search(value)
and CONTROL_CLAUSE_PREDICATE_PATTERN.search(value)
)
def split_independent_controls(
value: str,
separator_pattern: str,
) -> list[str]:
tokens = re.split(f"({separator_pattern})", value)
current = tokens[0].strip()
separated: list[str] = []
for index in range(1, len(tokens), 2):
separator = tokens[index]
next_item = tokens[index + 1].strip()
if looks_like_control(current) and looks_like_control(next_item):
if current:
separated.append(current)
current = next_item
else:
current = f"{current}{separator}{next_item}".strip()
if current:
separated.append(current)
return separated
coarse_fragments = [
item.strip()
for item in re.split(r"[,,。;;::!!??\r\n]+", utterance)
if item.strip()
]
fragments: list[str] = []
for coarse_fragment in coarse_fragments:
list_fragments = split_independent_controls(coarse_fragment, r"、")
for list_fragment in list_fragments:
fragments.extend(
split_independent_controls(
list_fragment,
r"然后|随后|接着|最后|同时|并且|以及|再|并",
)
)
clauses: list[str] = []
for fragment in fragments:
fragment = re.sub(
r"^(?:然后|随后|接着|最后|同时|并且|以及|并)",
"",
fragment,
).strip()
fragment = re.sub(
r"^再(?=(?:打开|开启|启动|关闭|关掉|关上|停止|暂停|取消))",
"",
fragment,
).strip()
if not fragment:
continue
# A massage mode/strength phrase may omit both "主驾" and "按摩"
# after the first clause. Keep the whole request atomic so the ON
# fallback does not emit relax before an explicit later mode.
massage_continuation = bool(
clauses
and "按摩" in clauses[-1]
and DRIVER_SEAT_MASSAGE_CONTINUATION_PATTERN.search(fragment)
)
# An exclusion is a qualifier of the control immediately before it,
# not an independent command. Keeping both pieces together lets
# the deterministic window parser remove the excluded targets
# before the command list is finalized.
exclusion_qualifier = bool(
clauses and re.search(r"除外|除去|不包括|不含", fragment)
)
if massage_continuation or exclusion_qualifier:
clauses[-1] = f"{clauses[-1]},{fragment}"
else:
clauses.append(fragment)
return clauses or [utterance.strip()]
@staticmethod
def _local_clause_status(parsed: dict[str, Any], clause: str) -> str:
"""Classify one deterministic parse as complete, partial, or unparsed."""
controls = any(
parsed.get(key)
for key in (
"actions",
"domain_actions",
"native_can_actions",
"preset_actions",
"preset_events",
"semantic_constraints",
)
)
real_ambiguities = [
item
for item in parsed.get("ambiguities", [])
if str(item.get("field", "")) != "capability"
]
unresolved = bool(parsed.get("unresolved_spans"))
# Scene/fuzzy-goal language may contain valid atomic controls and still
# ask for additional orchestration. Keep the trusted local controls but
# give only that clause to the model for the uncovered meaning.
fuzzy_goal = bool(
re.search(
r"观影|影院|看电影|休息|睡眠|露营|播放|音乐|场景|"
r"舒适(?:点|一些)?|凉快(?:点|一些)?|暖和(?:点|一些)?",
clause,
re.IGNORECASE,
)
)
if controls and not real_ambiguities and not unresolved and not fuzzy_goal:
return "complete"
if controls:
return "partial"
return "unparsed"
@classmethod
def _merge_parsed_results(
cls, results: list[dict[str, Any]]
) -> dict[str, Any]:
"""Merge clause-level parses while retaining their source order."""
merged: dict[str, Any] = {
"actions": [],
"domain_actions": [],
"native_can_actions": [],
"preset_actions": [],
"preset_events": [],
"deferred_requests": [],
"model_requests": [],
"ambiguities": [],
"decision_summary": [],
"semantic_constraints": [],
"unresolved_spans": [],
"detected_domains": [],
"confidence": 0.0,
}
list_fields = tuple(
key
for key in merged
if key not in {"detected_domains", "confidence"}
)
for parsed in results:
for key in list_fields:
merged[key] = cls._merge_unique(
merged[key], list(parsed.get(key, []))
)
merged["detected_domains"] = cls._merge_domains(
merged["detected_domains"],
list(parsed.get("detected_domains", [])),
)
merged["confidence"] = max(
float(merged["confidence"]), float(parsed.get("confidence", 0))
)
return merged
@classmethod
def _merge_local_and_model_intent(
cls,
local: dict[str, Any],
model: dict[str, Any],
) -> dict[str, Any]:
"""Merge a fallback model plan without overriding local controls."""
merged = deepcopy(model)
for key in ("actions", "domain_actions", "native_can_actions"):
local_items = list(local.get(key, []))
local_signatures = {
cls._control_signature(item) for item in local_items
}
model_items = [
item
for item in model.get(key, [])
if cls._control_signature(item) not in local_signatures
]
merged[key] = cls._merge_unique(local_items, model_items)
for key in (
"preset_actions",
"preset_events",
"deferred_requests",
"model_requests",
"ambiguities",
"semantic_constraints",
"unresolved_spans",
):
merged[key] = cls._merge_unique(
list(local.get(key, [])), list(model.get(key, []))
)
merged["decision_summary"] = cls._merge_unique(
list(local.get("decision_summary", [])),
list(model.get("decision_summary", [])),
)
merged["detected_domains"] = cls._merge_domains(
list(local.get("detected_domains", [])),
list(model.get("detected_domains", [])),
)
merged["confidence"] = max(
float(local.get("confidence", 0)), float(model.get("confidence", 0))
)
return merged
@staticmethod
def _mark_control_parse_source(
parsed: dict[str, Any],
source: str,
*,
overwrite: bool = False,
) -> dict[str, Any]:
"""Attach per-control provenance without changing semantic content."""
marked = deepcopy(parsed)
for key in ("actions", "domain_actions", "native_can_actions"):
for action in marked.get(key, []):
if overwrite or not action.get("parse_source"):
action["parse_source"] = source
return marked
def _local_clause_results(
self,
utterance: str,
context: dict[str, Any],
) -> list[tuple[str, dict[str, Any], str]]:
"""Parse each clause deterministically and preserve inherited seat scope."""
clause_results: list[tuple[str, dict[str, Any], str]] = []
def parse_clause(clause: str) -> dict[str, Any]:
local = parse_local_intent(clause, context)
relative = (
self.native_can_relative_resolver.resolve(clause, context)
if self.native_can_relative_resolver.matches(clause)
else None
)
local = merge_native_can_intent(
local,
clause,
relative_actions=relative.actions if relative else None,
relative_ambiguities=relative.ambiguities if relative else None,
relative_claimed_controls=(
relative.claimed_controls if relative else None
),
)
return self._mark_control_parse_source(local, "local_rule")
def has_seat_domain(parsed: dict[str, Any]) -> bool:
if parsed.get("actions") or "seat" in parsed.get("detected_domains", []):
return True
for key in (
"domain_actions",
"native_can_actions",
"deferred_requests",
"model_requests",
):
if any(item.get("domain") == "seat" for item in parsed.get(key, [])):
return True
return any(
str(item.get("field", "")).startswith("seat.")
for item in parsed.get("ambiguities", [])
)
inherited_seat_scope: str | None = None
for raw_clause in self._split_intent_clauses(utterance):
clause = raw_clause
local = parse_clause(clause)
clause_has_seat = has_seat_domain(local)
explicit_driver = bool(DRIVER_SEAT_SCOPE_PATTERN.search(raw_clause))
explicit_passenger = bool(PASSENGER_SEAT_SCOPE_PATTERN.search(raw_clause))
if clause_has_seat and explicit_driver != explicit_passenger:
inherited_seat_scope = "driver" if explicit_driver else "passenger"
elif clause_has_seat and inherited_seat_scope:
scope_label = (
"主驾座椅"
if inherited_seat_scope == "driver"
else "副驾座椅"
)
clause = f"{scope_label},{raw_clause}"
local = parse_clause(clause)
clause_results.append(
(clause, local, self._local_clause_status(local, clause))
)
return clause_results
@classmethod
def _trusted_local_preview(
cls,
clause_results: list[tuple[str, dict[str, Any], str]],
) -> dict[str, Any]:
"""Keep deterministic controls while deferring incomplete text to GPT."""
trusted = cls._merge_parsed_results(
[parsed for _, parsed, status in clause_results if status != "unparsed"]
)
# Ambiguities and unresolved spans belong to the supplement phase. A
# deterministic action still goes through the normal schema, compiler,
# whitelist and physical execution gates before it can be sent.
trusted["ambiguities"] = []
trusted["unresolved_spans"] = []
trusted["model_requests"] = []
return trusted
@classmethod
def _filter_excluded_controls(
cls,
parsed: dict[str, Any],
excluded: set[tuple[Any, ...]],
) -> dict[str, Any]:
filtered = deepcopy(parsed)
for key in ("actions", "domain_actions", "native_can_actions"):
filtered[key] = [
action
for action in filtered.get(key, [])
if cls._control_signature(action) not in excluded
]
return filtered
def _interpret(
self,
utterance: str,
provider: str,
context: dict[str, Any],
vehicle_profile: VehicleProfile | None = None,
*,
reasoning_effort: str | None = None,
local_preview: bool = False,
model_parse_source: str = "cloud_model",
assistant_plan: dict[str, Any] | None = None,
) -> tuple[dict[str, Any], str, str | None]:
vehicle_profile = vehicle_profile or self.vehicle_profiles.default()
clause_results = self._local_clause_results(utterance, context)
trusted_local = self._merge_parsed_results(
[parsed for _, parsed, status in clause_results if status != "unparsed"]
)
preview_local = self._trusted_local_preview(clause_results)
all_local = self._merge_parsed_results(
[parsed for _, parsed, _ in clause_results]
)
fallback_clauses = [
clause for clause, _, status in clause_results if status != "complete"
]
trusted_controls = any(
trusted_local.get(key)
for key in ("actions", "domain_actions", "native_can_actions")
)
safe_fallback = trusted_local if trusted_controls else all_local
# A Codex-authored plan is an untrusted normalization hint for only
# the clauses the deterministic parser could not finish. The guard
# binds every proposed capability back to that server-owned text and
# prevents it from replacing a locally resolved capability/target.
# ModelPlanAdapter then applies the same target, parameter, range and
# runtime-availability checks as the established hosted-model path.
if assistant_plan is not None and fallback_clauses:
fallback_utterance = ",".join(fallback_clauses)
guarded = guard_assistant_plan(
assistant_plan,
fallback_text=fallback_utterance,
capability_context=self.capability_context,
executable_capabilities={
str(capability.get("name") or "")
for domain in self.capability_context.get("domains", [])
for capability in domain.get("capabilities", [])
if domain.get("availability") == "executable"
or str(capability.get("name") or "")
in self.native_can_runtime.supported_capabilities
},
fallback_intents=[
parsed
for _, parsed, status in clause_results
if status != "complete"
],
trusted_local=trusted_local,
)
model = self.model_plan_adapter.adapt(
guarded.plan,
context,
vehicle_profile,
utterance=fallback_utterance,
)
model = self._mark_control_parse_source(
model,
model_parse_source,
overwrite=True,
)
return (
self._merge_local_and_model_intent(trusted_local, model),
"local_rules+codex_app_server",
"本地规则结果优先;Codex 结构化计划仅补足未解析子句,并已重新通过能力注册表校验。",
)
if provider == "local" or not fallback_clauses:
return (
(
preview_local
if provider == "local" and local_preview
else all_local
if provider == "local"
else trusted_local
),
"local_rules",
None,
)
if not self.openai.available:
if provider == "openai":
raise ValueError("存在本地规则无法完整解析的子句;GPT 模式需要设置 OPENAI_API_KEY")
return (
safe_fallback,
"local_rules",
"未配置 OPENAI_API_KEY;已保留本地规则结果,未解析子句没有下发控制。",
)
fallback_utterance = ",".join(fallback_clauses)
try:
interpret_options = {}
if reasoning_effort is not None:
interpret_options["reasoning_effort"] = reasoning_effort
plan = self.openai.interpret(
fallback_utterance,
context,
self.capability_context,
**interpret_options,
)
model = self.model_plan_adapter.adapt(
plan,
context,
vehicle_profile,
utterance=fallback_utterance,
)
model = self._mark_control_parse_source(
model,
model_parse_source,
overwrite=True,
)
return (
self._merge_local_and_model_intent(trusted_local, model),
f"local_rules+openai:{self.openai.model}",
"本地规则先完成确定性解析;仅未覆盖或有歧义的子句交由 GPT 补充。",
)
except OpenAIIntentError:
return (
safe_fallback,
"local_rules_fallback",
"GPT 调用失败,已降级到本地规则解析(逐子句);已保留确定性结果,CAN 编译和执行路径不变。",
)
@staticmethod
def _context_for_vehicle(
context: dict[str, Any],
vehicle_profile: VehicleProfile,
) -> dict[str, Any]:
projected = deepcopy(context)
projected["vehicle"] = vehicle_profile.public_dict()
for target_state in projected.get("devices", {}).values():
positions = target_state.get("position", {})
for axis, state in positions.items():
if not isinstance(state, dict):
continue
motor = vehicle_profile.seat_motors.get(str(axis))
if motor is None or motor.display_angle is None:
continue
position_pct = state.get("position_pct")
if (
not isinstance(position_pct, (int, float))
or isinstance(position_pct, bool)
):
continue
current_angle = state.get("angle_deg")
legacy_angle = position_pct_to_angle(str(axis), float(position_pct))
should_project = current_angle is None or (
legacy_angle is not None
and isinstance(current_angle, (int, float))
and not isinstance(current_angle, bool)
and abs(float(current_angle) - float(legacy_angle)) < 0.0001
)
if should_project:
state["angle_deg"] = _clean_vehicle_number(
motor.display_angle_for_pct(int(position_pct))
)
return projected
@staticmethod
def _trace(
traces: list[dict[str, Any]],
request_id: str,
stage: str,
status: str,
summary: str,
started: float,
details: dict[str, Any],
domain: str = "seat",
) -> None:
traces.append(
{
"apiVersion": "cognitive-agent/v1alpha1",
"kind": "TraceEvent",
"event_id": f"evt-{len(traces) + 1:02d}-{request_id}",
"request_id": request_id,
"timestamp": _now(),
"stage": stage,
"status": status,
"domain": domain,
"summary": summary,
"details": details,
"duration_ms": max(0, int((time.perf_counter() - started) * 1000)),
}
)
def persist_composed_result(self, result: dict[str, Any]) -> None:
"""Persist a validated result composed with a non-CAN control domain."""
request_id = str(result.get("request_id") or "")
if not MODE_EDITOR_REQUEST_ID_PATTERN.fullmatch(request_id):
raise ValueError("invalid composed request id")
result.setdefault("trace_events", [])
self._persist(result)
def _persist(self, result: dict[str, Any]) -> None:
request_dir = self.artifact_root / result["request_id"]
(request_dir / "functions").mkdir(parents=True, exist_ok=True)
(request_dir / "presets").mkdir(parents=True, exist_ok=True)
(request_dir / "command-plans").mkdir(parents=True, exist_ok=True)
try:
artifact_path = request_dir.relative_to(self.root)
except ValueError:
artifact_path = request_dir
result["artifact_path"] = str(artifact_path)
self._write_yaml(request_dir / "intent.yaml", result.get("intent_ir"))
function_configs = result.get("function_configs") or {}
if function_configs:
for domain, document in function_configs.items():
self._write_yaml(
request_dir / "functions" / f"{domain}.yaml", document
)
else:
self._write_yaml(
request_dir / "functions" / "seat.yaml",
result.get("function_config"),
)
self._write_yaml(request_dir / "scene.yaml", result.get("scene_config"))
self._write_yaml(
request_dir / "presets" / "seat.yaml", result.get("preset_config")
)
command_plans = result.get("command_plans") or {}
if command_plans:
for domain, document in command_plans.items():
self._write_yaml(
request_dir / "command-plans" / f"{domain}.yaml", document
)
else:
self._write_yaml(
request_dir / "command-plans" / "seat.yaml",
result.get("command_plan"),
)
self._write_yaml(
request_dir / "execution-report.yaml", result.get("execution_report")
)
trace_text = "".join(
json.dumps(event, ensure_ascii=False) + "\n" for event in result["trace_events"]
)
self._atomic_write(request_dir / "trace.jsonl", trace_text)
self._atomic_write(
request_dir / "result.json",
json.dumps(result, ensure_ascii=False, indent=2) + "\n",
)
def _write_yaml(self, path: Path, document: dict[str, Any] | None) -> None:
if document is None:
return
text = yaml.safe_dump(document, allow_unicode=True, sort_keys=False)
self._atomic_write(path, text)
@staticmethod
def _atomic_write(path: Path, content: str) -> None:
temporary = path.with_suffix(path.suffix + ".tmp")
temporary.write_text(content, encoding="utf-8")
temporary.replace(path)