feat: 事件异常会话话术与询价消息服务卡片

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-21 18:16:18 +08:00
co-authored by Cursor
parent 2527901efd
commit c24d8c19c0
2 changed files with 186 additions and 8 deletions
+125 -8
View File
@@ -2439,6 +2439,17 @@ def air_lock_fail(*, work_order_no: str, operator: str, reason: str) -> str:
)
def air_cabin_fail_reason(*, tms_reason: str = "", flight_date: str = "", today: str = "") -> str:
"""
锁舱/释放失败原因:原样回 TMS 的 failReason。
没有报价也可以锁舱,禁止改写成「报价选项不存在」或「航班已起飞」。
flight_date / today 只为兼容旧调用,不再用来改写原文。
"""
_ = flight_date
_ = today
return (tms_reason or "").strip()
def air_release_ok(
*,
work_order_no: str,
@@ -2459,9 +2470,10 @@ def air_release_ok(
def air_release_fail(*, work_order_no: str, operator: str, reason: str) -> str:
why = (reason or "").strip() or "TMS未返回原因"
return (
f"工单{work_order_no} 空运释放舱位失败\n"
f"工单{work_order_no} 空运释放舱位失败。\n"
f"操作人:{operator}\n"
f"释放舱位失败的原因:{why}"
f"释放舱位失败的原因:{why}\n"
"已通知IT运维排查,请稍等。"
)
@@ -2484,6 +2496,8 @@ STEP_LLM_NO_TICKET = "llm_no_ticket"
STEP_LLM = "llm"
STEP_TMS_QUOTE = "tms_quote"
STEP_TMS_CABIN = "tms_cabin"
STEP_TMS_LOCK = "tms_lock"
STEP_TMS_RELEASE = "tms_release"
STEP_GROUP = "group"
SYS_EXC_LLM_FALLBACK = "大模型服务异常,IT排查中..."
@@ -2497,6 +2511,8 @@ IMPACT_LLM_NO_TICKET = "尚未建单,不落后台工单"
IMPACT_LLM = "当前识别暂停,已有字段与工单状态保持不变"
IMPACT_TMS_QUOTE = "当前询价流程暂停,识别字段与原业务节点保持不变"
IMPACT_TMS_CABIN = "当前锁舱/释放暂停,工单状态保持不变"
IMPACT_TMS_LOCK = "锁舱失败,工单状态保持不变"
IMPACT_TMS_RELEASE = "释放舱位失败,工单状态保持不变"
IMPACT_GROUP = "当前建群/加人暂停,已建群不新建第二个群"
@@ -2519,7 +2535,7 @@ def system_exception_recovery(step: str) -> str:
key = (step or "").strip()
if key == STEP_LLM or key == STEP_LLM_NO_TICKET:
return SYS_EXC_RECOVERY_LLM
if key == STEP_TMS_CABIN:
if key in {STEP_TMS_CABIN, STEP_TMS_LOCK, STEP_TMS_RELEASE}:
return SYS_EXC_RECOVERY_CABIN
if key == STEP_GROUP:
return SYS_EXC_RECOVERY_GROUP
@@ -2533,6 +2549,10 @@ def system_exception_impact(step: str) -> str:
return IMPACT_LLM_NO_TICKET
if key == STEP_LLM:
return IMPACT_LLM
if key == STEP_TMS_LOCK:
return IMPACT_TMS_LOCK
if key == STEP_TMS_RELEASE:
return IMPACT_TMS_RELEASE
if key == STEP_TMS_CABIN:
return IMPACT_TMS_CABIN
if key == STEP_GROUP:
@@ -2554,11 +2574,11 @@ def system_exception_alert_payload(
系统异常通知卡:走询价消息服务,不走询价小助手。
五行对齐现网示例:异常类型、接口/服务、错误码、影响范围、发生时间。
标题不加 [test]:运维要看的就是这张卡本身,test_prefix 仅保留兼容入参。
"""
no = (work_order_no or "").strip() or "无"
_ = test_prefix
title = "系统异常通知"
if test_prefix:
title = "[test] " + title
rows = [
{"keyname": "异常类型", "value": (exception_type or "").strip() or "-"},
{"keyname": "接口/服务", "value": (service or "").strip() or "-"},
@@ -2596,11 +2616,10 @@ def system_exception_alert_text(
occurred_at: str,
test_prefix: bool = False,
) -> str:
"""告警卡纯文本兜底(通道降级时仍能看懂)。"""
"""告警卡纯文本兜底(通道降级时仍能看懂)。标题同样不加 [test]。"""
no = (work_order_no or "").strip() or "无"
_ = test_prefix
head = "系统异常通知"
if test_prefix:
head = "[test] " + head
lines = [
"询价机器人系统告警",
head,
@@ -2612,3 +2631,101 @@ def system_exception_alert_text(
f"发生时间:{(occurred_at or '').strip() or '-'}",
]
return "\n".join(lines)
def event_exception_session_text(
*,
exception_type: str,
work_order_no: str,
names: list[str] | None,
hours: float,
mention: bool,
test_prefix: bool = False,
) -> str:
"""
事件异常会话两行话术。
mention=True 时姓名前加 @,多人空格拼接。小时数用后台规则值。
"""
from agent.policy.event_exception import format_timeout_hours
no = (work_order_no or "").strip() or "-"
kind = (exception_type or "").strip() or "事件异常"
hour_text = format_timeout_hours(hours)
people = [str(n).strip() for n in (names or []) if str(n).strip()]
if mention:
tagged = " ".join(f"@{n}" for n in people) if people else "相关人员"
else:
tagged = " ".join(people) if people else "您"
body = (
f"关联询价工单 {no}。{tagged}已超过 {hour_text} 工作小时 无响应,"
f"事件异常已记录工单,请及时处理。"
)
text = f"{kind}\n{body}"
if test_prefix and not text.startswith("[test]"):
return f"[test] {text}"
return text
def event_exception_alert_payload(
*,
card_title: str,
work_order_no: str,
hours: float,
test_prefix: bool = False,
) -> dict[str, Any]:
"""
事件异常通知卡:走询价消息服务,不走询价小助手。
对齐现网「询价机器人事件告警」:标题用响应异常,正文用「您已超过」。
"""
from agent.policy.event_exception import format_timeout_hours
no = (work_order_no or "").strip() or "-"
title = (card_title or "").strip() or "事件响应异常"
if test_prefix and not title.startswith("[test]"):
title = f"[test] {title}"
hour_text = format_timeout_hours(hours)
from agent.config import get_settings
base = str(getattr(get_settings(), "public_base_url", "") or "").strip().rstrip("/")
action_url = base or "https://work.weixin.qq.com"
return {
"msgtype": "template_card",
"wecom_app": "notify",
"template_card": {
"card_type": "text_notice",
"source": {"desc": "询价机器人事件告警", "desc_color": 0},
"main_title": {
"title": title,
"desc": f"关联询价工单 {no}",
},
"sub_title_text": f"您已超过 {hour_text} 工作小时无响应,事件异常已记录工单,请及时处理。",
"card_action": {"type": 1, "url": action_url},
},
}
def event_exception_alert_text(
*,
card_title: str,
work_order_no: str,
hours: float,
test_prefix: bool = False,
) -> str:
"""应用卡片纯文本兜底。"""
payload = event_exception_alert_payload(
card_title=card_title,
work_order_no=work_order_no,
hours=hours,
test_prefix=test_prefix,
)
card = payload.get("template_card") or {}
main = card.get("main_title") or {}
lines = [
str((card.get("source") or {}).get("desc") or "询价机器人事件告警"),
str(main.get("title") or ""),
str(main.get("desc") or ""),
str(card.get("sub_title_text") or ""),
]
return "\n".join(x for x in lines if x)