- Added `status` query parameter to `list_sessions_api` for filtering sessions by their status (active/closed). - Introduced `close_all_sessions_api` endpoint to allow users to close all active sessions for the current actor. - Updated `SessionRepository` to support status filtering in session listing and implemented logic for closing active sessions. - Improved Redis connection settings for better performance and reliability. This update enhances the chat functionality by providing more control over session management, improving user experience and system efficiency.
107 lines
6.4 KiB
HTML
107 lines
6.4 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">
|
||
NL→SQL 编排(<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":"② 模板匹配:published 模板填参 → 可跳过 LLM","packet":true,"from":"1","to":"2"},
|
||
{"highlight":"flow-actor-2","label":"②b 未命中 → DeepSeek + SCHEMA_PROMPT 生成 SQL","packet":true,"from":"1","to":"2"},
|
||
{"highlight":"flow-actor-3","label":"③ SQL 安全校验(sql_guard.py)五层校验:只读 / 表白名单 / 行级归属…","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 安全校验</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 安全校验(sql_guard.py)在拦什么</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 · NL→SQL 编排(analyst_agent.py).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 安全校验(sql_guard.py),执行只走只读连接。</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>
|