"""双要话术静态锚(spec 2026-11-02-jiancai-dual-contact-ask §5.3)。 首次索要联系方式 = WhatsApp+邮箱一起要(双要);已留资后的确认/收尾话术 渠道中性(邮箱-only 客户不再听到"发到你 WhatsApp")。wall.json 侧锚在 本文件前半,system.md/company_facts.md 侧锚在后半(Task 2 追加)。 """ import json from pathlib import Path SEED = Path(__file__).resolve().parents[1] / "seed_profiles" / "jiancai" def _wall() -> dict: return json.loads((SEED / "wall.json").read_text(encoding="utf-8")) # ---- wall 侧 ---- def test_deflections_dual_ask_all_languages(): w = _wall() assert any("and email" in s for s in w["deflections"]["en"]) assert any("和邮箱" in s for s in w["deflections"]["zh"]) assert any("y tu correo" in s for s in w["deflections"]["es"]) def test_pivots_dual_ask_all_languages(): w = _wall() assert "and email" in w["pivots"]["en"][0] assert "和邮箱" in w["pivots"]["zh"][0] assert "y tu correo" in w["pivots"]["es"][0] def test_post_contact_templates_channel_neutral(): """已留资确认/收尾模板不含渠道词(三语逐一钉死,防只用 en 子串漏检)。""" w = _wall() for group in ("deflections_contact", "pivots_contact"): for lang, items in w[group].items(): for s in items: assert "whatsapp" not in s.lower(), (group, lang, s) assert "whatsapp" not in w["no_contact"]["already"].lower() def test_app_reverse_ask_dual_and_synced(): """App 反向留资句双要,且 no_contact.app = contact_ask.app 逐字句同源。""" w = _wall() app = w["no_contact"]["app"] assert "and email" in app assert app in w["reminders"]["contact_ask"]["app"] # ---- system.md / company_facts.md 侧 ---- def _md(name: str) -> str: return (SEED / "prompts" / name).read_text(encoding="utf-8") def test_system_md_dual_ask_principle_and_pool(): src = _md("system.md") assert "双渠道一次要齐" in src assert "WhatsApp and email" in src # en 话术池 assert "WhatsApp和邮箱" in src # zh 话术池 # 邮箱轻补两条逐字句原文存活(与引擎 email_light 模板逐字一致) assert ("Got it! And an email address would help too — our manager " "will send the formal quotation PDF there.") in src assert "收到!邮箱也留一个呗?客户经理把正式报价单 PDF 发你邮箱。" in src def test_app_reverse_ask_line_matches_wall(): w = _wall() assert w["no_contact"]["app"] in _md("system.md") # L214 与 wall 一字不差 def test_company_facts_dual_ask(): assert "WhatsApp 和邮箱" in _md("company_facts.md") def test_deflections_no_either_or_wording(): """负向锚: 首次索要池不得回退二选一措辞(en "or email"/zh "或邮箱"/es "o correo")。 作用域仅限 deflections(首问池):通用化补句子句合法存在于 reminders 与 system.md("号码或邮箱"/"number or email" 等),若把负向锚扩到那里会误伤—— 别"修正"子串也别扩作用域。""" w = _wall() for s in w["deflections"]["en"]: assert " or email" not in s for s in w["deflections"]["zh"]: assert "或邮箱" not in s for s in w["deflections"]["es"]: assert "o correo" not in s.lower()