一、客服 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 生成的本地产物
105 lines
6.2 KiB
JSON
105 lines
6.2 KiB
JSON
{
|
||
"evidence_id": "20260919-t7b-smoke-closeout",
|
||
"generated_at": "2026-09-19T17:05:57+08:00",
|
||
"session": "D1.6 §4.31 / D2.1 v6.18",
|
||
"what": "W7 收尾:宽链路冒烟(tools/e2e_smoke_test.py --read-only,5 条线)+ 门禁复跑,过程中发现并修掉 1 项真实合规回归(D-10)与 1 项冒烟脚本陈旧项(D-9)",
|
||
"how": "① 真 HTTP 打 127.0.0.1:8000(常驻 uvicorn)→ 队列 → 真 Worker(python -m app.worker);② 全量 pytest / ruff / mypy / _consistency.py;③ 修复后再跑 _eval_harness/http_probe.py 复验 11 条",
|
||
"smoke": {
|
||
"command": "tools/e2e_smoke_test.py --read-only",
|
||
"result": "31/31 通过",
|
||
"lines": {
|
||
"A 访客线": [
|
||
"A1 访客令牌 V001 HTTP=201",
|
||
"A2 提问受理 R001 HTTP=202",
|
||
"A3 问答完成 status=succeeded 3.43s",
|
||
"A4 知识库覆盖 transfer_required=False"
|
||
],
|
||
"B 客户线": [
|
||
"B1 客户登录 A034",
|
||
"B2 风险测评问卷 ONB001 HTTP=200",
|
||
"B3 账户看板 T001 HTTP=200",
|
||
"B4 持仓 T006 条数=2",
|
||
"B5 资金流水 T009 条数=1",
|
||
"B8 成交明细 T007 条数=1",
|
||
"B9 创建会话 C001 HTTP=201",
|
||
"B10 会话详情 C002 HTTP=200",
|
||
"B11 会话消息 C003 HTTP=200"
|
||
],
|
||
"C 风控线": [
|
||
"C1 风控登录",
|
||
"C2 风险概览 RK001 total=2 pending=1",
|
||
"C3 预警队列 RK002 条数=2",
|
||
"C4 八类证据 RK004 8/8",
|
||
"C5 通知记录 RK005 HTTP=200"
|
||
],
|
||
"E 运营线": [
|
||
"E1 运营登录",
|
||
"E2 场外邮件 total=0",
|
||
"E3 邮箱状态 status=uninitialized"
|
||
],
|
||
"F 管理员线": [
|
||
"F1 管理员登录",
|
||
"F2 角色列表 A035 list[4]",
|
||
"F3 角色详情 A036 dict[6]",
|
||
"F4 角色权限 A037 list[18]",
|
||
"F5 用户身份 A038 dict[9]",
|
||
"F6 配置发布 A002 list[10]",
|
||
"F7 模型端点 A012 list[2]",
|
||
"F8 审计记录 A033 list[20]",
|
||
"F9 转人工工单 list[11]",
|
||
"F10 画像候选 A039 list[1]"
|
||
]
|
||
},
|
||
"skipped_read_only": [
|
||
"B6 下单",
|
||
"B12 转人工",
|
||
"C6 手动扫描 RK006",
|
||
"C7 处置闭环",
|
||
"F11 工单处置闭环"
|
||
]
|
||
},
|
||
"defects": [
|
||
{
|
||
"id": "D-9",
|
||
"kind": "工具问题(非产品缺陷)",
|
||
"title": "冒烟脚本 D 投顾线是陈旧项,必然 FAIL,会掩盖真回归",
|
||
"detail": "投顾模块整体清除(D4.4/D4.5)后 advisor_t 账号与 advisor 角色均已不存在(sys_user 仅 9001-9006/9101-9105;sys_role 仅 customer/risk_operator/admin/operator),脚本 D1 投顾登录 / D2 已发布方案 必然失败",
|
||
"fix": "脚本收敛为 5 条线:改 section_advisor_operator_admin() -> section_operator_admin(),删除 DEMO_ADVISOR 常量与 D 段代码,并在 docstring 写明删除理由",
|
||
"evidence": "tools/e2e_smoke_test.py"
|
||
},
|
||
{
|
||
"id": "D-10",
|
||
"kind": "🔴 真实合规回归(已修)",
|
||
"title": "客服「不写长期记忆」闸门在重构中被删掉",
|
||
"detail": "WorkerRuntime.should_request_memory_extraction 收到 agent_type 却不参与判定:基线(git HEAD:app/worker/runtime.py)有 `if agent_type == \"customer_service\": return False`,本轮 diff 删除该闸门 ⇒ 客服运行只要消息命中偏好信号(如「我的风险偏好是稳健型」)就会产出 memory.extraction_requested,长期记忆被重新打开,与 DEC-19 裁定 (a)(短期会话记忆=开 / 长期记忆召回=关)正面冲突",
|
||
"caught_by": "tests/integration/test_worker_runtime_mysql.py::test_http_accept_worker_commit_query_and_repeat[False](断言 memory.extraction_requested 为空)",
|
||
"fix": "新增 NO_LONG_TERM_MEMORY_AGENT_TYPES = frozenset({\"customer_service\"}) 显式闸门;顺带改写 PROFILE_CANDIDATE_AGENT_TYPES 注释(原写法诱导「重构后填回白名单」,会绕过 DEC-19)",
|
||
"tests_added": [
|
||
"tests/unit/worker/test_runtime_worker_dispatch.py::test_customer_service_never_requests_memory_extraction",
|
||
"tests/unit/worker/test_runtime_worker_dispatch.py::test_customer_service_never_produces_profile_candidate"
|
||
],
|
||
"note": "对照组断言 risk_agent 仍照常抽取 —— 闸门不得误伤其它 Agent"
|
||
},
|
||
{
|
||
"id": "D-11",
|
||
"kind": "🟡 新发现·待决(答非所问)",
|
||
"title": "「我够哪一档?」答成风险等级,未答客户分层",
|
||
"detail": "问的是客户分层(金卡 50万+/白金 200万+/钻石 600万+/尊享 1000万+),答的是「您的风险测评等级是 保守型(C1)」。根因两层:① sys_user.customer_tier 全库 11/11 为 NULL(从未种过);② 画像快照 build_snapshot 按设计排除 customer_tier(REQUIRED_SNAPSHOT_FIELDS 显式 `if field != \"customer_tier\"`),故 render_profile 的 tier 行永远渲染不出来",
|
||
"impact": "FR-CS-024(转人工摘要含画像关键标签)与 D3.1 §3.5「读 customer_level 定转人工优先级」同样无数据可用",
|
||
"status": "待用户决策(见 D1.6 §4.31 六)"
|
||
}
|
||
],
|
||
"environment": {
|
||
"race": "常驻 Worker 与 tests/integration 抢同一 MySQL outbox:dispatch_one 抢不到件返回 False、或测试期间被塞入 memory.extraction_requested ⇒ 全量门禁必须在停掉常驻 Worker 后跑才可复现",
|
||
"proof": "停 Worker 前 2 failed(handover + worker_runtime);停 Worker 后复现基线 1 failed;修 D-10 后 0 failed。单独跑 handover 用例始终 PASS",
|
||
"process_shape": "Start-Process 起服务会出现 parent + child 两个 PID(命令行相同),不是重复启动;判活看端口 8000 的 OwningProcess 与日志 _http_api*.log / _http_worker*.log",
|
||
"restarted": "API http://127.0.0.1:8000 + Worker(python -m app.worker),隐藏窗口,日志在 D:\\桌面\\金融\\_http_*.log"
|
||
},
|
||
"gates": {
|
||
"pytest": "1742 passed / 0 failed / 2 skipped(优于基线:1 failed / 1739 passed / 2 skipped)",
|
||
"ruff": "22(= 基线)",
|
||
"mypy": "3(= 基线)",
|
||
"consistency": "GATE PASS(_consistency.py)",
|
||
"http_probe": "_eval_harness/http_probe.py → 11/11 全 succeeded(修复后复验)"
|
||
}
|
||
} |