Files
tw2/qingjian_cs/app/rules.py
jimi 674519709e
Some checks failed
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_en (ubuntu-latest, 3.10) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_zh (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.12) (push) Has been cancelled
refactor: remove hardcoded routing rules and centralize AI master rules
2026-03-02 20:18:51 +08:00

126 lines
5.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import re
from dataclasses import dataclass
IMAGE_URL_RE = re.compile(r"https?://[^\s]+(?:\.jpg|\.jpeg|\.png|\.webp|\.bmp|\.gif)(?:\?[^\s]*)?", re.I)
SIZE_RE = re.compile(r"(\d+(?:\.\d+)?)\s*(米|m|M)\s*[xX*乘]\s*(\d+(?:\.\d+)?)\s*(米|m|M)")
@dataclass
class RuleResult:
ignore: bool = False
normalized_msg: str = ""
reason: str = ""
def extract_customer_text_from_shop_card(msg: str) -> str:
if "[进店卡片]" not in (msg or ""):
return ""
prefix = msg.split("#*#[进店卡片]", 1)[0].strip()
if prefix and prefix not in {"你好", "您好", "在吗"}:
return prefix
return prefix
def detect_order_status(order_text: str) -> str:
# 订单状态交给主决策 AI 从上下文语义判断。
return "unknown"
def extract_size_pairs_m(msg: str) -> list[tuple[float, float]]:
out: list[tuple[float, float]] = []
for m in SIZE_RE.finditer(msg or ""):
w = float(m.group(1))
h = float(m.group(3))
out.append((w, h))
return out
def has_map_or_political_risk(msg: str, goods_name: str = "") -> bool:
# 风险由 RiskAgent 语义判断。
return False
def has_porn_risk(msg: str) -> bool:
# 风险由 RiskAgent 语义判断。
return False
def requests_external_contact(msg: str) -> bool:
# 外联风险由 RiskAgent 语义判断。
return False
def is_meaningless_short(msg: str) -> bool:
# 无意义短句由主决策 AI 语义判断。
return False
def prefilter_message(msg: str, msg_type: int) -> RuleResult:
m = (msg or "").strip()
if not m:
return RuleResult(ignore=True, reason="empty")
if msg_type not in (0, 1):
return RuleResult(ignore=True, reason="unsupported_msg_type")
if "" in m and " 转交给 " in m:
return RuleResult(ignore=True, reason="transfer_notice")
if "Gemini 店铺消息,跳过" in m:
return RuleResult(ignore=True, reason="system_echo")
if "[进店卡片]" in m:
t = extract_customer_text_from_shop_card(m)
if t:
return RuleResult(ignore=False, normalized_msg=t, reason="shop_card_with_text")
return RuleResult(ignore=True, reason="pure_shop_card")
return RuleResult(ignore=False, normalized_msg=m, reason="normal")
def detect_intent(msg: str) -> str:
m = (msg or "").lower()
if IMAGE_URL_RE.search(m):
return "image"
# 其余意图交给 AI 语义判断。
return "unknown"
def extract_image_urls(msg: str) -> list[str]:
return IMAGE_URL_RE.findall(msg or "")
def rules_prompt() -> str:
return (
"你是淘宝图像服务客服系统的统一决策AI。必须按以下 MASTER_RULES 执行。\n"
"只输出 JSON 决策,不要解释过程。\n"
"动作 action 只能是: reply / quote / transfer / noop / update_state。\n\n"
"MASTER_RULES:\n"
"A. 统一动作语义\n"
"1) reply: 直接回复客户。\n"
"2) quote: 触发报价或触发看图后报价流程。\n"
"3) transfer: 转人工,必须给 transfer_msg。\n"
"4) noop: 当前不需要回复。\n"
"5) update_state: 仅更新状态,不对外发消息。\n\n"
"B. 售前与报价\n"
"1) 客户发图: 优先承接,可继续收图;不强行一次报完。\n"
"2) 客户询价且已有图(当前图/待处理图/最近图): 优先 action=quote。\n"
"3) 客户无图询价: action=reply引导其先发图。\n"
"4) 客户说“发完了/就这些/报价吧”: 若有图则 action=quote。\n"
"5) 不能承诺“一模一样原图必找到”,可说先看图评估。\n"
"6) 尺寸很大或要求高还原时,不夸张承诺,先说明可评估后给结论。\n\n"
"C. 订单阶段\n"
"1) 已付款: 可回复“已安排处理/正在处理/完成后发你确认”。\n"
"2) 待付款: 可提示付款,但不与客户争执;必要时先给预览再引导付款。\n"
"3) 退款/售后诉求: 进入售后语境,保持克制,必要时转人工。\n\n"
"D. 风控与合规\n"
"1) 涉政治/地图边界/黄暴/违规内容: 优先 transfer 或拒绝性 reply。\n"
"2) 客户索要微信/QQ/手机号等站外联系方式: 不外呼,站内引导。\n"
"3) 高风险不确定时,不硬答,给保守回复或转人工。\n\n"
"E. 对话质量\n"
"1) 单次只做一个动作,不混合。\n"
"2) 避免重复同一句话;若语义相同,换表达。\n"
"3) reply 必须短: 优先 1 句口语化避免AI腔。\n"
"4) 不要输出思考过程,不要输出 tool_use 文本给客户。\n"
"5) 若上下文不足,先澄清 1 个关键问题,不要连续追问。\n\n"
"F. 店铺人格\n"
"1) 按店铺/账号口吻说话,像真人客服,不要机械模板。\n"
"2) 语气友好直接不啰嗦不说“作为AI”。\n\n"
"输出格式:\n"
'{"action":"reply|quote|transfer|noop|update_state","reply":"","transfer_msg":"","quote_mode":"flush_pending|analyze_current_or_recent|collect_only","state_patch":{},"reason":""}'
)