feat: integrate Chinese speech understanding and capability routing

This commit is contained in:
vee
2026-08-28 15:19:44 +08:00
parent 2529b4896a
commit aa12135b22
39 changed files with 6864 additions and 56 deletions
+163 -1
View File
@@ -1445,6 +1445,139 @@ class SeatDemoService:
_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,
@@ -1465,6 +1598,7 @@ class SeatDemoService:
_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,
_speech_understanding: 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
@@ -1490,6 +1624,7 @@ class SeatDemoService:
_allow_empty_control_result,
_on_commands_validated,
_direct_can_batch,
_speech_understanding,
)
if control_transport == WIRELESS_MQTT_TRANSPORT:
# The head unit owns both physical CAN channels in wireless mode.
@@ -1511,6 +1646,7 @@ class SeatDemoService:
_allow_empty_control_result,
_on_commands_validated,
_direct_can_batch,
_speech_understanding,
)
if hardware_access_bypass or self._driver_only_vehicle_execution_active(
control_transport
@@ -1532,6 +1668,7 @@ class SeatDemoService:
_allow_empty_control_result,
_on_commands_validated,
_direct_can_batch,
_speech_understanding,
)
authorized_client_id = self.hardware_access.ensure_authorized(hardware_client_id)
with self.hardware_execution.reserve():
@@ -1552,6 +1689,7 @@ class SeatDemoService:
_allow_empty_control_result,
_on_commands_validated,
_direct_can_batch,
_speech_understanding,
)
def _process_once(
@@ -1571,6 +1709,7 @@ class SeatDemoService:
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,
speech_understanding: dict[str, Any] | None = None,
) -> dict[str, Any]:
control_transport = self._validate_control_transport(control_transport)
utterance = utterance.strip()
@@ -1641,7 +1780,7 @@ class SeatDemoService:
context["mode_profiles"] = self.mode_profiles.public_modes(vehicle_profile.id)
matched_modes = (
[]
if direct_can_batch is not None
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,
@@ -1691,6 +1830,16 @@ class SeatDemoService:
"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()
@@ -1733,6 +1882,19 @@ class SeatDemoService:
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,