Files
ai-kefu-jc/docs/2026-08-19-ai-role-boundary-changelog.md
T
2026-09-29 14:15:12 +08:00

127 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 改动文档:AI 职能边界(价格红线 + 转介机制)
- **日期**:2026-08-18 ~ 2026-08-19
- **分支**:`chany_dev_dev`,提交 `3028685`(三层防线)+ `9433f96`(句子粒度门控)
- **状态**:106 项测试全绿(基线 77),本地真实 GLM 冒烟 7 路径通过,WS 探针实证打字机恢复
- **实施档案**:[superpowers/plans/2026-08-18-ai-role-boundary.md](superpowers/plans/2026-08-18-ai-role-boundary.md)(两轮对抗评审 + 13.1-13.7 全过程)
---
## 1. 背景与解决的问题
**定位反转**:改造前 system.md 明文要求 AI 报价("宁可给一个不完美的价格估计",且报价是好回复示例)——用户实测 AI 张口就是 "$25k"。改造后 AI 定位为:
> **信息型助手 + 线索收集器**——介绍车、挖意向、收联系方式。不承担销售职能:
> 不做任何承诺(价格/时间/库存)、不说绝对化的话、不代表公司做决定。
价格/优惠/交付时效/实质请求一律迂回引导留资,由客户经理承接——与询盘邮件体系闭环(AI 收集意向+联系方式 → 邮件 → 客户经理报价/安排)。
## 2. 核心架构:三层防线(冒烟实测逐层验证)
```
客户消息(问价/磨价/问时效/要安排/报预算)
│
├─ 第1层 prompt:system.md 职能边界章节
│ 三类请求处理(信息直答 / 价格时效迂回 / 实质转介)
│ 预算路径(推荐车型但零数字复现→反问意向→引导留资)
│ 节奏豁免(价格红线立即生效;零上下文询价按钮首触先挖一轮需求)
│ 红线五条(无价格数字/无占位符/无交付时间/无承诺/安排主体是客户经理)
│ 追问坚持策略 + 六种磨法应对手册(态度软立场硬)
│
├─ 第2层 提醒:当轮红线注入(BOUNDARY_TRIGGER_RE)
│ 价格/时效/安排词 + 施压话术(roughly/说个大概/求你了——磨价轮不裸奔)
│ 预算路由(BUDGET_TRIGGER_RE → 推荐+反问 playbook,不一刀切要号)
│ 前置到空画像早退之前(Get Quotation 首条消息即有防护)
│ 与 D6 联动(已留资→"经理会发给你",禁止再索要)
│
└─ 第3层 过滤墙:句子粒度门控流式(确定性保证)
token 攒到句子边界 → 逐句检测价格形态
├─ 干净句子 → 立即流出(打字机效果保留,WS 探针实证渐进到达)
└─ 违规句子 → 一个字不流:掐断生成(省token)、接过渡话术收尾
(有干净前缀→自然转折"Actually, for the exact price…";
无前缀→整段标准迂回话术;已留资→不重复要号变体)
```
**为什么三层**:GLM 有不守 prompt 的前科(本项目已实测两次),价格又是最强诱导——冒烟数据证明单靠第 1+2 层守不住(预算复述 6/6 全破、压力轮 1/3 报出真实价格),按方案预设的 >10% 破防率阈值激活第 3 层;第 3 层最初为整段缓冲(牺牲打字机),后升级为句子门控(两全)。
## 3. 文件清单
### 新增
| 文件 | 内容 |
|---|---|
| `app/services/boundary_filter.py` | 价格形态检测($/货币单位/k形式/动词邻接/交付时间/预算复述/金融裸数),白名单排除(年份/车型编号L7/里程30k km/3万公里/质保期/kWh);无损句子切分(标点边界/小数保护/CJK免空格);迂回话术与过渡话术(中英路由/已留资变体/轮换) |
| `scripts/breach_rate.py` | 破防率观测(只读,锚定式匹配)——改造前基线 **18%** 存档于 `2026-08-18-breach-baseline.txt` |
| `tests/test_boundary_filter.py` | 17 项单测(五模式命中/白名单/切分/话术路由) |
| `docs/superpowers/plans/2026-08-18-ai-role-boundary.md` | 完整实施档案(含两轮评审 12 条 + 最终评审 8 条发现的处置) |
### 修改
| 文件 | 改动 |
|---|---|
| `app/prompts/system.md` | 插入职能边界章节(+58行,位于语言规则后最高优先级);**删除/反转 9 处旧价格规则**("宁可给价格估计"规则、3 个含价好示例、"报价范围"要求、推进成交定义、3轮节奏两处加豁免);价值交换章节并入职能边界;术语统一"客户经理/manager" |
| `app/prompts/evaluator.md` | 问价/优惠/运费/试驾 = 意向积极信号(标签更准) |
| `app/services/conversation.py` | `BOUNDARY_TRIGGER_RE`/`BUDGET_TRIGGER_RE` 触发词表;`_build_context_reminder` 重构(边界检查前置到空画像早退前 + 预算路由 + D6 联动分支);流式区重构为句子门控(缓冲按句→判定→流出/拦截);拦截记 `Boundary filter replaced` warning |
| `tests/test_lead_email_trigger.py` | +10 项(边界提醒 8 例 + 流式门控集成 2 例) |
**测试 77 → 106**;前端零改动(事件协议不变);无 schema 变更;邮件体系零影响。
## 4. 行为对照
| 场景 | 改前 | 改后 |
|---|---|---|
| "Tang 多少钱 / 有优惠吗" | 直接报价 "$25k 起步" | 迂回:先问意向车型,同回复引导留资,零数字 |
| "运费/关税多少" | 视作报价的一部分回答 | 同上迂回 |
| "多久能到货" | 给时间估计 | 一律不给时间,引导留资 |
| "我预算2万有什么推荐" | 报价推荐 | **推荐车型但零数字复现**(不说"2万/20K")→ 反问意向 → 引导留资 |
| "just tell me roughly"(磨价) | 磨两下就报区间 | 坚持:换理由不重复原话→第三次推 WhatsApp 按钮;**过滤器兜底** |
| "能约试驾吗 / 帮我留一台" | 自行应承 | 转介:"对接客户经理安排,留个联系方式" |
| "Get Quotation" 快捷按钮首触 | 立即报价 | 价格红线立即生效 + **先一轮了解需求**再引导留资 |
| 已留联系方式后问价 | (无此状态) | "经理会把报价发你 WhatsApp",不再索要 |
| 正常问续航/里程/配置 | 正常回答 | **照常回答**(30k km / 3万公里 / 85kWh 白名单保护,实测未误杀) |
| 回复呈现 | 逐 token 打字机 | **保留**:逐句渐进流出(实测首句 t=2.3s,第二句 t=5.5s) |
## 5. 关键决策记录(全部用户拍板存档)
| 决策 | 内容 |
|---|---|
| 职能定位 | 需求了解+留资;介绍车、挖意向、收联系方式;不是销售、无承诺权(价格/时间/库存)、无绝对化表述、不代表公司做决定 |
| 预算处理 | 可推荐车型,**不复述预算数字**(含 20K 等改写形式),反问意向后引导留资 |
| 交付时效 | 一律不直接回答,同样引导留资 |
| 询价按钮首触 | 价格红线立即生效,但**先用一轮了解需求**再要号(低质量线索 vs 转化节奏的取舍) |
| 方案 B 激活 | 按预设计划的 >10% 破防率阈值(冒烟 6/6+1/3 击穿)→ 输出过滤上墙 |
| 打字机恢复 | 句子粒度门控方案(用户确认)——干净句子流出、违规句不流,两全 |
## 6. 实测驱动的演进链
| 阶段 | 发现 | 处置 |
|---|---|---|
| 冒烟第1轮 | 预算复述 6/6 全破("budget of 20K")、压力轮 1/3 报真实价、50% 输出"[价格区间]"占位符 | prompt 加固(具体替代措辞) |
| 冒烟第2轮(加固后) | **依旧 6/6 破**——GLM 把复述预算当礼貌确认 | 方案 B 激活:整段缓冲过滤,违规归零 |
| 最终评审 | 里程误杀(30k km)、裸预算复述漏检(恰是旗舰形态且观测不可见)、已留资重复要号 | 过滤器收严(F1-F6:白名单/预算模式/金融/质保/联系变体) |
| 用户反馈 | 打字机效果没了 | 句子粒度门控(13.7)——渐进流出恢复,红线不降级 |
## 7. 部署注意事项 ⚠️
1. **重启即生效**:prompt 在启动时加载、触发词表在代码中——所谓"词表热调"实为重新部署。
2. **无 schema 变更、邮件体系零影响**;询盘邮件正文里的 AI 回复将全是过滤后文本(破防原文只在日志)。
3. **体验变化预告**:回复从逐字打字机变为**逐句出现**(首句 ~1s,人格回复 1-2 句所以观感接近);AI 从此不报任何价格——**提前告知销售团队**,报价承接节奏要跟上。
4. **上线后观测**:`docker compose logs app | grep "Boundary filter"` = 真实破防率(GLM 想报但被墙拦的频率);改造前基线 18% 已存档,以此对比第 1+2 层的自律率。
5. **触发词调参**:漏词案例(新问法/多语言)回流补 `BOUNDARY_TRIGGER_RE`;误触发低害(多一条提醒)。
6. **回滚**:`git revert 9433f96 3028685` 两提交即完整回滚。
## 8. 已知限制与残余(台账)
| 项 | 状态 |
|---|---|
| 单句回复仍需等该句生成完(~1s)——那句本身可能含价,无更快解 | 接受(物理约束) |
| 句子切分为启发式(小数已保护;切歪只影响节奏不影响安全) | 接受 |
| 多语言问价(法/斯瓦希里/阿拉伯语)触发词盲区 | 第1层全局红线 + 第3层过滤器兜底(价格形态与语言无关) |
| 极端假阳性:"这车跑了两万"(无"公里"后缀的里程表述)会被当价格拦截 | 罕见,方向安全(多拦不泄露) |
| 破防率脚本与过滤器模式不再严格同步(过滤器更严) | 上线后以日志行为准,脚本留作历史基线 |
| 过渡话术单一池(不轮换) | 观察重复感,需要时扩池 |
## 9. 未包含(后续批次)
与之前清单一致:批次 2(widget 会话持久化/前端历史渲染/WS 限流)、批次 3(admin 卡单筛选/UI 留资卡片)、健康轨(超时清理会话、SQLite 备份、删 Redis、ADMIN_PASSWORD、GLM 超时重试)。可基于 `Boundary filter` 日志做 admin 侧"破防监控面板"(若需要)。