- 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.
102 lines
6.4 KiB
HTML
102 lines
6.4 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">ChatPanel / useChatPanel:<br>三条 API 线的头不一样</h1>
|
||
<p class="module-lead animate-in">
|
||
对话 UI 统一用 <code>ChatPanel</code> + <code>useChatPanel</code>,但后端入口分三条:
|
||
<code>/api/chat</code>(四 Agent)、<code>/api/analyst/*</code>(问数平台)、风控台账 REST。
|
||
指挥 AI 接新页面时,<strong>先对表再写 headers</strong>。
|
||
</p>
|
||
|
||
<div class="screen animate-in">
|
||
<h2>鉴权差异对照</h2>
|
||
<table style="width:100%; border-collapse:collapse; margin: 1.5rem 0; font-size: 0.95rem;">
|
||
<thead><tr><th>前端模块</th><th>API</th><th>Authorization</th><th>X-Agent-Type</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><code>api/chat.ts</code></td><td>/api/chat, /stream, sessions</td><td>Bearer</td><td><strong>必须</strong>(与 prop agentType 一致)</td></tr>
|
||
<tr><td><code>api/analyst.ts</code></td><td>/api/analyst/chat 等</td><td>Bearer(apiFetch)</td><td><strong>不要</strong></td></tr>
|
||
<tr><td><code>api/risk.ts</code></td><td>/api/risk/*</td><td>Bearer</td><td><strong>必须 risk</strong></td></tr>
|
||
<tr><td>持仓/产品页</td><td>/api/customers/* 等</td><td>Bearer</td><td>不要</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<div class="callout callout-warning">
|
||
<strong>真实 bug:</strong> <code>risk.ts</code> 曾漏 <code>X-Agent-Type: risk</code>,JWT 正确也 401。接新风控页先 grep 这个头。
|
||
</div>
|
||
</div>
|
||
|
||
<div class="screen animate-in">
|
||
<h2>数据流:理财师打开顾问助手</h2>
|
||
<div class="flow-animation" data-steps='[
|
||
{"highlight":"flow-actor-1","label":"ChatPanel(agentType=advisor, mode=stream)"},
|
||
{"highlight":"flow-actor-2","label":"useChatPanel → listChatSessions(token, advisor)","packet":true,"from":"1","to":"2"},
|
||
{"highlight":"flow-actor-3","label":"chat.ts:Bearer + X-Agent-Type: advisor","packet":true,"from":"2","to":"3"},
|
||
{"highlight":"flow-actor-4","label":"chat.py:get_auth_context + 准入矩阵","packet":true,"from":"3","to":"4"},
|
||
{"highlight":"flow-actor-1","label":"SSE 流式 delta → UI 逐字显示","packet":true,"from":"4","to":"1"}
|
||
]'>
|
||
<div class="flow-actors">
|
||
<div class="flow-actor" id="flow-actor-1"><span class="flow-actor-icon">💬</span><span>ChatPanel</span></div>
|
||
<div class="flow-actor" id="flow-actor-2"><span class="flow-actor-icon">🪝</span><span>useChatPanel</span></div>
|
||
<div class="flow-actor" id="flow-actor-3"><span class="flow-actor-icon">📡</span><span>api/chat.ts</span></div>
|
||
<div class="flow-actor" id="flow-actor-4"><span class="flow-actor-icon">🚪</span><span>chat.py</span></div>
|
||
</div>
|
||
<p class="flow-step-label">对比:问数走 analyst.ts,无 X-Agent-Type</p>
|
||
<div class="flow-controls">
|
||
<button class="btn flow-next-btn">下一步</button>
|
||
<button class="btn flow-reset-btn">重来</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="screen animate-in">
|
||
<div class="translation-block">
|
||
<div class="translation-code">
|
||
<span class="translation-label">chat.ts vs analyst.ts</span>
|
||
<pre><code><span class="code-line"><span class="code-comment">// chat.ts — 对话线</span></span>
|
||
<span class="code-line"><span class="code-keyword">function</span> <span class="code-function">chatHeaders</span>(token, agentType) {</span>
|
||
<span class="code-line"> <span class="code-keyword">return</span> {</span>
|
||
<span class="code-line"> Authorization: <span class="code-string">`Bearer ${token}`</span>,</span>
|
||
<span class="code-line"> <span class="code-string">'X-Agent-Type'</span>: agentType,</span>
|
||
<span class="code-line"> }</span>
|
||
<span class="code-line">}</span>
|
||
<span class="code-line"></span>
|
||
<span class="code-line"><span class="code-comment">// analyst.ts — 问数平台线</span></span>
|
||
<span class="code-line">apiFetch(<span class="code-string">'/api/analyst/chat'</span>, { method: <span class="code-string">'POST'</span>, token, body })</span>
|
||
<span class="code-line"><span class="code-comment">// apiFetch 只加 Authorization,不加 X-Agent-Type</span></span></code></pre>
|
||
</div>
|
||
<div class="translation-english">
|
||
<span class="translation-label">白话</span>
|
||
<div class="translation-lines">
|
||
<p class="tl">Chat 相关请求:token 证明身份,agentType 证明走哪条 Agent 线。</p>
|
||
<p class="tl">useChatPanel 把 agentType 从页面 prop 一路传到每次 list/send/close。</p>
|
||
<p class="tl">问数用 apiFetch 通用封装,后端走 get_platform_auth_context,不需要 Agent 头。</p>
|
||
<p class="tl">customer 线 mode 常用 sync(后端 LangGraph 整图跑完再推);advisor/risk 用 stream SSE。</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="quiz-container" id="quiz-frontend-m2">
|
||
<div class="quiz-question-block"
|
||
data-correct="option-c"
|
||
data-explanation-right="风控对话页 ChatPanel agentType=risk,chat.ts 自动带 X-Agent-Type: risk。"
|
||
data-explanation-wrong="风控 Chat 走 /api/chat,不是 /api/risk;但 REST 台账才用 risk.ts。">
|
||
<h3 class="quiz-question">RiskChatPage 发消息,请求头应长什么样?</h3>
|
||
<div class="quiz-options">
|
||
<button class="quiz-option" data-value="option-a" onclick="selectOption(this)">
|
||
<div class="quiz-option-radio"></div><span>只要 Bearer,和问数一样</span>
|
||
</button>
|
||
<button class="quiz-option" data-value="option-b" onclick="selectOption(this)">
|
||
<div class="quiz-option-radio"></div><span>Bearer + X-Agent-Type: analyst</span>
|
||
</button>
|
||
<button class="quiz-option" data-value="option-c" onclick="selectOption(this)">
|
||
<div class="quiz-option-radio"></div><span>Bearer + X-Agent-Type: risk</span>
|
||
</button>
|
||
</div>
|
||
<div class="quiz-feedback"></div>
|
||
</div>
|
||
<button class="quiz-check-btn" onclick="checkQuiz('quiz-frontend-m2')">检查答案</button>
|
||
<button class="quiz-reset-btn" onclick="resetQuiz('quiz-frontend-m2')">重做</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|