feat: 再索要墙渠道精确化——索要缺失渠道放行(双要前置)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -796,20 +796,24 @@ def test_no_contact_wall_already_contact_no_reask(async_db, monkeypatch):
|
||||
# ---- Task 5b: 再索要墙(已留资时输出侧确定性拦截索要句) ----
|
||||
def test_reask_wall_drops_ask_sentence_when_contact(async_db, monkeypatch):
|
||||
# 生产日志复现: 已留资 + GLM 仍在回复中夹索要句 → 该句静默丢弃,前后句照常
|
||||
# 渠道精确化(2026-10-08): 客户留的是 phone,故问 phone 的索要句才丢;
|
||||
# 问 WhatsApp(缺失渠道)的索要句放行流出——同轮钉死两个方向
|
||||
class ReAskAI(FakeAI):
|
||||
async def stream_chat(self, messages, context=""):
|
||||
for token in ("Great choice! ", "Got your WhatsApp? ",
|
||||
for token in ("Great choice! ", "Got your phone number? ",
|
||||
"Got your WhatsApp? ",
|
||||
"Manager will send the details."):
|
||||
yield token
|
||||
|
||||
(manager, conv), _ = _setup(async_db, monkeypatch,
|
||||
ai=ReAskAI(extract={}),
|
||||
source=ConversationSource.standalone)
|
||||
asyncio.run(_send(manager, conv.session_id, "00963944526529")) # 留资
|
||||
asyncio.run(_send(manager, conv.session_id, "00963944526529")) # 留资(phone)
|
||||
asyncio.run(_send(manager, conv.session_id, "i want 2model"))
|
||||
msgs = asyncio.run(manager._load_history(conv.id))
|
||||
tail = [m["content"] for m in msgs if m["role"] == "assistant"][-1]
|
||||
assert "Got your WhatsApp" not in tail # 索要句被丢
|
||||
assert "Got your phone number" not in tail # 索要已在手渠道 → 丢
|
||||
assert "Got your WhatsApp" in tail # 索要缺失渠道 → 放行流出
|
||||
assert "Great choice" in tail # 前句保留
|
||||
assert "send the details" in tail # 后句保留
|
||||
asyncio.run(manager.db.close())
|
||||
@@ -838,11 +842,13 @@ def test_reask_wall_closed_without_contact(async_db, monkeypatch):
|
||||
|
||||
def test_reask_wall_all_dropped_falls_back_to_closing(async_db, monkeypatch):
|
||||
# 整条回复只有一句索要 → 全被丢 → 兜底流出"经理主动联系"句,不能空回复
|
||||
# 渠道精确化(2026-10-08): 索要**已在手**渠道才全丢兜底——客户留的是
|
||||
# phone,语料改为问 phone;问缺失渠道(WhatsApp)放行流出的方向见第二段
|
||||
from app.services.boundary_filter import NO_CONTACT_ALREADY
|
||||
|
||||
class OnlyAskAI(FakeAI):
|
||||
async def stream_chat(self, messages, context=""):
|
||||
for token in ("Got your WhatsApp? ",):
|
||||
for token in ("Got your phone number? ",):
|
||||
yield token
|
||||
|
||||
(manager, conv), _ = _setup(async_db, monkeypatch,
|
||||
@@ -854,6 +860,22 @@ def test_reask_wall_all_dropped_falls_back_to_closing(async_db, monkeypatch):
|
||||
assert tails[-1] == NO_CONTACT_ALREADY
|
||||
asyncio.run(manager.db.close())
|
||||
|
||||
# 同一场景反向钉死: 有 phone 在手问 WhatsApp(缺失渠道) → 放行并流出,
|
||||
# 不触发兜底(Task 3 邮箱轻补句依赖此放行,否则会被墙吞成兜底句)
|
||||
class OnlyAskMissingAI(FakeAI):
|
||||
async def stream_chat(self, messages, context=""):
|
||||
for token in ("Got your WhatsApp? ",):
|
||||
yield token
|
||||
|
||||
(manager2, conv2), _ = _setup(async_db, monkeypatch,
|
||||
ai=OnlyAskMissingAI(extract={}),
|
||||
source=ConversationSource.standalone)
|
||||
asyncio.run(_send(manager2, conv2.session_id, "00963944526529"))
|
||||
msgs2 = asyncio.run(manager2._load_history(conv2.id))
|
||||
tails2 = [m["content"] for m in msgs2 if m["role"] == "assistant"]
|
||||
assert "Got your WhatsApp?" in tails2[-1] # 放行流出,非兜底句
|
||||
asyncio.run(manager2.db.close())
|
||||
|
||||
|
||||
# ---- Task 9: 端到端回归(用户报告场景复现) ----
|
||||
def test_e2e_no_reasking_after_contact_provided(async_db, monkeypatch):
|
||||
|
||||
Reference in New Issue
Block a user