feat(cs)+docs: 修掉 P1 错分「风险测评结果」—— 画像问答改由受控工具作答(W15)
背景(用户提问触发):
§1.2.1「客户能看本人的持仓/交易/账户/画像与风评」与 §1.4.5 P1
「账户与个人数据(含风险测评结果)Agent 无权限读取」读起来互相矛盾。
实测根因(两处):
1) route_message() 在画像分支之前,且 P1_KEYWORDS 含裸词「风险测评结果」
⇒「我的风险等级是多少」走画像作答,
「我的风险测评结果是什么」被降级成「无法读取本人账户数据」
—— 同一诉求两种结论,属「能答而不答」(H-03 同类)。
2) 画像词在前、账户词在后的混问法漏网
(「我的风险测评结果和持仓一起给我」落 P3 ⇒ 只答画像、静默忽略账户诉求)。
依据(决定性):D2.2 §1.7 第 21 项「画像问答字段直返」;
D3.1 §0.3 术语表「画像问答属客服能力,与持仓查询严格区分」。
代码:
- app/core/customer_service_rules.py:P1_KEYWORDS 移除裸词 + 口径说明;
P1_PATTERNS 新增混问法守卫(第一人称 + 画像词 + 并列连词 + 账户词)。
- customer_service.py / profile_projection.py:补「投影层白名单 ⊇ 客服对话
渲染集」口径(total_asset / behavior_score / risk_tags 刻意不陈述,
渲染它们等于用画像工具绕过 P1)。
守卫:
- test_customer_service_rules.py:RT-004 → None;新增 RT-004b → P1;
SAFETY_CASES 由字面区间改显式名单(RT-004b 字母后缀会落区间外被静默漏掉)。
- test_customer_service_agent.py:画像端到端 3 条 + 混问法反向守卫。
安全不降:答案只来自 query_customer_profile(self 作用域 + 字段白名单 + 工具
审计),查不到失败关闭、绝不猜等级;P0/P2 未动、P1 其余字面未动;
混问法仍走 P1;访客问画像仍引导登录。
文档:D2.2 v2.5→v2.6 / D3.1 v2.4→v2.5 / D2.6 更正 / D4.6 追加 §3(不改正文)
/ D1.1 §24 + 版本位 / D1.6 §4.41 修正误记 + §4.42 / D2.1 v6.29。
实测:pytest 1914 passed / 3 skipped / 0 failed(+5);ruff 20(无新债);
check_authoritative_docs 54 文档无冲突;_consistency GATE PASS;
http_probe 11/11;定向真机复验 9/9;portal_api_check 35/0/5;
e2e_smoke 31/31;fe_boundary 12/12;demo.ps1 五项自检全过。
This commit is contained in:
@@ -446,13 +446,41 @@ P1_PATTERNS = (
|
||||
# 账户类问法落进了知识检索。判据取「第一人称 + 账户词 + 金额疑问词」三件同现。
|
||||
re.compile(r"(我|本人)[^。;!?,,]{0,4}(账户|账号|资金)[^。;!?,,]{0,6}"
|
||||
r"(多少|余额|还有多少|剩多少|剩余)"),
|
||||
# `W15`:**画像与账户的混问法**(「我的风险测评结果和持仓一起给我」)。
|
||||
# 背景:`W15` 把裸词「风险测评结果」从 `P1_KEYWORDS` 移出(改由画像出口作答,依据
|
||||
# `D2.2` §1.7 第 21 项)。但上面第 1 条正则锚定在「我」,且窗口只有 2 字 ——
|
||||
# 一旦**画像字段在前、账户字段在后**(「我的风险测评结果**和持仓**…」),
|
||||
# 账户字段离「我」超过 2 字就兜不到,整句会被画像豁免顺带放行、只答画像不答账户诉求。
|
||||
# 这类句子的真实诉求**包含账户数据**(那是 Agent 无权读取的部分),必须照旧走 `P1`。
|
||||
# 判据刻意要求**第一人称 + 画像词 + 并列连词 + 账户词**四件同现:
|
||||
# 「基金份额和风险等级有什么关系」这类**规则题**没有第一人称,不会被误拦。
|
||||
re.compile(r"(我|本人)[^。;!?,,]{0,6}(风险测评|风险等级|投资者类型|客户分层|画像)"
|
||||
r"[^。;!?,,]{0,8}(和|与|以及|、|跟)[^。;!?,,]{0,8}"
|
||||
r"(持仓|份额|余额|订单|银行卡|交易记录|定投|收益|账户)"),
|
||||
)
|
||||
|
||||
#: P1 的**字面**裸词表。判据是「账户/资产类字段」,**画像类字段不在此表** ——
|
||||
#: 详见紧接本表之后的口径说明。
|
||||
P1_KEYWORDS = (
|
||||
"我的持仓", "我的收益", "我的订单", "我的账户余额", "我的余额",
|
||||
"银行卡号", "我的银行卡", "定投进度", "投诉进度", "风险测评结果",
|
||||
"银行卡号", "我的银行卡", "定投进度", "投诉进度",
|
||||
"我的份额", "帮我查一下我的", "订单状态", "查询订单", "我的交易记录",
|
||||
)
|
||||
#: `W15` 口径(2026-09-20):**「风险测评结果」从 P1 移除**,改由画像出口作答。
|
||||
#:
|
||||
#: 为什么这不是放宽安全边界,而是修一处**错分**:
|
||||
#:
|
||||
#: - `D2.2` §1.7 第 21 项明确写「画像字段级读取(`risk_level` / `customer_level`)…
|
||||
#: **只读,且仅用于确定性规则**(适当性过滤 / 转人工优先级 / **画像问答字段直返**)」;
|
||||
#: `D3.1` §3.5/§5 同样写明「**画像问答属客服能力**,与持仓查询严格区分」。
|
||||
#: - 既有实现里 `is_profile_question()` 对「我的风险测评结果是什么」返回 `True`,
|
||||
#: 而 `P1_KEYWORDS` 的裸词又先一步拦下 —— 同一诉求换个说法结论相反:
|
||||
#: 「我的风险**等级**是多少」走画像工具作答,「我的风险**测评结果**是什么」被降级成
|
||||
#: 「无法读取本人账户数据」。这是**能答而不答**(`H-03` 同类缺陷),不是合规收紧。
|
||||
#: - 安全性不降:答案来自底座受控工具 `query_customer_profile`(自我作用域 + 字段白名单 +
|
||||
#: 工具审计),且查不到时 `_exit_profile_miss()` **失败关闭为「如实告知」,绝不猜等级**。
|
||||
#: - **混问法仍然拦**:「我的持仓和风险测评结果」含账户字段 ⇒ 由 `P1_PATTERNS` 第 1 条命中,
|
||||
#: 照旧走 `P1`(见 `tests/unit/core/test_customer_service_rules.py` 的反向守卫 `RT-004b`)。
|
||||
|
||||
#: P2 里「客户明确要求人工」这一子集。与写操作 / 争议分开,是为了把
|
||||
#: ``transfer_reason`` 落成可区分的枚举码(``explicit_request`` 对 ``write_or_dispute``)。
|
||||
|
||||
@@ -13,6 +13,18 @@
|
||||
**白名单**而非黑名单:上游快照是 JSON,新增字段若用黑名单会**自动对外可见**。
|
||||
白名单只放行**画像属性**,**不放行任何 PII** ——
|
||||
`real_name` / `birth_date` / `mobile_masked` / `trade_account` 及持仓明细都不在列内。
|
||||
|
||||
|
||||
## 本白名单 ≠ 客服对话可直接陈述的字段集(`W15` 登记)
|
||||
|
||||
本表是**投影层**白名单,HTTP 画像端点与 `query_customer_profile` 工具共用;
|
||||
客服**对话**能直接讲给客户的字段由
|
||||
`app/service/agent/implementations/customer_service.py::render_profile` 的**渲染层**决定,
|
||||
且刻意更窄:`total_asset` / `behavior_score`(账户与资产维度)与 `risk_tags`(内部风控标签)
|
||||
**在投影层放行、在渲染层不陈述**。
|
||||
|
||||
原因:账户/资产维度正是 `D2.2` `FR-CS-023` 的 `P1` 裁定对象(Agent 无权限读取),
|
||||
在对话里渲染它等于用画像工具**绕过 `P1`**。⇒ 「白名单 ⊇ 渲染集」是设计意图,不是漏渲染。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
Reference in New Issue
Block a user