feat: integrate safer vehicle CAN reasoning

This commit is contained in:
vee
2026-08-12 22:50:55 +08:00
parent 9ce64b3a01
commit fd3c219c39
15 changed files with 1768 additions and 30 deletions
+161 -5
View File
@@ -53,6 +53,12 @@ from cognitive_agent.application.model_plan_adapter import ( # noqa: E402
SEAT_MOTION_CAPABILITIES,
)
from cognitive_agent.application.native_can_intent import merge_native_can_intent # noqa: E402
from cognitive_agent.application.native_can_relative import ( # noqa: E402
NativeCanRelativeResolver,
)
from cognitive_agent.application.semantic_ir import ( # noqa: E402
apply_language_safety_guards,
)
from cognitive_agent.application.native_can_feedback import ( # noqa: E402
VehicleSafetyPreflightError,
authoritative_physical_ch1_feedback_state,
@@ -75,6 +81,7 @@ from cognitive_agent.application.zcan_seat_executor import ( # noqa: E402
from cognitive_agent.infrastructure.llm.openai_responses import ( # noqa: E402
OpenAIIntentError,
OpenAIIntentInterpreter,
REASONING_EFFORTS,
)
from cognitive_agent.infrastructure.can.hardware import ( # noqa: E402
HardwareSafetyError,
@@ -202,6 +209,7 @@ class SeatDemoService:
self.hardware_settings = SeatHardwareSettings.from_env(repository_root)
self.vehicle_can_settings = VehicleCanSettings.from_env(repository_root)
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
@@ -217,6 +225,16 @@ class SeatDemoService:
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")
self.mode_profiles = ModeProfileRegistry(repository_root / "config" / "modes")
@@ -316,6 +334,9 @@ class SeatDemoService:
},
"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,
@@ -633,6 +654,7 @@ class SeatDemoService:
*,
hardware_client_id: str | None = None,
hardware_access_bypass: bool = True,
reasoning_effort: str | None = None,
) -> dict[str, Any]:
# Creating a reusable scene is a configuration workflow. Synthesize
# and validate it in dry-run even when the console currently targets
@@ -649,6 +671,7 @@ class SeatDemoService:
approval_token,
vehicle_id,
simulation_fault,
reasoning_effort,
)
if hardware_access_bypass or self.driver_only_ch1_execution_active:
with self.hardware_execution.reserve():
@@ -660,6 +683,7 @@ class SeatDemoService:
approval_token,
vehicle_id,
simulation_fault,
reasoning_effort,
)
authorized_client_id = self.hardware_access.ensure_authorized(hardware_client_id)
with self.hardware_execution.reserve():
@@ -672,6 +696,7 @@ class SeatDemoService:
approval_token,
vehicle_id,
simulation_fault,
reasoning_effort,
)
def _process_once(
@@ -683,6 +708,7 @@ class SeatDemoService:
approval_token: str | None = None,
vehicle_id: str | None = None,
simulation_fault: str = "normal",
reasoning_effort: str | None = None,
) -> dict[str, Any]:
utterance = utterance.strip()
if not utterance:
@@ -691,6 +717,11 @@ class SeatDemoService:
raise ValueError("utterance must be at most 1000 characters")
if provider not in {"auto", "openai", "local"}:
raise ValueError("provider must be auto, openai, or local")
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"
@@ -755,6 +786,8 @@ class SeatDemoService:
"conversation_turn": context["turn_count"],
"status": "processing",
"provider_requested": provider,
"reasoning_effort_requested": effective_reasoning_effort,
"reasoning_effort_used": None,
"execution_mode": execution_mode,
"hardware_execution_allowed": (
hardware_request and not control_plane_request
@@ -828,6 +861,7 @@ class SeatDemoService:
provider,
context,
vehicle_profile,
reasoning_effort=reasoning_effort,
)
if self._is_dynamic_mode_stop_request(utterance):
parsed = self._apply_dynamic_mode_stop_request(parsed, utterance)
@@ -835,7 +869,25 @@ class SeatDemoService:
parsed = self._apply_mode_profiles(
parsed, matched_modes, context, vehicle_profile
)
parsed = merge_native_can_intent(parsed, utterance)
relative_resolution = (
self.native_can_relative_resolver.resolve(utterance, context)
if self.native_can_relative_resolver.matches(utterance)
else None
)
parsed = merge_native_can_intent(
parsed,
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
),
)
parsed = apply_language_safety_guards(parsed, utterance)
parsed = self._partition_native_actions(
parsed,
promote_supported=(hardware_request and self.vehicle_can_settings.enabled),
@@ -891,10 +943,43 @@ class SeatDemoService:
if provider_notice:
parsed["decision_summary"].append(provider_notice)
result["provider_used"] = provider_used
if provider_used.startswith("openai:"):
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,
@@ -960,7 +1045,13 @@ class SeatDemoService:
vehicle_profile=vehicle_profile,
)
intent_ir = build_intent_ir(parsed, utterance, request_id, provider_used)
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)
@@ -2791,7 +2882,13 @@ class SeatDemoService:
"""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)
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", []),
@@ -2823,6 +2920,47 @@ class SeatDemoService:
)
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)
@@ -3531,6 +3669,8 @@ class SeatDemoService:
provider: str,
context: dict[str, Any],
vehicle_profile: VehicleProfile | None = None,
*,
reasoning_effort: str | None = None,
) -> tuple[dict[str, Any], str, str | None]:
vehicle_profile = vehicle_profile or self.vehicle_profiles.default()
if provider == "local":
@@ -3539,7 +3679,15 @@ class SeatDemoService:
if not self.openai.available:
raise ValueError("GPT 模式需要设置 OPENAI_API_KEY")
try:
plan = self.openai.interpret(utterance, context, self.capability_context)
interpret_options = {}
if reasoning_effort is not None:
interpret_options["reasoning_effort"] = reasoning_effort
plan = self.openai.interpret(
utterance,
context,
self.capability_context,
**interpret_options,
)
return (
self.model_plan_adapter.adapt(
plan,
@@ -3558,7 +3706,15 @@ class SeatDemoService:
)
if self.openai.available:
try:
plan = self.openai.interpret(utterance, context, self.capability_context)
interpret_options = {}
if reasoning_effort is not None:
interpret_options["reasoning_effort"] = reasoning_effort
plan = self.openai.interpret(
utterance,
context,
self.capability_context,
**interpret_options,
)
return (
self.model_plan_adapter.adapt(
plan,