Files
group_fqcd_jr/docs/evidence/20260919-t2m-h04-graded-fallback-whitelist.json
T
张胜宇 9675df8453 chore(sync): zsy_developcc 全量同步至 qyqy_develop(W26 口径)
- 分支内容对齐 qyqy_develop b6ec3aa,树完全一致(同步后 git diff 为空)
- 覆盖本轮全部交付:客服 Agent 重构(安全路由 / 五出口 / 记忆与画像 / RAG 全链路)
  + 开发文档 62 份编号体系(D1.1 v1.17 索引)
  + 新增 D2.10-客服Agent端到端答辩文档-2026-09-21.html
- 基线:e239eb7(2026-09-17 品牌口径统一快照),本提交为其直接后继
2026-09-21 21:26:30 +08:00

89 lines
6.7 KiB
JSON
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.
{
"artifact": "t2m-h04-graded-fallback-and-transfer-whitelist",
"generated_at": "2026-09-19T02:04:04.969488+00:00",
"date_local": "2026-09-19",
"purpose": "执行批次 H 的 H-04「分级回退 E5 + 转人工白名单收口」(答辩反馈「很多问题都强制转人工」的最正面修复)",
"scope": [
"app/core/customer_service_rules.py(P1 口径显式化)",
"tests/unit/core/test_customer_service_rules.py(P1 口径用例)",
"tests/unit/service/test_customer_service_agent.py(白名单结构性守卫 + 零残留守卫)",
"客服agent/D2.2-客服Agent需求文档.html(US-CS-08 残留口径修正)",
"开发文档/D3.1-客服Agent需求开发文档与设计方案.html(TK-CS-011 残留口径修正)"
],
"pre_existing_state_取证": {
"① 回退链 E5a/E5b/E5c": "已在位(E5a 见 H-01;E5b `_exit_partial` 不建单;E5c `_exit_transfer` 为唯一置位出口)",
"② 白名单 4 类码": "已在位:`TRANSFER_REASONS = {safety_risk, account_data, write_or_dispute, explicit_request}`",
"③ E5c 上下文摘要": "已在位:`app/service/customer_service_handover_context.py`(转接原因 / 澄清轮次 / 知识来源数 / 脱敏会话摘要),由 `agent_persistence_service` 写入工单与 Outbox",
"④ 回退不得跨档位": "**结构性成立**:`tiers` 是 `search()` 必填参数、由 `tiers_for_roles(context.roles)` 从鉴权推导一次;三条召回路径(向量 / 产品名词法 / 父块)共用同一 `expression = visibility_expression(tiers)`,不存在放宽档位的回退分支",
"low_score_repeat 残留": "**代码零残留**(`rg` 全仓无命中)"
},
"real_gaps_found": {
"gap_A_第四条白名单码未发出": "`TRANSFER_REASON_ACCOUNT`(P1 账户数据)声明在白名单里,但 `route_message` 的 P1 分支 `transfer_required=False` ⇒ 白名单 4 类实际只发出 3 类",
"gap_B_无结构性守卫": "「白名单外不得转人工」只靠 `_exit_transfer` 的运行时校验;`route_message` 是**直接**置 `transfer_required=True` 的,新增分支可以绕过,跑一遍看不出明天的分支",
"gap_C_文档残留": "`D2.2` `US-CS-08` 与 `D3.1` `TK-CS-011` 仍写「连续 2 轮答不上来 → 转人工(low_score_repeat)」,与 v2.5 `FR-CS-023`(已删除该触发)**自相矛盾**,且 `low_score_repeat` 不在白名单内"
},
"implementation": {
"gap_A": {
"decision": "P1 账户数据**有意不建单**(不改行为,只把口径显式化)",
"rationale": "账户数据 Agent 读不到、人工接线同样读不到;建单只会把客户从自助路径推到排队,与 H-04 的目标相反。白名单是「允许上限」而非「必须转」。",
"code": "P1 分支补注释写明「有意不建单、`TRANSFER_REASON_ACCOUNT` 保留但不发出、若启用须同步更新 DoD」",
"test": "tests/unit/core/test_customer_service_rules.py::test_p1_account_data_never_opens_a_ticket_by_design",
"flagged_for_user": "若你希望对**已登录客户**的账户数据问题也建单,改动是一行(P1 分支置 `transfer_required=True` + `TRANSFER_REASON_ACCOUNT`),但转人工率会上升,且与 D3.7 的 ≤15% 目标相抵。我的建议是维持不建单。"
},
"gap_B": {
"guard": "AST 扫源码:所有 `transfer_required=True` 的落点,必须 ① 落在客服 Agent 的 `_exit_transfer` 内(该函数有 `reason_code not in TRANSFER_REASONS` 运行时校验),或 ② 在规则模块里带可解析的 `TRANSFER_REASON_*` 常量且值在白名单内",
"test": "tests/unit/service/test_customer_service_agent.py::test_transfer_requires_a_whitelisted_reason_code",
"extra": "同时断言白名单里**实际会发出**的三类必须在场、`_exit_transfer` 守卫入口存在"
},
"gap_B2": {
"guard": "`low_score_repeat` / 「连续 2 轮兜底」在规则模块与客服 Agent 源码中零残留",
"test": "tests/unit/service/test_customer_service_agent.py::test_consecutive_fallback_transfer_rule_has_no_residue"
},
"gap_C": {
"D2.2": "`US-CS-08` 改为「**不转人工**(v2.5 已删除 low_score_repeat):第 2 轮起走 E5b 部分答 + 引导;转人工仅限 FR-CS-023 白名单 4 类」",
"D3.1": "`TK-CS-011` 改为「连续兜底**不**触发转人工」并写明白名单外一律判不合格",
"eol": "D2.2 保持 CRLF(1085);D3.1 保持 LF —— 编辑后逐项核对"
}
},
"dod_mapping": {
"① 回退链 E5a→E5b→E5c": "已在位;本步取证确认(E5a 见 H-01 证据)",
"② 白名单 4 类 + 删除连续 2 轮兜底": "白名单 4 码已在位;连续 2 轮兜底代码零残留(守卫用例);P1 第 4 类口径显式化为「允许但不发出」",
"③ E5c 带上下文摘要": "已在位并经取证(`customer_service_handover_context.py`)",
"④ 回退不得跨档位": "**结构性成立**并经取证:`tiers` 必填 + 单点推导 + 三路召回共用同一 expression",
"⑤ 白名单外转人工 = 不合格": "新增结构性守卫用例(AST 扫全仓 `transfer_required=True` 落点)"
},
"gates": {
"targeted_pytest": {
"files": 2,
"passed": 128,
"failed": 0,
"note": "test_customer_service_agent.py + test_customer_service_rules.py"
},
"full_pytest": {
"result": "2 failed / 1594 passed / 2 skipped",
"note": "2 failed = T0 基线同两项;passed 1591 → 1594(+3 = 本轮新单测)"
},
"ruff": {
"scope": "app tests tools",
"errors": 22,
"note": "= 基线"
},
"mypy": {
"scope": "app",
"errors": 3,
"note": "= 基线"
}
},
"honest_notes": [
"本轮**没有**把 P1 改成建单 —— 这是**有意的口径确认**,不是漏改;已写成注释 + 单测 + 文档",
"`TRANSFER_REASON_ACCOUNT` 因此是「白名单内的保留码」,代码里没有任何发出点;这与「白名单外不得转人工」不冲突(白名单是允许上限)",
"`客服agent/_build/_body_requirements.html` 仍是**旧版**(US-CS-08 与 FR-CS-023 都是删除前的文本)—— 属构建中间产物,本轮未动,需你裁定是否重生成或删除",
"`开发文档/D3.5` 第 125 行仍写「FR-CS-023 的『连续 2 轮兜底』规则不变」—— 那是备选方案池文档的历史记录,本轮未动(它不是需求来源)",
"DoD 的 `D3.7` `B-01`~`B-06` 金标回归与 `M-6` 转人工率属 `H-06`,本轮未跑"
],
"open_follow_ups": [
"H-02(E2 计算型)、H-03(E4 证据约束生成)、H-06(金标评测 46 条 + M-6 转人工率)",
"H-05(档位分区隔离)与 `_build/` 旧件的处置"
]
}