refactor: remove hardcoded routing rules and centralize AI master rules
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
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
This commit is contained in:
@@ -4,15 +4,6 @@ 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)")
|
||||
|
||||
MAP_POLITICAL_KWS = ["地图", "国界", "边界", "南海", "台湾", "香港", "澳门", "西藏", "新疆", "政治"]
|
||||
PORN_RISK_KWS = ["裸", "成人视频", "成人视频", "性爱", "激情", "成人视频"]
|
||||
EXTERNAL_CONTACT_KWS = ["微信", "vx", "vx", "qq", "手机号", "电话", "加我", "私下"]
|
||||
PRICE_KWS = ["多少钱", "怎么收费", "报价", "价格", "多少米", "多少"]
|
||||
GREETING_KWS = ["你好", "您好", "在吗", "在不在", "hello", "hi"]
|
||||
FINISH_KWS = ["发完了", "没了", "就这些", "报价吧", "可以报价", "先这样"]
|
||||
NONSENSE_KWS = ["嗯", "哦", "好的", "ok", "1", "收到"]
|
||||
|
||||
|
||||
@dataclass
|
||||
class RuleResult:
|
||||
ignore: bool = False
|
||||
@@ -30,13 +21,7 @@ def extract_customer_text_from_shop_card(msg: str) -> str:
|
||||
|
||||
|
||||
def detect_order_status(order_text: str) -> str:
|
||||
t = (order_text or "")
|
||||
if "买家已付款" in t:
|
||||
return "paid"
|
||||
if "等待买家付款" in t or "待付款" in t:
|
||||
return "pending_payment"
|
||||
if "已退款" in t or "退款" in t:
|
||||
return "refund"
|
||||
# 订单状态交给主决策 AI 从上下文语义判断。
|
||||
return "unknown"
|
||||
|
||||
|
||||
@@ -50,25 +35,23 @@ def extract_size_pairs_m(msg: str) -> list[tuple[float, float]]:
|
||||
|
||||
|
||||
def has_map_or_political_risk(msg: str, goods_name: str = "") -> bool:
|
||||
t = f"{msg or ''} {goods_name or ''}".lower()
|
||||
return any(k.lower() in t for k in MAP_POLITICAL_KWS)
|
||||
# 风险由 RiskAgent 语义判断。
|
||||
return False
|
||||
|
||||
|
||||
def has_porn_risk(msg: str) -> bool:
|
||||
t = (msg or "").lower()
|
||||
return any(k.lower() in t for k in PORN_RISK_KWS)
|
||||
# 风险由 RiskAgent 语义判断。
|
||||
return False
|
||||
|
||||
|
||||
def requests_external_contact(msg: str) -> bool:
|
||||
t = (msg or "").lower()
|
||||
return any(k.lower() in t for k in EXTERNAL_CONTACT_KWS)
|
||||
# 外联风险由 RiskAgent 语义判断。
|
||||
return False
|
||||
|
||||
|
||||
def is_meaningless_short(msg: str) -> bool:
|
||||
t = (msg or "").strip().lower()
|
||||
if len(t) <= 2:
|
||||
return True
|
||||
return t in NONSENSE_KWS
|
||||
# 无意义短句由主决策 AI 语义判断。
|
||||
return False
|
||||
|
||||
|
||||
def prefilter_message(msg: str, msg_type: int) -> RuleResult:
|
||||
@@ -93,16 +76,7 @@ def detect_intent(msg: str) -> str:
|
||||
m = (msg or "").lower()
|
||||
if IMAGE_URL_RE.search(m):
|
||||
return "image"
|
||||
if any(k in m for k in FINISH_KWS):
|
||||
return "finish_or_quote_trigger"
|
||||
if any(k in m for k in PRICE_KWS):
|
||||
return "pricing"
|
||||
if any(k in m for k in GREETING_KWS):
|
||||
return "greeting"
|
||||
if requests_external_contact(m):
|
||||
return "external_contact"
|
||||
if is_meaningless_short(m):
|
||||
return "nonsense"
|
||||
# 其余意图交给 AI 语义判断。
|
||||
return "unknown"
|
||||
|
||||
|
||||
@@ -112,22 +86,40 @@ def extract_image_urls(msg: str) -> list[str]:
|
||||
|
||||
def rules_prompt() -> str:
|
||||
return (
|
||||
"你是淘宝客服主决策。只输出JSON动作,不要解释。\n"
|
||||
"动作 action 只能是: reply / quote / transfer / noop。\n"
|
||||
"规则提炼(严格执行):\n"
|
||||
"1) 客户发图: 先承接, 允许继续收图。\n"
|
||||
"2) 询价且有图(当前/待处理): 优先 quote。\n"
|
||||
"3) 无图询价: reply 承接并引导发图。\n"
|
||||
"4) 客户说发完了/报价吧/没图: 若有待处理图则 quote。\n"
|
||||
"5) 外部联系方式请求: reply 站内引导, 不给微信QQ手机号。\n"
|
||||
"6) 地图/政治/黄暴风险: transfer 或拒绝性 reply。\n"
|
||||
"7) 仅无意义短句(嗯/哦/ok): 给简短自然承接, 不要长回复。\n"
|
||||
"8) 避免重复同一句; 若上句语义相同则换表达。\n"
|
||||
"9) 订单已付款: 可回复已安排; 待付款: 提示先付款。\n"
|
||||
"10) 尺寸明显超大(如>=2m*2m): 提示需补图/重做边缘, 不要直接承诺一模一样。\n"
|
||||
"11) 店铺差异化: 按 acc_id/persona 口吻回复, 保持真人聊天。\n"
|
||||
"12) 最终输出只允许一个动作, 不能混合。\n"
|
||||
"13) reply 必须很短: 1句为主, 不超过20字, 口语化, 不要客服官话和AI腔。\n"
|
||||
"你是淘宝图像服务客服系统的统一决策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","reply":"","transfer_msg":"","quote_mode":"flush_pending|analyze_current_or_recent|collect_only","reason":""}'
|
||||
'{"action":"reply|quote|transfer|noop|update_state","reply":"","transfer_msg":"","quote_mode":"flush_pending|analyze_current_or_recent|collect_only","state_patch":{},"reason":""}'
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user