Files
group_xinghuo_jinrong/docs/course/jinrong-module-advisor/modules/02-agent-flow.html
T
zhanghongyu_0626 6f222f1c56 feat(course): Add course assembly script and module structure for advisor training
- 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.
2026-09-09 23:22:59 +08:00

147 lines
8.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<section class="module" id="module-2">
<div class="module-inner">
<p class="eyebrow animate-in">模块 2 · 三张牌编排</p>
<h1 class="module-title animate-in">tool → llm → guard<br>群聊走一遍</h1>
<p class="module-lead animate-in">
顾问线 LangGraph 只有三个业务节点(加 START/END):
<span class="term" data-definition="tool 节点 = 根据用户消息关键词匹配 Tool 名,调 run_tool 查库并落 agent_tool_call 留痕。">tool</span>
→ llm → guard。比客户 14 节点图短,但<strong>归属校验在 tool_service.run_tool 里</strong>,和客户线铁律口径一致。
</p>
<div class="screen animate-in">
<h2>节点职责(背下来少踩坑)</h2>
<div class="pattern-cards">
<div class="pattern-card">
<h3>tool_node</h3>
<p><code>match_intent("advisor", message)</code> 命中关键词 → <code>run_tool</code> 查 Core / 知识库 → 结果写入 <code>tool_results</code>。</p>
</div>
<div class="pattern-card">
<h3>llm_node</h3>
<p>System prompt(顾问边界)+ Tool 摘要注入 + 历史窗口 + 本轮用户话 → DeepSeek 组织自然语言。</p>
</div>
<div class="pattern-card">
<h3>guard_node</h3>
<p>顾问线<strong>不附</strong>客户/风控那种尾部免责声明(<code>needs_disclaimer("advisor")</code> 为 false)。</p>
</div>
</div>
</div>
<div class="screen animate-in">
<h2>群聊:理财师问「客户 CUST-9527 持仓如何」</h2>
<p>简化播放 tool → llm → guard 全链路(前面 chat.py 已验 JWT + G-01)。</p>
<div class="chat-window" id="chat-advisor-m2">
<div class="chat-messages">
<div class="chat-message" data-msg="0" data-sender="browser" style="display:none">
<div class="chat-avatar" style="background:#D4A843">前</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#D4A843">浏览器</span>
<p>POST /api/chat/stream · X-Agent-Type: advisor · customer_id: CUST-9527</p>
</div>
</div>
<div class="chat-message" data-msg="1" data-sender="chat" style="display:none">
<div class="chat-avatar" style="background:#B8922F">API</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#B8922F">chat.py</span>
<p>验 JWT → assert_customer_access(G-01:名下客户)→ 建/续 session</p>
</div>
</div>
<div class="chat-message" data-msg="2" data-sender="tool" style="display:none">
<div class="chat-avatar" style="background:#E0C06A">Tool</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#E0C06A">tool_node</span>
<p>关键词「持仓」→ <code>query_holdings</code> → tool_service.run_tool</p>
</div>
</div>
<div class="chat-message" data-msg="3" data-sender="core" style="display:none">
<div class="chat-avatar" style="background:#2D8B55">DB</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#2D8B55">core_ro</span>
<p>只读 SQL 查持仓 → fact_text 摘要写入 tool_results;agent_tool_call 落库 success</p>
</div>
</div>
<div class="chat-message" data-msg="4" data-sender="llm" style="display:none">
<div class="chat-avatar" style="background:#7B6DAA">AI</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#7B6DAA">llm_node</span>
<p>Tool 摘要注入 system 上下文 → DeepSeek 生成顾问口吻回复(数据引用 fact_text)</p>
</div>
</div>
<div class="chat-message" data-msg="5" data-sender="guard" style="display:none">
<div class="chat-avatar" style="background:#2A7B9B">盾</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#2A7B9B">guard_node</span>
<p>advisor 不拼 CHAT_DISCLAIMER;reply 原样传出</p>
</div>
</div>
<div class="chat-message" data-msg="6" data-sender="sse" style="display:none">
<div class="chat-avatar" style="background:#D4A843">流</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#D4A843">stream_chat</span>
<p>Tool 同步跑完后逐块推 LLM 文本;收完 done 后 api 层统一落 MySQL 回合</p>
</div>
</div>
</div>
<div class="chat-typing" id="chat-advisor-m2-typing" style="display:none">
<div class="chat-avatar" id="chat-advisor-m2-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">CODE · agent_service.tool_node</span>
<pre><code><span class="code-line">tool_name = tool_service.match_intent(</span>
<span class="code-line"> state[<span class="code-string">"agent_type"</span>], state[<span class="code-string">"user_message"</span>]</span>
<span class="code-line">)</span>
<span class="code-line">record = tool_service.run_tool(</span>
<span class="code-line"> tool_name=tool_name,</span>
<span class="code-line"> agent_type=state[<span class="code-string">"agent_type"</span>],</span>
<span class="code-line"> customer_id=state.get(<span class="code-string">"customer_id"</span>) or <span class="code-string">""</span>,</span>
<span class="code-line"> ...</span>
<span class="code-line">)</span></code></pre>
</div>
<div class="translation-english">
<span class="translation-label">白话</span>
<div class="translation-lines">
<p class="tl">先靠关键词判断要不要查库;闲聊不命中就 tool_results 为空,LLM 纯聊。</p>
<p class="tl">customer_id 来自会话绑定(chat.py 归属校验后的值),不是 LLM 生成的。</p>
<p class="tl">query_holdings 和 customer 线共用 core_tools 定义,但跑在 agent_service 图里。</p>
</div>
</div>
</div>
<div class="quiz-container" id="quiz-advisor-m2">
<div class="quiz-question-block"
data-correct="option-a"
data-explanation-right="对。一期用关键词 match_intent;命中「持仓」等词才调 query_holdings。"
data-explanation-wrong="顾问线 Tool 意图是一期关键词规则,不是 LLM 自由选函数名。">
<h3 class="quiz-question">理财师问「这位客户仓位怎么样」,tool_node 如何选中 query_holdings?</h3>
<div class="quiz-options">
<button class="quiz-option" data-value="option-a" onclick="selectOption(this)">
<div class="quiz-option-radio"></div><span>match_intent 关键词命中「仓位」</span>
</button>
<button class="quiz-option" data-value="option-b" onclick="selectOption(this)">
<div class="quiz-option-radio"></div><span>LLM 在回复里输出函数名</span>
</button>
<button class="quiz-option" data-value="option-c" onclick="selectOption(this)">
<div class="quiz-option-radio"></div><span>前端在 body 里传 tool_name</span>
</button>
</div>
<div class="quiz-feedback"></div>
</div>
<button class="quiz-check-btn" onclick="checkQuiz('quiz-advisor-m2')">检查答案</button>
<button class="quiz-reset-btn" onclick="resetQuiz('quiz-advisor-m2')">重做</button>
</div>
</div>
</div>
</section>