Update project and configurations

This commit is contained in:
Zou-Seay
2026-06-11 16:28:00 +08:00
parent 12d3922091
commit a29a91867d
237 changed files with 164880 additions and 90 deletions

View File

@@ -0,0 +1,299 @@
from __future__ import annotations
import re
from typing import Any
from app.schemas.intent import IntentDefinition
class ResponsePolicy:
_DEFAULT_INTENT_HINTS = {
"cabin_nav_to": "导航",
"cabin_nav_cancel": "结束导航",
"cabin_set_ac": "调空调",
"cabin_ac_on": "打开空调",
"cabin_ac_off": "关闭空调",
"cabin_fan_up": "调大风量",
"cabin_fan_down": "调小风量",
"cabin_defog_front_on": "打开前挡除雾",
"cabin_defog_rear_on": "打开后挡除雾",
"cabin_window_open": "打开车窗",
"cabin_window_close": "关闭车窗",
"cabin_sunroof_open": "打开天窗",
"cabin_sunroof_close": "关闭天窗",
"cabin_trunk_open": "打开后备箱",
"cabin_trunk_close": "关闭后备箱",
"cabin_lock_doors": "锁车门",
"cabin_unlock_doors": "解锁车门",
"cabin_play_music": "播放音乐",
"cabin_pause_music": "暂停音乐",
"cabin_next_track": "下一首",
"cabin_previous_track": "上一首",
"cabin_volume_up": "调大音量",
"cabin_volume_down": "调小音量",
"cabin_volume_mute": "静音",
"cabin_lights_on": "打开车灯",
"cabin_lights_off": "关闭车灯",
"cabin_reading_light_on": "打开阅读灯",
"cabin_reading_light_off": "关闭阅读灯",
"cabin_seat_heat_on": "打开座椅加热",
"cabin_seat_heat_off": "关闭座椅加热",
"cabin_seat_vent_on": "打开座椅通风",
"cabin_seat_vent_off": "关闭座椅通风",
"cabin_mirror_fold": "折叠后视镜",
"cabin_mirror_unfold": "展开后视镜",
"cabin_wiper_on": "打开雨刷",
"cabin_wiper_off": "关闭雨刷",
"cabin_screen_brightness_up": "调亮屏幕",
"cabin_screen_brightness_down": "调暗屏幕",
"cabin_answer_call": "接听电话",
"cabin_hang_up_call": "挂断电话",
"cs_query_order": "查订单",
"cs_query_logistics": "查物流",
"cs_cancel_order": "取消订单",
"cs_transfer_human": "转人工",
}
def __init__(
self,
templates: dict[str, str] | None = None,
intent_hints: dict[str, str] | None = None,
) -> None:
self._templates = templates or {}
self._intent_hints = {**self._DEFAULT_INTENT_HINTS, **(intent_hints or {})}
def ask_for_slot(self, intent: IntentDefinition, slot_name: str, default_template: str) -> str:
if slot_name == "order_id":
if intent.intent_id == "cs_cancel_order":
return self._template("ask_cancel_order_id", "请告诉我订单号。")
return self._template("ask_order_id", "请提供订单号。")
if slot_name == "destination":
return self._template("ask_destination", "请告诉我要去哪里。")
if slot_name == "temperature":
return self._template("ask_temperature", "请告诉我要设置多少度。")
if slot_name == "media_query":
return self._template("ask_media_query", "想听什么风格或者具体的歌名?")
return default_template.strip() or "请补充一个关键信息。"
def workflow_result(self, intent: IntentDefinition, plugin_result: dict[str, Any]) -> str:
if not plugin_result.get("success", True):
return self._template("workflow_failed", "这次没处理成功,请稍后再试。")
message = str(plugin_result.get("message") or "").strip()
if not message:
return self.ack(intent)
if len(message) > 42:
return message[:39].rstrip(",。;; ") + "..."
return message
def workflow_summary(self, messages: list[str]) -> str:
cleaned = [item.strip() for item in messages if item and item.strip()]
if not cleaned:
return self._template("workflow_summary_empty", "好的,已经处理完成。")
if len(cleaned) == 1:
return cleaned[0]
natural_clauses: list[str] = []
previous_subject: str | None = None
for index, item in enumerate(cleaned[:3]):
clause, subject = self._vehicle_style_clause(item, index=index, previous_subject=previous_subject)
natural_clauses.append(clause)
previous_subject = subject or previous_subject
summary = f"好,{''.join(natural_clauses)}"
if len(cleaned) > 3:
summary = summary.rstrip("") + ",其余步骤也已完成。"
if len(summary) > 70:
return summary[:67].rstrip(",。;; ") + "..."
return summary
def ask_for_confirmation(self, intent: IntentDefinition, detail: str | None = None) -> str:
if intent.intent_id == "cs_cancel_order":
if detail:
return f"即将取消订单,{detail}。请回复“确认”或“取消”。"
return "即将取消订单。请回复“确认”或“取消”。"
if detail:
return f"{detail}。请回复“确认”或“取消”。"
return "请确认是否继续执行。回复“确认”或“取消”。"
def confirm_retry(self) -> str:
return self._template("confirm_retry", "我需要一个明确确认。请回复“确认”继续,或回复“取消”终止。")
def confirm_cancelled(self) -> str:
return self._template("confirm_cancelled", "好的,已取消这一步。")
def step_skipped(self, intent: IntentDefinition, reason: str | None = None) -> str:
if intent.intent_id == "cs_cancel_order":
base = "订单取消步骤未执行。"
else:
base = "这一步已跳过。"
if reason:
return f"{base}{reason}"
return base
def ack(self, intent: IntentDefinition | None = None) -> str:
if intent is None:
return self._template("ack_default", "收到,马上处理。")
if intent.domain == "cabin":
return self._template("ack_cabin", "好的,马上处理。")
return self._template("ack_service", "收到,我来处理。")
def reject(self) -> str:
return self._template("reject", "这个我暂时做不了,但我可以帮你查询、控制或转人工。")
def short_social(self, social_kind: str) -> str:
if social_kind == "greeting":
return self._template("short_social_greeting", "你好,我在。")
if social_kind == "thanks":
return self._template("short_social_thanks", "不客气。")
if social_kind == "goodbye":
return self._template("short_social_goodbye", "好的,有需要再叫我。")
if social_kind == "capability":
return self._template(
"short_social_capability",
"我可以帮你查订单、查物流、取消订单、导航、调空调、播放音乐或转人工。",
)
return self._template("short_social_default", "我在。")
def open_social_fallback(self) -> str:
return self._template("open_social_fallback", "可以和你聊两句,你也可以继续告诉我想处理什么。")
def with_pending_hint(self, text: str, pending_hint: str | None = None) -> str:
base = text.strip() or self.open_social_fallback()
hint = (pending_hint or "").strip()
if not hint:
return base
return f"{base} {hint}"
def pending_task_hint(self, status: str, pending_slots: list[str], current_intent: str | None = None) -> str | None:
if status == "waiting_confirmation":
return self._template("pending_confirmation_hint", "当前这一步还在等你确认,回复“确认”或“取消”即可。")
if status == "waiting_slot" and pending_slots:
if pending_slots[0] == "order_id":
return self._template("pending_slot_order_id", "当前还缺订单号,你继续告诉我订单号就行。")
if pending_slots[0] == "temperature":
return self._template("pending_slot_temperature", "当前还缺温度,你继续告诉我要设置多少度就行。")
if pending_slots[0] == "destination":
return self._template("pending_slot_destination", "当前还缺目的地,你继续告诉我要去哪里就行。")
if pending_slots[0] == "media_query":
return self._template("pending_slot_media_query", "当前还缺歌名或风格,你直接说歌名、歌手或风格就行。")
return self._template("pending_slot_default", "当前还缺一个关键信息,你继续补充就行。")
if status == "running" and current_intent:
return self._template("pending_running", "当前任务还在继续,你也可以直接继续下一个指令。")
return None
def task_stopped(self) -> str:
return self._template("task_stopped", "好的,已停止当前任务。")
def clarify(self, candidate_intents: list[str]) -> str:
options = [
self._intent_hints.get(intent_id, intent_id)
for intent_id in candidate_intents
if intent_id
]
deduped: list[str] = []
for item in options:
if item not in deduped:
deduped.append(item)
if not deduped:
return "我理解得还不够确定,你是想查询、控制,还是转人工?"
if len(deduped) == 1:
return f"请确认一下,你是想{deduped[0]}吗?"
if len(deduped) == 2:
return f"请确认一下,你是想{deduped[0]}还是{deduped[1]}"
return f"请确认一下,你是想{deduped[0]}{deduped[1]},还是{deduped[2]}"
def fallback(self) -> str:
return self._template("fallback", "我还没完全听懂,你可以换个简短说法,或告诉我是查询、控制还是转人工。")
def _template(self, key: str, default: str) -> str:
value = str(self._templates.get(key, default)).strip()
return value or default
def _naturalize_workflow_message(self, text: str) -> str:
normalized = text.strip().rstrip("。;; ")
normalized = re.sub(r"^好的[,\s]*", "", normalized)
normalized = re.sub(r"^收到[,\s]*", "", normalized)
if normalized.startswith("已将"):
normalized = normalized[2:]
elif normalized.startswith("已经将"):
normalized = normalized[3:]
elif normalized.startswith("已经"):
normalized = normalized[2:]
elif normalized.startswith(""):
normalized = normalized[1:]
normalized = normalized.strip(", ")
if not normalized:
return "已经处理好了"
if normalized.endswith(""):
return normalized
return f"{normalized}"
def _vehicle_style_clause(
self,
text: str,
*,
index: int,
previous_subject: str | None = None,
) -> tuple[str, str | None]:
normalized = self._naturalize_workflow_message(text)
match = re.match(r"^(打开|关闭)(.+)了$", normalized)
if match:
action, subject = match.groups()
subject = subject.strip()
if action == "打开":
if previous_subject and previous_subject == subject:
return "也打开了", subject
if index > 0:
return f"{subject}也打开了", subject
return f"{subject}已经打开了", subject
if previous_subject and previous_subject == subject:
return "也帮你关上了", subject
if index > 0:
return f"{subject}也帮你关上了", subject
return f"{subject}已经关上了", subject
match = re.match(r"^(锁定|解锁)(.+)了$", normalized)
if match:
action, subject = match.groups()
subject = subject.strip()
action_text = "锁好了" if action == "锁定" else "解锁了"
if previous_subject and previous_subject == subject:
return f"{action_text}", subject
if index > 0:
return f"{subject}{action_text}", subject
return f"{subject}已经{action_text}", subject
match = re.match(r"^(.+)调到\s*(.+)度了$", normalized)
if match:
subject, value = match.groups()
subject = subject.strip()
value = value.strip()
if previous_subject and previous_subject == subject:
return f"也调到 {value} 度了", subject
if index > 0:
return f"{subject}也调到 {value} 度了", subject
return f"{subject}调到 {value} 度了", subject
match = re.match(r"^(调大|调小)(.+)了$", normalized)
if match:
action, subject = match.groups()
subject = subject.strip()
if previous_subject and previous_subject == subject:
return f"{action}", subject
if index > 0:
return f"{subject}{action}", subject
return f"{subject}已经{action}", subject
if normalized.startswith("正在播放 "):
target = normalized[len("正在播放 ") :].strip()
if index > 0:
return f"也开始播放 {target}", "播放"
return f"开始播放 {target}", "播放"
if normalized.startswith("订单 ") and normalized.endswith(" 已取消"):
order_text = normalized[:-4].strip()
return f"{order_text}已经取消了", "订单"
if normalized.startswith("订单 ") and "当前" in normalized:
return normalized, "订单"
return normalized, None