Co-Authored-By: Claude Code <noreply@anthropic.com>
29 KiB
公司知识库(Hesu Auto 事实卡)实施方案
状态:方案定稿——四个待确认问题已全部确认(2026-09-10),待批准执行。
Goal: 给 AI 客服补充公司事实知识(品牌/覆盖/网络/经验),使客户问公司情况时零编造、简短、可信,且不改变职能边界(不报价/不承诺/不报联系方式)。
Architecture: 新建独立事实卡文件 app/prompts/company_facts.md(知识与行为分离),由 ai_service.py 拼接进对话 prompt(+3 行 loader;顺序:system.md 行为 prompt → 事实卡 → 当轮 context 提醒);事实卡内容经输出墙安全审查(每句过价格墙+零号码墙);不动 extractor/evaluator/前端/配置。
Tech Stack: 零新依赖、零新基础设施。纯 prompt 文件 + 2 行 loader 改动 + 静态安全测试。
1. 背景与问题
现状:system.md 是纯行为 prompt,不含任何公司事实。客户问"你们是什么公司/有哪些品牌/送不送到肯尼亚/是不是真的"时,GLM 只能编造。这正是 2026-08-20 changelog 记录过的失效模式——"prompt 不含真值 → GLM 编造假号"的同款问题,只是从号码换成了公司事实(编造品牌/国家/年限=信任崩塌,客户流失)。
生产实证(2026-09,Ghana 案例):客户问 "I'm in Ghana, do you have branch in Ghana",AI 答 "Not yet, but we've got partners worldwide..."——而公司在 Ghana 有分支(五分支之一,且有保税仓)。客户就在 Ghana、公司就在当地有网点,本是信任建立的绝佳时机,被知识真空变成当面否认。诊断:GLM 对无法验证的事实问题选择"听起来合理的否定+转进"(hedging),否定性编造与肯定性编造同样致命;且此类问题("do you have branch in X")没有特征触发词(只有国名),触发词注入方案(C)无法覆盖——这是选型 A(事实常驻)的最强论据。
编造面全量盘点(2026-09-10,用户发起):除公司事实外又发现两个编造向量,均折入 Task 3/6——
- 库存/现车断言:system.md 红线禁"库存承诺",但其"好回复"示例有三处在教 AI 说 "we got tons of them"/"in stock"——本会话已两次实证 GLM 听逐字示例不听抽象红线,示例教唆必被模仿;AI 无任何库存数据源,照示例回答=编造现货。(修正:Task 3 改动 3,按文本锚点定位)
- 保修/质保承诺:价格墙时间模式显式豁免 warranty(
(?!\s*(warranty|质保|保修)),boundary_filter.py:22)——AI 若输出编造的 "6 months warranty" 承诺,墙级零拦截(实证)。修法升级:Task 6 墙级收紧(prompt 红线 Task 3 改动 4 为 belt)。复查中还发现中文时限/质保承诺("3天到货"/"6个月质保")今天全部零拦截(时间模式只有英文单位)——Task 6 一并补。 其余编造面(车型公共参数过期、认证资质)接受为观察项,见 §6。
知识库要解决的四类真实问题:
| 客户问题类型 | 例子 | 需要的事实 |
|---|---|---|
| 信任核实 | "are you a real company?" | 首批出口企业/集团背景/累计出口量 |
| 品牌/产品 | "do you have ZEEKR? trucks?" | 30+ 品牌清单/动力类型/车型类别 |
| 覆盖范围 | "do you ship to Ghana?" | 70+ 国家/五分支/Dubai 枢纽 |
| 运作模式 | "how does this work?" | 中国采购→迪拜枢纽→海外交付 |
2. 方案选型
| A. 静态事实卡(推荐) | B. RAG 检索 | C. 触发词条件注入 | |
|---|---|---|---|
| 做法 | 事实卡文件常驻 prompt | 向量/关键词检索,按轮注入相关块 | 仿 CONTACT_ASK_TRIGGER_RE,问公司才注入 |
| 优点 | 零新设施;事实常在,漏触发=零;单 commit 回滚;与现有架构(prompt 驱动)完全一致 | token 省;可扩展到车型库存/FAQ | token 省 |
| 缺点 | 每轮 +~700-900 token(实测草案 1289 chars;glm-4-flash 成本可忽略) | 需引入检索设施(本项目零基础);检索漏=编造风险回归;新失效模式 | 触发漏(客户间接问)→ 编造风险回归 |
| 结论 | ✅ 本期采用 | KB 长大到库存/FAQ 规模再议 | 事实卡膨胀后再优化,loader 设计已留位 |
选 A 的决定性理由:本项目的可靠性哲学是"确定性优先"——RAG/触发词都引入"该注入时没注入"的 probabilistic 失效,而编造的代价(信任崩塌)远高于每轮几百 token。
3. 事实卡内容草案(即 Task 1 的文件内容)
# 公司事实卡(仅供回答客户关于公司的事实性问题)
## 我是谁
- Hesu Auto 是 Hesu Group 旗下专注汽车出口的公司
- 中国首批汽车出口企业之一,累计出口 50,000+ 台(用户已确认,2026-09-10)
- **累计出口到 70+ 国家与地区**(中东、非洲、南美、欧洲)——⚠️ 出口目的国口径,不是分支/网点覆盖(用户明确区分,2026-09-10)
- 与 30+ 汽车品牌深度合作:BYD、Changan、Geely、Leopard、ROX、Nissan、Zeeker、Li Auto 等
## 卖什么
- **新车与二手车都做**(用户已确认)
- 全动力类型:燃油、混动、插混、纯电
- 全车型类别:轿车、SUV、皮卡、重卡;乘用车、商用车、新能源车、车队/项目采购
## 网络与运作
- 模式:中国采购供应 → 迪拜(Jebel Ali)枢纽备货分拨 → 海外交付
- 海外分支五处:Dubai、Ghana、Ethiopia、Djibouti、Belarus
- Abu Dhabi 有展车节点;Ghana 西非保税仓;Djibouti+Ethiopia 东非协同;Venezuela(南美)在网络节点内(用户确认列入)
## 使用规则(与 system.md 红线同级)
- **统一兜底(最高优先,用户定调 2026-09-10)**:任何不清楚、事实卡没有、需要核实的问题——**不编造、不承诺、不否定、不断言**,统一动作只有一个:顺势引导留联系方式转客户经理——"这个我让我们经理给你准确答复——留个 WhatsApp,他直接联系你"
- 客户问公司情况/品牌/是否可信时,取上述事实**简短作答(1-2 句)**,建立信任
- **分支问题与出口覆盖问题是两回事,分开答**:
- 问分支/网点("do you have branch in X")→ 只认卡里列的:五分支+节点(Dubai/Ghana/Ethiopia/Djibouti/Belarus/Abu Dhabi/Venezuela)必须答"有"并点出当地节点;卡里没有的 → **走统一兜底**——绝对禁止编造否定答案("Not yet"/"we don't have"/"没有")(Ghana 案例教训:公司明明有分支被答成 Not yet)
- 问出口覆盖("do you ship to X")→ 70+ 国是**累计出口目的国**,不代表 X 一定有网点:答"已出口到 70+ 国家"事实 + **走统一兜底**(X 具体情况经理确认)——不承诺、不否定
- 绝不主动长篇介绍公司,绝不用成营销腔——风格仍按【说话风格】
- 涉及具体价格/交付时效/库存承诺 → 仍按【职能边界】转客户经理,本卡不改变任何红线;售后只说"有本地协同支持",不做任何保证
- 事实卡没写的细节(某国代理、某车型现车、认证资质细节)→ **走统一兜底**(不编造,留资+经理沟通)
- 数字一律照抄本卡写法(如 50,000+、70+),不改写成中文数字
内容取舍说明(对照原始资料)
| 原资料内容 | 处置 | 理由 |
|---|---|---|
| About 正文全篇 | 压缩为"我是谁"4 行 | 营销语气("strives to win recognition")违背说话风格;KB 只要事实 |
| Contact Us 节 | 整节剔除 | 红线:AI 绝不输出我方联系方式(web 指按钮/App 反向留资,2026-08-20 已定) |
| "Why Work with HESU" 四点 | 融入"网络与运作"1 行 | 与 Global Network 高度重复,留骨架 |
| Key Facts "10,000+" | 不采信 | 与 About "50,000+" 矛盾,用户确认以 50,000+ 为准 |
| "72+ Countries" | 取 "70+" | About 正文"more than 70"更稳,两处兼容 |
| "Zeeker" | 保留原文 Zeeker(用户确认) | 用户口径为准,不作 ZEEKR 规范化 |
| Venezuela | 列入(用户确认) | 仅网络图出现,按用户口径列为网络节点 |
| 新车/二手车定位 | 新旧车都做(用户确认) | 人设第一句同步改为"汽车出口外贸销售(新车与二手车都做)",Task 3 一并处理 |
| Leopard、ROX 品牌名 | 保留原文(用户确认) | 照抄入卡:Leopard、ROX |
4. 与现有防线/风格的兼容性审查(本方案的核心约束)
| 审查项 | 结论 | 说明 |
|---|---|---|
价格墙 violates_boundary |
⚠️ 有陷阱,已规避 | 五万 这类中文数字+万会被裸预算模式击杀(`(两 |
零号码墙 no_contact_value |
✅ 安全 | 卡内数字均 <10 位(50,000 逗号断开=5 位);无邮箱;Task 2 扫描钉死——同时防止未来有人往 KB 里加电话/邮箱 |
再索要墙 asks_contact |
✅ 交互正确(实证) | 使用规则里的兜底话术"留个 WhatsApp,他直接联系你"命中再索要墙——客户已留资时 GLM 复读兜底话术会被正确丢弃(墙换成"经理主动联系"版),未留资时放行(合法引导)。两层设计自洽,无需处理 |
| 职能边界 | ✅ 显式锁定 | 使用规则第 3 条:事实不改变报价/承诺转接;交付措辞避免时限含义("faster delivery"不进卡) |
| 说话风格 | ✅ 规则化 | 1-2 句简短作答,禁营销腔;品牌/地名保留英文(GLM 按客户语言自然组织) |
| extractor/evaluator | ✅ 不受影响 | 两者用独立 prompt,不拼事实卡;extractor 只提取客户信息 |
| token 成本 | ✅ 可忽略 | 实测草案 1289 chars ≈ 700-900 GLM token/轮(glm-4-flash 成本仍可忽略);换"事实常在零漏触发" |
| 回滚 | ✅ | 事实卡文件+2 行 loader,单 commit revert |
5. 任务分解
Task 1: 事实卡文件 + loader 拼接(TDD)
Files:
-
Create:
app/prompts/company_facts.md(§3 草案,按 §7 确认结果定稿) -
Modify:
app/services/ai_service.py(+3 行) -
Create:
tests/test_company_facts.py -
Step 1: 失败测试——prompt 组装顺序
"""公司事实卡: 拼接位置与墙安全性静态测试。"""
from pathlib import Path
from app.services.ai_service import AIService, COMPANY_FACTS, SYSTEM_PROMPT
FACTS_PATH = Path("app/prompts/company_facts.md")
class _CapturingProvider:
def __init__(self):
self.prompts = []
async def chat(self, messages, system_prompt):
self.prompts.append(system_prompt)
return "ok"
async def stream_chat(self, messages, system_prompt):
self.prompts.append(system_prompt)
for t in ("ok",):
yield t
def test_facts_concatenated_between_system_and_context():
# 顺序: 行为 prompt → 事实卡 → 当轮提醒(提醒必须最后,保持最新鲜)
svc = AIService(provider=_CapturingProvider())
svc.provider.prompts.clear()
import asyncio
asyncio.run(svc.chat([{"role": "user", "content": "hi"}], context="⚠️ 提醒"))
p = svc.provider.prompts[0]
assert "Hesu" in p # 事实在场
assert p.index(SYSTEM_PROMPT) < p.index(COMPANY_FACTS) < p.index("⚠️ 提醒")
def test_facts_absent_from_extractor_prompt():
# extractor/evaluator 不拼事实卡(它们有自己的 prompt,不该看到公司事实)
from app.services.ai_service import EVALUATOR_PROMPT, EXTRACTOR_PROMPT
assert "Hesu" not in EXTRACTOR_PROMPT
assert "Hesu" not in EVALUATOR_PROMPT
-
Step 2:
python -m pytest tests/test_company_facts.py -v→ FAIL(两个测试均以 ImportError/收集错误失败——COMPANY_FACTS是模块级常量且 company_facts.md 未建,属预期红灯形态;本文件仅此两测试,无部分通过预期被破坏) -
Step 3: 实现
ai_service.py:
SYSTEM_PROMPT = _load_prompt("system.md")
COMPANY_FACTS = _load_prompt("company_facts.md") # 公司事实卡(知识与行为分离)
EXTRACTOR_PROMPT = _load_prompt("extractor.md")
EVALUATOR_PROMPT = _load_prompt("evaluator.md")
# 对话基座: 行为 prompt 在前(红线显著性),事实卡居中(参考资料),
# 当轮 context 提醒最后拼接(在 chat/stream_chat 内,保持最新鲜)
_BASE_CHAT_PROMPT = SYSTEM_PROMPT + "\n\n" + COMPANY_FACTS
chat/stream_chat 内 SYSTEM_PROMPT + "\n\n" + context 改为 _BASE_CHAT_PROMPT + ("\n\n" + context if context else "")。
创建 app/prompts/company_facts.md(§3 定稿内容)。
-
Step 4: 测试通过;全量回归
python -m pytest tests/ -q全绿(基线 160) -
Step 5: 提交
feat(kb): 公司事实卡入 prompt——Hesu 品牌/覆盖/网络零编造
Task 2: 事实卡墙安全静态测试(TDD)
- Step 1: 失败测试(追加到
tests/test_company_facts.py)
from app.services.boundary_filter import (
no_contact_value, split_sentences, violates_boundary,
)
def _fact_sentences():
# 注: 事实卡是无句读的 markdown 列表,多行会合并成大句块——无害,
# 检测是 search 式,违规在任何块内都能命中(§3 草案独立评审实测 1289 chars
# 过两墙全清——长度数字随草案修订变化,以实施时实测为准)
complete, tail = split_sentences(COMPANY_FACTS)
return complete + ([tail] if tail else [])
def test_facts_pass_both_output_walls():
# AI 原样引用事实也必须安全: 每句过价格墙+零号码墙
# (陷阱实证: "五万"会被裸预算模式击杀、"累计出口五万台"→整句被替换成要号话术)
for s in _fact_sentences():
assert not violates_boundary(s), s[:60]
assert not no_contact_value(s), s[:60]
def test_facts_length_capped():
# 防营销手册化: 事实卡是卡片不是 brochure——超限=该精简或走检索方案
assert len(COMPANY_FACTS) < 2500
def test_facts_no_contact_channel():
# 我方联系方式绝不入卡(红线: web 指按钮/App 反向留资)
# 只查确定性标记: 邮箱@与 wa.me 链接("WhatsApp"品牌词是合法内容,不作标记)
assert "@" not in COMPANY_FACTS
assert "wa.me" not in COMPANY_FACTS.lower()
-
Step 2: FAIL(文件未建/或内容违规)→ Step 3: 按 §3 定稿 → Step 4: 全绿
-
Step 5: 提交
test(kb): 事实卡墙安全静态网——中文数字陷阱/联系方式/长度上限
Task 3: system.md 集成(人设定调 + 事实作答口径 + 编造面封堵)
- 改动 1——人设第一句(业务定位用户已确认"新旧车都做")。⚠️ 该句是 system.md L1 的前缀(同行后续为"你说话像在 WhatsApp…"风格句)——做子串替换,不要整行替换删除风格句:
原(子串):
你是一位二手车外贸销售
改为:
你是一位汽车出口外贸销售(新车与二手车都做)
- 改动 2——在【职能边界】"三类请求的处理"第 1 类(汽车信息)后追加一行:
- 公司情况(品牌/覆盖国家/出口经验/运作模式)按系统提供的事实卡简短作答建立信任——但不因此报价、承诺或跳过留资引导
- 改动 3——库存断言示例修正(三处,按文本锚点定位,勿信行号):示例在教学"现货断言",而 AI 无库存数据源(编造向量,见 §1 盘点)。保留各示例的原本教学意图(围绕客户说的车型/年份),只把现货断言改为"可采购调配"。⚠️ 行号会随文件改动漂移(方案撰写时实测在 L105/L111/L117,上轮修复后已漂移过一次)——实施时以
grep -n "in stock\|tons of them" app/prompts/system.md定位,以下原文为准:
| 原文(逐字) | 改为 |
|---|---|
| "Corolla is a solid pick, we got tons of them. Any preference on year?" | "Corolla is a solid pick, we can source it easily. Any preference on year?" |
| "For 2025 models we got BYD Han, Toyota Camry, Honda Accord in stock. Budget range?" | "For 2025 models we can source BYD Han, Toyota Camry, Honda Accord. Budget range?" |
| "We got the 2024 Tang EV 7-seater and the DM-i AWD in stock. Want specs?" | "The 2024 Tang comes as the EV 7-seater and DM-i AWD — we can source either. Want specs?" |
⚠️ 差示例(L104 附近,"我们在非洲和东南亚市场有大量现车")是反面教材,不要顺手修改——它被标为"差"正是因为客服腔,保留其反例作用。
- 改动 4——红线区补三条(价格墙对 warranty 形态白名单放行,保修承诺只能靠 prompt 封;Task 6 墙收紧为确定性兜底):
- 一切不清楚、没把握的问题(卡外国家/库存/保修/认证/任何事实细节)——不编造、不承诺、不否定、不断言,引导客户留联系方式,由客户经理沟通
- 不对库存/现车做断言——有没有现车、什么时候有,由客户经理确认;你只能说"可以采购调配"
- 不报任何保修/质保期限("X months warranty"这类数字承诺)——一律"客户经理会给你完整的保修方案"
- 回归
python -m pytest tests/ -q全绿(prompt 改动无单测,靠既有安全网;注意 test_boundary_filter.py 的test_whitelist_clean用例含 "we got tons of them" 作为墙输入——那是测试自有字符串,不依赖 system.md,不受影响) - 提交
feat(prompt): 人设定调+事实作答口径+库存/保修编造面封堵
Task 4: 改动文档 + 冒烟清单
- 新建
docs/2026-09-10-company-knowledge-base-changelog.md(体例同前两份:背景/选型/文件清单/已知限制/部署回滚/冒烟结果) - 真机 GLM 冒烟(部署前置项,单测覆盖不到 GLM 引用事实的方式):
| 场景 | 期望 |
|---|---|
| "I'm in Ghana, do you have branch in Ghana?"(生产案例复现) | 必须有分支——"Yes, we've got a Ghana branch with bonded storage" + 顺势问需求;绝不再说 Not yet |
| "are you a real company?" | 1-2 句事实作答(首批出口企业/出口量/70+ 国),零编造零营销腔 |
| "do you have Zeeker? pickup trucks?" | 按卡答(品牌清单;皮卡重卡都有),不报库存承诺 |
| "do you ship to Kenya?"(卡内未列的国家) | 区分口径:答"已出口到 70+ 国家,肯尼亚具体我让经理确认"——不承诺有网点、不说 Not yet |
| 中文问"你们公司在哪/怎么运作" | 中文简短作答,数字用出口量/70+ 形式(不能被价格墙拦) |
| "how much for Song L in Ghana?" | 公司事实不得软化价格红线——仍迂回+要联系方式 |
| App 会话问公司情况 | 同样作答,零联系方式输出 |
| "are your cars new or used?" | 按确认口径作答:"新旧车都做"(事实卡+人设已定调) |
| "do you have Song L in stock right now?" | 不断言现货——"可以采购调配"+顺势留资("留个 WhatsApp 经理告诉你现车情况")(改动 3 验证) |
| "is there warranty? how long?" | 不报期限数字——"客户经理会给你完整保修方案"+留资引导(改动 4 + Task 6 墙级兜底:即使 GLM 硬说 "6 months warranty" 也会被价格墙替换) |
| "do you have office in Tokyo?"(卡外冷门事实) | 统一兜底:不否定不肯定——"出口 70+ 国,东京这边我让经理确认,留个 WhatsApp" |
| 中文问"多久能到 Ghana?"/"质保多久?" | 不给时限/期限数字(中文单位形态 Task 6 已墙级兜底:"3天到货"/"6个月质保"会被替换) |
- 提交
docs: 公司知识库——改动文档
Task 5: 再索要墙补裸碎片模式(Ghana 案例顺带发现,已实证)
原理: Ghana 案例的 AI 回复结尾 "Your number, please?" 经实证穿透现有 REASK_PATTERNS(只拦得住 "email or WhatsApp?" 组合形态;"Your number, please?"/"Your WhatsApp?" 裸碎片全漏)。已留资场景下这类碎片就是再索要。收紧版模式(动词后顾排除跟进确认句)。
⚠️ 既有测试同步更新(独立评审实证抓出,2026-09-10):新模式会命中 test_asks_contact_clean_phrasings_pass 里的 "Could you confirm your number?"(该断言编码 suspect 礼貌确认旧政策)。行为本身正确——asks_contact 仅在 confirmed(has_contact=True)时启用,此时再问"确认号码"就是冗余再索要;suspect 流(has_contact=False)闸门关着不受影响。按 Task 6 处理 test_warranty_clean 的同款方式:把该句从 clean 清单移入命中清单,注明政策依据与日期。实施时必须对既有 asks_contact 全部测试句重跑核对(首版"5 命中/5 放行"只验了自拟句集,漏了既有套件——教训)。
Files:
-
Modify:
app/services/boundary_filter.py(REASK_PATTERNS 追加一条) -
Test:
tests/test_boundary_filter.py -
Step 1: 失败测试 + 既有政策测试更新(追加到 asks_contact 测试组;同步把 "Could you confirm your number?" 从 clean 测试移入命中测试,注释注明政策变更)
def test_asks_contact_bare_fragment_asks():
# Ghana 生产案例: "Your number, please?" 曾穿透(5b 首版只覆盖动词/疑问词形态)
assert asks_contact("Your number, please?")
assert asks_contact("Your WhatsApp?")
assert asks_contact("So... your number?")
assert asks_contact("And your contact?")
# 政策变更(2026-09-10): confirmed 语境下"确认号码"也是冗余再索要
# (suspect 流闸门关着不受影响)——自 clean 清单移入
assert asks_contact("Could you confirm your number?")
def test_asks_contact_fragment_followup_still_pass():
# 动词后顾排除: 跟进确认(非索要)放行
assert not asks_contact("Did the manager reach your WhatsApp?")
assert not asks_contact("Got your number, thanks!")
assert not asks_contact("Our manager will send it to your WhatsApp shortly.")
- Step 2: FAIL → Step 3: REASK_PATTERNS 追加(模式已实证 5/5):
# 裸碎片问句: "Your number, please?"/"Your WhatsApp?"(Ghana 案例穿透补)
# 后顾排除跟进确认句("reach/got/sent/did your X?" 是问进度不是要号)
re.compile(r"(?<!reach )(?<!reached )(?<!got )(?<!sent )(?<!did )"
r"your\s+(?:whatsapp|number|phone|e-?mail|contact)\b[^.!?]{0,20}[??]", re.I),
- Step 4: 全量回归
python -m pytest tests/ -q全绿 - Step 5: 提交
fix(contact): 再索要墙补裸碎片形态——Your number, please?(Ghana案例)
Task 6: 时限/保修承诺墙级收紧(编造承诺从 prompt 层升为墙级确定性)
原理(2026-09-10 实证): 政策依据=用户定调"不清楚的不承诺":保修期限/交付时限是公司政策事实,AI 无真值,输出即编造。三处墙缺口(均已实证):
- 价格墙时间模式的 warranty 豁免使 "we offer 6 months warranty" 确定性放行(现墙 False)——直接去掉豁免即可(day/week/month 单位收紧后命中)。
- ⚠️ 首版计划的测试 bug(实证抓出):
8 years warranty的 year 不在(day|week|month)内,去豁免后仍放行——且不能把 year 加进时间模式("under 5 years old" 二手车龄聊天会误杀)。修法:新增定向模式\d+\s*years?\b[^.!?]{0,15}warranty(英文年+质保)与\d+\s*年[^。!?]{0,8}(?:质保|保修)(中文年+质保),"8 years warranty" ✓ 命中、"under 5 years old" ✓ 放行。 - 中文时限/质保承诺今天全部零拦截(时间模式只有英文单位):"3天到货"/"6个月质保"/"电池保修8个月" 现墙全 False。补
\d+\s*(?:天|周|个月)——实证 9/9 安全("2024年款"/"5座SUV"/"600km续航"/"2024 Tang"/"50,000+ 台"全放行;年 不收,防车型年款误杀;"一周"中文数字形态不收,观察)。
Files:
-
Modify:
app/services/boundary_filter.py(PRICE_PATTERNS) -
Test:
tests/test_boundary_filter.py -
Step 1: 失败测试 + 既有政策测试更新
def test_warranty_commitment_violates():
# 政策变更(2026-09-10 用户定调: 不清楚的留资转经理): AI 输出的保修期限
# 是编造的公司政策承诺——从"白名单放行"改为墙级违规
assert violates_boundary("we offer 6 months warranty on every car")
assert violates_boundary("battery has 8 years warranty") # year 不在时间模式内,靠定向模式
assert violates_boundary("warranty of 8 years on battery") # EN 倒装
assert violates_boundary("电池保修8个月")
assert violates_boundary("8年质保")
assert violates_boundary("整车质保3年10万公里") # CN 倒装(质保在前)
assert violates_boundary("质保3年或10万公里")
def test_chinese_time_commitment_violates():
# 既有缺口: 中文时限承诺此前零拦截(时间模式只有英文单位)
assert violates_boundary("3天到货")
assert violates_boundary("6个月质保")
def test_age_and_year_talk_still_clean():
# 误杀防护: 二手车龄/车型年款/无数字保修讨论必须放行(实证 13 条全清)
assert not violates_boundary("under 5 years old")
assert not violates_boundary("5 years old car")
assert not violates_boundary("2024年款")
assert not violates_boundary("5座SUV")
assert not violates_boundary("600km续航")
assert not violates_boundary("does it come with warranty?")
assert not violates_boundary("warranty details from our manager")
assert not violates_boundary("how many years warranty?") # 无承诺数字的反问
同步更新既有 test_warranty_clean(其断言 "6 months warranty on the engine" 干净编码的是旧政策,改为断言违规;保留无数字形态干净的断言),测试内注释注明政策变更依据与日期。
- Step 2: 新测试 FAIL(现墙放行)→ Step 3: 实现——PRICE_PATTERNS 时间模式替换 + 新增(模式集已实证 10 命中/13 放行):
# 时间承诺: 去掉 warranty 豁免(政策变更 2026-09-10——保修期限是编造的
# 公司政策承诺,不再白名单)
re.compile(r"\d+\s*[- ]?(day|week|month)s?\b", re.I),
# 年限保修定向(双向): year 不能进时间模式(车龄 "under 5 years old" 会误杀),
# 只收 数字+年 与 warranty/质保/保修 的组合(含倒装"质保3年"/"warranty of 8 years")
re.compile(r"\d+\s*years?\b[^.!?]{0,15}warranty", re.I),
re.compile(r"warranty\s*(?:of|for|:)?\s*\d+\s*years?\b", re.I),
re.compile(r"\d+\s*年[^。!?]{0,8}(?:质保|保修)"),
re.compile(r"(?:质保|保修)[^。!?]{0,8}\d+\s*年"),
# 中文时限承诺: 天/周/个月(年不收——"2024年款" 是车型年款)
re.compile(r"\d+\s*(?:天|周|个月)"),
- Step 4: 全量回归
python -m pytest tests/ -q全绿。前置安全检查:grep -rn "个月\|[0-9]天\|months\|years" app/ tests/全目录核对模板/既有测试文案无 "N 天/N个月/N months/N years" 形态(独立评审已预核当前零命中,但检查范围必须全 tests/——防测试文案藏形态) - Step 5: 提交
fix(contact): 时限/保修承诺墙级封堵——去warranty豁免+中文单位+年限定向(编造面盘点#2)
6. 残余风险
- GLM 把事实用成长篇——使用规则+风格约束双保险,冒烟场景 1 验证;失守再考虑事实卡减条
- 事实过时无察觉——KB 是静态文件,出口量/分支变化需人工更新;changelog 记"事实来源=2026-09 官网资料+用户口径确认"
- 事实软化红线的间接路径——如客户问"你们出口 70 国那运费多少",GLM 可能借势聊运费;冒烟专项验证
- 车型公共参数编错(接受残余)——GLM 内部知识答续航/电池/配置,新款(2026 款)可能过期失真;零防线,方向性通常正确,冒烟观察
- 关税/税率百分比(观察项)——"25% duty" 形态价格墙不拦,靠 tariff/关税触发词的提醒层兜底;冒烟观察,失守再议墙收紧
- 库存断言为 prompt 层封堵(非墙级)——Task 3 改动 3/4 是软约束;若冒烟仍出现 "in stock" 断言,升级方案:库存断言句式进再索要墙同款输出过滤。保修/时限承诺已由 Task 6 升为墙级确定性封堵(双向年限定向+中文单位,2026-09-10 实证 10 命中/13 放行)
- token 常驻成本实测草案 1289 chars ≈ 700-900 GLM token/轮——glm-4-flash 可忽略;若未来换贵模型,再评估方案 C
- 中文数字时限形态不收("一周到货"/"三天发货")——中文数字+单位组合形态长尾大、误杀面难控,观察期;若冒烟出现,回流补模式(与"漏词回流补词"同机制)
- 英文非保修年限表述不拦("it lasted 8 years"类)——year 进时间模式会误杀二手车龄聊天,定向模式只收 warranty 邻接;边缘接受
- Task 5 误杀类(知情):confirmed 语境下引用/核对类问句("Has your number changed?"/"Could you confirm your email?")会被当再索要丢弃——丢句不掐断,损失一句可接受;suspect 流不受影响
- Task 6 时态不区分:"N天前"过去式引用(中文)现在也会被墙替换——与英文 "3 days ago" 既有行为一致(此前中文恰好豁免),一致性修复而非回归
7. 待确认问题(执行前)
| # | 问题 | 影响 | 结论(全部确认于 2026-09-10) |
|---|---|---|---|
| 1 | 人设第一句 + 事实卡一行 | ✅ 新旧车都做 | |
| 2 | 事实卡一行 | ✅ 50,000+ | |
| 3 | 事实卡一行 | ✅ 列入 | |
| 4 | 品牌清单 | ✅ 保留原文(Leopard、ROX;Zeeker 亦保留原文拼写,用户确认) |
确认方式:直接回复"1 新车出口 2 五万 3 不列 4 剔除"这类即可,或改任意项。
8. 部署与回滚
重启生效(prompt 启动加载);无迁移/配置/前端;六个任务各自成 commit,逐个可 revert。