张胜宇
|
5d0becb67d
|
客服 Agent 重构收口:五出口决策链 + 知识库档位隔离 + 前端入参边界(答辩演示版本)
一、客服 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 生成的本地产物
|
2026-09-20 14:33:30 +08:00 |
|
lzf_0626
|
2c5fe9cdef
|
把风控扫描演示数据接进一键准备(第 12 步),并让编排支持给步骤传参
## 改动(`tools/seed_demo_data.py`)
1. **`STEPS` 增加第四个字段「额外命令行参数」**。这不是可有可无的装饰:
`seed_risk_demo_data.py` **不带参数时只打印计划、不连数据库**(默认是干跑模式),
不给它传 `--apply` 的话,接进编排只会打印一句"确认无误后使用 --apply 正式写入"
然后退出码 0 —— **看起来像成功,实际什么都没准备**。这类"假成功"最难查,
所以参数必须能传得进去。
2. **新增第 12 步「风控扫描演示数据」**,带 `--apply --dry-run-scan`:先写入上游
业务数据,再跑一次规则干跑并把结果回滚。净效果是「写入上游数据 + 顺带验证
5 个场景都真的命中预期规则」。
3. `run_step` / `--list` / 主循环相应支持第四项;执行时把参数**打印出来**,
这样手跑时一眼能看出这一步到底会不会落库。
## 为什么放第 12 步,而不是插在「风控预警样本」旁边
避免**改动后续步骤的编号**。文档与肌肉记忆里已经有 `--only 4,10`、`--from 4`
这类用法,插队会让它们全部错位。而且它本身是独立号段(客户 12001-12005、
产品 159991-159995),放最后不影响任何既有步骤。
## 第 6 步与第 12 步不是重复(docstring 已写明)
- 第 6 步 `seed_risk_alert_demo_data.py`:**直接摆好**三条现成预警,
保证风控页面一打开就有东西可看;
- 第 12 步 `seed_risk_demo_data.py`:**只造上游**,刻意不写 `fin_risk_alert`,
让风控扫描器按规则生成 —— 演示覆盖的是**规则 → 证据 → 通知**完整链路,
而不是"预先摆好的假预警"。
## 实测
- `--list` -> **12 步**,第 12 步末尾显示 `[--apply --dry-run-scan]`
- `--only 12 --dry-run` -> 只打印、不执行(并且打印出了参数)
- `--only 12` -> **真跑通**,退出码 0:
[12/12] 风控扫描演示数据 用时 3.8s 退出码 0
[命中] 客户 12001:RW-003 (高)
[命中] 客户 12002:RW-007 (高)
[命中] 客户 12003:RW-007 (中)
[命中] 客户 12004:RW-012 (高)
[命中] 客户 12005:RW-015、RW-018 (低)
- `ruff check tools/seed_demo_data.py` -> All checks passed
|
2026-09-14 20:50:55 +08:00 |
|
lzf_0626
|
7208713c17
|
feat(portal): 接入历史净值,产品详情页恢复真实走势图
`fin_nav_history` 一直是**空表**,所以产品详情页画不出走势图 —— 此前那条曲线是
前端 `mock-data.js` 里编的 12 个点位,接入公开产品接口时把它去掉了
(走势图最容易被当成真实业绩),页面改为显示"尚未接入"。本次补上完整链路。
## 1. 取数(app/infrastructure/fund_market_adapter.py)
新增 `fetch_nav_history()`:东方财富历史净值接口(`api.fund.eastmoney.com/f10/lsjz`)
分页取序列。
- **为什么不复用 `fetch_kline`**:它走 `push2his.eastmoney.com`,
该域名在本环境实测连接被拒(`RemoteProtocolError`);
- **为什么不复用 `hq.get_southern_fund_nav_history`**:那个函数校验**南方基金白名单**,
而 `fin_product` 里有非南方基金的产品(510300 是华泰柏瑞的),调它会直接 `ValueError`。
⚠️ 实现里踩到一个坑:接口**会忽略请求中的 `pageSize`**(实测固定每次返回 20 条)。
最初按硬编码的 30 判断"是否最后一页",于是 `len(items) < 30` 永远成立、只取到第一页 ——
走势图看上去"有数据",其实只有最近 20 天,而且毫无报错。
改为按首屏**实际条数** + `TotalCount` 推算页数后,同样区间取到 124 条。
## 2. 落库(tools/sync_nav_history.py,新增)
写入 `fin_nav_history`,按 `(product_id, nav_date)` 幂等 upsert。
实测:20 只产品 / **2513 行** / 2026-03-17 ~ 09-13;重跑**新写入 0 行**。
## 3. 接口(P002)
`GET /api/v1/products/{product_code}/nav-history`,编号 **P002**,已登记 `docs/05` §19。
鉴权口径与 P001 相同(要求有效令牌、不校验权限码,访客令牌可用);`days` 有界 1–365。
**表为空时返回 `count=0` 与空数组,而不是报错** —— 调用方据此显示"尚未接入",
**不得回退到编造曲线**。产品不存在或未上市 → `404`(否则前端分不清"没有数据"
和"没有这只产品")。
## 4. 前端
详情页按序列画 SVG 折线,期数标题改为动态("近 N 个交易日")。
表为空时仍显示"尚未接入"占位,并补上此前缺失的 `.detail-chart__empty` 样式。
`product-detail.js` / `.css` / `index.html` 的缓存版本参数一并 bump 到 `-7`。
## 5. 演示数据
`tools/seed_demo_data.py` 增加第 5 步「历史净值」(现 **11 步**),
否则换台机器演示时走势图又会是空的。
验证:P002 实测 515450 / 510300 各 120 个净值点;ruff 通过;mypy 251 文件 0 错;
unit+contract 1391 passed;integration 108 passed;e2e 冒烟 40/40。
|
2026-09-13 23:38:13 +08:00 |
|
lzf_0626
|
bfd3964ef8
|
feat(demo): 演示数据一键准备、启动脚本与演示流程文档
交付"别人能自己把系统跑起来做演示"所需的四件东西:
- tools/seed_demo_data.py 演示数据一键准备:10 步按依赖排序
(此前散在 10 个脚本里,没人知道该跑哪些、按什么顺序跑,且知识库那步
根本没有脚本、靠手工调接口)
- tools/seed_knowledge_demo.py 知识库演示素材灌入,幂等(先删同 source_file 再灌)
- start.ps1 启动 API + Worker,含依赖检查与**自动刷新行情**
- docs/44-演示流程.md 8 个主线场景的照读流程 + 排障表 + 账号/命令速查
两条硬约束同时写进了脚本和文档:
1. **Worker 必须常驻**:没有它客服对话一直停在 queued(前端只显示"超时")、
新知识不进 Milvus 且**没有任何报错**。
2. **行情有效期仅 15 分钟**(`trade_service.MAX_QUOTE_AGE`):超时后所有委托
直接 503「行情已过期」,而系统**没有自动刷新机制**。故 start.ps1 启动时刷一次,
文档另给"演示中途 503 时补刷、无需重启服务"的处置方法(已实测)。
实测证据:
- start.ps1 在 8099 完整启动,`/docs` 与 `/portal/` 均 HTTP 200(测试进程已清理)
- `tools/e2e_smoke_test.py` → 40/40 通过
- `tools/seed_demo_data.py --dry-run` → 10 步全部正常列出
- 文档中的下单命令实跑:510300 成交价 4.579、金额 457.90、手续费 0.05
|
2026-09-13 21:56:27 +08:00 |
|