Files
group_xinghuo_jinrong/docs/course/jinrong-module-risk/modules/03-tools-rest.html
T

136 lines
7.6 KiB
HTML
Raw Normal View History

<section class="module" id="module-3">
<div class="module-inner">
<p class="eyebrow animate-in">模块 3 · REST 与对话 Tool</p>
<h1 class="module-title animate-in">台账 REST 要带 risk 头<br>对话靠六只 Tool</h1>
<p class="module-lead animate-in">
风控<span class="term" data-definition="REST:用 URL + HTTP 方法(GET/POST)访问的后端接口,浏览器和脚本都能调。">REST</span>
走模块鉴权工厂(<code>deps.py</code> 的 <code>get_auth_context</code>),前端必须带 <code>X-Agent-Type: risk</code>。
风控对话走顾问通用编排(<code>agent_service.py</code>)的 risk 分支 + SSE;Tool 由风控对话 Tool 注册表(<code>chat_tools.py</code>)注册 <strong>六只</strong>只读 Tool,经对话 Tool 编排(<code>tool_service.py</code>)分发。
</p>
<div class="screen animate-in">
<h2>预警 REST 两端点</h2>
<div class="pattern-cards">
<div class="pattern-card">
<h3>GET /api/risk/alerts</h3>
<p>risk_officer / risk_manager 全量;compliance 强制只看 aml;响应含 <code>disclaimer</code></p>
</div>
<div class="pattern-card">
<h3>POST /api/risk/alerts/{id}/handle</h3>
<p>仅 risk_officer;handler_result 三枚举 + 审计;409 状态冲突</p>
</div>
</div>
<div class="callout callout-warning">
<strong>曾踩过的坑:</strong> <code>web/src/api/risk.ts</code> 若漏 <code>X-Agent-Type: risk</code>,JWT 正确也 401。
问数 API 则相反——不要带这个头。
</div>
</div>
<div class="screen animate-in">
<h2>风控对话 Tool(chat_tools.py)注册表(C1 + 扩展)</h2>
<div class="file-tree animate-in">
<div class="tree-item tree-folder">风控对话 Tool(app/service/risk/chat_tools.py)</div>
<div class="tree-item tree-indent">alert_query — 客户或全量待审预警</div>
<div class="tree-item tree-indent">customer_context — L0 + L3 + 待审预警</div>
<div class="tree-item tree-indent">suitability_check — 只读校验 + 审计落库</div>
<div class="tree-item tree-indent">aml_lookup — 名单匹配</div>
<div class="tree-item tree-indent">query_overdue_alerts — 超期未处置(FR-9)</div>
<div class="tree-item tree-indent">query_agent_behavior — 代理人行为链(FR-10)</div>
</div>
<p>红线:Tool 只读,不生成预警单、不处置、不改正式风险等级。</p>
</div>
<div class="screen animate-in">
<h2>群聊:风控问「今天多少待审预警」</h2>
<div class="chat-window" id="chat-risk-m3">
<div class="chat-messages">
<div class="chat-message" data-msg="0" data-sender="browser" style="display:none">
<div class="chat-avatar" style="background:#D94F30">前</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#D94F30">RiskChatPage</span>
<p>POST /api/chat/stream · X-Agent-Type: risk</p>
</div>
</div>
<div class="chat-message" data-msg="1" data-sender="chat" style="display:none">
<div class="chat-avatar" style="background:#E87A62">API</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#E87A62">tool_service</span>
<p>意图命中「待审预警」→ alert_query(customer_id 空缺=全量)</p>
</div>
</div>
<div class="chat-message" data-msg="2" data-sender="tool" style="display:none">
<div class="chat-avatar" style="background:#B83D24">Tool</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#B83D24">alert_query</span>
<p>RiskRepository.list_alerts → 今日新增口径 + alert_id 列表</p>
</div>
</div>
<div class="chat-message" data-msg="3" data-sender="llm" style="display:none">
<div class="chat-avatar" style="background:#D4A843">AI</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#D4A843">DeepSeek</span>
<p>组织自然语言;可追问 query_agent_behavior 查 STAFF 行为链</p>
</div>
</div>
</div>
<div class="chat-typing" id="chat-risk-m3-typing" style="display:none">
<div class="chat-avatar" id="chat-risk-m3-typing-avatar">…</div>
<div class="chat-typing-dots"><span class="typing-dot"></span><span class="typing-dot"></span><span class="typing-dot"></span></div>
</div>
<div class="chat-controls">
<button class="btn chat-next-btn">下一条</button>
<button class="btn chat-all-btn">全部播放</button>
<button class="btn chat-reset-btn">重播</button>
<span class="chat-progress"></span>
</div>
</div>
</div>
<div class="screen animate-in">
<div class="translation-block">
<div class="translation-code">
<span class="translation-label">前端 · risk.ts + simulate</span>
<pre><code><span class="code-line">apiFetch(<span class="code-string">'/api/risk/alerts'</span>, {</span>
<span class="code-line"> token, agentType: <span class="code-string">'risk'</span>, <span class="code-comment">// 必须</span></span>
<span class="code-line">})</span>
<span class="code-line"><span class="code-comment">// RiskSimulatePage → /api/simulate/trade 同样带 risk</span></span></code></pre>
</div>
<div class="translation-english">
<span class="translation-label">白话</span>
<div class="translation-lines">
<p class="tl">台账页、模拟交易页、风控对话——凡走 get_auth_context 的都要声明 risk 线。</p>
<p class="tl">处置预警用 handle REST;日常问数用对话 Tool,两者不要混接口。</p>
</div>
</div>
</div>
<div class="quiz-container" id="quiz-risk-m3">
<div class="quiz-question-block"
data-correct="option-a"
data-explanation-right="query_agent_behavior 查 audit/行为链,支持 agent_id 过滤;是 FR-10 专用 Tool。"
data-explanation-wrong="customer_context 是客户档案+L3;行为链是独立 Tool。">
<h3 class="quiz-question">风控对话里问「代理人 STAFF-Q 有没有越权记录」,应触发哪个 Tool?</h3>
<div class="quiz-options">
<button class="quiz-option" data-value="option-a" onclick="selectOption(this)">
<div class="quiz-option-radio"></div><span>query_agent_behavior</span>
</button>
<button class="quiz-option" data-value="option-b" onclick="selectOption(this)">
<div class="quiz-option-radio"></div><span>customer_context</span>
</button>
<button class="quiz-option" data-value="option-c" onclick="selectOption(this)">
<div class="quiz-option-radio"></div><span>aml_lookup</span>
</button>
</div>
<div class="quiz-feedback"></div>
</div>
<button class="quiz-check-btn" onclick="checkQuiz('quiz-risk-m3')">检查答案</button>
<button class="quiz-reset-btn" onclick="resetQuiz('quiz-risk-m3')">重做</button>
</div>
<p style="margin-top:2rem; font-size:0.9rem; color: var(--color-text-muted);">
仓库现状(2026-09-10):STAFF-30001 含 risk_demo;<code>python -m pytest</code> → <strong>804 passed</strong>。
</p>
</div>
</div>
</section>