Files
ai-kefu-admin/tests/test_site_import_template.py
T
浅念 ec2a40086c feat(prompts): 建材 system.md/company_facts.md 双要改写——话术池/原则节/App反向留资/互留句/提醒配合全量对齐
计划外裁量(1 处): tests/test_site_import_template.py test_固定段行级来源
补 DUAL_ASK_DIVERGED 豁免清单——jiancai 双要演化后模板按 spec §3.4 有意
保持分步双要出厂,19 行联系方式话术分叉按特征子串豁免,其余行仍受 ≤8 阈值
守护(不豁免则该测试 19>8 必红,spec 冻结模板与全量绿冲突的唯一解)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-11-02 22:54:30 +08:00

102 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""site_import 骨架模板与 few-shot 语料守卫(Task 4)。"""
import json
import re
from pathlib import Path
TPL_DIR = Path(__file__).resolve().parents[1] / "app" / "platform" / "site_import" / "template"
FS = Path(__file__).resolve().parents[1] / "app" / "platform" / "site_import" / "fewshot" / "fewshot_excerpts.md"
JIANCAI_SYSTEM = Path(__file__).resolve().parents[1] / "seed_profiles" / "jiancai" / "prompts" / "system.md"
# 行业专属词黑名单(jiancai 管道 / yotai 储能 / televics 炉辊三行业的专属词;
# WhatsApp、manager、quotation 等通用外贸词不在其列)
INDUSTRY_WORDS = [
"管道", "pipe", "HESUPIPE", "储能", "storage", "炉辊", "roller",
"TELEVIC", "YOTAI", "HDPE", "PVC", "PE100", "SDR", "DN110",
"给水", "燃气", "排污", "灌溉", "波纹管", "球墨铸铁",
]
# 双要分叉豁免清单(2026-11-02,spec docs/superpowers/specs/
# 2026-11-02-jiancai-dual-contact-ask-design.md §3.4):jiancai system.md
# 已演化到"双渠道一次要齐"话术,生成器模板按 spec 决策**有意保持**"分步双要"
# 出厂默认——联系方式引导话术族的行级分叉是已归档的合规分叉,按行特征子串
# 豁免不计 missing;其余行仍受 ≤8 阈值守护(防未归档漂移)。
DUAL_ASK_DIVERGED = (
'互留句 "Drop yours too..."', # 模板句族行(L7 同构)
"I'll connect you with our manager to arrange that — drop your WhatsApp", # 转接句
"Tell me your WhatsApp and our manager", # 预算后引导句
"drop your WhatsApp and our manager will reply within 12 hours", # 磨价应对句
"记录里确有号码", # 已留资确认句
"4. 拿到联系方式后", # 对话流程尾括注
"What's your WhatsApp number with the country code", # en 池①
"who handles [country] market", # en 池②
"Drop your WhatsApp or email?", # en 池③
"Want me to get you a full quotation?", # en 池④
"你WhatsApp多少?带上国家代码", # zh 池①
"我让负责[国家]市场的同事加你", # zh 池②
"留个WhatsApp或者邮箱吧", # zh 池③
"你给我个联系方式,我让客户经理", # zh 池④
"**分步双要,一次只问一个**", # 双渠道原则行
'"Drop your WhatsApp and our manager', # App 反向留资句
"【互留】", # 互留句
'提醒说"已提供"时', # 系统提醒配合行
"拿到后说一句", # 注意节尾括注
)
class TestTemplatePurity:
def test_骨架无行业残留(self):
for fname in ("system", "extractor", "evaluator"):
t = (TPL_DIR / f"{fname}_template.md").read_text(encoding="utf-8")
low = t.lower()
for w in INDUSTRY_WORDS:
assert w.lower() not in low, f"{fname}_template.md 行业残留: {w}"
def test_占位符与清单一致(self):
ph = json.loads((TPL_DIR / "placeholders.json").read_text(encoding="utf-8"))
assert set(ph) == {"system", "extractor", "evaluator"}
for fname, keys in ph.items():
t = (TPL_DIR / f"{fname}_template.md").read_text(encoding="utf-8")
for k in keys:
assert "{{" + k + "}}" in t, f"{fname} 缺占位符 {k}"
# 反向:模板中每个 {{xxx}} 都在清单
for fname, keys in ph.items():
t = (TPL_DIR / f"{fname}_template.md").read_text(encoding="utf-8")
found = set(re.findall(r"\{\{([^}]+)\}\}", t))
assert found == set(keys), f"{fname} 占位符不对称: {found ^ set(keys)}"
def test_红线段锚点齐全(self):
t = (TPL_DIR / "system_template.md").read_text(encoding="utf-8")
for anchor in ["语言规则(最高优先级)", "职能边界", "红线(任何情况下不可违反)",
"不能出现任何价格数字", "施压", "禁止说的话", "联系方式引导策略",
"12 hours", "国家代码"]:
assert anchor in t, f"缺锚点: {anchor}"
def test_固定段行级来源(self):
"""非占位行应逐字存在于 jiancai 原文(允许空行/标题差异 ±5% 行)。
双要改版后联系方式话术族的分叉按 DUAL_ASK_DIVERGED 豁免(见其注)。
"""
j = JIANCAI_SYSTEM.read_text(encoding="utf-8").splitlines()
jset = set(l.strip() for l in j)
t = (TPL_DIR / "system_template.md").read_text(encoding="utf-8").splitlines()
missing = [l.strip() for l in t
if l.strip() and "{{" not in l and "#" not in l[:2]
and l.strip() not in jset
and not any(m in l for m in DUAL_ASK_DIVERGED)]
# 允许少量结构性改写(拆段标题等)——阈值 ≤8 行,且打印出来人工可核
assert len(missing) <= 8, f"来源外行过多: {missing}"
class TestFewshot:
def test_尺寸上限(self):
assert len(FS.read_bytes()) <= 18000
def test_两租户都在(self):
s = FS.read_text(encoding="utf-8")
assert "yotaienergys" in s and "televicsic" in s
def test_用途段落齐(self):
s = FS.read_text(encoding="utf-8")
for sec in ["身份", "追问", "好差", "事实卡", "提取器", "评估器"]:
assert sec in s, f"fewshot 缺用途段: {sec}"