refactor(backend): update service layers

- services/ontology.py: update ontology service logic
- services/user_agent.py: update user agent service logic
This commit is contained in:
caoxiaozhu
2026-05-12 15:12:57 +00:00
parent 6137bb5cdc
commit 4d9b071e37
2 changed files with 67 additions and 28 deletions

View File

@@ -325,7 +325,11 @@ class SemanticOntologyService:
entities = self._extract_entities(query, compact_query, reference)
rule_scenario, scenario_score = self._detect_scenario(compact_query)
time_range, _time_score = self._extract_time_range(query, compact_query)
time_range, _time_score = self._extract_time_range(
query,
compact_query,
context_json=context_json,
)
context_scenario = self._resolve_context_scenario(context_json)
if rule_scenario == "unknown" and context_scenario is not None:
rule_scenario = context_scenario
@@ -1175,16 +1179,22 @@ class SemanticOntologyService:
self,
query: str,
compact_query: str,
*,
context_json: dict[str, Any],
) -> tuple[OntologyTimeRange, float]:
today = datetime.now(UTC).date()
today = self._resolve_reference_today(context_json)
direct_mappings = {
"": self._single_day_range(today, "", "day"),
"昨日": self._single_day_range(today - timedelta(days=1), "昨日", "day"),
"": self._single_day_range(today - timedelta(days=1), "", "day"),
"": self._single_day_range(today + timedelta(days=1), "", "day"),
}
for keyword, value in direct_mappings.items():
direct_mappings = [
("大前", self._single_day_range(today - timedelta(days=3), "大前", "day")),
("前天", self._single_day_range(today - timedelta(days=2), "前天", "day")),
("", self._single_day_range(today - timedelta(days=1), "", "day")),
("", self._single_day_range(today - timedelta(days=1), "", "day")),
("今天", self._single_day_range(today, "今天", "day")),
("明天", self._single_day_range(today + timedelta(days=1), "明天", "day")),
("后天", self._single_day_range(today + timedelta(days=2), "后天", "day")),
("大后天", self._single_day_range(today + timedelta(days=3), "大后天", "day")),
]
for keyword, value in direct_mappings:
if keyword in query:
return value, 0.10
@@ -1263,6 +1273,29 @@ class SemanticOntologyService:
return OntologyTimeRange(), 0.0
@staticmethod
def _resolve_reference_today(context_json: dict[str, Any]) -> date:
client_now_iso = str(context_json.get("client_now_iso") or "").strip()
if not client_now_iso:
return datetime.now(UTC).date()
normalized = client_now_iso.replace("Z", "+00:00")
try:
client_now = datetime.fromisoformat(normalized)
except ValueError:
return datetime.now(UTC).date()
if client_now.tzinfo is None:
client_now = client_now.replace(tzinfo=UTC)
try:
offset_minutes = int(context_json.get("client_timezone_offset_minutes") or 0)
except (TypeError, ValueError):
offset_minutes = 0
local_now = client_now - timedelta(minutes=offset_minutes)
return local_now.date()
@staticmethod
def _single_day_range(target: date, raw: str, granularity: str) -> OntologyTimeRange:
return OntologyTimeRange(

View File

@@ -595,6 +595,7 @@ class UserAgentService:
payload,
can_proceed=can_proceed,
draft_payload=draft_payload,
missing_slot_labels=[SLOT_LABELS.get(key, key) for key in missing_slot_keys],
)
return UserAgentReviewPayload(
@@ -936,7 +937,7 @@ class UserAgentService:
emphasis="secondary",
),
UserAgentReviewAction(
label="修改",
label="修改识别信息",
action_type="edit_review",
description="打开结构化模板,按已识别字段逐项修改。",
emphasis="secondary",
@@ -958,9 +959,9 @@ class UserAgentService:
location = slots.get("location")
customer = slots.get("customer_name")
summary = "系统识别出您想要发起一笔报销。"
summary = "我先按你当前提供的信息整理出一笔报销。"
if expense_type and expense_type.value:
summary = f"系统识别出您想要报销{expense_type.value}"
summary = f"我理解你这次想报销{expense_type.value}"
details: list[str] = []
if customer and customer.value:
details.append(f"客户名称:{customer.value}")
@@ -970,8 +971,6 @@ class UserAgentService:
details.append(f"地点:{location.value}")
if amount and amount.value:
details.append(f"金额:{amount.value}")
if claim_groups and len(claim_groups) > 1:
details.append(f"建议拆分为 {len(claim_groups)} 张报销单")
if details:
return f"{summary} {''.join(details)}"
return summary
@@ -993,12 +992,15 @@ class UserAgentService:
review_action = str(payload.context_json.get("review_action") or "").strip()
if review_action == "save_draft":
if draft_payload is not None and draft_payload.claim_no:
return f"相关识别信息已在右侧展示,请核对。当前已先保存到草稿 {draft_payload.claim_no},缺失信息后续可继续补充。"
return "相关识别信息已在右侧展示,请核对。当前信息未补齐,已按你的要求先保存草稿"
return (
f"我已经把本轮识别结果整理好了,右侧可以继续核对"
f"当前先替你保存到草稿 {draft_payload.claim_no},后面把缺的信息补齐就可以继续。"
)
return "我已经把本轮识别结果整理好了,右侧可以继续核对。当前信息还没补全,我先按你的要求保存为草稿。"
if review_action == "next_step":
return "相关识别信息已在右侧展示,请核对。当前信息已满足继续流转条件,可进入下一步。"
return "我已经把识别到的关键信息整理好了,右侧是本轮识别结果。你确认无误后,可以直接进入下一步。"
if review_action == "edit_review":
return "相关识别信息已在右侧展示,请核对。我已根据你的修改更新识别结果,请继续确认。"
return "我已经按你修改后的内容重新识别了一遍。右侧是最新结果,下方还有待补信息和注意事项,你继续确认即可"
return review_payload.body_message or None
def _build_review_body_message(
@@ -1007,12 +1009,21 @@ class UserAgentService:
*,
can_proceed: bool,
draft_payload: UserAgentDraftPayload | None,
missing_slot_labels: list[str],
) -> str:
if can_proceed:
return "相关识别信息已在右侧展示,请核对。确认无误后可点击“下一步”。"
return "我已经把识别结果整理在右侧了。当前关键信息基本齐全,你核对无误后可以直接点“下一步”继续处理"
missing_hint = "".join(missing_slot_labels[:4])
missing_message = f"当前还缺少 {missing_hint}" if missing_hint else "当前仍有信息待补充。"
if draft_payload is not None and draft_payload.claim_no:
return f"相关识别信息已在右侧展示,请核对。当前信息还未补齐,可修改后继续,或先保存到草稿 {draft_payload.claim_no}"
return "相关识别信息已在右侧展示,请核对。当前信息还未补齐,可点击“修改”继续补充,或先“保存草稿”。"
return (
f"我先根据你当前提供的信息完成了初步识别,右侧是识别结果。{missing_message}"
f"如果现在还拿不全,也可以先保存到草稿 {draft_payload.claim_no},后面再补。"
)
return (
f"我先根据你当前提供的信息完成了初步识别,右侧是识别结果。{missing_message}"
"你可以继续补充;如果暂时不方便提供,也可以先保存草稿。"
)
@staticmethod
def _can_proceed_review(
@@ -1413,12 +1424,8 @@ class UserAgentService:
time_range = payload.ontology.time_range
if time_range.start_date and time_range.end_date:
if time_range.start_date == time_range.end_date:
if time_range.raw and time_range.raw != time_range.start_date:
return f"{time_range.start_date}(原文:{time_range.raw}"
return time_range.start_date
normalized = f"{time_range.start_date}{time_range.end_date}"
if time_range.raw and time_range.raw != normalized:
return f"{normalized}(原文:{time_range.raw}"
return normalized
if time_range.raw:
return time_range.raw
@@ -1493,7 +1500,7 @@ class UserAgentService:
if edited_value:
raw_value = str(review_form_values.get("time_range_raw") or edited_value).strip()
return self._build_slot_value(
value=edited_value if raw_value == edited_value else f"{edited_value}(原文:{raw_value}",
value=edited_value,
raw_value=raw_value,
normalized_value=edited_value,
source="user_form",
@@ -1509,9 +1516,8 @@ class UserAgentService:
else f"{time_range.start_date}{time_range.end_date}"
)
raw_value = str(time_range.raw or "").strip()
value = normalized_value if not raw_value or raw_value == normalized_value else f"{normalized_value}(原文:{raw_value}"
return self._build_slot_value(
value=value,
value=normalized_value,
raw_value=raw_value,
normalized_value=normalized_value,
source="user_text",