- 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.
91 lines
6.3 KiB
HTML
91 lines
6.3 KiB
HTML
<section class="module" id="module-3">
|
||
<div class="module-inner">
|
||
<p class="eyebrow animate-in">模块 3 · 归属铁闸</p>
|
||
<h1 class="module-title animate-in">G-01 归属校验 +<br>run_tool 留痕</h1>
|
||
<p class="module-lead animate-in">
|
||
理财师能查谁的数据,由
|
||
<span class="term" data-definition="G-01 = 平台客户数据归属规则:customer 只能本人;advisor 只能名下 active 客户;risk_officer 全量只读。">G-01 归属校验</span>
|
||
决定。API 层越权抛 403;对话 Tool 层(对话 Tool 编排(tool_service.py))拒绝则落 <code>blocked</code> 行留痕,不抛异常挡整条链路。
|
||
</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>位置</th><th>拒绝时</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>API(四 Agent 对话 HTTP 入口(chat.py))</td><td><code>assert_customer_access</code></td><td>HTTP 403 + 鉴权审计</td></tr>
|
||
<tr><td>Tool(对话 Tool 编排(tool_service.py))</td><td><code>run_tool</code> 内归属检查</td><td><code>status='blocked'</code> + agent_tool_call 留痕</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<div class="callout callout-accent">
|
||
<strong>advisor 规则:</strong> 角色含 advisor 时,<code>customer_advisor_rel</code> 里该客户与当前 <code>actor_id</code> 须为 active。查非名下客户 → API 403 或 Tool blocked(<code>AUTH_403_NOT_ASSIGNED</code>)。
|
||
</div>
|
||
</div>
|
||
|
||
<div class="screen animate-in">
|
||
<div class="translation-block">
|
||
<div class="translation-code">
|
||
<span class="translation-label">CODE · 对话 Tool 编排(tool_service.py)run_tool(节选)</span>
|
||
<pre><code><span class="code-line"><span class="code-keyword">def</span> run_tool(</span>
|
||
<span class="code-line"> tool_name, agent_type, actor, customer_id,</span>
|
||
<span class="code-line"> tool_input, session_id, trace_id=<span class="code-keyword">None</span></span>
|
||
<span class="code-line">) -> dict:</span>
|
||
<span class="code-line"> <span class="code-comment"># 1. 归属校验(对齐 模块鉴权工厂(deps.py)assert_customer_access)</span></span>
|
||
<span class="code-line"> denial = _check_access(agent_type, actor, customer_id)</span>
|
||
<span class="code-line"> <span class="code-keyword">if</span> denial:</span>
|
||
<span class="code-line"> <span class="code-keyword">return</span> _blocked_record(denial, ...)</span>
|
||
<span class="code-line"> <span class="code-comment"># 2. 执行 Core 只读 Tool 定义(core_tools.py)func</span></span>
|
||
<span class="code-line"> data = spec[<span class="code-string">"func"</span>](customer_id, ...)</span>
|
||
<span class="code-line"> <span class="code-comment"># 3. agent_tool_call 落库(失败降级 warning)</span></span>
|
||
<span class="code-line"> _persist_tool_call(status=<span class="code-string">"success"</span>, ...)</span></code></pre>
|
||
</div>
|
||
<div class="translation-english">
|
||
<span class="translation-label">白话</span>
|
||
<div class="translation-lines">
|
||
<p class="tl">run_tool 是顾问线查数据的唯一编排入口:先验你有没有资格看这个 customer_id。</p>
|
||
<p class="tl">blocked 不是 403——对话里会呈现「查询被拒绝」,同时 MySQL 留一行 tool 调用记录。</p>
|
||
<p class="tl">customer_id 参数由 四 Agent 对话 HTTP 入口(chat.py)会话注入;Tool 不信 LLM 或用户消息里随便写的客户号。</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="screen animate-in">
|
||
<h2>相关文件</h2>
|
||
<div class="file-tree animate-in">
|
||
<div class="tree-item tree-folder">app/api/</div>
|
||
<div class="tree-item tree-indent">模块鉴权工厂(deps.py)模块鉴权工厂 — app/api/deps.py · assert_customer_access(G-01 权威口径)</div>
|
||
<div class="tree-item tree-indent">四 Agent 对话 HTTP 入口(chat.py)四 Agent 对话 HTTP 入口 — app/api/chat.py · 会话绑定 customer_id + 入口归属</div>
|
||
<div class="tree-item tree-folder">app/service/</div>
|
||
<div class="tree-item tree-indent">对话 Tool 编排(tool_service.py)Tool 编排 — match_intent + run_tool</div>
|
||
<div class="tree-item tree-indent">顾问通用 tool→llm→guard 编排(agent_service.py)顾问通用 tool→llm→guard 编排 — tool_node 调用 run_tool</div>
|
||
<div class="tree-item tree-folder">app/tool/</div>
|
||
<div class="tree-item tree-indent">Core 只读 Tool 定义(core_tools.py)Core 只读 Tool 定义 — query_holdings 等</div>
|
||
</div>
|
||
|
||
<div class="quiz-container" id="quiz-advisor-m3">
|
||
<div class="quiz-question-block"
|
||
data-correct="option-b"
|
||
data-explanation-right="对。Tool 层归属拒绝落 agent_tool_call status=blocked,对话继续;API 层才抛 403。"
|
||
data-explanation-wrong="run_tool 的 blocked 是留痕位,不是 HTTP 403;和 模块鉴权工厂(deps.py)抛异常是两层。">
|
||
<h3 class="quiz-question">理财师在对话里查非名下客户,对话 Tool 编排(tool_service.py)会怎么做?</h3>
|
||
<div class="quiz-options">
|
||
<button class="quiz-option" data-value="option-a" onclick="selectOption(this)">
|
||
<div class="quiz-option-radio"></div><span>抛 PermissionDenied 整请求 403</span>
|
||
</button>
|
||
<button class="quiz-option" data-value="option-b" onclick="selectOption(this)">
|
||
<div class="quiz-option-radio"></div><span>返回 blocked 记录,LLM 可解释拒绝原因</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-advisor-m3')">检查答案</button>
|
||
<button class="quiz-reset-btn" onclick="resetQuiz('quiz-advisor-m3')">重做</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|