- Introduced `build_all.py` script to automate the assembly of course modules into a single `index.html` file. - Created `index.html` for the main course overview, featuring a structured layout and navigation for various modules. - Developed `_base.html` and `_footer.html` templates for the advisor module, ensuring consistent styling and structure. - Added `build.sh` script for individual module assembly, enhancing modularity and ease of updates. - Implemented multiple module HTML files detailing specific training scenarios and functionalities for advisors, including interactive elements and quizzes. This update significantly enhances the course delivery framework, providing a comprehensive and interactive learning experience for advisors.
89 lines
6.0 KiB
HTML
89 lines
6.0 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 + memory_service:<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":"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":"memory_service.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>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>memory_service · Redis</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">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>
|