一、知识库建设 - 新增 tools/build_knowledge_chunks.py:把 knowledge/ 下文档切成可检索知识块。 采用「叶子标题」策略(其后没有更深标题的标题即切分点),同时覆盖三种真实结构: 带子条款的按子条款切、无子条款的条款单独成块、无小节的章整章成块。 第一版按固定标题级别切是失败的——适当性指南的条款是 ### 而没有 ####,产品手册的 ### 1.1 又不匹配「第X条」,两条规则互相打架,导致 4 个文件一块都没切出来。 - 新增 tools/load_knowledge_milvus.py:向量化并写入 Milvus,用 upsert 保证幂等。 schema 按方案 §4.2 统一字段,另加 chapter/section/source_file/doc_no/visibility 五个 检索与合规必需字段;索引 IVF_FLAT + COSINE + nlist=128;向量输入取「标题+正文」, 标题含条款号与章节名,是比正文更干净的检索信号。 - 知识内容按业务范围裁剪:反洗钱合规操作手册不入客服知识库(业务只做公募基金、 不涉及资金划付,且该手册标注内部机密、禁止向客户透露可疑交易信息),留给后续风控; 高净值客户服务规范只保留「客户分层标准」与「各层级专属权益」两章, 家族信托、资产配置流程、客户经理考核、隐私应急预案等内部管理章节不入库。 - 入库现状:fin_faq_collection 61 块、fin_product_collection 26 块、 fin_policy_collection 73 块,合计 160 块。检索自检 5/6——未命中的一条分数 0.660 落在中置信区间,按三档兜底策略本应提示信息可能不完整,属于预期行为。 二、embedding 端点 - 新增 tools/configure_embedding_endpoint.py:走管理 API(draft→approved→active) 配置并激活 qwen-embedding 端点,而不是直接写库。理由是状态机与审计都要留痕, 且 DatabaseModelGateway 只认 status='active',手工写错状态会报成与病因无关的 「模型端点未注册或未激活」。脚本先查 endpoint_code 是否已存在,幂等可重跑。 三、修复模型端点筛选缺陷(app/service/model_gateway.py) - 原 DatabaseModelEndpointResolver 忽略 agent_type 与 task_type、直接返回全部 active 端点,而 ModelDispatchService 只按顺序尝试前 max_attempts(默认 2)个。两者叠加使 「能否选到支持该任务的端点」取决于端点表顺序:实测每次 embedding 都先拿文本生成 端点失败一次再落到向量端点(0.61s,修复后 0.42s)。 - 新增 TASK_CAPABILITY 显式映射后按能力筛选。用映射而不是同名筛选是必需的: memory_extraction 并不是任何端点的能力名(deepseek 声明的是 text_generation 等), 按同名筛会得到空集、把记忆抽取打成失败关闭——这是本次修复最容易引入的回归。 - 保守兜底:未映射的 task_type、以及没有任何端点声明该能力时,都退回全部端点, 让配置缺口表现为调用失败,而不是让上层收到「解析为空」这种与病因无关的报错。 - 验证结果:embedding→[qwen-embedding]、intent_classification→[deepseek-flash]、 memory_extraction→[deepseek-flash]、未映射 task_type→全部;ruff 通过、 mypy 103 文件无错、unit+contract 447 passed。 四、需求文档提取物 - 新增 _flows/:三份流程文档(智能客服 Agent 专项设计方案、投资顾问流程、基金运营流程) 的纯文本提取,供开发期对照。原始 .docx/.html 保留在业务方目录侧。 说明:本次仅本地提交,未推送远程仓库。knowledge/ 内含公司内部制度与产品资料, 是否入远程库待确认。
1664 lines
73 KiB
HTML
1664 lines
73 KiB
HTML
智能客服Agent专项设计方案
|
||
目录
|
||
- 0. 文档说明与评审指引
|
||
- 0.1 定位与范围
|
||
- 0.2 依据文件(优先级从高到低)
|
||
- 0.3 合规基线速览(本文档反复引用)
|
||
- 0.4 评审关注点(读前提示)
|
||
- 1. 定位与设计目标
|
||
- 1.1 一句话定位
|
||
- 1.2 服务对象与边界
|
||
- 1.3 业务目标与度量指标
|
||
- 1.4 合规红线(客服 Agent 视角)
|
||
- 1.5 AI 接入前后的工作流程对比
|
||
- 2. 整体流程闭环
|
||
- 2.1 闭环流程图
|
||
- 2.2 七步拆解(与统一执行骨架对齐)
|
||
- 2.3 关键决策点:置信度与三档兜底
|
||
- 2.4 三集合路由代码与 Milvus 关键概念(带详细中文注释)
|
||
- 3. 细分流程(按意图类型)
|
||
- 3.0 意图体系总览(5 类)
|
||
- 3.1 账户与交易查询(持仓 / 收益 / 交易记录)
|
||
- 3.2 产品信息咨询(一般性介绍)
|
||
- 3.3 费率与收益计算
|
||
- 3.4 赎回与到账时效
|
||
- 3.5 投诉与情绪安抚
|
||
- 3.6 风险揭示与合规禁答
|
||
- 3.7 各意图数据流向图(汇总)
|
||
- 4. 知识库清单
|
||
- 4.1 三集合总览
|
||
- 4.2 字段结构(建议统一 schema)
|
||
- 4.3 更新频率与数据来源
|
||
- 4.4 知识运营机制
|
||
- 4.5 FAQ 示例(fin_faq_collection 内容样例)
|
||
- 5. 人工审核机制
|
||
- 5.1 转人工触发条件(分级)
|
||
- 5.2 事后复核与质检(兜底保障)
|
||
- 5.3 升级路径
|
||
- 5.4 国标对齐检查(《顾客联络服务 人工与智能客户服务协同要求》,2026-09-01 实施)
|
||
- 6. 回复策略
|
||
- 6.1 标准回复模板(分场景)
|
||
- 6.2 语气与措辞规范
|
||
- 6.3 必备风险提示与免责声明
|
||
- 6.4 拒答与兜底话术
|
||
- 6.5 负面词清单(7 个,零容忍)
|
||
- 7. 数据与状态流转 / 接口与异常
|
||
- 7.1 接口约定
|
||
- 7.2 会话状态机
|
||
- 7.3 异常处理表
|
||
- 8. 度量与验收(双轨评测)
|
||
- 9. 完善方向与最终交付要求
|
||
- 9.0 差距总览(一页速览)
|
||
- 9.1 功能完整性
|
||
- 9.2 用户体验
|
||
- 9.3 性能优化
|
||
- 9.4 错误处理
|
||
- 9.5 安全性
|
||
- 9.6 代码可读性
|
||
- 9.7 最终交付要求(上线门禁 Go/No-Go 清单)
|
||
- 10. 附录
|
||
- 10.1 参考来源清单
|
||
- 10.2 需核实项清单
|
||
文档:智能财富管家系统 · 智能客服 Agent 专项设计方案
|
||
版本:v1.3
|
||
日期:2026-09-07
|
||
状态:内部方案评审稿
|
||
智能客服 Agent 专项设计方案(内部评审稿)
|
||
0. 文档说明与评审指引
|
||
0.1 定位与范围
|
||
本文档是《智能财富管家系统-业务与技术全景设计说明书》中「智能客服 Agent」的深化专项设计,聚焦对外客户服务入口这一条链路(用户提问 → 意图识别 → 知识检索 → 答案生成 → 合规校验 → 人工转接 → 结果反馈),供内部方案评审使用。
|
||
范围边界:只覆盖智能客服 Agent(自然语言问答 / FAQ / 产品一般性介绍 / 政策解读 / 转人工)。投顾助手、风控监测、数据分析三个 Agent 不在本文档范围(详见全景说明书 §5.3/§5.4/§5.5)。
|
||
设计原则:所有结论都落到项目实际内容(意图、集合、阈值、表、接口),并对照主流平台真实落地做法标注来源;信息不确定处标注「需核实」,不做臆测。
|
||
0.2 依据文件(优先级从高到低)
|
||
优先级 |
|
||
文件 |
|
||
作用 |
|
||
1 |
|
||
需求文档-修改版.html |
|
||
需求基线:四大 Agent、合规定位(不代客交易)、分阶段需求、评分标准 |
|
||
2 |
|
||
统一执行骨架_七步模板方法与职责边界.html |
|
||
执行骨架权威:七步模板方法与基类/子类职责边界 |
|
||
3 |
|
||
功能设计文档.html(v1.5) |
|
||
意图体系、推理范式、工具清单(注意:其「业务操作 Agent / NL2API」已被修改版下线) |
|
||
4 |
|
||
智能财富管家系统-业务与技术全景设计说明书.md |
|
||
全景说明书:置信度阈值、三集合路由、数据表、架构 |
|
||
0.3 合规基线速览(本文档反复引用)
|
||
红线 |
|
||
要求 |
|
||
依据 |
|
||
不代客交易 |
|
||
AI 不得代替客户执行申购/赎回/转账,不触及资金与下单环节 |
|
||
需求文档-修改版合规定位 |
|
||
适当性 |
|
||
客户 C1-C5 ↔ 产品 R1-R5 匹配,取最严口径 |
|
||
全景说明书 A4 |
|
||
负面词 |
|
||
7 个零容忍:保本 / 稳赚 / 无风险 / 保证收益 / 预期收益率 / 年化收益率 / 安全 |
|
||
全景说明书 §14 |
|
||
免责声明 |
|
||
面向客户的输出末尾必须附固定话术 |
|
||
全景说明书 B3 |
|
||
留痕 |
|
||
客户沟通记录保存 ≥ 20 年 |
|
||
《证券基金经营机构信息技术管理办法》〔S15〕 |
|
||
0.4 评审关注点(读前提示)
|
||
- §2 闭环流程中「合规校验」是否应前置到「答案生成」之前(当前为生成后置校验 + 二次打回)。
|
||
- §3.3「费率与收益计算」的收益口径:字段名 expected_return,对外一律表述「业绩比较基准」并附测算依据,不得出现「预期收益率」。
|
||
- §5 转人工阈值(意图 0.6、RAG 混合判定)是否需按业务容忍度二次校准。
|
||
- §6 回复话术的「负面词 7 个」是否要扩充(如「零风险」「稳赚不赔」等变体)。
|
||
1. 定位与设计目标
|
||
1.1 一句话定位
|
||
智能客服 Agent 是系统的唯一对外自然语言入口,负责把「客户想问的」映射到「合规能答的」,答不了的果断转人工——价值在于 7×24 秒回标准问题,边界在于绝不越合规红线。
|
||
1.2 服务对象与边界
|
||
服务对象 |
|
||
可问范围 |
|
||
权限边界 |
|
||
游客(未登录) |
|
||
仅公开产品信息、一般性介绍 |
|
||
不涉及任何账户数据 |
|
||
零售客户 |
|
||
客服咨询、产品一般性介绍、政策解读、持仓查询、投资分析报告(带免责声明) |
|
||
仅本人资产与持仓 |
|
||
高净值客户 |
|
||
同上,且可触发「AI 初筛 + 投顾人工审核」闭环 |
|
||
仅本人数据 |
|
||
1.3 业务目标与度量指标
|
||
指标 |
|
||
目标值 |
|
||
度量来源 |
|
||
高频意图回答准确率 |
|
||
≥ 90% |
|
||
会话归档 + RAG 判定日志(对标天弘智能客服公开的 94%〔S6〕) |
|
||
首响时长 |
|
||
< 3 s |
|
||
埋点 |
|
||
转人工率 |
|
||
≤ 20% |
|
||
工单统计 |
|
||
违规话术次数 |
|
||
0(负面词后置校验拦截率 100%) |
|
||
后置校验日志 |
|
||
适当性违规 |
|
||
0 起 |
|
||
安全审计日志 |
|
||
1.4 合规红线(客服 Agent 视角)
|
||
客服 Agent 的职责是「信息提供」,因此它只读不写、只答不荐、只解释不下结论:
|
||
允许 |
|
||
不允许 |
|
||
产品一般性介绍、政策规则解读(适当性、反洗钱、销售办法) |
|
||
承诺收益、使用「保本/稳赚/安全/预期收益率」等违规表述 |
|
||
持仓/收益查询(本人数据) |
|
||
代客申购/赎回/转账 |
|
||
风险等级客观转述(R1-R5) |
|
||
主观评价「这个产品风险很低」 |
|
||
转人工引导 |
|
||
对低置信问题「硬答」 |
|
||
行业对标:华云天下提出的金融 AI 客服「十条铁律」首条即「不能承诺收益」——任何含「左右/大约/预期」的收益数字都不允许〔S13〕。招商基金总经理级口径强调「AI 不能代替信任、责任与人文关怀」〔S7〕,与本项目「不代客交易」定位同源。
|
||
1.5 AI 接入前后的工作流程对比
|
||
本节用两张流程对比图说明:AI 智能客服解决了什么问题、在哪些环节提升了效率。量化结论见 §1.3 度量指标。
|
||
1.5.1 未接入 AI 智能客服时的工作流程
|
||
flowchart TD
|
||
A[用户有问题] --> B[电话/邮件/线下网点求助]
|
||
B --> C[排队等待人工客服]
|
||
C --> D{人工客服在线?}
|
||
D -- 否(夜间/节假日) --> E[等到工作日再处理]
|
||
E --> C
|
||
D -- 是 --> F[人工逐条答疑口径依赖个人经验]
|
||
F --> G{能解决?}
|
||
G -- 是 --> H[解决 + 人工记录]
|
||
G -- 否 --> I[转资深客服/持证投顾]
|
||
I --> F
|
||
H --> J[人工汇总工单/报表易遗漏]
|
||
痛点:响应慢(排队 + 非工作时间无人值守)、口径不一致(靠个人经验)、重复劳动高、留痕靠人工易遗漏。
|
||
1.5.2 接入 AI 智能客服后的工作流程
|
||
flowchart TD
|
||
A[用户提问] --> B[AI 7×24 秒级响应]
|
||
B --> C{意图识别 + 置信度}
|
||
C -- 标准问题 --> D[三集合检索 → 秒回]
|
||
C -- 低置信/情绪/投诉 --> E[自动转人工携带完整上下文]
|
||
D --> F[合规校验负面词 + 免责声明]
|
||
F --> G[结果反馈 SSE]
|
||
E --> H[人工接管聚焦复杂问题]
|
||
G --> I[自动归档 + 脱敏留痕]
|
||
H --> I
|
||
1.5.3 两者对比(AI 解决了什么 / 在哪提升效率)
|
||
环节 |
|
||
未接入 AI 的痛点 |
|
||
接入 AI 后 |
|
||
效率提升 |
|
||
响应时效 |
|
||
排队等待、非工作时间无人 |
|
||
7×24 秒级响应标准问题 |
|
||
首响分钟级 → <3s |
|
||
标准问题 |
|
||
人工重复回答 |
|
||
FAQ 直返 / RAG 秒回 |
|
||
高频意图准确率 ≥90% |
|
||
口径一致性 |
|
||
各客服口径不一 |
|
||
统一话术 + 来源引用 |
|
||
违规话术 0 次 |
|
||
转人工 |
|
||
靠用户自己找入口 |
|
||
智能触发 + 上下文无缝传递 |
|
||
转人工率 ≤20%,且不复述 |
|
||
留痕与合规 |
|
||
人工整理、易遗漏 |
|
||
全量自动归档(脱敏) |
|
||
100% 留痕 ≥20 年 |
|
||
人力投入 |
|
||
大量重复劳动 |
|
||
人力聚焦复杂/高价值问题 |
|
||
释放约 40% 重复人力(对标天弘〔S6〕) |
|
||
2. 整体流程闭环
|
||
2.1 闭环流程图
|
||
flowchart TD
|
||
A[用户提问] --> B[① 输入接收与校验鉴权]
|
||
B --> B1{鉴权通过?}
|
||
B1 -- 否 --> B2[返回登录引导]
|
||
B1 -- 是 --> C[② 记忆召回读 Redis 短期会话]
|
||
C --> D[③ 意图路由qwen-turbo 分类·阈值0.6]
|
||
D --> D1{置信度>=0.6?}
|
||
D1 -- 否 --> D2[澄清提问]
|
||
D2 --> D3{仍无法识别?}
|
||
D3 -- 是 --> H[转人工]
|
||
D3 -- 否 --> D
|
||
D1 -- 是 --> E[④ 知识检索Milvus 三集合路由]
|
||
E --> F[⑤ 答案生成temperature 0.3 + 来源引用]
|
||
F --> G[⑥ 合规校验负面词后置 + 免责声明注入]
|
||
G --> G1{命中负面词?}
|
||
G1 -- 是 --> G2[打回重生成]
|
||
G2 --> G3{二次仍命中?}
|
||
G3 -- 是 --> G4[返回安全话术 + 告警]
|
||
G3 -- 否 --> F
|
||
G1 -- 否 --> I[⑦ 结果反馈 SSE 流式]
|
||
I --> J{需转人工?}
|
||
J -- 是 --> H
|
||
J -- 否 --> K[数据沉淀:写回会话+归档脱敏]
|
||
H --> K
|
||
K --> L[事件广播:命中敏感意图 publish suspicious_intent]
|
||
2.2 七步拆解(与统一执行骨架对齐)
|
||
步 |
|
||
名称 |
|
||
输入 → 输出 |
|
||
归属 |
|
||
① |
|
||
输入接收与校验鉴权 |
|
||
session_id/user_id/message → JWT 解析画像标签 |
|
||
基类 |
|
||
② |
|
||
记忆召回 |
|
||
读 Redis 会话(List,TTL 30min,最长 24h,超 4096 token 截断旧消息) |
|
||
基类 |
|
||
③ |
|
||
意图路由 |
|
||
消息 + 上下文 → 5 类意图 + 置信度(qwen-turbo,阈值 0.6) |
|
||
基类调用、子类给分类器 |
|
||
④ |
|
||
核心逻辑 |
|
||
意图 → Milvus 三集合检索 → 混合判定 |
|
||
子类唯一实现 |
|
||
⑤ |
|
||
结果生成 |
|
||
检索结果 → LLM 生成(temperature 0.3)+ 来源引用 + 负面词后置校验 + 免责声明注入 |
|
||
基类 |
|
||
⑥ |
|
||
数据沉淀 |
|
||
写回会话、归档(脱敏)、记录 tool_calls |
|
||
基类(强制) |
|
||
⑦ |
|
||
事件广播 |
|
||
命中转账/敏感意图 → publish event:suspicious_intent |
|
||
基类(强制) |
|
||
对齐说明:⑥⑦ 由基类强制,防止「某个 Agent 忘了归档/忘了广播」——这是合规留痕与跨 Agent 协作的结构性保障〔全景说明书 §8〕。
|
||
2.3 关键决策点:置信度与三档兜底
|
||
决策点 |
|
||
规则 |
|
||
结果 |
|
||
意图置信度 |
|
||
< 0.6 → 澄清提问;澄清后仍无法识别 → 转人工 |
|
||
不硬答 |
|
||
RAG 混合判定 |
|
||
绝对阈值 AND(相对间隙 OR 分布优势) |
|
||
三档:高/中/低 |
|
||
高置信 |
|
||
— |
|
||
直接回答 |
|
||
中置信 |
|
||
— |
|
||
回答 + 「以上信息可能不完整…」提示 |
|
||
低置信 |
|
||
— |
|
||
兜底话术 + 建议转人工 |
|
||
2.4 三集合路由代码与 Milvus 关键概念(带详细中文注释)
|
||
§2.1 的流程图里多次出现「Milvus 三集合路由」——这里给出对应工程模块(tool/milvus_tool.py,与本文档 §0.2 / §4.1 引用一致)的完整代码与逐行中文注释,方便评审时逐字段、逐分支核对。
|
||
阅读顺序:先看 2.4.1 概念与原理(建立直觉)→ 2.4.2 三级路由职责速查 → 2.4.3 Milvus 概念速查(扫盲)→ 2.4.4 完整代码(带注释的实现)→ 2.4.6 操作类查询规划。
|
||
2.4.1 三集合路由的概念与原理(先看懂,再看代码)
|
||
一句话定义:三集合路由 = 把知识库分成三个「书架」(FAQ / 产品 / 政策),用户每问一个问题,系统先判断「该去哪个/哪几个书架找」,再去对应书架里用「语义相似度」找出最相关的资料。
|
||
用一个图书馆的类比理解:
|
||
概念 |
|
||
图书馆类比 |
|
||
本项目 |
|
||
collection(集合) |
|
||
一个书架 |
|
||
fin_faq / fin_product / fin_policy |
|
||
路由(routing) |
|
||
图书管理员判断「去哪个书架找」 |
|
||
_step1_route_intent |
|
||
向量检索 |
|
||
按「内容像不像」找书,而非按书名精确匹配 |
|
||
_step2_search_in_collection |
|
||
重排 |
|
||
从几个书架找到的书里挑最好的几本 |
|
||
_step3_rerank_and_merge |
|
||
为什么需要「路由」而不是一个大集合:如果把 FAQ、产品、政策全塞进一个大集合,问「赎回费怎么算」时,可能把「反洗钱政策」也检索出来,干扰答案。分成三个集合 + 路由,等于先缩小查找范围,再精确定位——既快又准。
|
||
原理(三步走):
|
||
flowchart LR
|
||
A[用户问题] --> B[意图识别这是哪类问题?]
|
||
B --> C{路由决策去哪个集合}
|
||
C -- 高频问答 --> D1[fin_faq_collection]
|
||
C -- 产品咨询 --> D2[fin_product_collection]
|
||
C -- 政策解读 --> D3[fin_policy_collection]
|
||
D1 & D2 & D3 --> E[集合内向量检索找最相关的 topK 条]
|
||
E --> F[重排去重输出最终素材]
|
||
注意:这里的「路由」不是网络路由器(转发数据包),而是「按问题类型分发检索目标」的业务路由——两者同名,含义完全不同。
|
||
2.4.2 三级路由职责速查表
|
||
级别 |
|
||
名称 |
|
||
干啥的 |
|
||
代码位置 |
|
||
第一级 |
|
||
意图路由 |
|
||
决定「去哪个/哪几个集合」找资料 |
|
||
ThreeCollectionRouter._step1_route_intent |
|
||
第二级 |
|
||
集合内检索 |
|
||
在选定的集合里做「向量相似度」搜索,取 topK 条 |
|
||
ThreeCollectionRouter._step2_search_in_collection |
|
||
第三级 |
|
||
重排与融合 |
|
||
把多个集合的候选合并、去重、排序,输出最终素材 |
|
||
ThreeCollectionRouter._step3_rerank_and_merge |
|
||
「三集合路由」= 在三个 Milvus collection(fin_faq / fin_product / fin_policy)之间,按用户意图选集合,再在集合内做向量检索。写入路径另有 upsert_document(往集合里塞新数据/更新旧数据)。
|
||
2.4.3 Milvus 关键概念速查(给不熟悉 Milvus 的同学)
|
||
概念 |
|
||
一句话解释 |
|
||
在本项目对应 |
|
||
collection 集合 |
|
||
Milvus 的「表」,存一类数据 |
|
||
三个:fin_faq / fin_product / fin_policy |
|
||
field 字段 |
|
||
表里的列 |
|
||
id、content、embedding、effective_date 等 |
|
||
primary key 主键 |
|
||
唯一标识一条记录的字段 |
|
||
每个集合的 id 字段(VARCHAR,max=64) |
|
||
vector field 向量字段 |
|
||
专门存 embedding(文本的数学表示)的字段,类型 FLOAT_VECTOR |
|
||
每个集合的 embedding 字段,维度 VECTOR_DIM |
|
||
scalar field 标量字段 |
|
||
非向量的普通字段(VARCHAR/INT 等),可用于精确过滤 |
|
||
tags、product_code、effective_date、expire_date |
|
||
partition 分区 |
|
||
集合内部的「子文件夹」,可按维度划分,搜索时指定分区可大幅缩范围 |
|
||
产品集合按 product_type 分区(股票/混合/债券) |
|
||
segment 段 |
|
||
Milvus 内部把数据切成的小块(存盘+加载的单位),对使用者透明 |
|
||
Milvus 自动管理 |
|
||
index 索引 |
|
||
为向量字段建的「加速查找结构」,常见 IVF_FLAT、HNSW、DISKANN |
|
||
本项目用 IVF_FLAT(nlist=128) |
|
||
ANN |
|
||
Approximate Nearest Neighbor 近似最近邻——速度极快、不要求 100% 精确 |
|
||
搜索的本质目标 |
|
||
topK |
|
||
返回相似度最高的 K 条记录 |
|
||
FAQ 取 3、其余取 5 |
|
||
metric_type 距离度量 |
|
||
向量之间「像不像」的衡量方式 |
|
||
COSINE 余弦相似度(最常用,忽略长度只看方向) |
|
||
hybrid search 混合检索 |
|
||
向量检索 + 标量过滤(如过期过滤) |
|
||
用 expr 参数实现 |
|
||
load() |
|
||
把集合从磁盘加载到内存(不加载不能搜,Milvus 是「磁盘省钱、内存才快」) |
|
||
初始化时调用一次 |
|
||
upsert |
|
||
插入或更新(Milvus 用 upsert,不用 insert+update) |
|
||
写入路径统一用 upsert |
|
||
flush |
|
||
把内存里的数据真正落盘+建索引(较重,高频写可批量) |
|
||
写入后调用 |
|
||
2.4.4 完整代码(带详细中文注释)
|
||
下面的代码可直接保存为 tool/milvus_tool.py。每一行 # 后的中文注释都对不熟悉 Milvus / RAG 的读者做了通俗解释。
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
milvus_tool.py
|
||
==============
|
||
智能客服 Agent 的「三集合路由」核心模块。
|
||
【三集合路由的定义和作用】
|
||
智能客服 Agent 回答用户问题时,需要先在「知识库」里找到相关资料。
|
||
我们的知识库分成了三个独立的 Milvus 集合(你可以理解为三张表),
|
||
分别存放不同类型的资料:
|
||
1. fin_faq_collection —— 高频操作类问答(如「怎么重置密码」)
|
||
2. fin_product_collection —— 产品说明书、费率、风险等级
|
||
3. fin_policy_collection —— 监管法规、适当性规则、销售办法
|
||
为什么要分三个?
|
||
- 不同类型资料的「更新频率」不同(FAQ 周更,政策即时更新)
|
||
- 不同意图需要「检索不同的内容」(问政策就不该去产品表里找)
|
||
- 权限/审计可以按集合隔离
|
||
【三级路由的协作流程】
|
||
用户提问 → 意图分类器判定意图 →
|
||
第一级(意图路由)→ 决定去哪些集合 →
|
||
第二级(集合内检索)→ 每个集合内做向量相似度搜索 →
|
||
第三级(重排与融合)→ 合并多集合候选 → 输出最终 topK
|
||
写入路径(更新/新增知识):upsert_document() → 写入对应 collection
|
||
"""
|
||
from __future__ import annotations # 让类型注解在 Python 3.9 之前也能用
|
||
import time # 用 time.strftime 取当前日期
|
||
import hashlib # 用 md5 做内容指纹(去重用)
|
||
from typing import List, Dict, Any, Optional # 类型注解,让代码可读性更强
|
||
from dataclasses import dataclass # 用装饰器快速定义「数据类」(只装数据的简单类)
|
||
# 下面这行是 pymilvus 的官方 SDK(Milvus 团队维护的 Python 客户端)。
|
||
# 如果项目里还没装,请先:pip install pymilvus
|
||
from pymilvus import (
|
||
connections, # 用来连 Milvus 服务(建立网络连接)
|
||
Collection, # 集合对象 = 指向 Milvus 某张「表」的引用
|
||
CollectionSchema, # 集合的「表结构」= 字段列表
|
||
FieldSchema, # 单个字段的「列定义」
|
||
DataType, # 字段类型枚举:VARCHAR / FLOAT_VECTOR / INT64 ...
|
||
utility, # 工具函数:判断 collection 是否存在、查版本等
|
||
)
|
||
# ============================================================
|
||
# 一、常量区:把会变的配置集中起来(单一真相源)
|
||
# ============================================================
|
||
# 「单一真相源」= 全项目只有这一处定义这个值,避免散落各处导致改一处忘改另一处。
|
||
# 比如你想把端口换了,只改这里就行。
|
||
# Milvus 服务器地址。本地开发用 standalone,集群用 cluster 地址。
|
||
MILVUS_HOST = "127.0.0.1"
|
||
MILVUS_PORT = "19530"
|
||
# 三个集合的名字(= 三张「表」)。改这里就够了,不要在代码里到处写字符串字面量。
|
||
FAQ_COLLECTION = "fin_faq_collection"
|
||
PRODUCT_COLLECTION = "fin_product_collection"
|
||
POLICY_COLLECTION = "fin_policy_collection"
|
||
ALL_COLLECTION_NAMES = [FAQ_COLLECTION, PRODUCT_COLLECTION, POLICY_COLLECTION]
|
||
# 意图 → 集合 的映射关系(= 「第一级路由」的配置表)
|
||
# 含义:用户问的是这个意图时,去这几个集合里找资料。
|
||
# 用列表而不是单个字符串,是允许「一个意图跨多个集合」的灵活设计。
|
||
INTENT_TO_COLLECTIONS: Dict[str, List[str]] = {
|
||
"faq": [FAQ_COLLECTION], # 高频问题只在 FAQ 表
|
||
"product_inquiry": [PRODUCT_COLLECTION], # 产品咨询去产品表
|
||
"policy_explain": [POLICY_COLLECTION], # 政策解读去政策表
|
||
"chitchat": [], # 闲聊不去任何表(走 LLM 自由生成)
|
||
"transfer_human": [], # 转人工也不查表
|
||
# 兜底:意图分类没把握时,三个集合都查,取并集后再重排
|
||
"low_confidence": [FAQ_COLLECTION, PRODUCT_COLLECTION, POLICY_COLLECTION],
|
||
}
|
||
# 每个集合检索时取 topK 条候选。TopK 越大召回越全但越慢。
|
||
COLLECTION_TOPK: Dict[str, int] = {
|
||
FAQ_COLLECTION: 3, # FAQ 是「一问一答」标准答案,取 3 条够用
|
||
PRODUCT_COLLECTION: 5, # 产品信息需要更多候选让 LLM 选
|
||
POLICY_COLLECTION: 5, # 政策条款同理
|
||
}
|
||
# 向量维度。要和 embedding 模型输出一致(如 Qwen Embedding 通常 1024 或 1536 维)。
|
||
# 「维度」= 一个向量有多少个数字。文本越长不一定维度越高,是模型决定的。
|
||
VECTOR_DIM = 1024
|
||
# 向量距离度量。COSINE = 余弦相似度(最常用,忽略向量长度只看方向)。
|
||
# 选 COSINE 而不是 L2(欧氏距离),因为我们关心「语义方向」而不是「距离绝对值」。
|
||
METRIC_TYPE = "COSINE"
|
||
# ============================================================
|
||
# 二、三个集合的「表结构」定义(schema)
|
||
# ============================================================
|
||
# Milvus 的 schema 就像建表语句:定义有哪些字段、各自什么类型。
|
||
# 用工厂函数(返回对象的函数)生成,方便统一管理 + 后续扩展。
|
||
def build_faq_schema() -> CollectionSchema:
|
||
"""FAQ 集合的表结构:高频问答。"""
|
||
fields = [
|
||
# 主键:用 md5(question) 做主键,保证同一问题只有一条记录
|
||
FieldSchema("id", DataType.VARCHAR, max_length=64, is_primary=True),
|
||
FieldSchema("question", DataType.VARCHAR, max_length=512), # 标准问法
|
||
FieldSchema("answer", DataType.VARCHAR, max_length=2048), # 标准答案
|
||
FieldSchema("tags", DataType.VARCHAR, max_length=256), # 标签,JSON 字符串
|
||
FieldSchema("effective_date",DataType.VARCHAR, max_length=32), # 生效日期 "2026-01-01"
|
||
FieldSchema("expire_date", DataType.VARCHAR, max_length=32), # 失效日期,空=长期有效
|
||
# 向量字段:把 question 文本转成 embedding 存进来
|
||
FieldSchema("embedding", DataType.FLOAT_VECTOR, dim=VECTOR_DIM),
|
||
]
|
||
# description 给这个集合写个说明,方便在 Milvus 管理界面(Attu)里辨认
|
||
return CollectionSchema(fields, description="FAQ 集合:高频操作类问答")
|
||
def build_product_schema() -> CollectionSchema:
|
||
"""产品集合的表结构:产品说明书/费率/风险等级。"""
|
||
fields = [
|
||
FieldSchema("id", DataType.VARCHAR, max_length=64, is_primary=True),
|
||
FieldSchema("product_code", DataType.VARCHAR, max_length=32), # 产品代码,如 "000001"
|
||
FieldSchema("product_name", DataType.VARCHAR, max_length=128),
|
||
FieldSchema("product_type", DataType.VARCHAR, max_length=32), # 股票/混合/债券
|
||
FieldSchema("risk_level", DataType.VARCHAR, max_length=8), # R1-R5
|
||
FieldSchema("content", DataType.VARCHAR, max_length=4096), # 文档片段原文
|
||
FieldSchema("source_url", DataType.VARCHAR, max_length=512),
|
||
FieldSchema("effective_date", DataType.VARCHAR, max_length=32),
|
||
FieldSchema("embedding", DataType.FLOAT_VECTOR, dim=VECTOR_DIM),
|
||
]
|
||
return CollectionSchema(fields, description="产品集合:产品说明书/费率/风险")
|
||
def build_policy_schema() -> CollectionSchema:
|
||
"""政策集合的表结构:监管法规/适当性/销售办法。"""
|
||
fields = [
|
||
FieldSchema("id", DataType.VARCHAR, max_length=64, is_primary=True),
|
||
FieldSchema("doc_id", DataType.VARCHAR, max_length=64), # 法规文号,如 "银发2016261号"
|
||
FieldSchema("title", DataType.VARCHAR, max_length=256),
|
||
FieldSchema("content", DataType.VARCHAR, max_length=4096),
|
||
FieldSchema("issuing_org", DataType.VARCHAR, max_length=64), # 发文机构:银保监会 / 证监会 ...
|
||
FieldSchema("effective_date", DataType.VARCHAR, max_length=32),
|
||
FieldSchema("embedding", DataType.FLOAT_VECTOR, dim=VECTOR_DIM),
|
||
]
|
||
return CollectionSchema(fields, description="政策集合:监管法规/适当性/销售办法")
|
||
# schema 字典:集合名 → schema 工厂函数
|
||
SCHEMA_FACTORIES: Dict[str, Any] = {
|
||
FAQ_COLLECTION: build_faq_schema,
|
||
PRODUCT_COLLECTION: build_product_schema,
|
||
POLICY_COLLECTION: build_policy_schema,
|
||
}
|
||
# ============================================================
|
||
# 三、检索结果的数据类(让返回值有结构,方便后续处理)
|
||
# ============================================================
|
||
# @dataclass 装饰器自动生成 __init__ / __repr__ / __eq__ 等方法,代码更短。
|
||
@dataclass
|
||
class Hit:
|
||
"""单条检索命中结果。相当于「一行记录」+「相似度分」。"""
|
||
collection: str # 来自哪个集合
|
||
doc_id: str # 该集合内的主键
|
||
score: float # 相似度分(越大越像;范围约 0~1)
|
||
content: str # 文本内容(问题/答案/产品/政策原文)
|
||
metadata: Dict[str, Any] # 其他元数据(tags、product_code、raw_distance 等)
|
||
@dataclass
|
||
class RoutePlan:
|
||
"""第一级路由的输出:决定去哪些集合、每个取多少条。"""
|
||
target_collections: List[str] # 要去查的集合名列表
|
||
per_collection_topk: Dict[str, int] # 每个集合分别取 topK
|
||
# ============================================================
|
||
# 四、三集合路由器(核心类)
|
||
# ============================================================
|
||
class ThreeCollectionRouter:
|
||
"""
|
||
三集合路由器的核心类。
|
||
一个实例 = 一个 Milvus 客户端连接。
|
||
设计要点:复用连接、缓存 collection 句柄,避免每次检索都重新建连。
|
||
"""
|
||
def __init__(self, host: str = MILVUS_HOST, port: str = MILVUS_PORT):
|
||
# 1. 连 Milvus 服务
|
||
self._connect(host, port)
|
||
# 2. 懒加载:集合句柄按需获取(用 self.get_collection(name) 时才建),
|
||
# 避免启动时全部加载占内存。
|
||
# 「懒加载」= 用到时再加载,不用时不加载。
|
||
self._collection_cache: Dict[str, Collection] = {}
|
||
# ---------- 连接与初始化 ----------
|
||
def _connect(self, host: str, port: str) -> None:
|
||
"""连 Milvus 服务。alias='default' 是默认连接名,后面 Collection 用默认连接。"""
|
||
connections.connect(alias="default", host=host, port=port)
|
||
# 用一个轻量调用确认连接成功(get_server_version() 返回类似 "v2.4.x")
|
||
if not utility.get_server_version():
|
||
# 抛异常而不是静默失败,让上层知道出问题了
|
||
raise RuntimeError(f"无法连接 Milvus 服务 {host}:{port}")
|
||
def get_collection(self, name: str) -> Collection:
|
||
"""
|
||
获取一个集合的句柄(带缓存)。
|
||
Collection 对象 = 指向 Milvus 某张「表」的引用,搜索/写入都要用它。
|
||
缓存的好处:第二次拿同一集合不用重新查 Milvus,省一次 RPC。
|
||
"""
|
||
# 第一步:先看缓存里有没有
|
||
if name not in self._collection_cache:
|
||
# 第二步:缓存里没有,检查 Milvus 里有没有这个集合
|
||
if not utility.has_collection(name):
|
||
# 都没有 → 首次启动,自动建表
|
||
self._create_collection(name)
|
||
# 第三步:拿句柄
|
||
coll = Collection(name)
|
||
# 第四步:load() 把集合从磁盘加载到内存,否则搜索会报「collection not loaded」
|
||
# Milvus 是「磁盘省钱、内存才快」的设计,不 load 就搜不了
|
||
coll.load()
|
||
# 放进缓存,下次直接拿
|
||
self._collection_cache[name] = coll
|
||
return self._collection_cache[name]
|
||
def _create_collection(self, name: str) -> None:
|
||
"""首次启动时创建集合(建表 + 建索引)。"""
|
||
# 用 schema 工厂拿表结构定义
|
||
schema = SCHEMA_FACTORIES[name]()
|
||
# 创建集合:参数 = 名字 + 表结构
|
||
Collection(name, schema=schema)
|
||
# 为向量字段建索引。
|
||
# IVF_FLAT:常用索引,简单稳;HNSW 更精确但费内存;DISKANN 大数据量省钱。
|
||
coll = Collection(name)
|
||
coll.create_index(
|
||
field_name="embedding", # 给哪个字段建索引
|
||
index_params={
|
||
"metric_type": METRIC_TYPE, # 用余弦相似度(前面常量区定义的)
|
||
"index_type": "IVF_FLAT",
|
||
# nlist:聚类中心数。越大越精确但越慢;128 是常用起点
|
||
"params": {"nlist": 128},
|
||
},
|
||
)
|
||
# ---------- 写入路径:往集合里塞一条新数据 / 更新旧数据 ----------
|
||
def upsert_document(
|
||
self,
|
||
collection_name: str,
|
||
doc_id: str,
|
||
text: str,
|
||
embedding: List[float],
|
||
extra_fields: Optional[Dict[str, Any]] = None,
|
||
) -> None:
|
||
"""
|
||
写入或更新一条文档。
|
||
调用时机:运营上传新知识、费率变更、政策更新时。
|
||
参数:
|
||
collection_name: 写入哪个集合(faq / product / policy)
|
||
doc_id: 主键(自己生成,md5 或雪花 ID 都行)
|
||
text: 文本内容
|
||
embedding: text 转成的向量(调用 embedding 模型得到)
|
||
extra_fields: 其他字段(product_code、effective_date 等)
|
||
"""
|
||
coll = self.get_collection(collection_name)
|
||
# 拼装一行数据。Milvus 要求:字段顺序和 schema 一致,且值都是列表(支持批量)。
|
||
row = {
|
||
"id": [doc_id],
|
||
"content": [text], # 简化:假设 schema 都有 content 字段
|
||
"embedding": [embedding],
|
||
}
|
||
# 把额外字段补进去(如果 schema 里有)
|
||
if extra_fields:
|
||
for k, v in extra_fields.items():
|
||
row[k] = [v]
|
||
# upsert:存在则更新、不存在则插入(Milvus 用 upsert 统一两种操作)
|
||
coll.upsert(row)
|
||
# 写完刷一下:让数据立即可见、开始建索引。
|
||
# flush 较重(要写盘),高频写入场景可以用 flush_buffer 批量刷
|
||
coll.flush()
|
||
# ---------- 读取路径:三集合路由的核心(对外入口) ----------
|
||
def query(
|
||
self,
|
||
intent: str,
|
||
query_text: str,
|
||
query_embedding: List[float],
|
||
user_id: Optional[int] = None,
|
||
partition_name: Optional[str] = None,
|
||
) -> List[Hit]:
|
||
"""
|
||
三级路由的对外入口——客服 Agent 每次问问题,都会调这个函数。
|
||
参数:
|
||
intent: 意图分类的结果("faq" / "product_inquiry" / "policy_explain" / "low_confidence")
|
||
query_text: 用户的原始问题(中文)
|
||
query_embedding: query_text 经 embedding 模型转成的向量(长度 = VECTOR_DIM)
|
||
user_id: 当前用户 ID(用于权限过滤,例如只让看本人产品)
|
||
partition_name: 可选——只查某个分区的数据(产品集合可按 product_type 分区)
|
||
返回:经过三级路由后的候选结果列表(已重排)
|
||
"""
|
||
# ============ 第一级:意图路由 → 决定去哪些集合 ============
|
||
route_plan = self._step1_route_intent(intent, query_text)
|
||
# 如果第一级判定不需要查表(闲聊/转人工),直接返回空,让上层走别的逻辑
|
||
if not route_plan.target_collections:
|
||
return []
|
||
# ============ 第二级:集合内检索(多集合可并行)============
|
||
# 多个集合之间没有依赖关系,可以并行查。这里用 for 串行演示;真实环境用线程池/异步并行。
|
||
per_collection_hits: Dict[str, List[Hit]] = {}
|
||
for coll_name in route_plan.target_collections:
|
||
# 每个集合的 topK 从 route_plan 里取
|
||
topk = route_plan.per_collection_topk.get(coll_name, 3)
|
||
hits = self._step2_search_in_collection(
|
||
coll_name=coll_name,
|
||
query_embedding=query_embedding,
|
||
top_k=topk,
|
||
user_id=user_id,
|
||
partition_name=partition_name,
|
||
)
|
||
per_collection_hits[coll_name] = hits
|
||
# ============ 第三级:重排与融合 → 输出最终 topK ============
|
||
final_hits = self._step3_rerank_and_merge(
|
||
per_collection_hits=per_collection_hits,
|
||
query_text=query_text,
|
||
)
|
||
return final_hits
|
||
# ---------- 第一级:意图路由 ----------
|
||
def _step1_route_intent(self, intent: str, query_text: str) -> RoutePlan:
|
||
"""
|
||
第一级路由:决定去哪些集合、每个集合取多少条。
|
||
决策逻辑:
|
||
1. 查 INTENT_TO_COLLECTIONS 配置,看该意图默认对应哪些集合
|
||
2. 如果意图不在配置里 → 走兜底:三个集合都查
|
||
"""
|
||
# 默认配置里的目标集合列表
|
||
targets = INTENT_TO_COLLECTIONS.get(intent)
|
||
# 没找到意图配置 → 兜底:全集合都查
|
||
if targets is None:
|
||
# list() 是为了拷贝一份,避免后面修改时改了常量
|
||
targets = list(ALL_COLLECTION_NAMES)
|
||
# 构造每个集合的 topK 配置
|
||
per_topk: Dict[str, int] = {}
|
||
for name in targets:
|
||
# 用 COLLECTION_TOPK 配置表查每个集合的 topK,找不到默认 3
|
||
per_topk[name] = COLLECTION_TOPK.get(name, 3)
|
||
return RoutePlan(target_collections=targets, per_collection_topk=per_topk)
|
||
# ---------- 第二级:集合内检索 ----------
|
||
def _step2_search_in_collection(
|
||
self,
|
||
coll_name: str,
|
||
query_embedding: List[float],
|
||
top_k: int,
|
||
user_id: Optional[int],
|
||
partition_name: Optional[str] = None,
|
||
) -> List[Hit]:
|
||
"""
|
||
第二级路由:在某一个集合内做「向量相似度检索」。
|
||
流程:
|
||
1. 构造过滤表达式(标量过滤,比如「未过期」+「本人可见」)
|
||
2. 用 embedding 向量在该集合内搜 topK(向量化最近邻)
|
||
3. 把 Milvus 返回的原始结果转成 Hit 对象
|
||
"""
|
||
coll = self.get_collection(coll_name)
|
||
# 构造标量过滤条件(标量 = 非向量字段)。
|
||
# 语法类似 SQL 的 WHERE 条件。含义:expire_date 为空 OR expire_date > 今天
|
||
today = time.strftime("%Y-%m-%d")
|
||
expr = f'expire_date == "" OR expire_date > "{today}"'
|
||
# 产品集合再加权限过滤(简化示意:实际应该是 OR 公开产品 OR user_id 匹配)
|
||
if coll_name == PRODUCT_COLLECTION and user_id is not None:
|
||
expr += f' OR user_id == "{user_id}"'
|
||
# 向量搜索参数:
|
||
# nprobe:搜索时探查的聚类数,越大越精确但越慢
|
||
search_params = {"metric_type": METRIC_TYPE, "params": {"nprobe": 16}}
|
||
# 准备 partition_names 参数(如果指定了 partition)
|
||
# 搜索时指定 partition 可以大幅缩范围、提速
|
||
search_kwargs: Dict[str, Any] = {
|
||
"data": [query_embedding], # 查询向量,外层是列表因为支持一次查多个
|
||
"anns_field": "embedding", # 用哪个向量字段做相似度
|
||
"param": search_params, # 搜索参数
|
||
"limit": top_k, # topK
|
||
"expr": expr, # 标量过滤条件
|
||
"output_fields": ["id", "content"], # 这些标量字段也一起返回
|
||
}
|
||
if partition_name:
|
||
# 如果传了分区名,只在该分区内搜(产品集合可按 product_type 分区)
|
||
search_kwargs["partition_names"] = [partition_name]
|
||
# 执行搜索(anns_field = Approximate Nearest Neighbor Search field)
|
||
results = coll.search(**search_kwargs)
|
||
# 解析结果。results 是 [[Hit1, Hit2, ...]],外层列表是因为可以一次查多个 query
|
||
hits: List[Hit] = []
|
||
for query_hits in results:
|
||
for h in query_hits:
|
||
# h.distance 是距离(越小越像)。
|
||
# 余弦距离 = 1 - 余弦相似度,所以 score = 1 - distance
|
||
# 我们把 score 统一成「越大越像」,方便后续按相似度排序
|
||
score = 1.0 - h.distance
|
||
hits.append(Hit(
|
||
collection=coll_name,
|
||
doc_id=h.id, # 命中记录的主键
|
||
score=score, # 相似度分(0~1)
|
||
content=h.entity.get("content", ""), # 命中的文本内容
|
||
metadata={"raw_distance": h.distance}, # 保留原始距离方便排查
|
||
))
|
||
return hits
|
||
# ---------- 第三级:重排与融合 ----------
|
||
def _step3_rerank_and_merge(
|
||
self,
|
||
per_collection_hits: Dict[str, List[Hit]],
|
||
query_text: str,
|
||
) -> List[Hit]:
|
||
"""
|
||
第三级路由:把多个集合的结果合并、重排、去重。
|
||
策略:
|
||
1. 把所有集合的 hits 合并到一个列表
|
||
2. 按 score 降序排(相似度高的排前面)
|
||
3. 简单去重:内容前 100 字相同的视为重复
|
||
4. 截断到最终 topK(这里固定 5,可改为参数)
|
||
进阶:可以接 rerank 模型(如 bge-reranker)做精排,这里简化。
|
||
"""
|
||
# 1. 合并:把所有集合的 hits 展平成一个列表
|
||
all_hits: List[Hit] = []
|
||
for hits in per_collection_hits.values():
|
||
all_hits.extend(hits)
|
||
# 2. 按相似度分数降序排(lambda h: h.score 意思是「取 h 的 score 作为排序键」)
|
||
all_hits.sort(key=lambda h: h.score, reverse=True)
|
||
# 3. 去重(用内容前 100 字符的 md5 做指纹)
|
||
# 为什么要去重?因为同一个答案可能在 FAQ 和产品集合里都出现(比如关于费率的说明)
|
||
seen: set = set()
|
||
deduped: List[Hit] = []
|
||
for h in all_hits:
|
||
# md5(内容前100字符) → 32 位字符串指纹
|
||
fingerprint = hashlib.md5(h.content[:100].encode("utf-8")).hexdigest()
|
||
if fingerprint in seen:
|
||
continue # 见过这个指纹了,跳过
|
||
seen.add(fingerprint)
|
||
deduped.append(h)
|
||
# 4. 截断到最终 topK(这里固定 5,可改为参数或按业务调整)
|
||
return deduped[:5]
|
||
# ============================================================
|
||
# 五、demo:怎么用
|
||
# ============================================================
|
||
# 下面是「怎么调用路由器」的最小示例。
|
||
# 真实 Agent 里:query_text 先送意图分类器(拿到 intent),再送 embedding 模型(拿到 vector),
|
||
# 然后调 router.query(...) 拿到候选 hits。
|
||
if __name__ == "__main__":
|
||
# 1. 初始化路由器(自动连 Milvus、自动建表、自动 load)
|
||
router = ThreeCollectionRouter()
|
||
# 2. 模拟一次用户提问
|
||
intent = "policy_explain" # 假设意图分类器说这是「政策解读」
|
||
query_text = "什么叫冷静期?" # 用户原话
|
||
# 真实场景:query_embedding = embedding_model.encode(query_text)
|
||
# 这里用全 0 占位(真实项目必须用真实 embedding,否则相似度全为 0)
|
||
query_embedding = [0.0] * VECTOR_DIM
|
||
# 3. 三级路由检索
|
||
hits = router.query(
|
||
intent=intent,
|
||
query_text=query_text,
|
||
query_embedding=query_embedding,
|
||
user_id=10001, # 当前用户 ID(用于权限过滤)
|
||
# partition_name="股票型", # 可选:只查「股票型」分区
|
||
)
|
||
# 4. 打印结果
|
||
for i, h in enumerate(hits, 1):
|
||
print(f"[{i}] 集合={h.collection} 分数={h.score:.3f}")
|
||
print(f" 内容: {h.content[:80]}...")
|
||
print()
|
||
2.4.5 三级路由协作时序(一句话总结)
|
||
用户提问 ──► 意图分类 ──► 第一级(去哪个集合)
|
||
──► 第二级(在集合里找 topK)
|
||
──► 第三级(合并去重排序)
|
||
──► 交给 LLM 生成最终回复
|
||
写入路径(与读取路径独立):upsert_document() 在运营/系统需要更新知识时调用,往指定 collection 塞一条或更新一条;不参与用户问问题的实时链路。
|
||
2.4.6 操作类查询的现状与实施规划(客户端未完成,暂缓)
|
||
本节回应一个常见疑问:为什么现在只有三个「知识类」集合,没有「操作类」(账户/持仓/交易查询)的向量库?
|
||
关键澄清:知识类 vs 操作类
|
||
类型 |
|
||
问的是 |
|
||
数据来源 |
|
||
是否依赖客户端 |
|
||
现在能做吗 |
|
||
知识类(当前三集合) |
|
||
「赎回费怎么算」「适当性规则是什么」 |
|
||
文档(说明书/法规/FAQ) |
|
||
否(文档已有) |
|
||
✅ 现在就能做 |
|
||
操作类(账户/持仓/交易查询) |
|
||
「我的持仓有哪些」「这笔交易到账了吗」 |
|
||
客户真实账户数据(账户系统) |
|
||
是(需客户端 + 账户系统产生数据) |
|
||
❌ 暂缓 |
|
||
为什么操作类暂缓:操作类查询需要「客户真实账户数据」,而这些数据由客户端(前端)+ 账户系统在真实业务运行中产生。客户端尚未开发完成,账户数据还不存在,因此现在无法实现操作类查询。
|
||
重要修正——操作类本质不是「向量库」:账户/持仓/交易查询是「精确查询」(查"我"的具体持仓、具体流水),应当通过 API 直连账户系统实现,而不是用向量库做「语义模糊检索」。向量库只服务于「答案散落在文档里的知识问答」。因此:
|
||
- 知识类(FAQ/产品/政策)→ 用 Milvus 向量库(本项目三集合)
|
||
- 操作类(账户/持仓/交易)→ 用账户系统 API 精确查询(待客户端完成后接入)
|
||
分阶段规划:
|
||
阶段 |
|
||
前置条件 |
|
||
实施内容 |
|
||
Phase 1(当前) |
|
||
仅需文档(已具备) |
|
||
知识类三集合路由(FAQ/产品/政策)+ RAG 问答 |
|
||
Phase 2(客户端完成后) |
|
||
客户端 + 账户系统 + 真实账户数据 |
|
||
接入操作类查询:账户/持仓/交易记录 API,与知识类问答打通(如「我的 XX 基金盈亏」→ API 查数) |
|
||
Phase 3(可选,视需求) |
|
||
操作类 API 稳定后 |
|
||
若出现「操作指引类」高频问题(如"如何修改定投"),可沉淀进 FAQ 知识类集合,不必新建操作类向量库 |
|
||
对 §3.1 账户与交易查询的影响:§3.1 将该场景列入 6 条业务路径,但当前其「可答范围」需标注为「Phase 2 待实现」——现阶段用户问账户问题,客服应引导转人工或说明「该功能即将上线」。
|
||
3. 细分流程(按意图类型)
|
||
3.0 意图体系总览(5 类)
|
||
意图 |
|
||
集合 |
|
||
TopK |
|
||
处理方式 |
|
||
product_inquiry 产品信息咨询 |
|
||
fin_product_collection |
|
||
5 |
|
||
RAG + LLM 生成 + 来源引用 |
|
||
policy_explain 政策解读 |
|
||
fin_policy_collection |
|
||
5 |
|
||
RAG + 解释 |
|
||
faq 常见问题 |
|
||
fin_faq_collection |
|
||
3 |
|
||
检索直返标准答案 |
|
||
chitchat 闲聊 |
|
||
— |
|
||
LLM 引导业务话题 |
|
||
transfer_human 转人工 |
|
||
— |
|
||
转接消息 + 生成转接工单 |
|
||
本方案将 5 类「技术意图」按业务场景细分为 6 条处理路径(§3.1-§3.6),便于评审时逐条核对;faq/product_inquiry/policy_explain 是检索主路径,chitchat/transfer_human 是控制路径。
|
||
3.1 账户与交易查询(持仓 / 收益 / 交易记录)
|
||
维度 |
|
||
内容 |
|
||
判定条件 |
|
||
命中「持仓、收益、交易记录、份额、到账」等实体 + 本人数据查询意图 |
|
||
可答范围 |
|
||
本人持仓明细、收益概览、在途交易、历史交易记录(只读) |
|
||
禁答范围 |
|
||
他人账户数据;未完成身份验证时的账户信息 |
|
||
处理路径 |
|
||
鉴权 → JWT 画像标签 → 只读查询 → 脱敏输出(mask_tool)→ 回复 |
|
||
转人工条件 |
|
||
涉及账户异常、冻结、司法冻结、到账失败、无法自助解释的账务问题 |
|
||
合规要点 |
|
||
完整敏感字段(卡号/身份证号)不回显,仅显示掩码;查询前必须完成身份验证〔S16〕 |
|
||
⚠️ 实施状态:本场景属「操作类」查询,依赖客户端 + 账户系统产生真实账户数据,当前为 Phase 2 待实现(客户端尚未完成)。现阶段用户问账户问题,客服应引导转人工或说明「该功能即将上线」。详见 §2.4.6。
|
||
参考做法:天天基金智能客服对「什么时候能看到基金份额」这类账务问题,直接给出「T+1 工作日确认,[我的]→持仓查看,未确认的在[在途交易]查看」的标准流程答案〔S3〕。富途帮助中心以「我的 > 我的客服 > 全部问题搜索 → 仍未解决再转电话/在线」的三步式自助+兜底〔S8〕。
|
||
3.2 产品信息咨询(一般性介绍)
|
||
维度 |
|
||
内容 |
|
||
判定条件 |
|
||
命中基金名称/代码 + 「是什么、怎么样、投资方向、基金经理、规模」等介绍类问法 |
|
||
可答范围 |
|
||
产品要素客观转述(类型、投资范围、风险等级 R1-R5、费率结构、开放状态) |
|
||
禁答范围 |
|
||
「这只基金适不适合我」「值不值得买」等个性化推荐(应转投顾流程,客服不越界) |
|
||
处理路径 |
|
||
意图 product_inquiry → 检索 fin_product_collection → 生成 + 来源引用 |
|
||
转人工条件 |
|
||
用户追问「该买哪个」「给我推荐」→ 引导至风险测评 + 转投顾/人工 |
|
||
合规要点 |
|
||
风险等级只做客观转述,不做「风险很低」主观评价〔S13 铁律三〕 |
|
||
参考做法:招商基金智能客服对「基金下跌怎么办」分析净值下跌 4 个原因(市场变化、分红、折算、转型)并提示异常可查公告〔S6〕——只解释现象、不给结论,与本项目「只答不荐」一致。华夏基金「机智小牛智能体」主打「会倾听、能共情、懂陪伴」,同样止步于陪伴与解答〔S7〕。
|
||
3.3 费率与收益计算
|
||
维度 |
|
||
内容 |
|
||
判定条件 |
|
||
命中「费率、手续费、申购费、赎回费、管理费、收益、赚多少、年化」等计算类问法 |
|
||
可答范围 |
|
||
费率结构与计算规则(申购/赎回/管理费率、前端/后端收费)、业绩比较基准及测算依据 |
|
||
禁答范围 |
|
||
任何收益数字承诺(含「左右/大约/预期」);「预期收益率/年化收益率」表述 |
|
||
处理路径 |
|
||
费率计算器(三维计算模型)→ 客观输出费率;收益类 → 检索产品文档 + 附「业绩比较基准 + 过往业绩不代表未来表现」 |
|
||
转人工条件 |
|
||
用户要求测算「到期能拿回多少钱」等个性化金额 → 转投顾(需画像与风评) |
|
||
合规要点 |
|
||
字段 expected_return 对外一律称「业绩比较基准」;提及收益必附风险提示〔S16〕 |
|
||
参考做法:百度智能云证券基金方案内置「申购费/赎回费/管理费三维费率计算器,支持前端/后端收费切换」〔S15〕。华云天下铁律一给出的标准应答为「根据产品说明书,该产品的业绩比较基准为 XX%。过往业绩不代表未来表现,投资有风险,请您仔细阅读产品说明书」〔S13〕——本项目固定免责话术与之同构。
|
||
3.4 赎回与到账时效
|
||
维度 |
|
||
内容 |
|
||
判定条件 |
|
||
命中「赎回、卖出、到账、多久到账、T+N」等时效类问法 |
|
||
可答范围 |
|
||
分类型到账时效规则(货币/债券/混合/股票/QDII)、T+N 确认规则 |
|
||
禁答范围 |
|
||
承诺「最快几秒/当天必到」等确定性时效(实际以产品为准) |
|
||
处理路径 |
|
||
意图 faq → 检索 fin_faq_collection → 直返标准答案 + 「具体以产品为准」 |
|
||
转人工条件 |
|
||
用户到账异常、超时未到账、金额不符 → 转人工核查 |
|
||
合规要点 |
|
||
时效表述必须带「具体以产品为准」,不做绝对承诺 |
|
||
参考做法:天天基金「基金吧答疑」对「基金赎回多久到账」给出分类型标准答案:货币 1-2 工作日、债券 2-4、混合/股票 2-4、QDII 4-13 工作日(具体以产品为准)〔S3〕。这正是本项目 fin_faq_collection 标准答案的组织范式——高频规则类问题固化为标准话术直返。
|
||
3.5 投诉与情绪安抚
|
||
维度 |
|
||
内容 |
|
||
判定条件 |
|
||
命中「投诉、差评、举报、生气、不满意」等情绪/投诉词,或情绪识别触发 |
|
||
可答范围 |
|
||
情绪共情 + 投诉渠道引导 + 受理承诺 |
|
||
禁答范围 |
|
||
对纠纷事实自行定性、承诺赔偿/退款(必须人工确认) |
|
||
处理路径 |
|
||
情绪识别 → 共情话术 → 生成投诉工单 → 自动转人工 |
|
||
转人工条件 |
|
||
情绪激烈、涉及投诉/纠纷/资金争议 → 无条件转人工 |
|
||
合规要点 |
|
||
涉及退款/赔偿/合同变更,AI 只解释规则、收集材料、生成工单,最终确认由人工或规范化流程完成〔S11〕 |
|
||
参考做法:捷通华声明确「涉及投诉/纠纷关键词」纳入转人工触发条件,「宁可早转,不可硬撑」〔S14〕。国标《顾客联络服务 人工与智能客户服务协同要求》明确「感知到客户负面情绪…系统应自动转接人工」〔S11〕。
|
||
3.6 风险揭示与合规禁答
|
||
维度 |
|
||
内容 |
|
||
判定条件 |
|
||
命中「保本、稳赚、零风险、绝对安全、保证收益、预期收益率」等违规词,或监管敏感问题 |
|
||
可答范围 |
|
||
风险揭示话术(非保本浮动收益、可能损失本金)、引导阅读产品说明书 |
|
||
禁答范围 |
|
||
任何合规禁答内容(见 §6.5 负面词清单) |
|
||
处理路径 |
|
||
负面词后置校验命中 → 打回重生成;二次仍命中 → 返回安全话术 + 告警 |
|
||
转人工条件 |
|
||
监管敏感问题(如「有没有内部消息」「能不能代持」)→ 转合规/法务 |
|
||
合规要点 |
|
||
命中负面词零容忍;告警留痕备查 |
|
||
参考做法:华云天下铁律四标准应答「理财产品是非保本浮动收益产品,您的本金和收益可能会因市场波动而产生损失」〔S13〕。蚂蚁财富《智能理财助理服务协议》明确「输出可能出现错误或遗漏…不构成投资建议/推介/要约」〔S1〕——本项目的免责声明与「AI 生成内容风险提示」双保险与此对齐。
|
||
3.7 各意图数据流向图(汇总)
|
||
下图把 §3.1-§3.6 的六类业务场景,映射回 §3.0 的五类技术意图,展示不同意图下数据从「提问」到「结果」的完整流转路径。账户/持仓/交易查询(操作类)当前为 Phase 2 待实现(见 §2.4.6),图中以虚线标出。
|
||
flowchart TD
|
||
U[用户提问] --> A[① 输入接收与校验鉴权]
|
||
A --> B[② 记忆召回:读 Redis 会话]
|
||
B --> INT{③ 意图路由qwen-turbo · 阈值0.6}
|
||
INT -- faq 高频问答 --> F1[检索 fin_faq_collection · TopK=3]
|
||
F1 --> F2[直返标准答案]
|
||
INT -- product_inquiry 产品咨询 --> P1[检索 fin_product_collection · TopK=5]
|
||
P1 --> P2[RAG + LLM 生成 + 来源引用]
|
||
INT -- policy_explain 政策解读 --> PL1[检索 fin_policy_collection · TopK=5]
|
||
PL1 --> PL2[RAG + 解释]
|
||
INT -- chitchat 闲聊 --> C1[不查集合LLM 引导业务话题]
|
||
INT -- transfer_human 转人工 --> T1[生成转接工单携带完整上下文]
|
||
T1 --> T2[运营/投顾后台接管]
|
||
INT -- 置信度 L1[澄清提问 → 仍低则转人工]
|
||
O1[账户/持仓/交易查询Phase 2 · API 直连账户系统] -.-> G
|
||
F2 --> G[⑤ 合规校验负面词 + 免责声明]
|
||
P2 --> G
|
||
PL2 --> G
|
||
C1 --> G
|
||
T2 --> G
|
||
L1 --> G
|
||
G --> H[⑥ 数据沉淀:归档 + 脱敏]
|
||
H --> I[⑦ 事件广播:敏感意图 → 风控]
|
||
H --> J[结果反馈 SSE]
|
||
各意图数据路径说明:
|
||
意图 |
|
||
数据从哪来 |
|
||
经过什么处理 |
|
||
到哪去 |
|
||
faq |
|
||
fin_faq_collection |
|
||
检索直返(不经 LLM) |
|
||
用户 |
|
||
product_inquiry |
|
||
fin_product_collection |
|
||
RAG + LLM 生成 + 来源引用 |
|
||
用户 |
|
||
policy_explain |
|
||
fin_policy_collection |
|
||
RAG + 解释 |
|
||
用户 |
|
||
chitchat |
|
||
无(不查集合) |
|
||
LLM 直接生成 |
|
||
用户 |
|
||
transfer_human |
|
||
会话上下文 |
|
||
生成转接工单 |
|
||
运营/投顾后台 |
|
||
账户/持仓/交易查询(操作类) |
|
||
账户系统 API(Phase 2) |
|
||
精确查询 |
|
||
用户(暂缓,见 §2.4.6) |
|
||
4. 知识库清单
|
||
4.1 三集合总览
|
||
集合 |
|
||
内容范围 |
|
||
TopK |
|
||
数据来源 |
|
||
fin_faq_collection |
|
||
高频操作类标准问答(开户/绑定/赎回时效/密码/换卡) |
|
||
3 |
|
||
客服历史工单 + 帮助中心 + 人工审校 |
|
||
fin_product_collection |
|
||
产品说明书、招募说明书、费率结构、风险等级、业绩比较基准 |
|
||
5 |
|
||
产品系统 + 官方法律文件 |
|
||
fin_policy_collection |
|
||
监管法规、适当性规则、销售办法、反洗钱政策、平台规则 |
|
||
5 |
|
||
监管原文 + 法务审校 |
|
||
4.2 字段结构(建议统一 schema)
|
||
字段 |
|
||
说明 |
|
||
示例 |
|
||
doc_id |
|
||
唯一标识 |
|
||
FAQ-0001 / PROD-001 / POL-2016-261 |
|
||
title |
|
||
标题 |
|
||
「基金赎回多久到账」 |
|
||
content |
|
||
正文(按条款完整性切分,例外与主条款同片段) |
|
||
— |
|
||
tags |
|
||
意图/主题标签 |
|
||
["faq", "redeem"] |
|
||
effective_date |
|
||
生效日期 |
|
||
2026-01-01 |
|
||
expire_date |
|
||
失效日期(过期自动下线) |
|
||
null / 2027-01-01 |
|
||
source_url |
|
||
来源链接 |
|
||
产品说明书 URL |
|
||
version |
|
||
版本号 |
|
||
v2.1 |
|
||
reviewer |
|
||
审校人 |
|
||
客服运营 / 法务 |
|
||
embedding |
|
||
向量 |
|
||
Qwen Embedding 生成 |
|
||
字段设计依据:阿里云开发者社区强调「知识条目需标注生效日期与适用范围,费率/期限/政策调整时旧条目及时下线,避免过期信息被检索命中」〔S16〕;富途微藤 AI 提供「知识库健康度检查」能力,识别并更正知识库瑕疵〔S9〕。
|
||
4.3 更新频率与数据来源
|
||
知识类型 |
|
||
更新频率 |
|
||
更新机制 |
|
||
FAQ |
|
||
周更新(高频新问题入池) |
|
||
从历史工单提取真实问法 → 人工审校 → 入库〔S9 FAQ 抽取〕 |
|
||
产品要素 |
|
||
产品变更即时同步 |
|
||
产品系统更新 → 触发知识库重建 → Agent 自动拉取(自动化同步管道)〔S14〕 |
|
||
费率/政策 |
|
||
变更即时下线旧条目 |
|
||
规则引擎 + 生效/失效日期控制〔S16〕 |
|
||
监管法规 |
|
||
法规发布后 N 个工作日内 |
|
||
法务审校后入库 |
|
||
参考做法:招商基金智能在线客服「集成招商银行小招X引擎及知识库」,并将「文字聊天记录生成工单内容总结、自动匹配受理产品和渠道」,实现知识沉淀的自动化〔S5〕。富途微藤 AI 的「语料智能扩写」可在冷启动阶段快速丰富语料〔S9〕。
|
||
4.4 知识运营机制
|
||
- 设立专职知识运营岗位,明确更新责任与频率——「上线后无人维护」是金融产品知识库快速失效的头号原因〔S16〕。
|
||
- 同义问法补充:从历史工单提取真实表达(用户说「到期能拿回多少钱」而非「现金价值」),作为同义问法补充进知识条目与意图示例〔S16〕。
|
||
- 按条款完整性切分:把「同一条款的适用条件与例外情况」放在同一片段,避免拆散导致回答不准〔S16〕。
|
||
4.5 FAQ 示例(fin_faq_collection 内容样例)
|
||
下表给出 fin_faq_collection 的典型问答对示例,覆盖不同意图类型。实际入库时按 §4.2 字段结构存储(question / answer / tags / effective_date / expire_date / embedding)。
|
||
类别 |
|
||
用户问法 |
|
||
标准答案要点 |
|
||
是否可直返 |
|
||
开户/注册 |
|
||
怎么开通账户? |
|
||
邮箱验证码注册 → 填写风险测评问卷 → 后台人工审核 → 邮件通知激活 |
|
||
是 |
|
||
账户绑定 |
|
||
怎么换绑定的银行卡? |
|
||
进入「我的」-「账户设置」-「银行卡」按提示操作(本功能 Phase 2 上线) |
|
||
是 |
|
||
密码重置 |
|
||
忘记密码怎么办? |
|
||
登录页点击「忘记密码」→ 邮箱验证 → 重置 |
|
||
是 |
|
||
风险测评 |
|
||
风险测评多久有效? |
|
||
有效期按监管要求;过期需重新测评(具体以系统提示为准) |
|
||
是 |
|
||
赎回时效 |
|
||
赎回多久到账? |
|
||
货币 1-2 工作日 / 债券 2-4 / 混合·股票 2-4 / QDII 4-13,具体以产品为准 |
|
||
是 |
|
||
费率 |
|
||
申购费怎么收? |
|
||
按产品费率表执行,前端/后端收费按合同约定;以产品说明书为准 |
|
||
是 |
|
||
适当性 |
|
||
我能买什么风险等级的基金? |
|
||
先完成风险测评得 C1-C5,再匹配 R1-R5;系统会校验是否匹配 |
|
||
是 |
|
||
收益 |
|
||
这个基金能赚多少? |
|
||
不能承诺收益;请参考「业绩比较基准」及产品说明书,过往业绩不代表未来 |
|
||
是(合规话术) |
|
||
投诉 |
|
||
我要投诉! |
|
||
共情安抚 → 登记投诉工单 → 转人工跟进 |
|
||
否(转人工) |
|
||
转人工 |
|
||
立即生成工单转人工 |
|
||
否(转人工) |
|
||
说明:「是否可直返」= 是否走「检索直返标准答案」路径(faq 意图)。涉及投诉/转人工的,直接走 transfer_human 意图,不检索 FAQ。
|
||
5. 人工审核机制
|
||
5.1 转人工触发条件(分级)
|
||
级别 |
|
||
触发条件 |
|
||
响应 |
|
||
P0 自动转 |
|
||
用户明确要求转人工;情绪激烈/投诉/纠纷关键词;涉人身财产安全/信息安全紧急场景;监管敏感问题 |
|
||
立即转人工,同步上下文 |
|
||
P1 兜底转 |
|
||
意图置信度 < 0.6 且澄清后仍无法识别;RAG 混合判定低置信;连续 2 轮未解决 |
|
||
兜底话术 + 建议转人工 |
|
||
P2 人工引导 |
|
||
涉及资金争议、个性化推荐诉求、大额操作意愿 |
|
||
生成工单 + 引导至投顾/运营人工 |
|
||
触发条件整合了项目既有设计(全景说明书场景 2 的四条件)与国标/行业要求:国标明确「交互失败达到设定阈值、感知负面情绪、涉信息安全人身安全紧急场景时自动转接人工」〔S11〕;捷通华声「连续 2 轮未理解意图、涉及投诉/纠纷关键词、客户主动要求人工」〔S14〕。
|
||
5.2 事后复核与质检(兜底保障)
|
||
即使 AI 未触发转人工,仍设事后质检:
|
||
质检项 |
|
||
规则 |
|
||
动作 |
|
||
负面词命中 |
|
||
后置校验拦截记录 |
|
||
告警 + 安全话术兜底 |
|
||
高风险表述 |
|
||
涉及收益/风险/推荐 |
|
||
抽检人工复核 |
|
||
低置信已答 |
|
||
中置信档回答 |
|
||
纳入质检池,定期回看 |
|
||
投诉关联会话 |
|
||
后续产生投诉的历史会话 |
|
||
全量复核 |
|
||
参考:金融 AI 客服「双轨评测」——一条测业务准确率、一条测合规通过率,任何一条不通过不发布〔S14〕;监管要求客户沟通记录保存 ≥ 20 年且不得含诱导性表述,传统录音抽检覆盖率不足 5%,AI 全量留痕可支撑 100% 质检〔S15〕。
|
||
5.3 升级路径
|
||
一线客服(运营/客户经理接管)
|
||
↓ 无法解决或涉合规
|
||
高级坐席(持证投顾/资深客服)
|
||
↓ 涉监管敏感、法律、重大投诉
|
||
合规/法务(合规官)
|
||
参考做法:招商基金「金谘机构」投顾服务强调「从『顾』到『投』再到『顾』的闭环」,高净值客户「AI 初筛 + 投顾人工审核」正是分级升级的实践〔S4〕。富途按「在线客服 7×24 / 电话人工交易日 24h / 紧急交易柜台」分层〔S8〕。
|
||
5.4 国标对齐检查(《顾客联络服务 人工与智能客户服务协同要求》,2026-09-01 实施)
|
||
国标要求 |
|
||
本项目对齐情况 |
|
||
转人工入口便捷、衔接顺畅 |
|
||
✅ 对话中固定「转人工」入口 + 关键词触发 |
|
||
切换后同步客户信息且不重复询问 |
|
||
✅ 转接工单携带 session_id 完整历史 + 意图 + 置信度 + 已引用来源 |
|
||
支持关键词/菜单/按键/语音多种切换 |
|
||
✅ 关键词 + 菜单(语音为后续扩展,需核实) |
|
||
AI 回复保留显著 AI 生成标识 + 风险提示 |
|
||
✅ 免责声明强制注入(AI 生成标识建议补充,见 §0.4 评审点) |
|
||
涉及退款/赔偿/合同变更最终确认由人工 |
|
||
✅ §3.5 投诉处理路径 |
|
||
不以「AI 回答不代表公司立场」拒履行承诺 |
|
||
✅ 将智能+人工视为完整服务〔S12〕 |
|
||
6. 回复策略
|
||
6.1 标准回复模板(分场景)
|
||
场景 A:产品信息 / 风险等级客观转述
|
||
「XX 基金为【股票型/混合型/债券型】基金,风险等级为 R【1-5】(【低/中低/中/中高/高】风险),适合【风险等级】及以上投资者。您可以在完成风险测评后查看是否匹配。」〔S13 铁律三模板〕
|
||
场景 B:费率 / 业绩比较基准
|
||
「根据产品说明书,该产品的【申购费率/赎回费率/管理费率】为 X%。该产品的业绩比较基准为 X%,过往业绩不代表未来表现,投资有风险,请您仔细阅读产品说明书。」〔S13 铁律一模板〕
|
||
场景 C:风险揭示(非保本)
|
||
「理财产品/基金是非保本浮动收益产品,您的本金和收益可能会因市场波动而产生损失,请在充分了解风险后做出决策。」〔S13 铁律四模板〕
|
||
场景 D:到账时效
|
||
「【货币 1-2 / 债券 2-4 / 混合·股票 2-4 / QDII 4-13】个工作日到账,具体以产品为准。」〔S3〕
|
||
场景 E:情绪安抚 + 投诉受理
|
||
「非常理解您的心情,很抱歉给您带来不好的体验。我已为您登记并转接人工专员跟进处理,稍后会有专人联系您。」
|
||
6.2 语气与措辞规范
|
||
规范 |
|
||
说明 |
|
||
共情优先 |
|
||
投诉/情绪场景先安抚、后解决(对标华夏「机智小牛」的情感计算〔S7〕) |
|
||
客观中性 |
|
||
只陈述事实与规则,不做主观判断与收益暗示 |
|
||
简洁明确 |
|
||
标准问题直返答案,不绕弯 |
|
||
主动引用 |
|
||
关键结论附来源引用,增强可信 |
|
||
6.3 必备风险提示与免责声明
|
||
固定免责声明(基类强制注入,子类不得绕过):
|
||
「本内容仅为投资分析参考,不构成任何直接投资建议,不构成对任何产品的收益承诺,据此操作风险自负,请谨慎对待。」
|
||
AI 生成标识(建议补充,见 §0.4 评审点 4):对 AI 生成的开放式回答,前置「本回答由 AI 生成,仅供参考」,对齐国标「保留显著 AI 生成标识和风险提示」〔S11〕。
|
||
6.4 拒答与兜底话术
|
||
情形 |
|
||
话术 |
|
||
低置信兜底 |
|
||
「抱歉,我暂时无法准确回答您的问题,建议您转接人工客服获取更准确的帮助。」 |
|
||
负面词命中(安全话术) |
|
||
「根据监管要求,我不能对收益做出任何承诺。本产品为非保本浮动收益产品,请以产品说明书为准。」 |
|
||
合规禁答 |
|
||
「非常抱歉,该问题涉及的内容我无法回答,为您转接人工客服。」 |
|
||
个性化推荐越界 |
|
||
「我无法为您推荐具体产品。您可以先完成风险测评,了解自己的风险承受能力后,咨询持证投顾为您提供服务。」 |
|
||
6.5 负面词清单(7 个,零容忍)
|
||
# |
|
||
负面词 |
|
||
合规原因 |
|
||
1 |
|
||
保本 |
|
||
资管新规后非保本 |
|
||
2 |
|
||
稳赚 |
|
||
误导性表述 |
|
||
3 |
|
||
无风险 |
|
||
绝对化表述 |
|
||
4 |
|
||
保证收益 |
|
||
禁止刚兑承诺 |
|
||
5 |
|
||
预期收益率 |
|
||
《理财销售办法》明文禁止 |
|
||
6 |
|
||
年化收益率 |
|
||
监管处罚点名措辞 |
|
||
7 |
|
||
安全 |
|
||
绝对化表述 |
|
||
评审建议:可将「零风险」「稳赚不赔」「躺着赚」「坐享收益」等变体纳入模糊匹配规则(见 §0.4 评审点 4)。
|
||
7. 数据与状态流转 / 接口与异常
|
||
7.1 接口约定
|
||
// POST /api/chat/customer 请求
|
||
{ "session_id": "...", "user_id": 10001, "message": "..." }
|
||
// 响应(统一信封)
|
||
{ "code": 200, "message": "success", "trace_id": "uuid",
|
||
"data": { "reply": "...", "source_references": [...], "intent": "faq",
|
||
"confidence": 0.86, "tool_calls": [...], "suggestions": [...] } }
|
||
7.2 会话状态机
|
||
状态 |
|
||
说明 |
|
||
迁移条件 |
|
||
idle |
|
||
空闲 |
|
||
收到消息 → processing |
|
||
processing |
|
||
处理中 |
|
||
意图清晰 → answering;需澄清 → clarifying |
|
||
clarifying |
|
||
澄清中 |
|
||
澄清后仍无法识别 → transferring |
|
||
answering |
|
||
回答 |
|
||
完成 → idle |
|
||
transferring |
|
||
转人工中 |
|
||
工单生成 → idle(会话交人工) |
|
||
7.3 异常处理表
|
||
异常 |
|
||
策略 |
|
||
检索无结果 / 低分 |
|
||
三档兜底(不硬答) |
|
||
Milvus 超时(>2s)/故障 |
|
||
降级 MySQL LIKE 关键词检索 |
|
||
LLM 失败 |
|
||
退避重试 1s/2s/4s ×3 → 备用模型 → 预设兜底话术 |
|
||
命中负面词 |
|
||
打回重生成;二次仍命中则返回安全话术 + 告警 |
|
||
Redis 故障 |
|
||
会话降级为进程内字典;画像直连 MySQL |
|
||
8. 度量与验收(双轨评测)
|
||
轨道 |
|
||
用例 |
|
||
通过标准 |
|
||
业务准确率 |
|
||
高频意图回答正确性、来源引用完整性 |
|
||
高频意图准确率 ≥ 90% |
|
||
合规通过率 |
|
||
负面词命中率、免责声明缺失率、适当性越界、越权查询 |
|
||
全部为 0 |
|
||
参考:捷通华声「上线前必须跑满合规用例,任何一条不通过都不发布」〔S14〕;百度智能云分层目标「试点期意图识别准确率 ≥85%、推广期多轮完成率 ≥70%、成熟期 NPS ≥40」〔S15〕。
|
||
9. 完善方向与最终交付要求
|
||
本章按六大维度逐项指出当前差距 → 完善标准(可量化)→ 交付要求,作为评审后的迭代排期依据。每条完善标准都对应可验收的量化门槛;文末给出上线门禁(Go/No-Go)清单作为最终交付要求。
|
||
9.0 差距总览(一页速览)
|
||
维度 |
|
||
关键差距(Top) |
|
||
完善标准(量化) |
|
||
优先级 |
|
||
功能完整性 |
|
||
闲聊无边界、澄清槽位缺失、同义问法缺失、suggestions 空转、转人工会话未回收、跨 Agent 联动未定义 |
|
||
闲聊 ≤3 轮引导;澄清 ≤2 轮;top20 问法各 ≥5 同义句;转人工会话 24h 内回收 |
|
||
P0 |
|
||
用户体验 |
|
||
无 SSE 流式、情绪识别后置、无首屏建议问题、引用不可点击、多端未适配 |
|
||
首 token <800ms;负面情绪直通安抚通道;引用可跳转原文 |
|
||
P0 |
|
||
性能优化 |
|
||
三段串行、FAQ 无缓存、无 rerank、截断策略粗糙、无并发限流 |
|
||
端到端 P95 <3s;热点缓存命中 ≥80%;意图与检索并行 |
|
||
P1 |
|
||
错误处理 |
|
||
无熔断/半开、无幂等、trace_id 未贯穿、兜底话术硬编码、无灰度回滚 |
|
||
熔断 N 次触发;同 trace_id 幂等;话术可热更新 |
|
||
P1 |
|
||
安全性 |
|
||
无提示词注入防护、检索层行级过滤未强制、脱敏清单未定、反诈触发词未列、审计字段未定 |
|
||
注入红队 0 穿透;越权查询 0;敏感字段 100% 脱敏 |
|
||
P0 |
|
||
代码可读性 |
|
||
常量散落、意图分类器硬编码、单测覆盖未定义、命名未固化、文档代码脱节 |
|
||
常量单一来源;核心逻辑覆盖率 ≥80%;合规用例 100% |
|
||
P1 |
|
||
9.1 功能完整性
|
||
改进点 |
|
||
现状差距 |
|
||
完善标准 |
|
||
交付要求 |
|
||
闲聊意图边界 |
|
||
chitchat 仅写「LLM 引导业务话题」,无话题边界与轮次上限,可被闲聊无限消耗资源 |
|
||
闲聊 ≤3 轮后强制引导业务;引导后仍无业务意图 → 转人工或静默结束 |
|
||
闲聊状态机 + 引导话术模板 + 轮次计数单测 |
|
||
澄清机制槽位 |
|
||
状态机有 clarifying 态,但未定义澄清追问的槽位(产品名/时间范围等)与次数上限 |
|
||
澄清 ≤2 轮;每轮追问 1 个关键槽位;澄清失败 → 转人工 |
|
||
槽位定义表 + 澄清话术 + 澄清失败转人工用例 |
|
||
同义问法/口语纠错 |
|
||
用户问法与官方术语差距大(「到期能拿回多少钱」vs「现金价值」)〔S16〕 |
|
||
高频意图 top20 问法各配置 ≥5 个同义问法;拼写/口语纠错 |
|
||
同义问法词表(历史工单提取)+ 纠错规则 |
|
||
suggestions 空转 |
|
||
接口有 suggestions 字段但无生成逻辑 |
|
||
每条回答附带 2-3 个关联建议问题,点击率埋点 |
|
||
建议问题生成规则 + 埋点 |
|
||
转人工会话回收 |
|
||
全景说明书提到「解决后提取摘要并回收对话」,客服专项未细化 |
|
||
转人工会话解决后 24h 内生成摘要,回收入 FAQ 候选池 |
|
||
摘要提取流程 + FAQ 沉淀机制 |
|
||
跨 Agent 联动 |
|
||
客服识别「高净值/大额意愿/敏感话题」后的联动规则未定义 |
|
||
三类联动触发词清单 + 事件发布契约(suspicious_intent 等) |
|
||
联动触发规则表 + 事件契约文档 |
|
||
9.2 用户体验
|
||
改进点 |
|
||
现状差距 |
|
||
完善标准 |
|
||
交付要求 |
|
||
流式输出(SSE) |
|
||
接口返回完整 JSON,长答案首字延迟高 |
|
||
首 token 延迟 <800ms;SSE 流式输出 |
|
||
SSE 改造 + 首字延迟监控 |
|
||
情绪识别前置 |
|
||
情绪识别在转人工判定时才做,负面情绪客户先在兜底话术打转 |
|
||
意图路由前做情绪预判;负面情绪直通「安抚+转人工」,不绕检索 |
|
||
情绪预判前置 + 负面情绪快速通道 |
|
||
首屏建议问题 |
|
||
对标天天基金「猜你想问」、支小宝「大家都在问」〔S2〕,本项目无首屏引导 |
|
||
首屏提供 top 高频问题 + 按画像的个性化建议 |
|
||
首屏问题推荐模块 |
|
||
引用可点击 |
|
||
来源引用格式未定义,长答案无分点 |
|
||
长答案分点/分段;来源引用可点击跳转原文 |
|
||
回答格式化模板 + 引用跳转 |
|
||
多端适配 |
|
||
未说明 APP/H5/网页端差异 |
|
||
三端统一会话(session 跨端)与展示适配 |
|
||
多端适配方案 |
|
||
9.3 性能优化
|
||
改进点 |
|
||
现状差距 |
|
||
完善标准 |
|
||
交付要求 |
|
||
三段串行链路 |
|
||
意图分类 → RAG 检索 → LLM 生成串行 |
|
||
意图分类与 RAG 检索并行;端到端 P95 <3s |
|
||
并行化改造 + 压测报告 |
|
||
FAQ 热点缓存 |
|
||
FAQ 直返未做缓存,重复计算 |
|
||
热点 FAQ 缓存命中率 ≥80%;缓存 TTL 10min |
|
||
Redis 缓存层 + 命中率监控 |
|
||
检索重排 rerank |
|
||
TopK=5 直接送 LLM,无精排 |
|
||
引入 rerank,top1 准确率相对基线提升 ≥5pp |
|
||
rerank 模型接入 + 离线评测 |
|
||
上下文截断策略 |
|
||
超 4096 token 截断旧消息,丢失关键信息 |
|
||
改为「摘要 + 近期原文」混合;按意图选择性保留 |
|
||
上下文压缩策略 + 效果对比 |
|
||
并发限流削峰 |
|
||
高峰并发无保护 |
|
||
令牌桶/滑动窗口限流;QPS 阈值;降级预案 |
|
||
限流配置 + 压测 |
|
||
9.4 错误处理
|
||
改进点 |
|
||
现状差距 |
|
||
完善标准 |
|
||
交付要求 |
|
||
熔断与半开恢复 |
|
||
只有超时降级,无熔断器 |
|
||
Milvus/LLM 连续失败 N 次熔断,半开探测恢复 |
|
||
熔断器 + 恢复策略 |
|
||
幂等性 |
|
||
重复提交/重试无幂等 |
|
||
同一 trace_id 幂等 |
|
||
幂等键设计 |
|
||
trace_id 全链路 |
|
||
接口有 trace_id,未说明贯穿到 Milvus/LLM 调用日志 |
|
||
一次请求一个 trace_id 贯穿所有下游调用 |
|
||
全链路日志规范 + 示例 |
|
||
兜底话术硬编码 |
|
||
安全话术/兜底话术硬编码在代码 |
|
||
话术配置化(独立配置/知识条目),可热更新 |
|
||
话术配置中心 |
|
||
灰度与回滚 |
|
||
新模型/新知识库上线失败无回滚 |
|
||
模型/知识库版本化,一键回滚 |
|
||
版本管理 + 回滚预案 |
|
||
9.5 安全性
|
||
改进点 |
|
||
现状差距 |
|
||
完善标准 |
|
||
交付要求 |
|
||
提示词注入防护 |
|
||
用户消息可夹带「忽略之前的指令」 |
|
||
输入侧注入检测 + 输出侧越界拦截;红队 0 穿透 |
|
||
注入防护规则 + 红队测试用例 |
|
||
检索层权限过滤 |
|
||
客服「仅本人数据」,但检索/SQL 层是否强制行级过滤未明确 |
|
||
数据访问在检索层强制 + 服务层二次校验(纵深防御) |
|
||
行级过滤设计 + 越权用例 |
|
||
脱敏字段清单 |
|
||
mask_tool 已提但字段清单未定 |
|
||
身份证/卡号/手机号全脱敏;日志/回显均脱敏 |
|
||
脱敏字段清单 + 脱敏时机 |
|
||
反诈敏感意图触发词 |
|
||
suspicious_intent 事件已提但触发词未列 |
|
||
反诈关键词(转账/验证码/安全账户)+ 实时预警 |
|
||
触发词清单 + 与风控联动 |
|
||
审计留痕完整性 |
|
||
留痕 ≥20 年已提但字段未定义 |
|
||
完整记录(人/时/问/答/意图/置信度/来源/工具调用) |
|
||
审计日志字段规范 |
|
||
9.6 代码可读性
|
||
改进点 |
|
||
现状差距 |
|
||
完善标准 |
|
||
交付要求 |
|
||
常量散落 |
|
||
阈值/负面词/免责声明可能散落在子类 |
|
||
唯一常量源(对标 SUITABILITY_MATRIX 单一来源) |
|
||
常量集中管理(config/constants) |
|
||
意图分类器硬编码 |
|
||
意图集 + 阈值配置化 |
|
||
配置化 + 文档 |
|
||
单元测试覆盖 |
|
||
未定义测试覆盖率 |
|
||
核心逻辑覆盖率 ≥80%;合规用例 100% 通过 |
|
||
单测 + 双轨评测 |
|
||
命名与目录规范 |
|
||
已有 api/service/tool 但未固化规范 |
|
||
统一命名 + 分层依赖规则 |
|
||
编码规范文档 |
|
||
文档代码同步 |
|
||
方案 vs 代码可能脱节 |
|
||
关键阈值/话术/集合在代码中有单一真相源,文档引用该源 |
|
||
文档-代码同步机制 |
|
||
9.7 最终交付要求(上线门禁 Go/No-Go 清单)
|
||
以下为必须全部满足才可进入联调/上线的硬性门禁;任一未达标即 No-Go。
|
||
功能门禁(Must)
|
||
# |
|
||
交付项 |
|
||
验收标准 |
|
||
F1 |
|
||
5 类意图 + 三集合路由可运行 |
|
||
每类意图至少 1 条端到端用例通过 |
|
||
F2 |
|
||
三档兜底(高/中/低置信) |
|
||
低置信 100% 不硬答,触发兜底/转人工 |
|
||
F3 |
|
||
转人工工单携带完整上下文 |
|
||
session_id + 历史 + 意图 + 置信度 + 来源一次传递 |
|
||
F4 |
|
||
负面词 7 个后置校验 |
|
||
命中率 0(拦截率 100%) |
|
||
F5 |
|
||
免责声明强制注入 |
|
||
面向客户的输出 100% 附固定话术 |
|
||
安全与合规门禁(Must)
|
||
# |
|
||
交付项 |
|
||
验收标准 |
|
||
S1 |
|
||
不代客交易 |
|
||
客服 Agent 无任何交易写接口 |
|
||
S2 |
|
||
适当性不越界 |
|
||
C1×R2+ 推荐 0 起(客服不荐,越界转投顾) |
|
||
S3 |
|
||
越权查询防护 |
|
||
本人数据之外查询 0 起 |
|
||
S4 |
|
||
敏感字段脱敏 |
|
||
身份证/卡号/手机号日志与回显 100% 脱敏 |
|
||
S5 |
|
||
留痕可追溯 |
|
||
全量会话归档,保存 ≥20 年 |
|
||
质量门禁(Must)
|
||
# |
|
||
交付项 |
|
||
验收标准 |
|
||
Q1 |
|
||
高频意图准确率 |
|
||
≥90% |
|
||
Q2 |
|
||
首响时长 |
|
||
<3s(SSE 首 token <800ms) |
|
||
Q3 |
|
||
转人工率 |
|
||
≤20% |
|
||
Q4 |
|
||
合规用例通过 |
|
||
双轨评测合规轨道 100% 通过 |
|
||
Q5 |
|
||
核心逻辑单测覆盖率 |
|
||
≥80% |
|
||
交付物清单(最终交付要求)
|
||
类别 |
|
||
交付物 |
|
||
代码 |
|
||
customer.py(5 意图 + 三档兜底)、rag_service.py、milvus_tool.py、mask_tool.py、memory_service.py + 常量集中管理模块 |
|
||
配置 |
|
||
意图阈值、负面词清单、免责声明话术、兜底话术、限流参数、熔断参数(全部配置化) |
|
||
数据 |
|
||
三集合知识库(FAQ/产品/政策)初始版本 + 同义问法词表 |
|
||
文档 |
|
||
本方案 v1.1 + 编码规范 + 接口契约 + 审计日志字段规范 + 联调测试用例 |
|
||
测试 |
|
||
单元测试(覆盖率 ≥80%)+ 双轨评测集(业务 + 合规) |
|
||
10. 附录
|
||
10.1 参考来源清单
|
||
编号 |
|
||
来源 |
|
||
要点 |
|
||
S1 |
|
||
蚂蚁财富《智能理财助理服务协议》〔render.alipay.com〕 |
|
||
「蚂小财」大模型局限免责、不构成投资建议/推介/要约、95188 人工热线 |
|
||
S2 |
|
||
天天基金研究(AI 客服「小天」/支小宝界面)〔ima.qq.com〕 |
|
||
常见问题、选基金/持仓分析/产品评测功能 |
|
||
S3 |
|
||
天天基金「基金吧答疑」〔guba.eastmoney.com〕 |
|
||
联系客服路径、赎回到账分类型时效、折算解释 |
|
||
S4 |
|
||
招商基金「懂投资更懂你」(金谘机构)〔cmfchina.com〕 |
|
||
7×24 在线机器人、持仓诊断、基金体检 |
|
||
S5 |
|
||
招商基金「数字金融建设」〔sohu.com/a/969419069〕 |
|
||
小招X引擎+知识库、AI 工单总结、营销知识图谱、DeepSeek R1+QWQ-32b |
|
||
S6 |
|
||
证券时报「沉浸式体验基金智能客服 九大趋势」〔stcn.com〕 |
|
||
招商/富国基金智能客服对「基金下跌」的回答差异、天弘 94% 准确率 |
|
||
S7 |
|
||
华夏基金李一梅「AI 时代的能力者」〔cet.com.cn〕 |
|
||
「机智小牛智能体」、情感计算、人机边界 |
|
||
S8 |
|
||
富途官方「7×24 小时服务」〔futunn.com〕 |
|
||
帮助中心三步、智能客服 7×24、人工分层时段 |
|
||
S9 |
|
||
富途 2024 年报〔newsfile.futunn.com〕 |
|
||
ContactBot(TextBot/VoiceBot)、微藤 AI:语料扩写/FAQ 抽取/文档问答/知识库健康度检查 |
|
||
S10 |
|
||
腾讯新闻「6000 次实测基金公司」〔so.html5.qq.com〕 |
|
||
国泰 84%、嘉实 42%、建信 31% AI 解答率;15 家仅 3 家周末有人工 |
|
||
S11 |
|
||
新华网《顾客联络服务 人工与智能客户服务协同要求》〔news.cn〕 |
|
||
国标转人工规则、切换同步不重复询问、AI 标识+风险提示 |
|
||
S12 |
|
||
人民论坛「新国标整治 AI 客服躲猫猫」〔rmlt.com.cn〕 |
|
||
不以「AI 回答不代表公司立场」拒履行承诺 |
|
||
S13 |
|
||
华云天下「金融 AI 客服十条合规铁律」〔huayunworld.com〕 |
|
||
不能承诺收益/推荐/风险评价/刚兑、三道防线、标准应答模板 |
|
||
S14 |
|
||
捷通华声「金融客服智能体可控比聪明更重要」〔sohu.com〕 |
|
||
置信度阈值转人工、连续 2 轮/投诉关键词、双轨评测 |
|
||
S15 |
|
||
百度智能云「智能客服赋能证券基金」〔cloud.baidu.com〕 |
|
||
留痕 ≥20 年、合规引擎 200+ 规则、分层落地指标 |
|
||
S16 |
|
||
阿里云开发者社区「AI 客服金融落地实践」〔developer.aliyun.com〕 |
|
||
生效日期标注、条款完整性切分、同义问法、脱敏、知识运营岗位 |
|
||
S17 |
|
||
嘉实基金「亮相世界人工智能大会」〔jsfund.cn〕 |
|
||
与浦发共建 AI 财富数字人联合实验室 |
|
||
10.2 需核实项清单
|
||
# |
|
||
待核实内容 |
|
||
说明 |
|
||
1 |
|
||
天天基金智能客服响应时效 ≤30 秒 |
|
||
来源为第三方(叩富网)引用「天天基金统计数据」,非官方口径,需核实 |
|
||
2 |
|
||
招商基金部署模型「QWQ-32b」具体版本 |
|
||
搜狐报道原文,模型名拼写需核实 |
|
||
3 |
|
||
蚂蚁财富「蚂小财」的意图分类体系与转人工阈值 |
|
||
未公开,需向平台方或公开资料核实 |
|
||
4 |
|
||
国泰/嘉实/建信 AI 解答率实测数据 |
|
||
为第三方媒体抽样测试,非官方披露,仅作行业参照 |
|
||
5 |
|
||
各平台知识库字段结构 |
|
||
均为非公开信息,本方案 §4.2 为设计建议,需按本项目实际数据核实 |
|
||
6 |
|
||
语音切换人工(§5.4 国标对齐) |
|
||
本项目当前仅文本,语音为后续扩展,需排期确认 | |