Files
ai-kefu-jc/docs/superpowers/plans/2026-09-10-company-knowledge-base.md
T
2026-09-29 14:15:12 +08:00

29 KiB
Raw Blame History

公司知识库(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——

  1. 库存/现车断言:system.md 红线禁"库存承诺",但其"好回复"示例有三处在教 AI 说 "we got tons of them"/"in stock"——本会话已两次实证 GLM 听逐字示例不听抽象红线,示例教唆必被模仿;AI 无任何库存数据源,照示例回答=编造现货。(修正:Task 3 改动 3,按文本锚点定位)
  2. 保修/质保承诺:价格墙时间模式显式豁免 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 无真值,输出即编造。三处墙缺口(均已实证):

  1. 价格墙时间模式的 warranty 豁免使 "we offer 6 months warranty" 确定性放行(现墙 False)——直接去掉豁免即可(day/week/month 单位收紧后命中)。
  2. ⚠️ 首版计划的测试 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. 中文时限/质保承诺今天全部零拦截(时间模式只有英文单位):"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. 残余风险

  1. GLM 把事实用成长篇——使用规则+风格约束双保险,冒烟场景 1 验证;失守再考虑事实卡减条
  2. 事实过时无察觉——KB 是静态文件,出口量/分支变化需人工更新;changelog 记"事实来源=2026-09 官网资料+用户口径确认"
  3. 事实软化红线的间接路径——如客户问"你们出口 70 国那运费多少",GLM 可能借势聊运费;冒烟专项验证
  4. 车型公共参数编错(接受残余)——GLM 内部知识答续航/电池/配置,新款(2026 款)可能过期失真;零防线,方向性通常正确,冒烟观察
  5. 关税/税率百分比(观察项)——"25% duty" 形态价格墙不拦,靠 tariff/关税触发词的提醒层兜底;冒烟观察,失守再议墙收紧
  6. 库存断言为 prompt 层封堵(非墙级)——Task 3 改动 3/4 是软约束;若冒烟仍出现 "in stock" 断言,升级方案:库存断言句式进再索要墙同款输出过滤。保修/时限承诺已由 Task 6 升为墙级确定性封堵(双向年限定向+中文单位,2026-09-10 实证 10 命中/13 放行)
  7. token 常驻成本实测草案 1289 chars ≈ 700-900 GLM token/轮——glm-4-flash 可忽略;若未来换贵模型,再评估方案 C
  8. 中文数字时限形态不收("一周到货"/"三天发货")——中文数字+单位组合形态长尾大、误杀面难控,观察期;若冒烟出现,回流补模式(与"漏词回流补词"同机制)
  9. 英文非保修年限表述不拦("it lasted 8 years"类)——year 进时间模式会误杀二手车龄聊天,定向模式只收 warranty 邻接;边缘接受
  10. Task 5 误杀类(知情):confirmed 语境下引用/核对类问句("Has your number changed?"/"Could you confirm your email?")会被当再索要丢弃——丢句不掐断,损失一句可接受;suspect 流不受影响
  11. Task 6 时态不区分:"N天前"过去式引用(中文)现在也会被墙替换——与英文 "3 days ago" 既有行为一致(此前中文恰好豁免),一致性修复而非回归

7. 待确认问题(执行前)

# 问题 影响 结论(全部确认于 2026-09-10)
1 主业务:新车出口 / 二手车 / 两者? 人设第一句 + 事实卡一行 ✅ 新旧车都做
2 累计出口量:About 写 50,000+,Key Facts 写 10,000+,矛盾 事实卡一行 ✅ 50,000+
3 Venezuela 要不要列入覆盖? 事实卡一行 ✅ 列入
4 "Leopard、ROX"两个品牌名是否准确? 品牌清单 ✅ 保留原文(Leopard、ROX;Zeeker 亦保留原文拼写,用户确认)

确认方式:直接回复"1 新车出口 2 五万 3 不列 4 剔除"这类即可,或改任意项。

8. 部署与回滚

重启生效(prompt 启动加载);无迁移/配置/前端;六个任务各自成 commit,逐个可 revert。