- 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.
179 lines
12 KiB
HTML
179 lines
12 KiB
HTML
<section class="module" id="module-2">
|
||
<div class="module-inner">
|
||
<p class="eyebrow animate-in">模块 2 · 编排内核</p>
|
||
<h1 class="module-title animate-in">14 节点 LangGraph:<br>从回忆到归档</h1>
|
||
<p class="module-lead animate-in">
|
||
客户线不走顾问通用 tool→llm→guard 编排(<code>agent_service.py</code>),而是
|
||
登录客户 14 节点 LangGraph 编排(<code>customer_service.py</code>)里一张更大的
|
||
<span class="term" data-definition="LangGraph = 用「节点 + 边」画出的对话流程图;每个节点是一段 Python 函数,边决定下一步走哪条分支。">LangGraph</span>。
|
||
入口永远是 <code>recall_memory</code>,出口经 <code>save_memory</code> 写记忆后收尾。
|
||
</p>
|
||
|
||
<div class="screen animate-in">
|
||
<h2>14 个节点一览(改分支前先认门牌号)</h2>
|
||
<div class="badge-list">
|
||
<div class="badge-item"><span class="badge-code">1</span><span class="badge-desc">recall_memory · 拉 Redis/MySQL 窗口</span></div>
|
||
<div class="badge-item"><span class="badge-code">2</span><span class="badge-desc">intent_classify · LLM 分意图</span></div>
|
||
<div class="badge-item"><span class="badge-code">3</span><span class="badge-desc">rag_search · fin_* Milvus</span></div>
|
||
<div class="badge-item"><span class="badge-code">4</span><span class="badge-desc">param_extract · 抽查询参数</span></div>
|
||
<div class="badge-item"><span class="badge-code">5</span><span class="badge-desc">tool_call · Core 只读 Tool(core_ro_tool.py)</span></div>
|
||
<div class="badge-item"><span class="badge-code">6</span><span class="badge-desc">interpret · 解读 fact_text</span></div>
|
||
<div class="badge-item"><span class="badge-code">7</span><span class="badge-desc">generate · RAG 拼回复</span></div>
|
||
<div class="badge-item"><span class="badge-code">8</span><span class="badge-desc">chitchat · 闲聊</span></div>
|
||
<div class="badge-item"><span class="badge-code">9</span><span class="badge-desc">reject · 合规拒绝</span></div>
|
||
<div class="badge-item"><span class="badge-code">10</span><span class="badge-desc">transfer_human · 转人工</span></div>
|
||
<div class="badge-item"><span class="badge-code">11</span><span class="badge-desc">fallback · 兜底话术</span></div>
|
||
<div class="badge-item"><span class="badge-code">12</span><span class="badge-desc">save_note · 客户记一笔</span></div>
|
||
<div class="badge-item"><span class="badge-code">13</span><span class="badge-desc">save_memory · 写回合记忆</span></div>
|
||
<div class="badge-item"><span class="badge-code">14</span><span class="badge-desc">profile_maybe_extract + archive_check</span></div>
|
||
</div>
|
||
<div class="callout callout-accent">
|
||
<strong>分支规则:</strong> <code>intent_classify</code> 之后走 RAG(产品/政策/FAQ)、走 Tool(持仓/流水/风评)、或走静态话术(拒绝/转人工/闲聊)。所有生成分支最后都汇入 <code>save_memory</code>。
|
||
<strong>口吻护栏与转人工决策</strong>见模块 4。
|
||
</div>
|
||
</div>
|
||
|
||
<div class="screen animate-in">
|
||
<h2>群聊动画:客户问「我持仓多少」</h2>
|
||
<p>下面按节点顺序播放(数据查询分支);若问「申购费率多少」则在节点 2 后改走 <code>rag_search → generate</code>。</p>
|
||
|
||
<div class="chat-window" id="chat-customer-m2">
|
||
<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="mem" style="display:none">
|
||
<div class="chat-avatar" style="background:#5BAE7A">①</div>
|
||
<div class="chat-bubble">
|
||
<span class="chat-sender" style="color:#5BAE7A">recall_memory</span>
|
||
<p>从 Redis 窗口 + MySQL 回源,拼 consult_memory / profile_context</p>
|
||
</div>
|
||
</div>
|
||
<div class="chat-message" data-msg="2" data-sender="intent" style="display:none">
|
||
<div class="chat-avatar" style="background:#236B44">②</div>
|
||
<div class="chat-bubble">
|
||
<span class="chat-sender" style="color:#236B44">intent_classify</span>
|
||
<p>LLM 输出意图 <code>holdings_query</code>(非 RAG、非闲聊)</p>
|
||
</div>
|
||
</div>
|
||
<div class="chat-message" data-msg="3" data-sender="param" style="display:none">
|
||
<div class="chat-avatar" style="background:#2D8B55">④</div>
|
||
<div class="chat-bubble">
|
||
<span class="chat-sender" style="color:#2D8B55">param_extract</span>
|
||
<p>抽查询参数;customer_id 已在 state 里(来自 JWT),不信用户口述</p>
|
||
</div>
|
||
</div>
|
||
<div class="chat-message" data-msg="4" 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><code>query_holdings(customer_id)</code> → Core 只读 → 返回 fact_text 模板</p>
|
||
</div>
|
||
</div>
|
||
<div class="chat-message" data-msg="5" data-sender="interp" style="display:none">
|
||
<div class="chat-avatar" style="background:#7B6DAA">⑥</div>
|
||
<div class="chat-bubble">
|
||
<span class="chat-sender" style="color:#7B6DAA">interpret</span>
|
||
<p>LLM 只「解读」fact_text,禁止编造持仓数字</p>
|
||
</div>
|
||
</div>
|
||
<div class="chat-message" data-msg="6" data-sender="rag-alt" style="display:none">
|
||
<div class="chat-avatar" style="background:#D4A843">③⑦</div>
|
||
<div class="chat-bubble">
|
||
<span class="chat-sender" style="color:#D4A843">RAG 分支(对照)</span>
|
||
<p>若意图是 product_consult / policy_interpret / faq → <code>rag_search</code>(fin_product / fin_policy / fin_faq)→ <code>generate</code></p>
|
||
</div>
|
||
</div>
|
||
<div class="chat-message" data-msg="7" data-sender="save" style="display:none">
|
||
<div class="chat-avatar" style="background:#2A7B9B">⑬</div>
|
||
<div class="chat-bubble">
|
||
<span class="chat-sender" style="color:#2A7B9B">save_memory</span>
|
||
<p>回合写入 Redis 窗口;失败 fail-open 不挡回复</p>
|
||
</div>
|
||
</div>
|
||
<div class="chat-message" data-msg="8" data-sender="profile" style="display:none">
|
||
<div class="chat-avatar" style="background:#5A9DB8">⑭</div>
|
||
<div class="chat-bubble">
|
||
<span class="chat-sender" style="color:#5A9DB8">profile_maybe_extract</span>
|
||
<p>每 5 轮节流,后台线程抽画像槽位(失败不影响响应)</p>
|
||
</div>
|
||
</div>
|
||
<div class="chat-message" data-msg="9" data-sender="archive" style="display:none">
|
||
<div class="chat-avatar" style="background:#6B6560">⑭</div>
|
||
<div class="chat-bubble">
|
||
<span class="chat-sender" style="color:#6B6560">archive_check</span>
|
||
<p>显式结束会话或懒扫描超时 → 归档 MySQL 长记忆</p>
|
||
</div>
|
||
</div>
|
||
<div class="chat-message" data-msg="10" data-sender="reply" style="display:none">
|
||
<div class="chat-avatar" style="background:#2D8B55">AI</div>
|
||
<div class="chat-bubble">
|
||
<span class="chat-sender" style="color:#2D8B55">回复客户</span>
|
||
<p>interpret 产出自然语言 + 合规 sanitize;SSE 路径在图跑完后分块推送</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="chat-typing" id="chat-customer-m2-typing" style="display:none">
|
||
<div class="chat-avatar" id="chat-customer-m2-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">
|
||
<div class="translation-block">
|
||
<div class="translation-code">
|
||
<span class="translation-label">图构建 · 登录客户 14 节点 LangGraph 编排(customer_service.py)</span>
|
||
<pre><code><span class="code-line">g.set_entry_point(<span class="code-string">"recall_memory"</span>)</span>
|
||
<span class="code-line">g.add_edge(<span class="code-string">"recall_memory"</span>, <span class="code-string">"intent_classify"</span>)</span>
|
||
<span class="code-line">g.add_conditional_edges(<span class="code-string">"intent_classify"</span>, _route, {...})</span>
|
||
<span class="code-line">g.add_edge(<span class="code-string">"tool_call"</span>, <span class="code-string">"interpret"</span>)</span>
|
||
<span class="code-line">g.add_edge(<span class="code-string">"rag_search"</span>, <span class="code-string">"generate"</span>)</span>
|
||
<span class="code-line"><span class="code-comment"># 各生成分支 → save_memory → profile → archive → END</span></span></code></pre>
|
||
</div>
|
||
<div class="translation-english">
|
||
<span class="translation-label">白话</span>
|
||
<div class="translation-lines">
|
||
<p class="tl">先回忆上下文,再让 LLM 判意图,然后走 RAG 或 Tool 或静态话术。</p>
|
||
<p class="tl">数据类问题:param_extract → tool_call → interpret,数字 100% 来自 Core。</p>
|
||
<p class="tl">无论哪条分支,最后都要 save_memory,再异步做画像和归档。</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="quiz-container" id="quiz-customer-m2">
|
||
<div class="quiz-question-block"
|
||
data-correct="option-b"
|
||
data-explanation-right="对。持仓/流水/风评走 param_extract→tool_call→interpret;产品规则走 rag_search→generate。"
|
||
data-explanation-wrong="RAG 只服务 fin_* 三库(产品/政策/FAQ),不查持仓表。">
|
||
<h3 class="quiz-question">客户问「基金申购费率多少」,LangGraph 走哪条分支?</h3>
|
||
<div class="quiz-options">
|
||
<button class="quiz-option" data-value="option-a" onclick="selectOption(this)">
|
||
<div class="quiz-option-radio"></div><span>tool_call → interpret</span>
|
||
</button>
|
||
<button class="quiz-option" data-value="option-b" onclick="selectOption(this)">
|
||
<div class="quiz-option-radio"></div><span>rag_search → generate</span>
|
||
</button>
|
||
<button class="quiz-option" data-value="option-c" onclick="selectOption(this)">
|
||
<div class="quiz-option-radio"></div><span>chitchat 闲聊</span>
|
||
</button>
|
||
</div>
|
||
<div class="quiz-feedback"></div>
|
||
</div>
|
||
<button class="quiz-check-btn" onclick="checkQuiz('quiz-customer-m2')">检查答案</button>
|
||
<button class="quiz-reset-btn" onclick="resetQuiz('quiz-customer-m2')">重做</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|