- 分支内容对齐 qyqy_develop b6ec3aa,树完全一致(同步后 git diff 为空) - 覆盖本轮全部交付:客服 Agent 重构(安全路由 / 五出口 / 记忆与画像 / RAG 全链路) + 开发文档 62 份编号体系(D1.1 v1.17 索引) + 新增 D2.10-客服Agent端到端答辩文档-2026-09-21.html - 基线:e239eb7(2026-09-17 品牌口径统一快照),本提交为其直接后继
89 lines
6.7 KiB
JSON
89 lines
6.7 KiB
JSON
{
|
||
"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/` 旧件的处置"
|
||
]
|
||
}
|