确定运输方式前的多句询价带到所选方式并按该方式抽字段;成交已定后放舱不再推跟进,未成交原因不再被当成新询价。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -91,6 +91,7 @@ def handle_continue_thread(
|
||||
sender_id=message.sender_id,
|
||||
work_order_no=wo,
|
||||
reply=reply,
|
||||
text=message.content or "",
|
||||
)
|
||||
logger.info(
|
||||
"continue_thread phase=%s sender=%s wo=%s",
|
||||
|
||||
@@ -22,7 +22,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _front_bookmark(sess) -> bool:
|
||||
"""补问/核对手稿优先于已出票旧书签。空的 need_mode 不算(那是丢会话后的误伤)。已转人工的票也不再当当前询价。"""
|
||||
"""补问/核对手稿优先于已出票旧书签。还没定运输方式的草稿不算当前询价,避免抢走另一条已在补问的单。已转人工的票也不再当当前询价。"""
|
||||
if sess is None:
|
||||
return False
|
||||
from agent.policy.handoff_human import is_handoff_frozen
|
||||
@@ -36,17 +36,92 @@ def _front_bookmark(sess) -> bool:
|
||||
return phase in {"clarify", "need_mode", "need_land_options", "wait_confirm"}
|
||||
|
||||
|
||||
def _is_waiting_mode(sess) -> bool:
|
||||
"""运输方式还没定、业务线空着。定方式前的几句询价字段都堆在这份草稿上。"""
|
||||
if sess is None:
|
||||
return False
|
||||
from agent.policy.handoff_human import is_handoff_frozen
|
||||
|
||||
if is_handoff_frozen(sess):
|
||||
return False
|
||||
if (sess.phase or "").strip() != "need_mode":
|
||||
return False
|
||||
return not (sess.business_line or "").strip()
|
||||
|
||||
|
||||
def _carry_waiting_inquiry_to_owner(*, owner: str, sender_id: str) -> None:
|
||||
"""
|
||||
销售点明空运/海运/陆运时,把定方式之前积累的询价字段整份交给该方式。
|
||||
|
||||
可能是连续几句,不只上一句。已在补问或核对的单不盖。不带旧工单号。
|
||||
"""
|
||||
kind = (owner or "").upper()
|
||||
sid = (sender_id or "").strip()
|
||||
if not sid or kind not in {"SEA", "AIR", "LAND"}:
|
||||
return
|
||||
flows = {
|
||||
"AIR": get_air_text_flow(),
|
||||
"SEA": get_sea_text_flow(),
|
||||
"LAND": get_land_text_flow(),
|
||||
}
|
||||
owner_flow = flows[kind]
|
||||
existing = owner_flow.session_of(sid)
|
||||
if _front_bookmark(existing):
|
||||
return
|
||||
drafts = []
|
||||
for flow in flows.values():
|
||||
sess = flow.session_of(sid)
|
||||
if _is_waiting_mode(sess):
|
||||
drafts.append(sess)
|
||||
if not drafts:
|
||||
return
|
||||
# 字段多的打底,其它草稿只补空缺,避免后写的空值盖掉已认出的港口。
|
||||
drafts.sort(key=lambda item: len(item.facts or {}), reverse=True)
|
||||
facts: dict[str, str] = {}
|
||||
texts: list[str] = []
|
||||
for sess in drafts:
|
||||
for key, value in (sess.facts or {}).items():
|
||||
text = str(value).strip()
|
||||
if text and not str(facts.get(key) or "").strip():
|
||||
facts[str(key)] = text
|
||||
raw = (sess.immutable_text or "").strip()
|
||||
if raw and raw not in texts:
|
||||
texts.append(raw)
|
||||
if not facts and not texts:
|
||||
return
|
||||
from agent.policy.air_text_flow import FlowSession
|
||||
|
||||
owner_flow._save(
|
||||
FlowSession(
|
||||
sender_id=sid,
|
||||
thread_id=sid,
|
||||
phase="need_mode",
|
||||
business_line="",
|
||||
facts=facts,
|
||||
immutable_text="\n".join(texts),
|
||||
)
|
||||
)
|
||||
logger.info(
|
||||
"text_inquiry 未定方式的询价字段交给 %s keys=%s sender=%s",
|
||||
kind,
|
||||
",".join(sorted(facts)),
|
||||
sid,
|
||||
)
|
||||
|
||||
|
||||
def _drop_other_mode_drafts(*, owner: str, sender_id: str) -> None:
|
||||
"""
|
||||
本句已点名空运/海运/陆运:丢掉其它方式未出号的补问/选项草稿。
|
||||
本句已点名空运/海运/陆运:先带走「还没定方式」时积下的询价字段,再丢掉其它方式的补问/选项草稿。
|
||||
|
||||
测服复现:旧陆运「选项未齐」书签还在,海运补「贸易条款 / 运输分类」会被陆运抢走,
|
||||
回「参数提取不全」并发送填写样例图。已出号会话留在工单号索引,不当当前草稿。
|
||||
定方式前的 need_mode 要整份移交,不能直接删。
|
||||
"""
|
||||
kind = (owner or "").upper()
|
||||
sid = (sender_id or "").strip()
|
||||
if not sid or kind not in {"SEA", "AIR", "LAND", "MULTI"}:
|
||||
return
|
||||
_carry_waiting_inquiry_to_owner(owner=kind, sender_id=sid)
|
||||
land = get_land_text_flow()
|
||||
air = get_air_text_flow()
|
||||
sea = get_sea_text_flow()
|
||||
@@ -74,6 +149,24 @@ def _drop_other_mode_drafts(*, owner: str, sender_id: str) -> None:
|
||||
clearer(sid)
|
||||
|
||||
|
||||
def _flow_waiting_lost_reason(sender_id: str, sea, air, land):
|
||||
"""谁在等未成交原因就交给谁。刚记下未成交的那条优先。"""
|
||||
from agent.policy.handoff_human import is_handoff_frozen
|
||||
|
||||
waiting = []
|
||||
for flow in (sea, air, land):
|
||||
sess = flow.session_of(sender_id)
|
||||
if sess is None or is_handoff_frozen(sess):
|
||||
continue
|
||||
if (sess.phase or "").strip() != "wait_lost_reason":
|
||||
continue
|
||||
waiting.append((int(getattr(sess, "wait_version", 0) or 0), flow))
|
||||
if not waiting:
|
||||
return None
|
||||
waiting.sort(key=lambda item: item[0], reverse=True)
|
||||
return waiting[0][1]
|
||||
|
||||
|
||||
def should_resume_other_as_inquiry(message: InboundMessage) -> bool:
|
||||
"""
|
||||
A 把补问短答标成闲聊时,仍续填当前询价。
|
||||
@@ -135,6 +228,10 @@ def _pick_text_flow(*, sender_id: str, text: str, injected_mode: str = "", prefe
|
||||
return land
|
||||
sea = get_sea_text_flow()
|
||||
air = get_air_text_flow()
|
||||
# 正在等未成交原因:下一句就是原因。调价、旧报价单、新的运输方式都不能把这句带走。
|
||||
lost_flow = _flow_waiting_lost_reason(sender_id, sea, air, land)
|
||||
if lost_flow is not None:
|
||||
return lost_flow
|
||||
spoken = (injected_mode or detect_transport_mode(text) or "").upper()
|
||||
multi_kind = classify_multi_text(text) if not injected_mode else "single"
|
||||
if multi_kind in {"multi", "same_mode"}:
|
||||
@@ -183,6 +280,15 @@ def _pick_text_flow(*, sender_id: str, text: str, injected_mode: str = "", prefe
|
||||
for sess, flow in ((sea_sess, sea), (air_sess, air), (land_sess, land)):
|
||||
if _front_bookmark(sess):
|
||||
return flow
|
||||
# 还在问运输方式:后面几句询价字段继续叠在同一份草稿上,不要另开一条只留最后一句。
|
||||
parked = [
|
||||
(sess, flow)
|
||||
for sess, flow in ((sea_sess, sea), (air_sess, air), (land_sess, land))
|
||||
if _is_waiting_mode(sess)
|
||||
]
|
||||
if parked:
|
||||
parked.sort(key=lambda item: len(item[0].facts or {}), reverse=True)
|
||||
return parked[0][1]
|
||||
if prefer_quoted:
|
||||
quoted = quoted_adjust_flow(sender_id)
|
||||
if quoted is not None:
|
||||
|
||||
Reference in New Issue
Block a user