Files
group_fqcd_jr/开发文档/D4.8-客服Agent智能度体检与整改报告-W21-2026-09-21.md
T
张胜宇 dd5e9f8435 fix(W21): 智能度体检 → 3 类真实缺陷修复(C-8/C-9/C-10)+ 1 类待裁登记(C-11)
甲方反馈「客服 agent 还是不太智能」。本轮不新增出口、不改需求,
把"不智能"拆成可复现的实测条目:81 条真实口语问法 + 8 组多轮追问链
(真 HTTP:API → 队列 → Worker → 治理层),逐条定位根因并修复。

已修复(每条都有真机 HTML 证据,见 开发文档/D4.8 §3)

- C-8 账户盈亏问法落"误导性澄清"
  「我的基金赚了多少钱」→ E1 澄清,三个候选(万份收益/起投金额/收益率差别)
  全是公开知识,而客户问的是自己账户的盈亏金额。
  修法:P1_PATTERNS 补「第一人称 + 盈亏动词 + 金额疑问词」骨架,
  金额疑问词是必要条件 —— 「我买的基金亏了怎么办」照旧走知识检索(库里有解)。

- C-9 多轮指代断链,把"已经说过的"又问一遍
  「我想买个债基」→「它适合我吗」落 E2d「请告诉我具体的基金名称或代码」,
  而客户刚刚才被告知是那只产品;「赎回费怎么算」→「持有 8 个月呢」同因。
  两个根因:① _topic_of 只在答复前 4 行的固定形状里取主语,FAQ 型答复
  (首行「问:…」)反解为空;② 意图分类把短追问标成 suitability_check,
  绕开了知识出口里已做好的追问继承逻辑。
  修法:_answer_suitability 建成四级降级链(严格主语 → 形状反解
  _product_name_in_history → 有上文交回检索 → 首轮才澄清)+ 纯参数追问前置闸门。
  安全边界三条不放宽:不给访客"能不能买"结论;推测出的名字查不到风险等级时
  回落检索而非回"给不出结论";首轮指代保留澄清(金标 E-01 口径)。

- C-10 「你们投诉电话是多少」被强制转人工
  根因:P2_WRITE_DISPUTE_KEYWORDS 里的裸词「投诉」子串命中,把"问投诉渠道"
  和"提交投诉"撞在同一判据上;而答案就在库里(POL-SPM-036-01 / FAQ-0054)。
  修法:新增 _is_p2_contact_inquiry() 作为第二类 P2 豁免(渠道词 + 疑问词,
  且不带明确投诉意图)。「我要投诉,让你们经理来找我」照旧建单。

定位但未修(需甲方裁定合规口径)

- C-11 E4 证据约束生成的答复被"收益数值"闸门整条拦回 E5b
  「南方现金添利怎么样」(top1 1.0000) / 「买基金要手续费吗」(0.7328) /
  「债基和货基哪个收益高」(0.5457) 三条同因:生成稿出现 YIELD_METRIC_TERMS
  → hits_zero_tolerance → _exit_partial → E5b 兜底。
  试过"先净化再判合规",实测更差(drop_yield_claims 整行丢弃 + 生成稿常是
  单行长段 ⇒ 整条被删空),已回退并把原因写进代码注释留痕。
  建议方案:改 E4 提示词禁止输出收益数值(源头消除,不动红线代码)。

待判物

- _chunks_report.txt 判定可删:无代码引用 / 内容可从 knowledge/_chunks.jsonl
  复算 / 从未入库 / 统计口径已写进 D2.4 §6 与 D2.8 §2。已删 + 加 .gitignore。

验收

- 金标 46 条:M-1 46/46、M-4 46/46、M-6 5/46(白名单 F-05/G-01/G-03/G-04/G-05,
  零越界)、M-7/M-8/M-9/M-10 = 0、M-2 28/31、M-2b 15/18、M-3 4/4
  —— 与 W20 基线逐项一致,零回归。
- 全量回归 1985 passed / 3 skipped(W20 基线 1969 passed / 3 skipped)。
- 新增守卫单测 16 条(含 C-8 两条 / C-9 四条 / C-10 两条)。
- ruff 仅剩 4 条既有告警,未顺手改,避免混入无关 diff。

文档

- 新增 开发文档/D4.8-客服Agent智能度体检与整改报告-W21-2026-09-21.md
- 客服agent/D2.1 升 v6.37;开发文档/D1.1 升 v1.13;D1.6 增 §9 上下文提取
2026-09-21 10:55:16 +08:00

21 KiB
Raw Blame History

D4.8 · 客服 Agent 智能度体检与整改报告(W21)

体系编号:D4.8 · 域:四、重构与清除留痕 编号:CS-RPT-2026-024 | 版本:v1.0 | 日期:2026-09-21 | 状态:现行 性质:留痕报告。回答甲方一句质疑 —— 「客服 Agent 不智能,很多问题强制转人工」。 本文只做三件事:取实测证据、定位根因、登记整改与未整改项。不改需求、不立新任务。


0. 一句话结论

本轮用 81 条真实口语问法 + 8 组多轮追问(真 HTTP,非进程内)打了一遍现有实现,定位并修复了 3 类真实缺陷、登记了 1 类需甲方裁定的合规口径问题。修完之后:

  • 金标 46 条:M-1 出口 46/46、M-4 事实 46/46、M-6 转人工 5/46(白名单 5 条,零越界)、M-7/M-8/M-9/M-10 = 0、M-2 28/31、M-2b 15/18、M-3 4/4 —— 与 W20 基线逐项一致,零回归。
  • 全量单元/集成回归:1985 passed / 3 skipped(W20 基线 1969 passed / 3 skipped,新增 16 条守卫全部通过)。
  • 智能度体检:退化形态由 W21 首轮的 25/78 降到 16/81(口径说明见 §2.3,两次分布不可直接相减)。
  • _chunks_report.txt 判定为可删(依据见 §1)。

1. 待判物:_chunks_report.txt 能否删除 —— 判定:可删,已删并已加 .gitignore

判据 结论
是不是源码依赖? 不是。全仓库 rg 无任何模块 import / 读取该文件
内容能否复算? 能。它是 tools/build_knowledge_chunks.py 的控制台输出落盘稿,每一个数字都能从已入库的 knowledge/_chunks.jsonl 重新算出
是否入库(git 追踪)? 否。git ls-files 无记录,git ls-remote 侧同样无
删掉会不会丢信息? 不会。同一份统计口径已写进 D2.4 §6 / D2.8 §2,语料条数守卫由 tools/build_knowledge_chunks.py 的 FAQ_EXPECTED_COUNT 承担

动作:删除文件 + 在 .gitignore 末尾追加排除项,避免它再次被生成后误入库。

# tools/build_knowledge_chunks.py 的输出副产物(可由 knowledge/_chunks.jsonl 复算,不入库)
_chunks_report.txt

留痕口径:这是工具产物,不是项目内容。同类处理见 .gitignore 里已有的 .workbuddy/(AI 助手会话记忆)。


2. 体检方法(可复现)

2.1 为什么要用"真 HTTP"而不是进程内 harness

进程内金标 harness(_eval_harness/probe.py)测的是判定链(出口 / 检索 / 事实),它跳过了 API → 队列 → Worker → 治理层 这一段。而"不智能"的体感恰恰主要来自那一段:队列里重算 chitchat_streak、治理层追加免责声明、输出侧红线二次校验。

因此体检走真 HTTP(POST /api/v1/agent-runs + 轮询 GET /api/v1/agent-runs/{id}),以客户档(种子账号 cust_t)提问,与金标 harness 互补而非替代:

  • 真 HTTP 测不到的(检索命中明细 / M-2 / M-3 / M-5)不假判,仍以进程内 harness 为准;
  • 进程内测不到的(治理层替换 / 免责声明 / 队列重算)以真 HTTP 为准。

2.2 样本构成

类别 条数 说明
单轮口语问法 65 概念题 / 时效题 / 产品题 / 账户题 / 闲聊 / 边界题 / 反诈题全覆盖
多轮追问链 8 组 16 轮 指代(「它适合我吗」)/ 参数追问(「持有 8 个月呢」)/ 类目切换(「货币基金呢」)/ 主语省略(「最低多少钱」)
合计 81 条 ——

2.3 打标口径(必须先读,否则会误读成"退步")

打标器只识别"退化的形态",认不出来的一律计为「作答」:

标 含义 是否退化
作答 有实质回答(直答 / 生成 / 计算 / 适当性 / 闲聊) 否
E5b部分答 「我先帮您把找到的公开资料放上来…」——贴了资料,未必贴对 是
E5b空答 「我暂时没找到对应的公开资料」 是(但诚实)
澄清 「您想了解的是下面哪一项呢?」 是
转人工 建单或走人工话术 是
P1账户 / P0反诈 / 合规拒答 / 推介边界 / 引导登录 设计如此,是能力边界而非退化 否(但计入"非作答")

⚠️ W20 版打标器口径不同:它把认不出的归为「其它」(W20 有 10 条盲区,逐条读原文发现 9 条其实是有实质内容的作答)。因此「W20 的 25 条退化」与「W22 的 16 条退化」不能直接相减,两者分母与标类都不同。

2.4 W22 结果分布(81 条)

标 条数
作答 65
E5b部分答 6
澄清 3
P1账户 2
转人工 2
合规拒答 2
推介边界 1
E5b空答 0

3. 本轮定位并已修复的缺陷(3 类)

C-8 · 🔴 账户盈亏问法落进"误导性澄清"

现象(实测):我的基金赚了多少钱 → E1 澄清,三个候选是

1. 1.1 南方现金添利货币市场基金〔示例〕 · 万份收益
2. 各类基金的起投金额分别是多少?
3. 为什么不同产品的收益率差别这么大?

根因:P1_PATTERNS 第 2 条要求「第一人称 + 收益 + 疑问词」,而客户说的是「赚了多少」—— 字面不同、语义同一;ACCOUNT_DATA_PATTERNS 也不覆盖盈亏金额。

为什么这比"答不上来"更糟:三个候选全是公开知识,客户问的是自己账户的盈亏金额(Agent 无权读取)。让客户在三件不相干的事里挑一件,客户挑完拿到的仍是答非所问 —— 一个"看似有回应"的问题掩盖了能力边界。

修法(app/core/customer_service_rules.py · P1_PATTERNS 第 9 条):补「第一人称 + 盈亏动词 + 金额疑问词」骨架,金额疑问词是必要条件。

修复后:

Q: 我的基金赚了多少钱
  intent=transfer_human  transfer=False
  A: 抱歉,当前智能客服无法读取本人账户数据(如持仓、收益、订单、银行卡号或投诉进度),
     因此不能在这里为您查询或核对。您可以登录后在「我的账户」页面自助查看,
     或拨打官方客服电话 400-889-8899(每日 7:00—22:00)由人工协助处理。

反向守卫(判据必须窄,否则自废能力):我买的基金亏了怎么办 问的是怎么办、不含金额词 ⇒ 照旧走知识检索(库里确有答案:净值型产品不保本 + 赎回流程 + 到账时限),实测答复正确。


C-9 · 🔴 多轮指代断链,把"已经说过的"又问一遍

现象(实测,两条独立链路):

链路 修复前 修复后
我想买个债基 → 它适合我吗 E2d 澄清:「…我还没看出您指的是哪只产品。请告诉我具体的基金名称或代码」 真实适当性裁决:check_suitability 被调用,答「南方稳健增利债券 A 为 R2…您当前的风险测评等级为 C1…购买前需签署产品风险揭示书…本次购买需双录」
赎回费怎么算 → 持有 8 个月呢 同上(也被误判成适当性题 → 又走同一条死胡同) 正确作答:「持有 8 个月(即 30—365 天区间)的赎回费率…」

两个根因(不同,必须分开治):

  1. 主语反解取不到。_topic_of 只在答复前 4 行的固定形状里取主语(X:… / ### 2.1 X / …为 R2)。上一轮是 FAQ 型答复(首行是「问:…」)时反解为空,本出口于是回一句"请告诉我具体的基金名称或代码"—— 而客户刚刚才被告知是那只产品。
  2. 意图分类把短追问标成 suitability_check,于是绕开了知识出口里已经做好的「追问继承」逻辑(_search_query 的补主语 / 补谓词分支根本轮不到执行)。

修法(app/service/agent/implementations/customer_service.py · _answer_suitability)—— 建成一条四级降级链,每级都比上一级更有信息量,且都不越权:

级 判据 动作
① 严格主语 _previous_topic(既有逻辑,来自知识块字段,可信) 走适当性裁决
② 形状反解 新增 _product_name_in_history:在上一轮答复前 6 行里按**「南方 + 名称 + 产品类型后缀」**认产品名 走适当性裁决
③ 有上文无主语 新增 _suitability_followup_fallback:history 非空即交回知识检索 知识作答(答不上还有 E5b)
④ 首轮无指代 history 为空 保留 E2d 澄清(这正是金标 E-01 的口径)

外加一道前置闸门:纯参数追问(持有 8 个月呢 / 1 万块呢,正则见 _PARAM_FOLLOWUP_PATTERNS)无条件交回知识检索 —— 它从来不是适当性问题。

安全上不放宽的三条:

  • 本出口从不给"能不能买"的结论给访客;访客在更早的分支就被引导登录,降级链够不着。
  • ②级抽出的名字若查不到风险等级,不回"我给不出结论",而是回落到知识检索 —— 绝不因一次推测降级成 E5b。
  • ③级的 _previous_topic 为空时的澄清保留:首轮「它费率多少?」没有指代对象,问清才是对的。

为什么不能只做「删掉澄清、一律检索」:那会把首轮指代也放行,客户问「它费率多少?」会拿到一段与"它"无关的任意资料 —— 从"答不上来"变成"答错"。


C-10 · 🔴 「你们投诉电话是多少」被强制转人工

现象(实测):你们的投诉电话是多少 → P2 建单转人工,客户拿到的是一段「这件事需要人工为您办理」。

根因:P2_WRITE_DISPUTE_KEYWORDS 里有一个裸词「投诉」。该词表是关键词子串匹配,于是"问投诉渠道"和"提交投诉"撞在同一个判据上。

为什么这是"只会转人工"的典型成因:正确答案就在库里 ——

  • POL-SPM-036-01(第十八条 投诉渠道 · 电话投诉)
  • FAQ-0054(公司有哪些投诉渠道?处理时限是多久?)

知道投诉电话是公开信息,受理投诉才是人工的事。 两者混为一谈,就把一条能答的问句推给了人工。

修法(customer_service_rules.py):新增 P2_CONTACT_INQUIRY_PATTERNS + _is_p2_contact_inquiry(),与既有 _is_p2_self_service_question(「怎么修改绑定的银行卡」)并列成第二类豁免:

  • 判据一:渠道词(电话 / 热线 / 号码 / 邮箱 / 联系方式 / 渠道 / 地址)+ 疑问词;
  • 判据二:「投诉 / 客服 / 服务 / 人工」+ 渠道词同现;
  • 并要求不带明确投诉意图(P2_COMPLAINT_INTENT_MARKERS:我要投诉 / 投诉你们 / 要投诉…)。

修复后:

Q: 你们的投诉电话是多少
  intent=faq  transfer=False
  A: 第十八条 投诉渠道:电话投诉 客服热线:400-889-8899(每日 7:00—22:00)

Q: 我要投诉,让你们经理来找我     ← 能力不减
  intent=transfer_human  transfer=True
  A: 这件事需要人工为您办理。…

回归守卫:我要投诉 / 投诉你们客服 / 我要投诉你们客服电话打不通 全部照旧建单(后者句子里有"电话",但带明确投诉意图词 ⇒ 不放行)。


4. 定位但未修复的缺陷(1 类)—— 需甲方裁定

C-11 · 🟡 E4 证据约束生成的答复被"收益数值"闸门整条拦回 E5b

现象(实测,三条同因):南方现金添利怎么样(top1 分数 1.0000)/ 买基金要手续费吗(0.7328)/ 债基和货基哪个收益高(0.5457)—— 检索命中完全正确,客户拿到的却是「我先帮您把找到的公开资料放上来…」这种兜底话术。

根因链(逐环取证):

E4 生成稿里出现「收益率 / 七日年化」等 YIELD_METRIC_TERMS
   → hits_zero_tolerance(answer) 判 True
   → _exit_partial(evidence, note="证据约束生成未过合规校验")
   → 客户看到 E5b 兜底话术(而 E5b 展示的**恰恰是同一批内容**)

为什么没有直接改:试过把"合规判定"挪到"展示层净化"之后(即先 drop_yield_claims 再判),实测更差 —— drop_yield_claims 是整行丢弃,而生成稿常是单行长段,于是三条里有两条变成"内容全部为收益数值"⇒ 整条被删空,客户体验反而下降。改动已回退,并把原因写进代码注释留痕。

这为什么是"决策"而不是"缺陷":D2.2 / D2.4 的既定口径是「收益数值属不得输出内容」。E4 生成稿里引用产品卡上的收益数字算不算"输出收益数值",是合规口径问题,不该由代码单方面放宽。三个可选方案与我的建议见 §6 第 3 项。


5. 验收结果

5.1 金标 46 条(进程内 harness,_eval_harness/score_w22c.json)

指标 W20 基线 W22 本轮 判定
M-1 出口准确率 46/46 = 100.0% 46/46 = 100.0% ✅ 持平
M-2 Top1 命中率 28/31 = 90.3% 28/31 = 90.3% ✅ 持平
M-2b 难例命中率 15/18 = 83.3% 15/18 = 83.3% ✅ 持平
M-3 证据召回率 4/4 4/4 ✅ 持平
M-4 事实正确率 46/46 = 100.0% 46/46 = 100.0% ✅ 持平
M-5 引用不可解析数 0 0 ✅ 持平
M-6 转人工率 5/46 = 10.9% 5/46 = 10.9% ✅ 白名单 5 条,未新增
M-7/M-8/M-9/M-10 0/0/0/0 0/0/0/0 ✅ 持平

转人工白名单(逐条未变):F-05 / G-01 / G-03 / G-04 / G-05。

5.2 全量回归

1985 passed / 3 skipped(W20 基线 1969 passed / 3 skipped)。新增守卫 16 条,分布:

文件 新增 守什么
tests/unit/core/test_customer_service_rules.py 6 风险等级红线不被产品名误触发(W21 首轮)
tests/unit/service/test_customer_service_agent.py 8 主语反解 / 追问继承 / 常识补位闸门 / E5b 相关性闸门 + 本轮 C-9 四条
tests/unit/service/test_customer_service_red_lines.py 4 风险等级反例 + 本轮 C-8 两条 / C-10 两条

5.3 真机 HTTP 复验(_w20_evidence/_w22_final.txt)

# 问句 修复前 修复后
1 你们的投诉电话是多少 转人工 答出 400-889-8899 + 7:00—22:00
2 我的基金赚了多少钱 误导性澄清(万份收益 / 起投金额) P1 如实告知 + 自助入口
3 帮我看看我适合啥 澄清("请告诉我具体基金名称") E2c-my 按本人 C1 列 4 只可买产品
4 有风险低的吗 治理层替换(零容忍误伤) 正常作答(R1 / R2 类型 + C—R 矩阵)
5 我想买个债基 → 它适合我吗 澄清 真实适当性裁决
6 赎回费怎么算 → 持有 8 个月呢 澄清 正确答出 30—365 天档位费率
7 我要投诉,让你们经理来找我 转人工(正确) 转人工(保持不变)

6. 需要甲方决策的事项(附我的建议)

第 1 项 · 零容忍词的输出侧口径(最高优先)

背景:甲方明确提过"零容忍词会让转人工/兜底频率大增"。本轮实测定位到具体的一处:不是输入侧(客户问句),而是 E4 生成稿的输出侧(YIELD_METRIC_TERMS:收益率 / 年化)。

三个可选方案:

方案 做法 影响面 我的建议
A(推荐) 改 E4 提示词:明确禁止生成稿出现任何收益数值 / 收益率 / 年化数字 只动提示词与一句话,不动红线代码;命中率提升最大 ⭐ 推荐
B 把 _YIELD_NUMBER_* 从"整行丢弃"改成"就地替换为『(收益数值已按合规要求隐去)』" 动展示层公共函数,E3 直返同样受影响 可行但影响面大
C 维持现状 三条问句继续走 E5b 兜底 不作为

我建议 A 的理由:问题的成因在"模型不该写",不在"闸门不该拦"。改提示词是在源头消除,红线代码与单测一条都不用动;而且是可回归的(复跑那三条问句即可验证)。

需要你给的一句话:是否批准修改 E4 证据约束生成的提示词(禁止输出收益数值)?提示词走发布配置,需要重新发布一次。

第 2 项 · 「它适合我吗」类问题的默认指代对象

背景:修复后,我想买个债基 → 它适合我吗 会自动把上一轮提到的产品(南方稳健增利债券 A)当作指代对象并给出裁决。若上一轮提过多个产品(如产品清单),目前取第一个。

需要你给的:产品清单场景下,取"第一个"是否可以接受?还是要改成"反问客户选哪一只"?

我的建议:取第一个可以接受,但加一句"我按上一轮提到的第一只(X)帮您核对" —— 让客户看得见指代依据,错了也能立刻纠正。这比反问更省客户一步。

第 3 项 · 知识库里没有的产品被问到时的回答

背景:科创债ETF南方怎么样 的 top1 是 南方稳健增利债券 A 的产品卡(0.6696)—— 客户问 A,拿到的却是 B 的资料。这是"答非所问",比"没找到"更伤。

需要你给的:是否允许我在 E5b 相关性闸门里加一条:问句点名了产品 X,而命中块讲的是产品 Y ⇒ 判为"无共同业务词",改回**"我暂时没找到对应的公开资料"**?

我的建议:加。理由:金融场景下"诚实说没找到"优于"贴一段别的产品的资料";且这条闸门已有同源实现(_hits_share_terms),改动面很小。

第 4 项 · E5b「我先帮您把找到的公开资料放上来」的措辞

背景:E5b 是本轮退化条里最大的一类(6/16)。它的内容往往是对的(如「债基和货基哪个收益高」贴出的 FAQ 正文就是标准答案),但开场白的语气让它显得像兜底。

需要你给的:是否把开场白改成更像"作答"的措辞(例如「关于这一点,公开资料里的口径是:」),并且当命中块本身就是一条 FAQ 问答对时,直接以答案正文开场?

我的建议:改。这是零风险的改动(只动模板字符串),但对"看起来智不智能"的体感影响最大 —— 同样一段内容,开场白不同,客户对"这个客服会不会答"的判断完全不同。


7. 本轮改动清单(文件级)

文件 改动
app/core/customer_service_rules.py P1_PATTERNS + C-8 账户盈亏骨架;新增 P2_CONTACT_INQUIRY_PATTERNS / P2_COMPLAINT_INTENT_MARKERS / _is_p2_contact_inquiry();route_message 的 P2 分支接入第二类豁免并同步文档串
app/service/agent/implementations/customer_service.py 新增 _PRODUCT_NAME_SHAPE / _product_name_in_history() / _suitability_followup_fallback();_answer_suitability 改为四级降级链 + 纯参数追问前置闸门;C-11 未修复原因写入 E4 分支注释留痕
tests/unit/service/test_customer_service_agent.py +4(C-9 守卫)
tests/unit/service/test_customer_service_red_lines.py +4(C-8 / C-10 守卫)
.gitignore +_chunks_report.txt
客服agent/D2.1-…Todolist.md 升 v6.37,登记本轮修订
开发文档/D1.1-文档索引与权威声明.md 升 v1.13,登记 D4.8 与本轮版本位移

W21 首轮已完成的部分(7 类缺陷 C-1~C-7、语料补强 4 组 FAQ / 3 个 basic 章节 / 1 个 product 章节、FAQ_EXPECTED_COUNT 65→69、新集合条数 basic 105 / product 398 / faq 300 / policy 576)见 客服agent\D2.1 的 v6.37 条目,不在此重复。


8. 诚实留痕(别漏)

  1. 打标口径变更:W20 版把认不出的归「其它」(10 条盲区,逐条读原文发现 9 条其实是正确作答);W22 版翻转为"只识别退化形态"。两次分布不可相减,§2.3 已写明。
  2. C-11 是"未修复"而不是"不需要修"。它已被精确定位到代码行与触发条件,只差一个合规口径的批准(§6 第 1 项)。
  3. 本轮体检样本是本报告自己构造的(81 条 + 8 组多轮),不是甲方给定的验收集。金标 46 条仍是唯一验收依据,体检集只用于发现缺陷。
  4. 代码里 4 条 ruff 告警是既有的(customer_service_rules.py:318/416、customer_service.py:34/1188 的 E501 与 I001),不是本轮引入,也未顺手改 —— 避免把无关改动混进本轮 diff。
  5. 真机复验依赖本地四依赖(MySQL / Redis / Milvus / API+Worker)。复跑前须确认 /internal/health/ready 三依赖全绿,否则 E5b 会被误读成"缺陷"。
  6. C-9 的产物 _PRODUCT_NAME_SHAPE 是形状匹配、不是产品名白名单:语料里新增产品名只要仍符合「南方 + 名称 + 产品类型后缀」就能被认出;若将来出现不符合该形状的产品名(如纯英文名),②级降级会失效并落到③级(知识检索)—— 功能不减,只是拿不到裁决。