私聊建单前拦住枪支弹药爆炸物和毒品,避免真家伙进入报价。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-14 20:24:13 +08:00
co-authored by Cursor
parent 9ffba70741
commit 617c5e2619
8 changed files with 656 additions and 7 deletions
+95 -1
View File
@@ -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(