# 改动文档: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 侧"破防监控面板"(若需要)。