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 上下文提取
This commit is contained in:
张胜宇
2026-09-21 10:55:16 +08:00
parent fc3aa67451
commit dd5e9f8435
17 changed files with 1385 additions and 33 deletions
+7 -2
View File
@@ -102,7 +102,12 @@ FAQ_REGISTERED_QIDS: frozenset[int] = frozenset({17, 20, 21, 27, 28, 29, 30, 33,
#: 语料下**不可达**(路由怎么改都救不回来)。这是**语料缺口**,补条目才是根因修复。
#: 新条目**必须追加在末尾**:`FAQ_REGISTERED_QIDS` 是 1-based 序号,中间插入会让其后
#: 所有条目的序号平移、档位整体错位(`H-05` 的档位隔离会被无声破坏)。
FAQ_EXPECTED_COUNT = 65
#: `W21`(2026-09-21)再补 4 组:`W20` 智能度体检(55 条真实口语问法)实测出的四类
#: **语料缺口** —— ① 按类型找产品(「你们有没有债基」)全库最高分只有 0.54,主集合里
#: 「债券基金」被「储蓄国债」抢答;② 「什么时候能卖」唯一的字面邻居是政策文件的
#: 「第二十七条 生效日期」;③ 「债基和货基哪个收益高」没有任何一条把两个品类的特征
#: 并排讲清楚;④ 「有没有不亏的产品」被答成「什么是净值型产品」。四组同样**追加在末尾**。
FAQ_EXPECTED_COUNT = 69
def leaf_split_points(marks: list[tuple[int, int, str]]) -> set[int]:
@@ -344,7 +349,7 @@ _faq_registered = [record for record in _faq_records if record["visibility"] ==
if len(_faq_records) != FAQ_EXPECTED_COUNT:
raise SystemExit(
f"FAQ 条数不符:实际 {len(_faq_records)},期望 {FAQ_EXPECTED_COUNT}。"
"`knowledge/faq/高频问答对.txt` 应为 `D6.1.3` 的 64 组 + `W6` 补 1 组 = 65 组版本;"
"`knowledge/faq/高频问答对.txt` 应为 `D6.1.3` 的 64 组 + `W6` 补 1 组 + `W21` 补 4 组 = 69 组版本;"
"条数不符通常是镜像被换成了旧版(V1.x 只有 44 条)。已中止,未写入 jsonl。"
)
if len(_faq_registered) != len(FAQ_REGISTERED_QIDS):