私聊建单前拦住枪支弹药爆炸物和毒品,避免真家伙进入报价。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -68,6 +68,8 @@ class FlowSession:
|
||||
virtual_work_order_no: str = ""
|
||||
# 核对卡还没有工单号时,图片/附件先记在这里,确定建单后再挂到主账。
|
||||
pending_files: tuple[dict[str, str], ...] = ()
|
||||
# 建单前出现过、还没被品名改口清掉的违禁原话。空表示当前没有。
|
||||
sensitive_hold: str = ""
|
||||
|
||||
|
||||
def is_waiting_mode_session(sess) -> bool:
|
||||
@@ -152,6 +154,7 @@ def _session_from_payload(raw: dict[str, Any]) -> FlowSession:
|
||||
for item in (raw.get("pending_files") or [])
|
||||
if isinstance(item, dict) and str(item.get("object_key") or "").strip()
|
||||
),
|
||||
sensitive_hold=str(raw.get("sensitive_hold") or ""),
|
||||
)
|
||||
|
||||
|
||||
@@ -306,6 +309,65 @@ class AirTextInquiryFlow:
|
||||
"invited_product_names": sess.invited_product_names,
|
||||
}
|
||||
|
||||
def _refuse_sensitive(
|
||||
self,
|
||||
*,
|
||||
sender_id: str,
|
||||
text: str,
|
||||
facts: dict[str, Any],
|
||||
reply: ReplyFn,
|
||||
sess: Optional[FlowSession],
|
||||
business_line: str = "",
|
||||
) -> Optional[str]:
|
||||
"""
|
||||
私聊还没建单时,违禁品只回一句并停在原处。
|
||||
|
||||
已有正式工单号的会话直接放过,销售后面补的字按原流程。
|
||||
没拦住时把品名改口写进 facts,并清掉书签上的旧原话。
|
||||
拦住时保存已认定字段,方便改口后接着补问。不建单、不追问缺项。
|
||||
调用在 Worker 的 on_text 里,不占企微回调线程。
|
||||
"""
|
||||
from agent.policy.handoff_human import is_virtual_no
|
||||
from agent.policy.sensitive_cargo import BLOCK_REPLY, judge
|
||||
|
||||
if sess is not None:
|
||||
number = (sess.work_order_no or "").strip()
|
||||
if number and not is_virtual_no(number):
|
||||
return None
|
||||
blocked, new_hold, _edits = judge(
|
||||
text=text,
|
||||
facts=facts,
|
||||
hold=str(getattr(sess, "sensitive_hold", "") or "") if sess else "",
|
||||
)
|
||||
if sess is not None:
|
||||
sess.sensitive_hold = new_hold
|
||||
if not blocked:
|
||||
return None
|
||||
kept = self._handoff_state(sess)
|
||||
pending = tuple(getattr(sess, "pending_files", ()) or ()) if sess else ()
|
||||
line = (business_line or (getattr(sess, "business_line", "") if sess else "") or "").strip()
|
||||
self._save(
|
||||
FlowSession(
|
||||
sender_id=sender_id,
|
||||
thread_id=sender_id,
|
||||
phase="sensitive_block",
|
||||
business_line=line,
|
||||
facts={str(key): str(value) for key, value in (facts or {}).items()},
|
||||
immutable_text=(sess.immutable_text if sess and sess.immutable_text else text),
|
||||
sensitive_hold=new_hold,
|
||||
wait_version=(sess.wait_version + 1) if sess else 1,
|
||||
pending_files=pending,
|
||||
clarify_missing_sig=str(kept.get("clarify_missing_sig") or ""),
|
||||
clarify_retry=int(kept.get("clarify_retry") or 0),
|
||||
handoff_offer=str(kept.get("handoff_offer") or ""),
|
||||
handoff_committed=bool(kept.get("handoff_committed")),
|
||||
virtual_work_order_no=str(kept.get("virtual_work_order_no") or ""),
|
||||
)
|
||||
)
|
||||
reply(BLOCK_REPLY)
|
||||
logger.info("sensitive_block sender=%s line=%s", sender_id, line or "-")
|
||||
return "sensitive_block"
|
||||
|
||||
def _block_if_handoff_committed(self, sess: Optional[FlowSession], reply: ReplyFn) -> bool:
|
||||
"""真正转人工之后这一票不再跟。返回 True 表示已经拦住。"""
|
||||
from agent.policy.handoff_human import is_handoff_frozen
|
||||
@@ -963,6 +1025,16 @@ class AirTextInquiryFlow:
|
||||
if sess and sess.phase == "wait_confirm" and looks_like_confirm(text):
|
||||
# 确定查价不再抽字段,避免「确定」被模型改成空 facts。
|
||||
logger.info("air.confirm sender=%s", sender_id)
|
||||
refused = self._refuse_sensitive(
|
||||
sender_id=sender_id,
|
||||
text=text,
|
||||
facts=dict(sess.facts),
|
||||
reply=reply,
|
||||
sess=sess,
|
||||
business_line=sess.business_line,
|
||||
)
|
||||
if refused:
|
||||
return refused
|
||||
return self._confirm_or_hold(
|
||||
sender_id=sender_id,
|
||||
facts=dict(sess.facts),
|
||||
@@ -997,7 +1069,7 @@ class AirTextInquiryFlow:
|
||||
return llm_phase
|
||||
mode = (snap.get("business_line") or "").upper()
|
||||
facts = dict(snap.get("facts") or {})
|
||||
if sess and sess.phase in {"clarify", "need_mode", "wait_confirm"}:
|
||||
if sess and sess.phase in {"clarify", "need_mode", "wait_confirm", "sensitive_block"}:
|
||||
# 补问轮:旧字段打底;运输方式沿用上一句(飞=空运)
|
||||
# 增值服务按句累加,本句没提时不能把上一句已记下的服务冲掉。
|
||||
from agent.llm.extract_text import detect_transport_mode
|
||||
@@ -1019,6 +1091,18 @@ class AirTextInquiryFlow:
|
||||
|
||||
facts = apply_air_value_added(added_source, facts, previous=prev_added)
|
||||
|
||||
if not mode or mode == "AIR":
|
||||
refused = self._refuse_sensitive(
|
||||
sender_id=sender_id,
|
||||
text=text,
|
||||
facts=facts,
|
||||
reply=reply,
|
||||
sess=sess,
|
||||
business_line=mode or (sess.business_line if sess else ""),
|
||||
)
|
||||
if refused:
|
||||
return refused
|
||||
|
||||
if not mode:
|
||||
reply(copy.ask_transport())
|
||||
self._save(
|
||||
@@ -1195,6 +1279,16 @@ class AirTextInquiryFlow:
|
||||
from agent.policy.handoff_human import void_virtual_no
|
||||
|
||||
old = self.session_of(sender_id)
|
||||
refused = self._refuse_sensitive(
|
||||
sender_id=sender_id,
|
||||
text="",
|
||||
facts=facts,
|
||||
reply=reply,
|
||||
sess=old,
|
||||
business_line="AIR",
|
||||
)
|
||||
if refused:
|
||||
return refused
|
||||
if old:
|
||||
void_virtual_no(old)
|
||||
self._save(
|
||||
|
||||
Reference in New Issue
Block a user