- 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.
89 lines
6.2 KiB
HTML
89 lines
6.2 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">session_repository + Redis 会话窗口(memory_service.py):<br>Redis 窗口,MySQL 权威</h1>
|
||
<p class="module-lead animate-in">
|
||
对话每轮消息<strong>同步写 MySQL</strong>(权威),同时用
|
||
<span class="term" data-definition="Redis = 内存数据库,适合存最近几条消息当「滑动窗口」,丢了可从 MySQL 重建。">Redis</span>
|
||
缓存最近 N≤20 条,TTL 2 小时。读历史时 Redis 命中快,miss 则回源
|
||
<code>session_repository</code>。
|
||
</p>
|
||
|
||
<div class="screen animate-in">
|
||
<h2>数据流:用户发第二条消息</h2>
|
||
<div class="flow-animation" data-steps='[
|
||
{"highlight":"flow-actor-1","label":"对话 HTTP 入口(chat.py)收到 POST /api/chat"},
|
||
{"highlight":"flow-actor-2","label":"session_repository:INSERT agent_message(MySQL 权威)","packet":true,"from":"1","to":"2"},
|
||
{"highlight":"flow-actor-3","label":"Redis 会话窗口(memory_service.py).append_window → Redis RPUSH","packet":true,"from":"2","to":"3"},
|
||
{"highlight":"flow-actor-3","label":"LTRIM 保留最近 20 条 + EXPIRE 2h"},
|
||
{"highlight":"flow-actor-2","label":"get_recent:先 Redis LRANGE,miss 回源 list_messages","packet":true,"from":"3","to":"2"},
|
||
{"highlight":"flow-actor-1","label":"拼进 LLM 上下文,生成回复","packet":true,"from":"2","to":"1"}
|
||
]'>
|
||
<div class="flow-actors">
|
||
<div class="flow-actor" id="flow-actor-1"><span class="flow-actor-icon">🚪</span><span>对话 HTTP 入口(chat.py)</span></div>
|
||
<div class="flow-actor" id="flow-actor-2"><span class="flow-actor-icon">🗄</span><span>session_repository</span></div>
|
||
<div class="flow-actor" id="flow-actor-3"><span class="flow-actor-icon">⚡</span><span>Redis 会话窗口(memory_service.py)</span></div>
|
||
</div>
|
||
<p class="flow-step-label">Key 格式:sess:{agent}:{session_id}:msgs</p>
|
||
<div class="flow-controls">
|
||
<button class="btn flow-next-btn">下一步</button>
|
||
<button class="btn flow-reset-btn">重来</button>
|
||
</div>
|
||
</div>
|
||
<div class="callout callout-accent">
|
||
<strong>降级策略:</strong> Redis 写失败只打 warning,不阻塞对话;读失败回源 MySQL。指挥 AI 加缓存时别破坏「MySQL 为准」这条铁律。
|
||
</div>
|
||
</div>
|
||
|
||
<div class="screen animate-in">
|
||
<div class="translation-block">
|
||
<div class="translation-code">
|
||
<span class="translation-label">Redis 会话窗口(memory_service.py)</span>
|
||
<pre><code><span class="code-line"><span class="code-keyword">def</span> <span class="code-function">window_key</span>(agent_type, session_id):</span>
|
||
<span class="code-line"> <span class="code-keyword">return</span> <span class="code-string">f"sess:{agent_type}:{session_id}:msgs"</span></span>
|
||
<span class="code-line"></span>
|
||
<span class="code-line"><span class="code-keyword">def</span> <span class="code-function">get_recent</span>(agent_type, session_id, limit=<span class="code-number">20</span>):</span>
|
||
<span class="code-line"> raw = get_gateway().lrange(window_key(...), -limit, -<span class="code-number">1</span>)</span>
|
||
<span class="code-line"> <span class="code-keyword">if</span> raw: <span class="code-keyword">return</span> [json.loads(item) <span class="code-keyword">for</span> item <span class="code-keyword">in</span> raw]</span>
|
||
<span class="code-line"> <span class="code-keyword">return</span> SessionRepository().list_messages(session_id, limit=limit)</span>
|
||
<span class="code-line"></span>
|
||
<span class="code-line"><span class="code-keyword">def</span> <span class="code-function">append_window</span>(...):</span>
|
||
<span class="code-line"> client.rpush(key, ...); client.ltrim(key, -<span class="code-number">20</span>, -<span class="code-number">1</span>); client.expire(key, <span class="code-number">7200</span>)</span></code></pre>
|
||
</div>
|
||
<div class="translation-english">
|
||
<span class="translation-label">白话</span>
|
||
<div class="translation-lines">
|
||
<p class="tl">每个会话在 Redis 里是一条 List,key 带 agent 类型防串线。</p>
|
||
<p class="tl">读:先捞 List 尾部 N 条;空或报错就去 MySQL 查 agent_message 表。</p>
|
||
<p class="tl">写:新消息 RPUSH 进去,LTRIM 砍掉太老的,EXPIRE 两小时自动过期。</p>
|
||
<p class="tl">session_repository 管表结构、分页列表、关闭会话——和合规审计表分开。</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="quiz-container" id="quiz-shared-m2">
|
||
<div class="quiz-question-block"
|
||
data-correct="option-c"
|
||
data-explanation-right="对。Redis 是加速窗口,丢数据可从 MySQL 重建;审计合规以落库为准。"
|
||
data-explanation-wrong="Redis TTL 过期不等于会话删除;权威记录在 agent_session / agent_message。">
|
||
<h3 class="quiz-question">Redis 里 sess:customer:xxx:msgs 过期了,历史消息去哪了?</h3>
|
||
<div class="quiz-options">
|
||
<button class="quiz-option" data-value="option-a" onclick="selectOption(this)">
|
||
<div class="quiz-option-radio"></div><span>全丢了,用户得新开对话</span>
|
||
</button>
|
||
<button class="quiz-option" data-value="option-b" onclick="selectOption(this)">
|
||
<div class="quiz-option-radio"></div><span>自动从 Milvus 向量库恢复</span>
|
||
</button>
|
||
<button class="quiz-option" data-value="option-c" onclick="selectOption(this)">
|
||
<div class="quiz-option-radio"></div><span>回源 MySQL session_repository.list_messages</span>
|
||
</button>
|
||
</div>
|
||
<div class="quiz-feedback"></div>
|
||
</div>
|
||
<button class="quiz-check-btn" onclick="checkQuiz('quiz-shared-m2')">检查答案</button>
|
||
<button class="quiz-reset-btn" onclick="resetQuiz('quiz-shared-m2')">重做</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|