模块 1 · 理财师视角
代理人助手:
聊名下客户,不外发草稿
理财师(如 STAFF-10086)登录后打开「代理人助手」,通过
X-Agent-Type: advisor
进入顾问线。只能讨论名下已绑定的客户,生成的沟通草稿不会自动发给客户。
三条和 customer 线的硬区别
编排入口
走 顾问通用 tool→llm→guard 编排(agent_service.py)顾问通用 tool→llm→guard 编排,不是 登录客户 14 节点编排(customer_service.py)登录客户 14 节点编排。
客户范围
会话可绑定某个 customer_id;查数据前过 G-01 归属——必须是 customer_advisor_rel 里 active 的名下客户。
能力边界
System prompt 固化:可查持仓/流水/风评、搜产品知识库;草稿不外发、不做投资建议承诺。
STAFF-10086 理财师 · 名下示例客户 CUST-9527。改功能时先确认会话里绑的是哪位客户,再测归属拒绝场景。
请求长什么样
POST /api/chat/stream
Authorization: Bearer <jwt>
"X-Agent-Type": "advisor"
{ "message": "客户持仓如何", "customer_id": "CUST-9527" }
和对话 API 一样要带 JWT,但 Agent 类型填 advisor,不是 customer。
body 里可指定要服务的客户号;后端会校验这位客户是否在该理财师名下。
缺 X-Agent-Type 直接 401,和业务代码无关。
顾问线能调哪些 Tool(关键词意图)
理财师打开「代理人助手」对话,编排跑在哪?
模块 2 · 三张牌编排
tool → llm → guard
群聊走一遍
顾问线 LangGraph 只有三个业务节点(加 START/END): tool → llm → guard。比客户 14 节点图短,但归属校验在 对话 Tool 编排(tool_service.py)run_tool 里,和客户线铁律口径一致。
节点职责(背下来少踩坑)
tool_node
match_intent("advisor", message) 命中关键词 → 对话 Tool 编排(tool_service.py)run_tool 查 Core / 知识库 → 结果写入 tool_results。
llm_node
System prompt(顾问边界)+ Tool 摘要注入 + 历史窗口 + 本轮用户话 → DeepSeek 组织自然语言。
guard_node
顾问线不附客户/风控那种尾部免责声明(needs_disclaimer("advisor") 为 false)。
群聊:理财师问「客户 CUST-9527 持仓如何」
简化播放 tool → llm → guard 全链路(前面 四 Agent 对话 HTTP 入口(chat.py)已验 JWT + G-01)。
tool_name = tool_service.match_intent(
state["agent_type"], state["user_message"]
)
record = tool_service.run_tool(
tool_name=tool_name,
agent_type=state["agent_type"],
customer_id=state.get("customer_id") or "",
...
)
先靠关键词判断要不要查库;闲聊不命中就 tool_results 为空,LLM 纯聊。
customer_id 来自会话绑定(四 Agent 对话 HTTP 入口(chat.py)归属校验后的值),不是 LLM 生成的。
query_holdings 和 customer 线共用 Core 只读 Tool 定义(core_tools.py)定义,但跑在 顾问通用 tool→llm→guard 编排(agent_service.py)图里。
理财师问「这位客户仓位怎么样」,tool_node 如何选中 query_holdings?
模块 3 · 归属铁闸
G-01 归属校验 +
run_tool 留痕
理财师能查谁的数据,由
G-01 归属校验
决定。API 层越权抛 403;对话 Tool 层(对话 Tool 编排(tool_service.py))拒绝则落 blocked 行留痕,不抛异常挡整条链路。
两层校验,别混口径
| 层级 | 位置 | 拒绝时 |
|---|---|---|
| API(四 Agent 对话 HTTP 入口(chat.py)) | assert_customer_access | HTTP 403 + 鉴权审计 |
| Tool(对话 Tool 编排(tool_service.py)) | run_tool 内归属检查 | status='blocked' + agent_tool_call 留痕 |
customer_advisor_rel 里该客户与当前 actor_id 须为 active。查非名下客户 → API 403 或 Tool blocked(AUTH_403_NOT_ASSIGNED)。
def run_tool(
tool_name, agent_type, actor, customer_id,
tool_input, session_id, trace_id=None
) -> dict:
# 1. 归属校验(对齐 模块鉴权工厂(deps.py)assert_customer_access)
denial = _check_access(agent_type, actor, customer_id)
if denial:
return _blocked_record(denial, ...)
# 2. 执行 Core 只读 Tool 定义(core_tools.py)func
data = spec["func"](customer_id, ...)
# 3. agent_tool_call 落库(失败降级 warning)
_persist_tool_call(status="success", ...)
run_tool 是顾问线查数据的唯一编排入口:先验你有没有资格看这个 customer_id。
blocked 不是 403——对话里会呈现「查询被拒绝」,同时 MySQL 留一行 tool 调用记录。
customer_id 参数由 四 Agent 对话 HTTP 入口(chat.py)会话注入;Tool 不信 LLM 或用户消息里随便写的客户号。
相关文件
理财师在对话里查非名下客户,对话 Tool 编排(tool_service.py)会怎么做?
模块 4 · 未做与误区
T-20 还没做
Neo4j 也不是对话 Tool
顾问线 chat 骨架已通,但草稿复核工作台、合规巡检、Neo4j 实时查询都还没进对话链路。 课程中心卡片写 Neo4j,容易让 AI 去改不存在的 Cypher 节点——这模块专门纠偏。
T-20 未实现清单(2026-09 快照)
A-03 话术草稿
无 advisor_draft 表流程 · 无复核 UI · prompt 里「草稿不外发」≠ 有工作台。
A-05 合规巡检
合规专员抽检全量顾问会话 — 无前端/API。
A-06 跟进日志
确认后回写 L2 — 对话线未入库跟进草稿。
已有
顾问通用 tool→llm→guard 编排(agent_service.py)tool→llm→guard · G-01 归属 · search_knowledge(kb_product_rules)。
review_status 门禁——上线前必须补 T-20。
Neo4j:同步脚本,不是 LangGraph 节点
python scripts/sync/sync_neo4j.py
python scripts/sync/sync_advisor_rel.py
# Neo4j 关系图离线同步(sync_neo4j.py)/ 顾问归属离线同步(sync_advisor_rel.py)离线同步脚本
顾问对话不在运行时发 Cypher;名下客户靠 G-01 + Core 只读 Tool(core_ro_tool.py)。
指挥 AI「加 Neo4j Tool 查客户关系」= 新需求,不是修现有 bug。
Neo4j 数据来自 Neo4j 关系图离线同步(sync_neo4j.py)/ 顾问归属离线同步(sync_advisor_rel.py)batch sync,和 Chat 会话无实时联动。