Files
group_xinghuo_jinrong/docs/course/jinrong-module-customer/modules/04-guardrails.html
T
zhanghongyu_0626 4b8e11c9bd feat(threshold): Implement customer loss threshold configuration and notification system
- Added `ThresholdRepository` for managing customer loss threshold configurations and notifications.
- Introduced `threshold_service` to handle loss threshold alerts based on customer portfolio performance.
- Enhanced `customer_prompts` to include new intent for querying product net values.
- Updated `customer_service` to integrate new threshold alert functionality into existing workflows.
- Implemented `sanitize_postprocess` for improved compliance handling in customer interactions.
- Enhanced course documentation to reflect updates in advisor training modules and interactive elements.

This update significantly improves the customer experience by providing proactive loss threshold notifications and enhancing the overall service framework.
2026-09-10 10:57:40 +08:00

194 lines
14 KiB
HTML
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.
<section class="module" id="module-4">
<div class="module-inner">
<p class="eyebrow animate-in">模块 4 · 口吻与转人工</p>
<h1 class="module-title animate-in">专业口吻怎么「锁住」?<br>什么时候该转人工?</h1>
<p class="module-lead animate-in">
机制课讲了 LangGraph 怎么走,但客服线还有两条你指挥 AI 时最常碰的线:
<strong>回复别漂成投顾/推销员</strong>,以及<strong>什么时候打「建议转人工」</strong>。
代码里不是单靠「写个好 prompt」,而是<strong>三层滤网 + 一条主动转人工路径</strong>(sanitize 命中<strong>不再</strong>自动转人工,拍板 1B)。
</p>
<div class="screen animate-in">
<h2>三层滤网:防止口吻漂移</h2>
<p>可以把客服回复想成「过三道闸」——前面两道尽量写对,最后一道不管 LLM 说什么都要拦违规词。</p>
<div class="step-cards">
<div class="step-card">
<span class="step-num">①</span>
<p><strong>Prompt 层</strong> · 客户线各分支 System Prompt 模板(<code>customer_prompts.py</code>)<br>
每个生成分支有独立 system 模板(<code>INTERPRET_SYSTEM</code> / <code>GENERATE_SYSTEM</code> / <code>CHITCHAT_SYSTEM</code>),开头就定身份:「金融客服助手(已登录客户模式)」——禁止投顾话术、禁止预测、画像只调措辞<strong>不能念出</strong>。</p>
</div>
<div class="step-card">
<span class="step-num">②</span>
<p><strong>记忆层</strong> · <code>recall_memory</code><br>
咨询走 <code>consult_memory</code>、闲聊走 <code>chitchat_memory</code>,再注入 <code>profile_context</code>。让回复有连续语境,但 prompt 里写死:不得主动复述年龄/收入等画像字段。</p>
</div>
<div class="step-card">
<span class="step-num">③</span>
<p><strong>事后扫描</strong> · <code>finalize_sanitized_reply()</code>(<code>sanitize_postprocess.py</code>)<br>
<code>interpret</code> / <code>generate</code> / <code>chitchat</code> 产出后过违禁词正则。命中后:<strong>数据查询类</strong>(含 nav_query)回退 Core <code>fact_text</code>;RAG/闲聊替换 <code>COMPLIANCE_REJECT</code>——<strong>均不</strong>自动 <code>transfer_to_human</code>。<strong>游客线已对齐</strong>。</p>
</div>
</div>
<div class="callout callout-accent">
<strong>入口还有第 0 道闸:</strong> 四 Agent 对话 HTTP 入口(<code>chat.py</code>)的 <span class="term" data-definition="input_guard = 进 LangGraph 之前对用户输入做检查:注入短语、超长、频率限制;命中直接拒答并写 input_guard_log。">input_guard</span> 在进图之前就拦注入/超长——这类请求根本到不了 LLM。
</div>
</div>
<div class="screen animate-in">
<div class="translation-block">
<div class="translation-code">
<span class="translation-label">事后扫描 · 共用分治(sanitize_postprocess.py)</span>
<pre><code><span class="code-line"><span class="code-keyword">def</span> finalize_sanitized_reply(reply, *, intent, fact_text):</span>
<span class="code-line"> safe, hit = sanitize_reply(reply)</span>
<span class="code-line"> <span class="code-keyword">if not</span> hit: <span class="code-keyword">return</span> safe, <span class="code-keyword">False</span></span>
<span class="code-line"> <span class="code-keyword">if</span> intent <span class="code-keyword">in</span> DATA_QUERY <span class="code-keyword">and</span> fact_text:</span>
<span class="code-line"> <span class="code-keyword">return</span> fact_text, <span class="code-keyword">False</span> <span class="code-comment"># 1B:保真账,不转人工</span></span>
<span class="code-line"> <span class="code-keyword">return</span> COMPLIANCE_REJECT, <span class="code-keyword">False</span></span></code></pre>
</div>
<div class="translation-english">
<span class="translation-label">白话</span>
<div class="translation-lines">
<p class="tl">数据查询分支:LLM 嘴瓢时<strong>回退 Core fact_text</strong>,客户仍能看到「持有 3 只产品」等真数。</p>
<p class="tl">RAG/闲聊分支:替换为固定拒答,但<strong>不打</strong>转人工标志——除非用户原话已是投诉/转人工。</p>
<p class="tl">底层 <code>sanitize_reply</code> 仍负责扫违禁词;分治逻辑在 customer 编排层。</p>
</div>
</div>
</div>
</div>
<div class="screen animate-in">
<h2>转人工 vs 直接拒绝:别混成一条线</h2>
<p>模块 2 列出了 <code>reject</code> 和 <code>transfer_human</code> 两个节点,但<strong>触发条件完全不同</strong>。指挥 AI 改逻辑时最容易搞混这里。</p>
<div class="flow-animation" data-steps='[
{"highlight":"flow-g-1","label":"用户输入进入 intent_classify(关键词优先,再 LLM)"},
{"highlight":"flow-g-2","label":"命中「转人工/投诉/纠纷/账户异常/被盗」→ transfer_human 节点","packet":true,"from":"1","to":"2"},
{"highlight":"flow-g-3","label":"命中「推荐/预测/竞品/实时行情」→ reject 节点(静态拒答,不自动打转人工标志)","packet":true,"from":"1","to":"3"},
{"highlight":"flow-g-4","label":"正常走 interpret/generate/chitchat → sanitize 分治(1B)","packet":true,"from":"1","to":"4"},
{"highlight":"flow-g-4","label":"数据查询命中 → 回退 fact_text;RAG/闲聊 → COMPLIANCE_REJECT;均 transfer=false","packet":true,"from":"4","to":"4"}
]'>
<div class="flow-actors">
<div class="flow-actor" id="flow-g-1"><span class="flow-actor-icon">💬</span><span>intent_classify</span></div>
<div class="flow-actor" id="flow-g-2"><span class="flow-actor-icon">👤</span><span>transfer_human</span></div>
<div class="flow-actor" id="flow-g-3"><span class="flow-actor-icon">🚫</span><span>reject</span></div>
<div class="flow-actor" id="flow-g-4"><span class="flow-actor-icon">🔍</span><span>sanitize_reply</span></div>
</div>
<p class="flow-step-label">点击「下一步」看决策分叉</p>
<div class="flow-controls">
<button class="btn flow-next-btn">下一步</button>
<button class="btn flow-reset-btn">重来</button>
</div>
</div>
<div class="pattern-cards">
<div class="pattern-card">
<h3>路径 A · 主动转人工</h3>
<p>关键词:<code>转人工</code>、<code>投诉</code>、<code>纠纷</code>、<code>账户异常</code>、<code>被盗</code> 等 → <code>TRANSFER_TEXT</code> + <code>transfer_to_human=True</code>。</p>
</div>
<div class="pattern-card">
<h3>路径 B · LLM 越界被拦(1B)</h3>
<p>数据查询 interpret 违禁 → <strong>回退 fact_text</strong>,transfer=false。RAG/闲聊违禁 → <code>COMPLIANCE_REJECT</code>,transfer=false。</p>
</div>
<div class="pattern-card">
<h3>路径 C · 边界外但不必转人工</h3>
<p>问「推荐稳赚基金」「明天会涨吗」→ <code>reject</code> 静态话术(如 <code>REJECT_ADVICE_TEXT</code>)。<strong>不</strong>自动打转人工标志;话术中可引导用户自行说「转人工」。</p>
</div>
</div>
<div class="callout callout-warning">
<strong>演示环境边界:</strong> 「建议转人工」只是 API 字段 + 前端橙色 Tag(<code>ChatPanel</code>),<strong>不会</strong>真的排队接入真人坐席——真转接要接呼叫中心/工单系统,那是产品层后续工作。
</div>
</div>
<div class="screen animate-in">
<h2>群聊动画:LLM 嘴瓢后被扫描层拦住</h2>
<p>客户问持仓,Tool 返回正常 fact_text,但 interpret 的 LLM 多嘴加了「建议您加仓」——扫描层介入。</p>
<div class="chat-window" id="chat-customer-m4">
<div class="chat-messages">
<div class="chat-message" data-msg="0" data-sender="user" style="display:none">
<div class="chat-avatar" style="background:#2D8B55">客</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#2D8B55">客户</span>
<p>我持仓情况怎么样?</p>
</div>
</div>
<div class="chat-message" data-msg="1" data-sender="tool" style="display:none">
<div class="chat-avatar" style="background:#1a5235">⑤</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#1a5235">tool_call</span>
<p>Core 返回 fact_text(脱敏模板,数字 100% 来自只读库)</p>
</div>
</div>
<div class="chat-message" data-msg="2" data-sender="llm" style="display:none">
<div class="chat-avatar" style="background:#7B6DAA">⑥</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#7B6DAA">interpret · LLM 草稿</span>
<p>「您目前持有 3 只基金…<strong>建议您加仓医药板块</strong>…」</p>
</div>
</div>
<div class="chat-message" data-msg="3" data-sender="guard" style="display:none">
<div class="chat-avatar" style="background:#B84A4A">③</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#B84A4A">sanitize 分治</span>
<p>命中「建议…加仓」→ <strong>丢弃 LLM 草稿,回退 Core fact_text</strong>(仍含「3 只产品」)</p>
</div>
</div>
<div class="chat-message" data-msg="4" data-sender="api" style="display:none">
<div class="chat-avatar" style="background:#2A7B9B">API</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#2A7B9B">返回前端</span>
<p><code>transfer_to_human: false</code> · 客户看到脱敏持仓事实,无橙色转人工 Tag</p>
</div>
</div>
</div>
<div class="chat-typing" id="chat-customer-m4-typing" style="display:none">
<div class="chat-avatar" id="chat-customer-m4-typing-avatar">…</div>
<div class="chat-typing-dots"><span class="typing-dot"></span><span class="typing-dot"></span><span class="typing-dot"></span></div>
</div>
<div class="chat-controls">
<button class="btn chat-next-btn">下一条</button>
<button class="btn chat-all-btn">全部播放</button>
<button class="btn chat-reset-btn">重播</button>
<span class="chat-progress"></span>
</div>
</div>
</div>
<div class="screen animate-in">
<h2>RAG 咨询还要加风险提示</h2>
<p>产品/政策/FAQ 类意图在 <code>generate</code> 通过后,若 intent 属于咨询类,会追加 <code>RISK_DISCLAIMER</code>(<code>should_add_disclaimer</code>)。这是「口吻」的另一面:<strong>该免责时必须免责</strong>,不是只有拦违规词。</p>
<div class="quiz-container" id="quiz-customer-m4">
<div class="quiz-question-block"
data-correct="option-b"
data-explanation-right="对。reject 走静态 REJECT_* 话术,不自动设 transfer_to_human;只有 transfer_human 节点(用户主动投诉/转人工等)才会打转人工标志。sanitize 命中也不再自动 transfer(1B)。"
data-explanation-wrong="reject 和 transfer_human 是两条线:边界外拒答 ≠ 自动转人工。">
<h3 class="quiz-question">客户说「推荐一个稳赚不赔的基金」,系统会怎么标记?</h3>
<div class="quiz-options">
<button class="quiz-option" data-value="option-a" onclick="selectOption(this)">
<div class="quiz-option-radio"></div><span>transfer_to_human=True,直接排队人工</span>
</button>
<button class="quiz-option" data-value="option-b" onclick="selectOption(this)">
<div class="quiz-option-radio"></div><span>走 reject 静态拒答,transfer 标志为 false(话术中可引导说「转人工」)</span>
</button>
<button class="quiz-option" data-value="option-c" onclick="selectOption(this)">
<div class="quiz-option-radio"></div><span>走 RAG generate,正常回答产品列表</span>
</button>
</div>
<div class="quiz-feedback"></div>
</div>
<button class="quiz-check-btn" onclick="checkQuiz('quiz-customer-m4')">检查答案</button>
<button class="quiz-reset-btn" onclick="resetQuiz('quiz-customer-m4')">重做</button>
</div>
<div class="callout">
<strong>指挥 AI 改客服时的检查清单:</strong>
① 改口吻 → 动客户线各分支 System Prompt 模板(<code>customer_prompts.py</code>)对应 SYSTEM,别只改前端;
② 加新禁语 → 同时更新 <code>FORBIDDEN_TERMS</code> 和意图关键词路由;
③ 加「必须转人工」场景 → 改 <code>_TRANSFER_KEYWORDS</code> 或 INTENT_SYSTEM 规则,别误放进 reject。
</div>
<p class="module-footer-note">相关文件:客户线各分支 System Prompt 模板(<code>app/service/customer_prompts.py</code>)· 回复违禁词合规扫描(<code>app/utils/compliance_guard.py</code>)· <code>web/src/components/chat/ChatPanel.tsx</code></p>
</div>
</div>
</section>