一、客服 Agent 智能增强(正面回应"不智能、动不动就转人工")
- 决策链由 2 个出口扩到 5 个:E1 澄清 / E2 计算型 / E3 知识直返 / E4 证据约束生成 / E5 分级回退
- 转人工从"默认动作"降为最后一档 E5c,只保留 4 类白名单:
P0 反诈 / P1 账户与个人数据 / P2 写操作与争议 / 用户明确要求人工
- 46 条金标实测(修复前 → 修复后):
转人工率 43.5% → 10.9%;出口准确率 45.7% → 100%;事实正确率 69.6% → 100%
禁忌违反 1 → 0;档位越权 / 无出处数字 / 误拒 四项零容忍全 0
- 安全不变量 INV-1~INV-5;零容忍规则未删,改的是挂载点
(输出侧字面黑名单 → 检索层档位隔离 + 判定层合规词表 + 输出守护)
二、知识库:档位单点化与物理隔离
- 新增 app/core/knowledge_tier.py 作为档位规则唯一落点(G-03),
knowledge_contracts.py 原定义块改为显式再导出(X as X,非副本)
- 档位过滤由 bool 默认值(fail-open)改为 tiers 必填集合(缺参即 TypeError)
- Milvus 侧四集合按 visibility 分区键物理隔离;双 schema 收敛为一套
- 新增 app/core/actor.py:访客三元组与匿名判定的唯一构造/判定点(G-01/G-01b)
- 新增 app/core/fund_fee_rules.py:费率计算纯函数
三、前端入参边界对齐(本轮 W11 新修,4 处"校验宽于存储")
- message 加 max_length=8000(与浮窗 widget.js 的 maxlength 一致)
- session_id 加 1—64;idempotency_key 上限 128 → 64(对齐列宽 String(64))
- feedback_type 加 max_length=32(对齐列宽 String(32))
- 8 条路径参数补 min_length=1 + max_length=64 + 字符集正则
({session_id} / {run_id} / {handover_id})
- 改前超限值会落到 MySQL 才失败(500);改后一律 422 AGENT_INPUT_INVALID + 字段级定位
- 新增 tests/unit/api/test_frontend_boundaries.py(33 例),含"端点表 ↔ OpenAPI 全量对照"
四、投顾模块整体清除(D4.4 / D4.5)
- 删除投顾相关 controller / schema / model / repository / service 及门户页面
- tools/portal_api_check.py 同步作废 AD003/AD005/AD011/A047 四条用例与 advisor_t 登录
(端点与账号均已不存在,此前稳定报 3 条假红)
五、验证(提交前实测)
- pytest -q:1856 passed / 2 skipped / 0 failed
- ruff check app tools tests:19(= 基线);mypy app:2(= 基线)
- 前端接口契约体检 portal_api_check.py:38 项,通过 34,失败 0,跳过 4
- 全链路冒烟 e2e_smoke_test.py --read-only:31/31
- HTTP 全链路探针 http_probe.py:11/11 succeeded
- 跨文档一致性 _consistency.py:GATE PASS
- 真机边界复验 12 条:12/12 符合预期
六、纪律与文档
- 可改文件白名单 A-09(docs/46)与底座会签申请单 A-10(docs/47,组 1—组 4 全部受理)
- 零 DDL:未新增/修改任何表结构,89 张业务表与基线一致
- 证据留痕:docs/evidence/**(含 46 条金标 score、快照、清除与重建记录)
- 未提交(刻意排除,见提交说明):仓库内 客服agent/ 与 开发文档/ 是 2026-09-16 前的
过期副本(Todolist 440 行 vs 权威 D2.1 1167 行),权威正本在仓库外;
_chunks_report.txt 是 tools/build_knowledge_chunks.py 生成的本地产物
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/` 旧件的处置"
|
||
]
|
||
}
|