- 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.
106 lines
6.2 KiB
HTML
106 lines
6.2 KiB
HTML
<section class="module" id="module-2">
|
||
<div class="module-inner">
|
||
<p class="eyebrow animate-in">模块 2 · NL→SQL 管线</p>
|
||
<h1 class="module-title animate-in">一句话怎么变成<br>可审计的表格?</h1>
|
||
<p class="module-lead animate-in">
|
||
<code>app/service/analyst_agent.py</code> 把用户问题拆成可测试步骤。
|
||
像<strong>翻译官 + 安检 + 记账员</strong>:先弄清指标含义,再生成 SQL,层层校验后才执行,最后解读并留痕。
|
||
</p>
|
||
|
||
<div class="screen animate-in">
|
||
<h2>七步流水线(动画)</h2>
|
||
<p>点击「下一步」看 <span class="term" data-definition="Natural Language to SQL:把自然语言问题翻译成数据库查询语句。">NL→SQL</span> 在代码里怎么走:</p>
|
||
|
||
<div class="flow-animation" data-steps='[
|
||
{"highlight":"flow-actor-1","label":"① 消歧:口径字典命中多义指标 → 返回 suggestions 让用户选"},
|
||
{"highlight":"flow-actor-2","label":"② 生成 SQL:DeepSeek + SCHEMA_PROMPT + 权限域提示","packet":true,"from":"1","to":"2"},
|
||
{"highlight":"flow-actor-3","label":"③ sql_guard 五层校验:只读 / 表白名单 / 行级归属…","packet":true,"from":"2","to":"3"},
|
||
{"highlight":"flow-actor-4","label":"④ 执行:AnalyticsRepo 只读查询(Redis 缓存可命中)","packet":true,"from":"3","to":"4"},
|
||
{"highlight":"flow-actor-5","label":"⑤ guardrail:解读数字必须与表格一致,不过则降级","packet":true,"from":"4","to":"5"},
|
||
{"highlight":"flow-actor-1","label":"⑥ 留痕:analytics_query_log 写入 SQL/耗时/护栏结果","packet":true,"from":"5","to":"1"}
|
||
]'>
|
||
<div class="flow-actors">
|
||
<div class="flow-actor" id="flow-actor-1"><span class="flow-actor-icon">📖</span><span>消歧 dict</span></div>
|
||
<div class="flow-actor" id="flow-actor-2"><span class="flow-actor-icon">🤖</span><span>LLM 生成</span></div>
|
||
<div class="flow-actor" id="flow-actor-3"><span class="flow-actor-icon">🛡</span><span>sql_guard</span></div>
|
||
<div class="flow-actor" id="flow-actor-4"><span class="flow-actor-icon">🗄</span><span>execute</span></div>
|
||
<div class="flow-actor" id="flow-actor-5"><span class="flow-actor-icon">✓</span><span>guardrail</span></div>
|
||
</div>
|
||
<p class="flow-step-label">校验失败 → 403 或 degrade(只给表格不给瞎编解读)</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">
|
||
<h2>sql_guard 在拦什么</h2>
|
||
<div class="pattern-cards">
|
||
<div class="pattern-card">
|
||
<h3>只读白名单</h3>
|
||
<p>拒绝 INSERT/UPDATE/DELETE 等写操作关键字</p>
|
||
</div>
|
||
<div class="pattern-card">
|
||
<h3>表白名单</h3>
|
||
<p><code>core_*</code> + <code>risk_alert</code> 等,超出即 403</p>
|
||
</div>
|
||
<div class="pattern-card">
|
||
<h3>行级归属</h3>
|
||
<p>customer 域强制 <code>customer_id = 本人</code>;advisor 只能名下客户</p>
|
||
</div>
|
||
<div class="pattern-card">
|
||
<h3>粒度控制</h3>
|
||
<p>ops 聚合域禁止下钻到单个客户明细</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="screen animate-in">
|
||
<div class="translation-block">
|
||
<div class="translation-code">
|
||
<span class="translation-label">CODE · analyst_agent.run</span>
|
||
<pre><code><span class="code-line">amb = self._detect_ambiguity(question)</span>
|
||
<span class="code-line"><span class="code-keyword">if</span> amb: <span class="code-keyword">return</span> self._clarify(amb, trace_id)</span>
|
||
<span class="code-line">sql_text, usage = self._generate_sql(...)</span>
|
||
<span class="code-line">vres = validate(sql_text, domain, scope) <span class="code-comment"># sql_guard</span></span>
|
||
<span class="code-line">exec_result = self.repo.execute_readonly(sql_text)</span>
|
||
<span class="code-line">answer, guard = self._generate_verified_answer(...)</span>
|
||
<span class="code-line">self._persist(...) <span class="code-comment"># analytics_query_log</span></span></code></pre>
|
||
</div>
|
||
<div class="translation-english">
|
||
<span class="translation-label">白话</span>
|
||
<div class="translation-lines">
|
||
<p class="tl">指标说不清就先反问,不瞎猜 SQL。</p>
|
||
<p class="tl">生成后必须过 sql_guard,执行只走只读连接。</p>
|
||
<p class="tl">AI 解读若数字对不上表,降级为「请看表格」;全程写入查询日志备查。</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="quiz-container" id="quiz-analyst-m2">
|
||
<div class="quiz-question-block"
|
||
data-correct="option-c"
|
||
data-explanation-right="guardrail 不通过时 status=degrade:保留 table/sql,answer 换成安全文案。"
|
||
data-explanation-wrong="不会静默丢数据;表格是 ground truth,解读可被降级。">
|
||
<h3 class="quiz-question">数字护栏(guardrail)校验失败时,用户还能看到查询结果表格吗?</h3>
|
||
<div class="quiz-options">
|
||
<button class="quiz-option" data-value="option-a" onclick="selectOption(this)">
|
||
<div class="quiz-option-radio"></div><span>不能,整单 500 错误</span>
|
||
</button>
|
||
<button class="quiz-option" data-value="option-b" onclick="selectOption(this)">
|
||
<div class="quiz-option-radio"></div><span>能,但 SQL 会被隐藏</span>
|
||
</button>
|
||
<button class="quiz-option" data-value="option-c" onclick="selectOption(this)">
|
||
<div class="quiz-option-radio"></div><span>能,解读降级为「以表格为准」</span>
|
||
</button>
|
||
</div>
|
||
<div class="quiz-feedback"></div>
|
||
</div>
|
||
<button class="quiz-check-btn" onclick="checkQuiz('quiz-analyst-m2')">检查答案</button>
|
||
<button class="quiz-reset-btn" onclick="resetQuiz('quiz-analyst-m2')">重做</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|