6162 lines
185 KiB
Markdown
6162 lines
185 KiB
Markdown
# 保险智能客服系统 — 需求文档
|
||
|
||
> **版本**:V1.0
|
||
|
||
> **日期**:2026-05-31
|
||
|
||
> **技术基础**:BaoDan(AI 引擎)+ 自研企微后端
|
||
|
||
> **开发人数**:1 人
|
||
|
||
> **功能项总计**:137 项(用户前端 35 + 管理前端 56 + 企微机器人 5 + 后端接口 41)
|
||
|
||
### 实现方式说明(必读)
|
||
|
||
每个功能点后标注了实现方式,含义如下:
|
||
|
||
| 标注 | 含义 | 说明 |
|
||
|------|------|------|
|
||
| **BaoDan 原生** | BaoDan 自带,无需写代码 | 在 BaoDan 后台配置即可使用 |
|
||
| **BaoDan + 增强** | BaoDan 有基础功能,需少量代码增强 | BaoDan 提供 80%,你补 20% |
|
||
| **BaoDan Workflow** | 在 BaoDan 可视化编辑器中拖拽编排 | 不写代码,但需要设计 Workflow 逻辑 |
|
||
| **自研** | 需要完全自己写代码 | 前端页面或后端接口 |
|
||
| **自研(调 BaoDan)** | 自研后端接口,内部调 BaoDan API | Flask Blueprint -> 直接调用 BaoDan Python 模块 |
|
||
| **自研 + 企微 API** | 自研后端,对接企微开放平台 API | 需要企微应用权限 |
|
||
|
||
---
|
||
|
||
## 一、项目概述
|
||
|
||
### 1.1 项目目标
|
||
|
||
为保险代理团队构建一个基于知识库的智能客服系统,核心能力:
|
||
|
||
1. **智能问答**:代理人/客户通过网页或企微提问,系统从知识库中检索准确答案
|
||
|
||
2. **产品推荐**:根据客户需求自动生成保险产品推荐方案,支持导出 PDF/Word/PPT
|
||
|
||
3. **知识库管理**:支持批量导入 9GB 的 MD 格式保险文档,按险种/保司分类管理
|
||
|
||
4. **企微集成**:通过企微机器人实现单聊/群聊问答,通过企微 OAuth 实现免密登录
|
||
|
||
5. **管理后台**:知识库管理、日志审计、用户权限、系统配置、数据统计
|
||
|
||
### 1.2 用户角色
|
||
|
||
|角色|说明|访问方式|核心操作|
|
||
|------|------|---------|---------|
|
||
|超级管理员|系统管理者|网页后台|全部功能,含系统配置和用户管理|
|
||
|管理员|运营/合规人员|网页后台|知识库管理、日志查看、数据统计|
|
||
|销售主管|团队负责人|网页/企微 H5|查看团队问答数据、产品推荐|
|
||
|销售人员|保险代理人|网页/企微 H5|智能问答、产品推荐|
|
||
|客户|投保人/被保人|网页/企微 H5|智能问答(受限)、产品推荐|
|
||
|客户|投保人/被保人|网页/企微 H5|智能问答(受限)、产品推荐|
|
||
|
||
### 1.3 技术架构与实现方式
|
||
|
||
|功能模块|实现方式|说明|
|
||
|---------|---------|------|
|
||
|智能问答(M1)|BaoDan 原生|直接用 BaoDan 的对话功能|
|
||
|产品推荐(M2)|自研|BaoDan Workflow + 自研前端表单,代码放 api/insurance/recommend/|
|
||
|知识库管理(M3)|BaoDan 原生 + 自研增强|BaoDan 后台为主,标签/编号等功能需自研|
|
||
|留痕与日志(M4)|BaoDan 原生 + 自研增强|BaoDan 有基础日志,导出/操作日志需自研|
|
||
|用户与权限(M5)|自研|企微 OAuth + 角色权限体系,代码放 api/insurance/permissions/|
|
||
|系统配置(M6)|BaoDan 原生 + 自研增强|BaoDan 管理模型/Prompt,模板和告警需自研|
|
||
|数据统计(M7)|自研|成本监控、健康度统计,代码放 api/insurance/stats/|
|
||
|企微机器人|自研|Flask Blueprint 对接企微 API + BaoDan,代码放 api/insurance/wecom/|
|
||
|后端接口(A1-A8)|自研|Flask Blueprint,复用 BaoDan 基础设施|
|
||
|企微 OAuth 登录|自研 + 企微 API|OAuth 免密登录,代码放 api/insurance/wecom/|
|
||
|
||
---
|
||
|
||
## 二、用户前端功能(M1 + M2)
|
||
|
||
### M1:智能问答
|
||
|
||
#### 1.1 对话交互
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|1.1.1|文字输入框|支持回车发送;Shift+Enter 换行;字数上限提示|高|BaoDan 原生|
|
||
|1.1.2|流式输出(Streaming)|回答逐字打印,降低等待焦虑感;显示「生成中...」状态|高|BaoDan 原生|
|
||
|1.1.3|多轮对话上下文保持|追问时携带历史上下文,支持「刚才你说的 XX 是什么意思」等追问|高|BaoDan 原生|
|
||
|1.1.4|会话管理|新建会话、切换历史会话列表、删除会话(软删除)|中|BaoDan 原生|
|
||
|1.1.5|会话标题自动命名|首条问题提取关键词作为会话标题,可手动重命名|低|BaoDan 原生|
|
||
|
||
#### 1.2 回复展示
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|1.2.1|Markdown 渲染|加粗、表格、有序/无序列表、代码块等格式正确渲染|高|BaoDan 原生|
|
||
|1.2.2|图片内联展示|回复中引用产品条款截图、对比图时可直接预览;支持点击放大|中|BaoDan + 增强|
|
||
|1.2.3|来源引用标注|回复末尾附来源文档名或 API 来源标签,可点击查看原始片段|高|BaoDan 原生|
|
||
|1.2.4|相关推荐问题|回答下方展示 2-3 条系统推荐的延伸问题,点击直接提问|低|BaoDan + 增强|
|
||
|1.2.5|一键复制回答|复制按钮,将完整回答文本复制到剪贴板|中|BaoDan + 增强|
|
||
|
||
#### 1.3 检索范围控制
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|1.3.1|险种筛选器|提问前可选定特定险种(重疾/寿险/医疗/储蓄等)缩小检索范围|中|BaoDan + 增强|
|
||
|1.3.2|保司筛选器|可指定一家或多家保司范围,避免跨保司混淆|中|BaoDan + 增强|
|
||
|1.3.3|全库检索(默认)|未选筛选器时跨险种、跨保司综合检索|高|BaoDan 原生|
|
||
|
||
#### 1.4 反馈与纠错
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|1.4.1|点赞/点踩按钮|回答下方快捷反馈,「有用」/「无用」一键投票|高|BaoDan 原生|
|
||
|1.4.2|纠错反馈入口|点击「回答有误」后弹窗,用户可输入正确信息提交|中|自研|
|
||
|
||
### M2:产品推荐方案
|
||
|
||
#### 2.1 基础信息表单
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|2.1.1|基础信息表单|姓名、年龄、性别、健康状况(体况选项)、职业类别|高|自研|
|
||
|2.1.2|险种选择|多选险种;选择后动态渲染对应必填字段(不同险种字段不同)|高|自研|
|
||
|2.1.3|保障需求设置|保额目标(滑块/输入框)、月供预算上限|高|自研|
|
||
|2.1.4|保障期限选择|定期 N 年 / 保至某岁 / 终身,与险种联动可选项|高|自研|
|
||
|2.1.5|已有保单录入|录入客户现有保障,AI 可避免重复建议;非必填|中|自研|
|
||
|2.1.6|表单草稿自动保存|每次输入变更后自动保存草稿,刷新或意外关闭后可恢复|中|自研|
|
||
|
||
**表单字段详细定义**:
|
||
|
||
|字段|类型|必填|选项/规则|说明|
|
||
|
||
|------|------|:---:|-----------|------|--------
|
||
|
||
|姓名|文本|否|最大 20 字符|客户姓名|
|
||
|年龄|数字|是|0-150 整数|影响保费计算|
|
||
|性别|单选|是|男 / 女|部分险种性别差异定价|
|
||
|健康状况|单选|是|健康 / 有既往病史 / 慢性病 / 重大疾病史|影响核保结果|
|
||
|职业|文本|是|最大 50 字符|高危职业部分险种不可投|
|
||
|关注险种|多选|是|寿险 / 重疾险 / 医疗险 / 意外险 / 年金险 / 储蓄险|决定推荐范围|
|
||
|保额目标|数字|是|1-1000 万元|每个险种独立设置|
|
||
|月预算上限|数字|是|单位:元|所有险种总预算|
|
||
|保障期限|下拉|是|定期 10/20/30 年 / 保至 60/70/80 岁 / 终身|随险种联动|
|
||
|
||
#### 2.2 AI 自动匹配产品组合
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|2.2.1|AI 自动匹配产品组合|基于客户信息 + 知识库,LLM 输出推荐产品列表及配置方案|高|BaoDan Workflow|
|
||
|2.2.2|多方案输出|生成 2-3 套保障方案(基础/均衡/全面),供销售选择|中|BaoDan Workflow|
|
||
|2.2.3|推荐理由说明|每款产品附加 AI 生成的推荐原因(1-2 句话)|中|BaoDan Workflow|
|
||
|
||
**BaoDan Workflow 设计**:
|
||
```
|
||
|
||
输入参数(JSON:年龄、性别、职业、收入、预算、关注险种等)
|
||
|
||
[节点1] 参数校验 - 检查必填项
|
||
|
||
[节点2] 检索策略 - 根据险种确定搜索范围
|
||
|
||
[节点3] 知识库检索 - 从对应知识库搜索匹配的产品条款
|
||
|
||
[节点4] LLM 方案生成 - 基于检索结果,生成基础/均衡/全面三套方案
|
||
|
||
[节点5] 格式化输出 - Markdown 格式的方案报告
|
||
```
|
||
#### 2.3 方案预览编辑
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|2.3.1|方案预览页|实时渲染推荐方案内容,支持编辑、导出、分享操作|高|自研|
|
||
|2.3.2|关键字段手动修改|保额、保费、受益人、保障期限等可直接在预览页编辑|高|自研|
|
||
|2.3.3|产品替换|从候选产品列表中替换某款产品,实时刷新预览|中|自研|
|
||
|
||
#### 2.4 方案导出分享
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|2.4.1|导出 PDF/PPT 格式|按客户方提供模板渲染,输出标准 PDF/PPT 文件|高|自研|
|
||
|2.4.2|导出 Word (.docx)|可编辑的 Word 格式,方便进一步调整|中|自研|
|
||
|2.4.3|分享链接|生成有时效的在线预览链接,无需登录即可查看|低|自研|
|
||
|2.4.4|重新生成|保留原始输入参数,一键重新调用 AI 生成新版本|中|自研|
|
||
|
||
#### 2.5 历史方案管理
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|2.5.1|历史方案列表页|展示所有已生成的推荐方案,支持分页浏览|高|自研|
|
||
|2.5.2|多维筛选与搜索|按客户姓名、险种、时间段、创建人筛选|高|自研|
|
||
|2.5.3|方案详情查看|查看单个方案的完整内容,包括各方案对比|高|自研|
|
||
|2.5.4|历史版本重新下载|任意历史版本均可再次导出 PDF/Word|高|自研|
|
||
|
||
---
|
||
|
||
## 三、管理前端功能(M3 - M7)
|
||
|
||
### M3:知识库管理
|
||
|
||
#### 3.1 文档上传管理
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|3.1.1|批量上传文档|支持 MD、Word、TXT 格式,可多文件同时上传,显示上传进度条|高|BaoDan 原生|
|
||
|3.1.2|文档列表|展示文件名、险种、保司、上传时间、处理状态;支持排序和搜索|高|BaoDan 原生|
|
||
|3.1.3|文档编号自动生成|上传时按「险种代码-保司代码-序号」规则自动分配编号,可手动修改|高|BaoDan + 增强|
|
||
|3.1.4|分类标签管理|险种标签、保司标签、自定义标签;支持新建/编辑/删除标签|高|BaoDan + 增强|
|
||
|3.1.5|文档删除与归档|软删除:归档后不再参与检索,但保留文件与记录;可恢复|中|BaoDan 原生|
|
||
|3.1.6|文档版本管理|上传同名新版本时保留旧版本;可切换使用哪个版本参与检索|中|BaoDan + 增强|
|
||
|
||
**知识库分类结构**:
|
||
|
||
|知识库名称|分类维度|预估规模|||
|
||
|-----------|---------|---------|--------|--------|
|
||
|寿险-产品条款|险种|数百到数千 MD 文件|||
|
||
|寿险-核保规则|险种|数十到数百|||
|
||
|重疾险-产品条款|险种|数百|||
|
||
|重疾险-核保规则|险种|数十|||
|
||
|医疗险-产品条款|险种|数百|||
|
||
|意外险-产品条款|险种|数百|||
|
||
|车险-产品条款|险种|数百|||
|
||
|年金险-产品条款|险种|数百|||
|
||
|通用-理赔流程|通用|数十|||
|
||
|通用-监管法规|通用|数十|||
|
||
|
||
**文档分段规则(Chunk 策略)**:
|
||
|
||
|设置项|推荐值|说明|
|
||
|--------|--------|------|
|
||
|分段标识符|按 Markdown 标题层级|用 `\n##` 或 `\n###` 作为分隔符|
|
||
|最大分段长度|800 tokens|保险条款句子长,太小会切断语义|
|
||
|分段重叠|150 tokens|避免边界处丢信息|
|
||
|索引模式|高质量|使用 DeepSeek Embedding|
|
||
|
||
#### 3.2 处理状态监控
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|3.2.1|处理流水线状态|每份文档展示状态:待处理/格式转换中/向量化中/完成/失败|高|BaoDan 原生|
|
||
|3.2.2|失败原因与重试|失败文档显示错误原因(OCR 失败/格式错误等),支持手动重试|高|BaoDan 原生|
|
||
|3.2.3|向量化进度|大文档分批处理时展示百分比进度条|低|BaoDan 原生|
|
||
|
||
#### 3.3 保司 API 数据源
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|3.3.1|数据源配置|新增/编辑保司 API:接口地址、密钥(加密存储)、同步频率(定时 CRON)|高|自研|
|
||
|3.3.2|手动立即同步|一键触发指定数据源立即同步,显示实时进度|高|自研|
|
||
|3.3.3|同步状态监控|上次同步时间、同步条目数、耗时、成功/失败状态|中|自研|
|
||
|3.3.4|数据变更日志|每次同步记录新增/更新/删除的产品条目明细|低|自研|
|
||
|3.3.5|同步异常告警|失败时通过配置渠道(邮件/企微)通知管理员|中|自研|
|
||
|
||
#### 3.4 检索效果测试
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|3.4.1|知识库测试入口|输入测试问题,预览检索命中的文档片段与相关度分数|中|BaoDan 原生|
|
||
|3.4.2|未命中问题列表|近 7/30 天用户提问中知识库无法回答的问题汇总,辅助补充文档|高|自研|
|
||
|3.4.3|FAQ 手动条目|高频问题可手动添加固定问答对,优先级高于向量检索结果|中|自研|
|
||
|
||
---
|
||
|
||
### M4:留痕与日志
|
||
|
||
#### 4.1 问答记录
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|4.1.1|全量对话记录查看|完整存储:问题/回答/引用来源/用户/时间戳;支持单条详情查看|高|BaoDan 原生|
|
||
|4.1.2|多维筛选|按用户、时间范围、险种、关键词、评分筛选|高|BaoDan + 增强|
|
||
|4.1.3|对话记录导出|BaoDan 对话 API 支持分页查询,后端封装导出为 CSV|中|自研(调 BaoDan)|
|
||
|4.1.4|负反馈管理|BaoDan 内置标注系统可查看反馈;处理流程需自研|中|BaoDan + 增强|
|
||
|
||
#### 4.2 导出日志
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|4.2.2|导出下载操作日志|记录每次导出操作(操作人 + 时间 + 格式)|高|自研|
|
||
|
||
#### 4.3 系统操作日志
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|4.3.1|用户登录日志|登录/登出记录:用户/IP/设备/时间|中|自研|
|
||
|4.3.2|知识库操作日志|文档上传/删除/归档/标签变更等操作记录|中|自研|
|
||
|4.3.3|配置变更日志|系统配置(模型/Prompt/模板)的变更记录,含变更前后值|低|自研|
|
||
|
||
---
|
||
|
||
### M5:用户与权限
|
||
|
||
#### 5.1 账号管理
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|5.1.1|新增/编辑/停用账号|基础信息维护;停用后立即踢出登录态|高|BaoDan 原生|
|
||
|5.1.2|绑定企微账号|通过企微 UserId 关联,实现企微 OAuth 免密登录|高|自研|
|
||
|5.1.3|分组管理|按城市/团队/部门建组,用于数据权限隔离|中|自研|
|
||
|5.1.4|批量导入用户|提供 Excel 模板,批量创建账号|低|自研|
|
||
|
||
#### 5.2 角色与权限
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|5.2.1|内置角色|超级管理员/管理员/销售主管/销售人员/客户 五档内置角色|高|BaoDan + 增强|
|
||
|5.2.2|功能权限配置|各角色可访问的功能模块开关,可视化勾选配置|高|自研|
|
||
|5.2.3|数据权限分层|销售见自己;主管见本组;管理员全量可见|高|自研|
|
||
|5.2.4|自定义角色|可新建角色并自由组合权限项|低|自研|
|
||
|
||
---
|
||
|
||
### M6:系统配置
|
||
|
||
#### 6.1 LLM 模型配置
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|6.1.1|多模型配置|支持 GPT-4o / DeepSeek 等,每个模型单独配置 API Key|高|BaoDan 原生|
|
||
|6.1.2|API Key 加密存储|Key 入库前 AES 加密,页面显示脱敏;不可明文查看|高|BaoDan + 增强|
|
||
|6.1.3|模型删除/切换默认|删除未使用的模型配置;切换默认模型|低|BaoDan 原生|
|
||
|6.1.4|模型参数调整|Temperature / Max Tokens / Top-P 等参数调节|低|BaoDan 原生|
|
||
|6.1.5|连通性测试|一键 Ping 检测模型 API 是否可达,显示延迟|中|BaoDan 原生|
|
||
|
||
#### 6.2 Prompt 提示词管理
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|6.2.1|问答 Prompt 编辑|富文本编辑器,支持变量占位符(如 `{{险种}}`)|高|BaoDan 原生|
|
||
|6.2.2|Prompt 变量配置|配置 Prompt 中使用的变量(如险种、保司),绑定数据源|中|BaoDan 原生|
|
||
|6.2.3|版本管理|每次保存自动生成版本快照,支持查看历史版本与一键回滚|中|BaoDan 原生|
|
||
|6.2.4|即时测试|编辑页内置测试入口,输入测试问题直接预览 Prompt 效果|中|BaoDan 原生|
|
||
|
||
**系统 Prompt 设计**:
|
||
```
|
||
你是一名专业的保险顾问助手,服务于保险代理团队。
|
||
|
||
## 回答规则
|
||
|
||
1. 只基于知识库内容回答。如果没有相关信息,明确告知"当前知识库中未找到相关内容",不要编造答案
|
||
|
||
2. 标注信息来源:回答时引用具体的文档名称或条款编号,方便用户查证
|
||
|
||
3. 回答格式:
|
||
|
||
- 先用 1-2 句话给出核心结论
|
||
|
||
- 再用条目列出详细说明
|
||
|
||
- 最后附上注意事项或免责声明
|
||
|
||
4. 专业术语处理:首次出现的专业术语用括号做简要解释
|
||
|
||
5. 涉及金额/比例:必须精确引用知识库中的数字,不可四舍五入或估算
|
||
|
||
6. 涉及免责/拒赔条款:必须完整列出,不可省略
|
||
|
||
## 特殊场景
|
||
|
||
- 如果用户问"推荐什么产品",引导用户提供年龄、职业、预算等信息
|
||
|
||
- 如果用户的问题模糊,先追问澄清再回答
|
||
|
||
- 如果知识库中有多个产品/条款适用,列出所有适用项并说明区别
|
||
```
|
||
#### 6.3 导出模板管理
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|6.3.1|模板文件上传|上传 Word/PDF 底版模板文件|高|自研|
|
||
|6.3.2|险种与模板映射|不同险种指定不同模板,支持一险种对应多模板(A/B 选择)|高|自研|
|
||
|6.3.3|占位符字段定义|配置模板中哪些字段由 AI 填充,字段名与数据字段映射|高|自研|
|
||
|
||
#### 6.4 通知告警配置
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|6.4.1|通知渠道设置|邮件 SMTP 配置 / 企微机器人 Webhook URL|中|自研|
|
||
|6.4.2|告警规则配置|API 同步失败 N 次告警 / Token 月消耗超 X 元告警|中|自研|
|
||
|
||
---
|
||
|
||
### M7:数据统计
|
||
|
||
#### 7.1 使用概览
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|7.1.1|核心指标卡片|今日问答数、活跃用户数、知识库命中率等关键指标|高|自研|
|
||
|7.1.2|趋势折线图|近 7/30 天问答量趋势、用户活跃度趋势|高|自研|
|
||
|7.1.3|险种/保司分布饼图|当期问答按险种、按保司维度分布|低|自研|
|
||
|
||
#### 7.2 知识库健康度
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|7.2.1|热门问题 TOP20|按提问频次排序,辅助补充/优化知识库|中|自研|
|
||
|7.2.2|未命中问题汇总|近 7/30 天知识库无法回答的问题列表,标记已处理状态|高|自研|
|
||
|7.2.3|文档覆盖率概览|各险种/保司文档数量、向量化状态、最后更新时间|低|自研|
|
||
|
||
#### 7.3 成本监控
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|7.3.1|Token 用量统计|按模型、按天统计 Token 消耗量和费用|中|自研|
|
||
|7.3.2|费用估算|基于 Token 单价估算当月累计费用,与预算阈值对比|中|自研|
|
||
|7.3.3|费用超限预警|当月费用超过设定阈值时页面内显示警告横幅|中|自研|
|
||
|
||
---
|
||
|
||
### 企微机器人(新增模块)
|
||
|
||
|编号|功能点|功能说明|优先级|实现方式|
|
||
|------|--------|---------|:---:|--------|
|
||
|WB-01|单聊对话|用户在企微私聊机器人,发送文字消息后收到 AI 回复|高|自研 + 企微 API|
|
||
|WB-02|群聊 @触发|在企微群内 @机器人 提问,AI 回复到群内|高|自研 + 企微 API|
|
||
|WB-03|非文本消息处理|用户发图片/表情/文件时,回复「暂不支持该消息类型」|中|自研 + 企微 API|
|
||
|WB-04|消息异步处理|多人同时提问不阻塞,企微 5 秒内返回响应|高|自研|
|
||
|WB-05|回复超长消息处理|AI 回复超过企微限制时自动分段发送|低|自研|
|
||
|
||
**单聊交互流程**:
|
||
```
|
||
|
||
用户私聊机器人发送"重疾险等待期多少天?"
|
||
|
||
-> 企微服务器推送消息到后端
|
||
|
||
-> 后端调 BaoDan API
|
||
|
||
-> BaoDan 检索知识库并生成回答
|
||
|
||
-> 后端通过企微 API 回复用户
|
||
```
|
||
|
||
**群聊交互流程**:
|
||
```
|
||
|
||
群内用户 "@机器人 重疾险等待期多少天?"
|
||
|
||
-> 企微服务器推送消息到后端
|
||
|
||
-> 后端去掉"@机器人"前缀,调 BaoDan API
|
||
|
||
-> 企微 API 回复到群内(所有人可见)
|
||
```
|
||
|
||
---
|
||
|
||
### 企微 OAuth 登录
|
||
|
||
**交互流程**:
|
||
```
|
||
|
||
1. 用户访问系统 -> 点击"企微登录"
|
||
|
||
2. 跳转到企微授权页面 -> 用户点击"同意授权"
|
||
|
||
3. 企微回调后端,携带 code 参数
|
||
|
||
4. 后端用 code 换取企微用户信息(userid、name、department)
|
||
|
||
5. 后端在系统中查找/创建对应用户
|
||
|
||
6. 生成 JWT Token -> 重定向到系统首页
|
||
```
|
||
|
||
---
|
||
|
||
## 四、后端接口详细设计(A1-A8)
|
||
|
||
### A1:认证鉴权
|
||
|
||
#### A1.1 身份认证接口
|
||
|
||
|接口编号|方法|URL|说明|优先级||实现方式|
|
||
|---------|------|-----|------|:---:|--------|--------|
|
||
|A1.1.1|POST|/auth/wework-login|接收企微用户信息,签发 JWT Token|高|自研(调 BaoDan)||
|
||
|A1.1.2|POST|/auth/password-login|账号 + 密码登录,返回 JWT;密码 bcrypt 哈希校验|中|自研(调 BaoDan)||
|
||
|A1.1.3|POST|/auth/refresh-token|刷新过期 Token,返回新 JWT|中|自研(调 BaoDan)|自研(调 BaoDan)|
|
||
|A1.1.4|POST|/auth/logout|吊销 Token(加入黑名单或清除 Redis Session)|中|自研|自研(调 BaoDan)|
|
||
|
||
**A1.1.1 企微登录接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
POST /auth/wework-login
|
||
|
||
{
|
||
|
||
"code": "企微授权回调code",
|
||
|
||
"state": "随机状态值"
|
||
|
||
}
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"message": "success",
|
||
|
||
"data": {
|
||
|
||
"token": "eyJhbGciOiJIUzI1NiIs...",
|
||
|
||
"expires_in": 7200,
|
||
|
||
"user": {
|
||
|
||
"id": "user-001",
|
||
|
||
"username": "张三",
|
||
|
||
"wecom_userid": "zhangsan",
|
||
|
||
"role": "sales",
|
||
|
||
"department": "上海团队"
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
**A1.1.2 账密登录接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
POST /auth/password-login
|
||
|
||
{
|
||
|
||
"username": "admin",
|
||
|
||
"password": "hashed_password"
|
||
|
||
}
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"message": "success",
|
||
|
||
"data": {
|
||
|
||
"token": "eyJhbGciOiJIUzI1NiIs...",
|
||
|
||
"expires_in": 7200
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
**A1.1.3 刷新 Token 接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
POST /auth/refresh-token
|
||
|
||
Headers: { "Authorization": "Bearer <old_token>" }
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"message": "success",
|
||
|
||
"data": {
|
||
|
||
"token": "eyJhbGciOiJIUzI1NiIs...",
|
||
|
||
"expires_in": 7200
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
**A1.1.4 退出登录接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
POST /auth/logout
|
||
|
||
Headers: { "Authorization": "Bearer <token>" }
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"message": "success"
|
||
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### A2:智能问答接口
|
||
|
||
#### A2.1 对话接口
|
||
|
||
|接口编号|方法|URL|说明|优先级||实现方式|
|
||
|---------|------|-----|------|:---:|--------|--------|
|
||
|A2.1.1|POST|/chat/message(SSE 流式)|接收问题 + 会话 ID,调用 RAG 检索 + LLM,SSE 流式返回回答与引用来源|高|自研(调 BaoDan)||
|
||
|A2.1.2|GET|/chat/sessions|获取当前用户的会话列表(分页)|高|自研(调 BaoDan)||
|
||
|A2.1.3|POST|/chat/sessions|创建新会话,返回 session_id|高|自研(调 BaoDan)|自研(调 BaoDan)|
|
||
|A2.1.4|DELETE|/chat/sessions/{id}|软删除指定会话|中|自研(调 BaoDan)|自研(调 BaoDan)|
|
||
|A2.1.5|GET|/chat/sessions/{id}/messages|获取指定会话完整消息记录(含来源引用)|高|自研(调 BaoDan)|自研(调 BaoDan)|
|
||
|A2.1.6|POST|/chat/messages/{id}/feedback|提交回答评分(有用/无用)或纠错内容|中|自研(调 BaoDan)|自研(调 BaoDan)|
|
||
|
||
**A2.1.1 发送消息接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
POST /chat/message
|
||
|
||
Headers: { "Authorization": "Bearer <token>" }
|
||
|
||
{
|
||
|
||
"session_id": "sess-001",
|
||
|
||
"message": "重疾险的等待期是多少天?",
|
||
|
||
"filters": {
|
||
|
||
"险种": "重疾险",
|
||
|
||
"保司": null
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
响应(SSE 流式):
|
||
```
|
||
|
||
data: {"type": "source", "data": {"doc_name": "XX重疾险条款.pdf", "chunk": "等待期为合同生效之日起90天..."}}
|
||
|
||
data: {"type": "delta", "data": "根据"}
|
||
|
||
data: {"type": "delta", "data": "XX重疾险"}
|
||
|
||
data: {"type": "delta", "data": "条款"}
|
||
|
||
data: {"type": "delta", "data": "规定"}
|
||
|
||
data: {"type": "delta", "data": ","}
|
||
|
||
data: {"type": "delta", "data": "等待期为合同生效之日起90天。"}
|
||
|
||
data: {"type": "done", "data": {"message_id": "msg-001", "conversation_id": "conv-001"}}
|
||
```
|
||
|
||
**A2.1.2 获取会话列表接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
GET /chat/sessions?page=1&page_size=20
|
||
|
||
Headers: { "Authorization": "Bearer <token>" }
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"total": 35,
|
||
|
||
"items": [
|
||
|
||
{
|
||
|
||
"id": "sess-001",
|
||
|
||
"title": "重疾险等待期问题",
|
||
|
||
"created_at": "2026-05-31T10:00:00Z",
|
||
|
||
"updated_at": "2026-05-31T10:05:00Z",
|
||
|
||
"message_count": 6
|
||
|
||
}
|
||
|
||
]
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
**A2.1.5 获取消息记录接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
GET /chat/sessions/{id}/messages
|
||
|
||
Headers: { "Authorization": "Bearer <token>" }
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"messages": [
|
||
|
||
{
|
||
|
||
"id": "msg-001",
|
||
|
||
"role": "user",
|
||
|
||
"content": "重疾险的等待期是多少天?",
|
||
|
||
"created_at": "2026-05-31T10:00:00Z"
|
||
|
||
},
|
||
|
||
{
|
||
|
||
"id": "msg-002",
|
||
|
||
"role": "assistant",
|
||
|
||
"content": "根据 XX 重疾险条款规定,等待期为合同生效之日起90天。",
|
||
|
||
"sources": [
|
||
|
||
{"doc_name": "XX重疾险条款.pdf", "chunk": "等待期为合同生效之日起90天..."}
|
||
|
||
],
|
||
|
||
"created_at": "2026-05-31T10:00:05Z"
|
||
|
||
}
|
||
|
||
]
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
**A2.1.6 反馈接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
POST /chat/messages/{id}/feedback
|
||
|
||
Headers: { "Authorization": "Bearer <token>" }
|
||
|
||
{
|
||
|
||
"rating": "helpful",
|
||
|
||
"comment": null
|
||
|
||
}
|
||
```
|
||
|
||
或纠错:
|
||
```json
|
||
|
||
POST /chat/messages/{id}/feedback
|
||
|
||
Headers: { "Authorization": "Bearer <token>" }
|
||
|
||
{
|
||
|
||
"rating": "not_helpful",
|
||
|
||
"comment": "等待期应该是180天而不是90天"
|
||
|
||
}
|
||
```
|
||
#### A2.2 知识库检索接口
|
||
|
||
|接口编号|方法|URL|说明|优先级||实现方式|
|
||
|---------|------|-----|------|:---:|--------|--------|
|
||
|A2.2.1|POST|/retrieval/search|向量检索:输入 query + 筛选条件(险种/保司),返回 Top-K 文档片段与相关度|高|自研(调 BaoDan)||
|
||
|A2.2.2|GET|/retrieval/suggest|基于当前问题生成推荐追问列表(2-3 条)|低|自研||
|
||
|
||
**A2.2.1 检索接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
POST /retrieval/search
|
||
|
||
Headers: { "Authorization": "Bearer <token>" }
|
||
|
||
{
|
||
|
||
"query": "重疾险等待期",
|
||
|
||
"filters": {
|
||
|
||
"险种": "重疾险",
|
||
|
||
"保司": null
|
||
|
||
},
|
||
|
||
"top_k": 5
|
||
|
||
}
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"results": [
|
||
|
||
{
|
||
|
||
"doc_name": "XX重疾险条款.md",
|
||
|
||
"chunk": "等待期为合同生效之日起90天...",
|
||
|
||
"score": 0.92,
|
||
|
||
"metadata": {
|
||
|
||
"险种": "重疾险",
|
||
|
||
"保司": "XX人寿"
|
||
|
||
}
|
||
|
||
}
|
||
|
||
]
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### A3:方案生成接口
|
||
|
||
|接口编号|方法|URL|说明|优先级||实现方式|
|
||
|---------|------|-----|------|:---:|--------|--------|
|
||
|A3.1.1|POST|/proposals/generate|接收客户信息 + 险种,调用 LLM 生成产品推荐方案,返回方案 JSON|高|自研(调 BaoDan)||
|
||
|A3.1.2|GET|/proposals/generate/{task_id}|轮询生成任务状态:pending / processing / done / failed|高|自研||
|
||
|A3.1.3|POST|/proposals/{id}/export|导出推荐方案为 PDF/PPT/Word 格式|高|自研||
|
||
|A3.1.4|POST|/proposals/{id}/share|生成有时效的分享链接(Token 鉴权,N 小时有效)|低|自研|自研(调 BaoDan)|
|
||
|
||
**A3.1.1 生成方案接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
POST /proposals/generate
|
||
|
||
Headers: { "Authorization": "Bearer <token>" }
|
||
|
||
{
|
||
|
||
"customer": {
|
||
|
||
"name": "李四",
|
||
|
||
"age": 35,
|
||
|
||
"gender": "male",
|
||
|
||
"health_status": "健康",
|
||
|
||
"occupation": "软件工程师",
|
||
|
||
"annual_income": 300000,
|
||
|
||
"monthly_budget": 2000
|
||
|
||
},
|
||
|
||
"insurance_types": ["重疾险", "医疗险", "意外险"],
|
||
|
||
"coverage_amount": 500000,
|
||
|
||
"coverage_period": "终身",
|
||
|
||
"existing_policies": []
|
||
|
||
}
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"task_id": "task-001",
|
||
|
||
"status": "processing"
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
**A3.1.2 轮询任务状态接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
GET /proposals/generate/{task_id}
|
||
|
||
Headers: { "Authorization": "Bearer <token>" }
|
||
```
|
||
|
||
响应(生成中):
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"task_id": "task-001",
|
||
|
||
"status": "processing"
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
响应(完成):
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"task_id": "task-001",
|
||
|
||
"status": "done",
|
||
|
||
"proposal": {
|
||
|
||
"id": "prop-001",
|
||
|
||
"plans": [
|
||
|
||
{
|
||
|
||
"name": "基础方案",
|
||
|
||
"total_premium": 4800,
|
||
|
||
"items": [
|
||
|
||
{
|
||
|
||
"product_name": "XX重疾险",
|
||
|
||
"coverage": 300000,
|
||
|
||
"premium": 2400,
|
||
|
||
"reason": "35岁男性投保,性价比高,覆盖120种重疾"
|
||
|
||
}
|
||
|
||
],
|
||
|
||
"summary": "基础保障方案,覆盖重疾、医疗、意外三大类..."
|
||
|
||
}
|
||
|
||
]
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
**A3.1.3 方案导出接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
POST /proposals/{id}/export
|
||
|
||
Headers: { "Authorization": "Bearer <token>" }
|
||
|
||
{
|
||
|
||
"format": "pdf"
|
||
|
||
}
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"download_url": "https://your-domain.com/api/proposals/{id}/download?token=xxx",
|
||
|
||
"expire_at": "2026-06-03T10:00:00Z"
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
**A3.1.4 分享链接接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
POST /proposals/{id}/share
|
||
|
||
Headers: { "Authorization": "Bearer <token>" }
|
||
|
||
{
|
||
|
||
"expire_hours": 72
|
||
|
||
}
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"share_url": "https://your-domain.com/shared/prop-001?token=xxx",
|
||
|
||
"expire_at": "2026-06-03T10:00:00Z"
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### A4:知识库接口
|
||
|
||
#### A4.1 文档管理接口
|
||
|
||
|接口编号|方法|URL|说明|优先级||实现方式|
|
||
|---------|------|-----|------|:---:|--------|--------|
|
||
|A4.1.1|POST|/kb/documents/upload|多文件上传,返回 upload_id;触发异步处理流水线(格式转换 -> 向量化)|高|自研(调 BaoDan)||
|
||
|A4.1.2|GET|/kb/documents|文档列表,支持按险种/保司/状态/关键词筛选|高|自研(调 BaoDan)||
|
||
|A4.1.3|GET|/kb/documents/{id}/status|查询单个文档处理流水线状态|高|自研(调 BaoDan)|自研(调 BaoDan)|
|
||
|A4.1.4|POST|/kb/documents/{id}/retry|手动重试处理失败的文档|高|自研(调 BaoDan)|自研(调 BaoDan)|
|
||
|A4.1.5|PATCH|/kb/documents/{id}|更新文档元数据(编号/分类/标签)|中|自研(调 BaoDan)|自研(调 BaoDan)|
|
||
|A4.1.6|DELETE|/kb/documents/{id}|归档/软删除文档,下线向量索引|中|自研(调 BaoDan)|自研(调 BaoDan)|
|
||
|
||
**A4.1.1 上传文档接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
POST /kb/documents/upload
|
||
|
||
Content-Type: multipart/form-data
|
||
|
||
Headers: { "Authorization": "Bearer <token>" }
|
||
|
||
Form Fields:
|
||
|
||
files: [file1.md, file2.md, file3.md]
|
||
|
||
险种: "重疾险"
|
||
|
||
保司: "XX人寿"
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"upload_id": "upload-001",
|
||
|
||
"documents": [
|
||
|
||
{"doc_id": "doc-001", "filename": "file1.md", "status": "processing"},
|
||
|
||
{"doc_id": "doc-002", "filename": "file2.md", "status": "processing"},
|
||
|
||
{"doc_id": "doc-003", "filename": "file3.md", "status": "processing"}
|
||
|
||
]
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
**A4.1.2 文档列表接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
GET /kb/documents?page=1&page_size=20&险种=重疾险&status=completed&keyword=等待期
|
||
|
||
Headers: { "Authorization": "Bearer <token>" }
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"total": 150,
|
||
|
||
"items": [
|
||
|
||
{
|
||
|
||
"doc_id": "doc-001",
|
||
|
||
"filename": "XX重疾险条款.md",
|
||
|
||
"编号": "CJ-XX-001",
|
||
|
||
"险种": "重疾险",
|
||
|
||
"保司": "XX人寿",
|
||
|
||
"tags": ["产品条款", "等待期"],
|
||
|
||
"status": "completed",
|
||
|
||
"uploaded_at": "2026-05-15T10:00:00Z",
|
||
|
||
"processed_at": "2026-05-15T10:05:00Z"
|
||
|
||
}
|
||
|
||
]
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
**A4.1.3 文档状态接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
GET /kb/documents/{id}/status
|
||
|
||
Headers: { "Authorization": "Bearer <token>" }
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"doc_id": "doc-001",
|
||
|
||
"filename": "XX重疾险条款.md",
|
||
|
||
"pipeline": [
|
||
|
||
{"stage": "upload", "status": "completed", "time": "2026-05-15T10:00:01Z"},
|
||
|
||
{"stage": "format_convert", "status": "completed", "time": "2026-05-15T10:00:02Z"},
|
||
|
||
{"stage": "chunking", "status": "completed", "time": "2026-05-15T10:00:03Z", "chunk_count": 45},
|
||
|
||
{"stage": "embedding", "status": "completed", "time": "2026-05-15T10:05:00Z"}
|
||
|
||
]
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
#### A4.2 数据源同步接口
|
||
|
||
|接口编号|方法|URL|说明|优先级||实现方式|
|
||
|---------|------|-----|------|:---:|--------|--------|
|
||
|A4.2.1|GET|/kb/datasources|获取保司 API 数据源列表及同步状态|高|自研||
|
||
|A4.2.2|POST|/kb/datasources|新建数据源配置|高|自研||
|
||
|A4.2.3|POST|/kb/datasources/{id}/sync|手动触发指定数据源立即同步|高|自研|自研(调 BaoDan)|
|
||
|A4.2.4|GET|/kb/datasources/{id}/logs|获取同步历史日志(变更明细)|低|自研|自研(调 BaoDan)|
|
||
|
||
**A4.2.1 数据源列表接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
GET /kb/datasources
|
||
|
||
Headers: { "Authorization": "Bearer <token>" }
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": [
|
||
|
||
{
|
||
|
||
"id": "ds-001",
|
||
|
||
"name": "XX人寿产品接口",
|
||
|
||
"api_url": "https://api.xxlife.com/products",
|
||
|
||
"sync_frequency": "0 2 * * *",
|
||
|
||
"last_sync": "2026-05-31T02:00:00Z",
|
||
|
||
"last_sync_status": "success",
|
||
|
||
"last_sync_count": 156
|
||
|
||
}
|
||
|
||
]
|
||
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### A5:用户权限接口
|
||
|
||
|接口编号|方法|URL|说明|优先级||实现方式|
|
||
|---------|------|-----|------|:---:|--------|--------|
|
||
|A5.1.1|GET/POST/PUT/DELETE|/admin/users|用户 CRUD;停用时吊销所有 Token|高|自研(调 BaoDan)||
|
||
|A5.1.2|POST|/admin/users/batch-import|解析 Excel 模板,批量创建用户|低|自研||
|
||
|A5.1.3|GET/POST/PUT/DELETE|/admin/roles|角色 CRUD 及权限项绑定|高|自研|自研(调 BaoDan)|
|
||
|
||
**A5.1.1 用户管理接口详细设计**:
|
||
|
||
列表:
|
||
```json
|
||
|
||
GET /admin/users?page=1&page_size=20&role=sales&department=上海团队
|
||
|
||
Headers: { "Authorization": "Bearer <admin_token>" }
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"total": 50,
|
||
|
||
"items": [
|
||
|
||
{
|
||
|
||
"id": "user-001",
|
||
|
||
"username": "张三",
|
||
|
||
"wecom_userid": "zhangsan",
|
||
|
||
"role": "sales",
|
||
|
||
"department": "上海团队",
|
||
|
||
"status": "active",
|
||
|
||
"created_at": "2026-05-01T10:00:00Z",
|
||
|
||
"last_active_at": "2026-05-31T09:30:00Z"
|
||
|
||
}
|
||
|
||
]
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
新增:
|
||
```json
|
||
|
||
POST /admin/users
|
||
|
||
{
|
||
|
||
"username": "李四",
|
||
|
||
"wecom_userid": "lisi",
|
||
|
||
"role": "sales",
|
||
|
||
"department": "上海团队"
|
||
|
||
}
|
||
```
|
||
|
||
**A5.1.3 角色管理接口详细设计**:
|
||
|
||
列表:
|
||
```json
|
||
|
||
GET /admin/roles
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": [
|
||
|
||
{
|
||
|
||
"id": "role-001",
|
||
|
||
"name": "超级管理员",
|
||
|
||
"permissions": ["*"],
|
||
|
||
"builtin": true
|
||
|
||
},
|
||
|
||
{
|
||
|
||
"id": "role-002",
|
||
|
||
"name": "管理员",
|
||
|
||
"permissions": ["kb_manage", "log_view", "user_manage", "config_manage"],
|
||
|
||
"builtin": true
|
||
|
||
},
|
||
|
||
{
|
||
|
||
"id": "role-003",
|
||
|
||
"name": "销售主管",
|
||
|
||
"permissions": ["chat", "proposal", "kb_view", "team_stats"],
|
||
|
||
"builtin": true
|
||
|
||
},
|
||
|
||
{
|
||
|
||
"id": "role-004",
|
||
|
||
"name": "销售人员",
|
||
|
||
"permissions": ["chat", "proposal"],
|
||
|
||
"builtin": true
|
||
|
||
},
|
||
|
||
{
|
||
|
||
"id": "role-005",
|
||
|
||
"name": "客户",
|
||
|
||
"permissions": ["chat"],
|
||
|
||
"builtin": true
|
||
|
||
}
|
||
|
||
]
|
||
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### A6:系统配置接口
|
||
|
||
|接口编号|方法|URL|说明|优先级||实现方式|
|
||
|---------|------|-----|------|:---:|--------|--------|
|
||
|A6.1.1|GET/POST/PUT|/admin/llm-configs|LLM 配置 CRUD;Key 写入时服务端加密|高|自研(调 BaoDan)||
|
||
|A6.1.2|POST|/admin/llm-configs/{id}/ping|测试模型 API 连通性,返回延迟 ms|中|自研(调 BaoDan)||
|
||
|A6.1.3|GET/POST|/admin/prompts|Prompt 模板 CRUD,保存时自动生成版本快照|高|自研(调 BaoDan)|自研(调 BaoDan)|
|
||
|A6.1.4|POST|/admin/prompts/test|用测试问题即时验证 Prompt 效果|中|自研(调 BaoDan)|自研(调 BaoDan)|
|
||
|
||
**A6.1.1 LLM 配置接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
POST /admin/llm-configs
|
||
|
||
Headers: { "Authorization": "Bearer <admin_token>" }
|
||
|
||
{
|
||
|
||
"provider": "deepseek",
|
||
|
||
"model": "deepseek-chat",
|
||
|
||
"api_key": "sk-xxxxxxxxxxxx",
|
||
|
||
"base_url": "https://api.deepseek.com/v1",
|
||
|
||
"is_default": true,
|
||
|
||
"params": {
|
||
|
||
"temperature": 0.7,
|
||
|
||
"max_tokens": 4096,
|
||
|
||
"top_p": 0.9
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"id": "llm-001",
|
||
|
||
"provider": "deepseek",
|
||
|
||
"model": "deepseek-chat",
|
||
|
||
"api_key_masked": "sk-xxxx****xxxx",
|
||
|
||
"is_default": true,
|
||
|
||
"status": "active"
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
**A6.1.2 连通性测试接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
POST /admin/llm-configs/{id}/ping
|
||
|
||
Headers: { "Authorization": "Bearer <admin_token>" }
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"reachable": true,
|
||
|
||
"latency_ms": 320,
|
||
|
||
"model": "deepseek-chat"
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
**A6.1.3 Prompt 管理接口详细设计**:
|
||
|
||
列表:
|
||
```json
|
||
|
||
GET /admin/prompts
|
||
```
|
||
|
||
新增/更新:
|
||
```json
|
||
|
||
POST /admin/prompts
|
||
|
||
{
|
||
|
||
"name": "保险顾问系统提示词",
|
||
|
||
"type": "system",
|
||
|
||
"content": "你是一名专业的保险顾问助手...",
|
||
|
||
"variables": ["险种", "保司"]
|
||
|
||
}
|
||
```
|
||
|
||
响应(保存时自动生成版本快照):
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"id": "prompt-001",
|
||
|
||
"name": "保险顾问系统提示词",
|
||
|
||
"version": 3,
|
||
|
||
"versions": [
|
||
|
||
{"version": 1, "created_at": "2026-05-01T10:00:00Z"},
|
||
|
||
{"version": 2, "created_at": "2026-05-15T10:00:00Z"},
|
||
|
||
{"version": 3, "created_at": "2026-05-31T10:00:00Z"}
|
||
|
||
]
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
**A6.1.4 Prompt 测试接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
POST /admin/prompts/test
|
||
|
||
{
|
||
|
||
"prompt_id": "prompt-001",
|
||
|
||
"test_query": "重疾险等待期多少天?"
|
||
|
||
}
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"response": "根据条款规定,重疾险等待期为90天...",
|
||
|
||
"latency_ms": 1200,
|
||
|
||
"token_usage": {"prompt_tokens": 520, "completion_tokens": 180}
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### A7:留痕日志接口
|
||
|
||
|接口编号|方法|URL|说明|优先级||实现方式|
|
||
|---------|------|-----|------|:---:|--------|--------|
|
||
|A7.1.1|GET|/admin/logs/chat|问答记录查询(分页 + 多维筛选 + 关键词),支持导出 CSV|高|自研(调 BaoDan)||
|
||
|A7.1.2|GET|/admin/logs/chat/export|导出对话记录为 CSV 文件流|高|自研(调 BaoDan)||
|
||
|A7.1.3|GET|/admin/logs/system|系统操作日志查询|中|自研||
|
||
|
||
**A7.1.1 问答日志接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
GET /admin/logs/chat?page=1&page_size=50&user_id=user-001&start_date=2026-05-01&end_date=2026-05-31&keyword=重疾险&rating=not_helpful&export=csv
|
||
|
||
Headers: { "Authorization": "Bearer <admin_token>" }
|
||
```
|
||
|
||
响应(JSON 模式):
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"total": 1200,
|
||
|
||
"items": [
|
||
|
||
{
|
||
|
||
"id": "msg-001",
|
||
|
||
"user_id": "user-001",
|
||
|
||
"username": "张三",
|
||
|
||
"session_id": "sess-001",
|
||
|
||
"question": "重疾险等待期多少天?",
|
||
|
||
"answer": "根据 XX 重疾险条款规定,等待期为合同生效之日起90天。",
|
||
|
||
"sources": ["XX重疾险条款.md"],
|
||
|
||
"rating": "helpful",
|
||
|
||
"created_at": "2026-05-31T10:00:00Z"
|
||
|
||
}
|
||
|
||
]
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
导出(CSV 模式):
|
||
```
|
||
|
||
当 export=csv 时,返回 CSV 文件流
|
||
|
||
Content-Type: text/csv
|
||
|
||
Content-Disposition: attachment; filename="chat_logs_202605.csv"
|
||
```
|
||
|
||
**A7.1.2 导出对话记录接口详细设计**:
|
||
|
||
请求:同 A7.1.1,export=csv 时返回 CSV 文件流
|
||
|
||
**A7.1.3 系统日志接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
GET /admin/logs/system?page=1&page_size=50&action=login&user_id=user-001
|
||
|
||
Headers: { "Authorization": "Bearer <admin_token>" }
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"total": 200,
|
||
|
||
"items": [
|
||
|
||
{
|
||
|
||
"id": "log-001",
|
||
|
||
"action": "login",
|
||
|
||
"user_id": "user-001",
|
||
|
||
"username": "张三",
|
||
|
||
"ip": "192.168.1.100",
|
||
|
||
"device": "Chrome/120 Windows",
|
||
|
||
"detail": "企微 OAuth 登录",
|
||
|
||
"created_at": "2026-05-31T09:00:00Z"
|
||
|
||
}
|
||
|
||
]
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### A8:统计报表接口
|
||
|
||
|接口编号|方法|URL|说明|优先级||实现方式|
|
||
|---------|------|-----|------|:---:|--------|--------|
|
||
|A8.1.1|GET|/stats/overview|使用概览:今日问答数、活跃用户数、知识库命中率|高|自研||
|
||
|A8.1.2|GET|/stats/trend|指定指标的日趋势数据(折线图数据源)|中|自研||
|
||
|A8.1.3|GET|/stats/kb-health|知识库健康度:热门问题/未命中列表/文档覆盖率|中|自研||
|
||
|A8.1.4|GET|/stats/token-cost|Token 消耗统计(按模型/用途/时间段),含费用估算|中|自研|自研(调 BaoDan)|
|
||
|
||
**A8.1.1 使用概览接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
GET /stats/overview
|
||
|
||
Headers: { "Authorization": "Bearer <admin_token>" }
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"today_chats": 45,
|
||
|
||
"active_users": 12,
|
||
|
||
"kb_hit_rate": 0.87,
|
||
|
||
"total_documents": 1500
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
**A8.1.2 趋势数据接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
GET /stats/trend?metric=chat_count&start_date=2026-05-01&end_date=2026-05-31&granularity=day
|
||
|
||
Headers: { "Authorization": "Bearer <admin_token>" }
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"metric": "chat_count",
|
||
|
||
"granularity": "day",
|
||
|
||
"data": [
|
||
|
||
{"date": "2026-05-01", "value": 45},
|
||
|
||
{"date": "2026-05-02", "value": 62},
|
||
|
||
{"date": "2026-05-03", "value": 38}
|
||
|
||
]
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
**A8.1.3 知识库健康度接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
GET /stats/kb-health?days=30
|
||
|
||
Headers: { "Authorization": "Bearer <admin_token>" }
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"hot_questions": [
|
||
|
||
{"question": "重疾险等待期多少天", "count": 230},
|
||
|
||
{"question": "车险理赔流程", "count": 180}
|
||
|
||
],
|
||
|
||
"missed_questions": [
|
||
|
||
{"question": "XX新产品费率", "count": 45, "handled": false},
|
||
|
||
{"question": "跨境保险政策", "count": 30, "handled": true}
|
||
|
||
],
|
||
|
||
"coverage": {
|
||
|
||
"寿险": {"doc_count": 320, "status": "completed", "last_update": "2026-05-20"},
|
||
|
||
"重疾险": {"doc_count": 280, "status": "completed", "last_update": "2026-05-18"},
|
||
|
||
"医疗险": {"doc_count": 150, "status": "completed", "last_update": "2026-05-15"}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
**A8.1.4 Token 消耗接口详细设计**:
|
||
|
||
请求:
|
||
```json
|
||
|
||
GET /stats/token-cost?start_date=2026-05-01&end_date=2026-05-31&group_by=model
|
||
|
||
Headers: { "Authorization": "Bearer <admin_token>" }
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"data": {
|
||
|
||
"total_tokens": 2500000,
|
||
|
||
"total_cost_usd": 12.5,
|
||
|
||
"by_model": [
|
||
|
||
{
|
||
|
||
"model": "deepseek-chat",
|
||
|
||
"input_tokens": 1500000,
|
||
|
||
"output_tokens": 500000,
|
||
|
||
"cost_usd": 10.0
|
||
|
||
},
|
||
|
||
{
|
||
|
||
"model": "deepseek-embedding",
|
||
|
||
"input_tokens": 500000,
|
||
|
||
"output_tokens": 0,
|
||
|
||
"cost_usd": 2.5
|
||
|
||
}
|
||
|
||
],
|
||
|
||
"daily_trend": [
|
||
|
||
{"date": "2026-05-01", "tokens": 80000, "cost_usd": 0.4},
|
||
|
||
{"date": "2026-05-02", "tokens": 95000, "cost_usd": 0.475}
|
||
|
||
]
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 五、非功能性需求
|
||
|
||
### 5.1 性能要求
|
||
|
||
|指标|要求|说明|
|
||
|------|------|------|
|
||
|AI 问答响应时间|< 15 秒|从发送到开始看到回复(Streaming 首字)|
|
||
|产品推荐生成时间|< 60 秒|从提交表单到方案展示|
|
||
|页面首屏加载|< 3 秒|首次访问|
|
||
|同时在线用户|>= 20 人|单服务器承载|
|
||
|知识库容量|9GB / 50 万 chunk|当前规模|
|
||
|
||
### 5.2 可用性要求
|
||
|
||
|指标|要求|说明|
|
||
|------|------|------|
|
||
|系统可用率|>= 99%|工作时间 9:00-21:00|
|
||
|数据备份|每日自动|PostgreSQL + 知识库数据|
|
||
|故障恢复|< 30 分钟|重启服务后自动恢复|
|
||
|企微消息不丢失|是|5 秒内返回"处理中",异步生成回答|
|
||
|
||
### 5.3 安全要求
|
||
|
||
|项目|要求|说明|
|
||
|------|------|------|
|
||
|传输加密|HTTPS|全站 HTTPS,企微要求|
|
||
|API Key 存储|AES 加密|DeepSeek Key 等不得明文存储|
|
||
|企微消息验证|签名校验|回调 URL 必须验证企微签名|
|
||
|XSS 防护|输入过滤 + 输出编码|所有用户输入经过转义|
|
||
|SQL 注入防护|参数化查询|使用 ORM 或参数化查询|
|
||
|密码策略|bcrypt 哈希|密码不可明文存储|
|
||
|Token 有效期|JWT + 刷新机制|Access Token 2 小时过期|
|
||
|
||
### 5.4 兼容性要求
|
||
|
||
|平台|浏览器|最低版本|
|
||
|------|--------|---------|
|
||
|PC|Chrome / Edge / Firefox|90+|
|
||
|手机|微信/企微内置浏览器|iOS 14+ / Android 10+|
|
||
|手机|手机 Chrome|90+|
|
||
|
||
### 5.5 部署环境
|
||
|
||
|项目|要求||
|
||
|------|------|--------|
|
||
|服务器|自有服务器,Linux(推荐 Ubuntu 22.04)||
|
||
|CPU|4 核+||
|
||
|内存|8 GB+||
|
||
|磁盘|100 GB+ SSD||
|
||
|网络|公网 IP + 域名 + HTTPS 证书||
|
||
|运行时|Python 3.12 + Node.js 22 + PostgreSQL 15 + Redis 6||
|
||
|
||
---
|
||
|
||
## 六、数据库设计
|
||
|
||
### 6.1 数据存储说明
|
||
|
||
|数据类型|存储位置|说明|
|
||
|---------|---------|------|
|
||
|BaoDan 核心数据(用户、对话、知识库)|BaoDan PostgreSQL|BaoDan 自己管理,不直接操作|
|
||
|企微用户映射|你的 PostgreSQL|企微 userid 与 BaoDan 用户的对应关系|
|
||
|推荐方案记录|你的 PostgreSQL|产品推荐的历史记录|
|
||
|BaoDan 服务配置|BaoDan PostgreSQL|模型配置、Prompt、工作流等|
|
||
|
||
### 6.2 自研表结构
|
||
|
||
#### 表 1:wecom_user_mapping(企微用户映射表)
|
||
|
||
**用途**:存储企微用户 ID 与 BaoDan 用户 ID 的对应关系,企微 OAuth 登录时查找或创建用户。
|
||
|
||
|字段|类型|约束|说明|
|
||
|------|------|------|------|
|
||
|id|SERIAL|PRIMARY KEY|自增主键|
|
||
|wecom_userid|VARCHAR(64)|NOT NULL, UNIQUE|企微用户 ID|
|
||
|baodan_user_id|VARCHAR(64)|NOT NULL|BaoDan 内部用户 ID|
|
||
|username|VARCHAR(128)||用户姓名|
|
||
|department|VARCHAR(128)||所属部门|
|
||
|role|VARCHAR(32)|DEFAULT 'sales'|角色:super_admin/admin/manager/sales|
|
||
|status|VARCHAR(16)|DEFAULT 'active'|状态:active/disabled|
|
||
|created_at|TIMESTAMP|DEFAULT NOW()|创建时间|
|
||
|last_active_at|TIMESTAMP||最后活跃时间|
|
||
|
||
**索引**:
|
||
|
||
- UNIQUE INDEX ON wecom_userid
|
||
|
||
- INDEX ON baodan_user_id
|
||
|
||
#### 表 2:recommendation_records(推荐方案记录表)
|
||
|
||
**用途**:记录每次产品推荐的输入参数和生成结果,用于历史查询和方案对比。
|
||
|
||
|字段|类型|约束|说明|
|
||
|------|------|------|------|
|
||
|id|SERIAL|PRIMARY KEY|自增主键|
|
||
|user_id|VARCHAR(64)|NOT NULL, FOREIGN KEY|创建人(关联 wecom_user_mapping)|
|
||
|customer_name|VARCHAR(64)||客户姓名|
|
||
|customer_age|SMALLINT||客户年龄|
|
||
|customer_gender|VARCHAR(8)||性别:male/female|
|
||
|health_status|VARCHAR(32)||健康状况|
|
||
|occupation|VARCHAR(64)||职业|
|
||
|annual_income|INTEGER||年收入(元)|
|
||
|monthly_budget|INTEGER||月预算(元)|
|
||
|insurance_types|TEXT||关注险种(JSON 数组,如 ["重疾险","医疗险"])|
|
||
|coverage_amount|INTEGER||保额目标(元)|
|
||
|coverage_period|VARCHAR(32)||保障期限|
|
||
|existing_policies|TEXT||已有保单(JSON,可为空)|
|
||
|generated_plan|TEXT||生成的方案内容(Markdown 文本)|
|
||
|plan_variants|TEXT||多套方案(JSON:基础/均衡/全面)|
|
||
|baodan_task_id|VARCHAR(64)||BaoDan Workflow 任务 ID|
|
||
|status|VARCHAR(16)|DEFAULT 'pending'|状态:pending/processing/done/failed|
|
||
|error_message|TEXT||失败原因(可为空)|
|
||
|created_at|TIMESTAMP|DEFAULT NOW()|创建时间|
|
||
|completed_at|TIMESTAMP||生成完成时间|
|
||
|
||
**索引**:
|
||
|
||
- INDEX ON user_id
|
||
|
||
- INDEX ON status
|
||
|
||
- INDEX ON created_at
|
||
|
||
#### 表 3:system_operation_logs(系统操作日志表)
|
||
|
||
**用途**:记录关键系统操作(登录、知识库变更、配置修改等),用于审计。
|
||
|
||
|字段|类型|约束|说明|
|
||
|------|------|------|------|
|
||
|id|SERIAL|PRIMARY KEY|自增主键|
|
||
|user_id|VARCHAR(64)|NOT NULL|操作人|
|
||
|action|VARCHAR(32)|NOT NULL|操作类型:login/logout/upload/delete/config_change|
|
||
|target_type|VARCHAR(32)||操作对象类型:document/user/prompt/llm_config|
|
||
|target_id|VARCHAR(64)||操作对象 ID|
|
||
|detail|JSONB||操作详情(如 {"old_value": "...", "new_value": "..."})|
|
||
|ip|VARCHAR(45)||操作 IP|
|
||
|user_agent|VARCHAR(256)||设备信息|
|
||
|created_at|TIMESTAMP|DEFAULT NOW()|操作时间|
|
||
|
||
**索引**:
|
||
|
||
- INDEX ON user_id
|
||
|
||
- INDEX ON action
|
||
|
||
- INDEX ON created_at
|
||
|
||
---
|
||
|
||
## 七、页面结构与导航
|
||
|
||
### 7.1 页面清单
|
||
|
||
|页面|路径|说明|权限|
|
||
|------|------|------|------|
|
||
|登录页|/login|企微 OAuth + 账密登录|所有人|
|
||
|对话页|/chat|智能问答主界面(BaoDan WebApp)|销售/主管/管理员|
|
||
|产品推荐页|/recommend|填写客户需求 + 查看生成结果|销售/主管/管理员|
|
||
|方案历史页|/recommend/history|历史推荐方案列表|销售/主管/管理员|
|
||
|管理后台首页|/admin|管理后台入口|管理员/超级管理员|
|
||
|知识库管理|/admin/knowledge-base|文档上传/列表/状态监控|管理员|
|
||
|对话日志|/admin/logs/chat|问答记录查询/导出|管理员|
|
||
|系统日志|/admin/logs/system|操作日志查询|超级管理员|
|
||
|用户管理|/admin/users|用户 CRUD|超级管理员|
|
||
|角色管理|/admin/roles|角色权限配置|超级管理员|
|
||
|LLM 配置|/admin/llm-configs|模型 API Key 管理|超级管理员|
|
||
|Prompt 管理|/admin/prompts|提示词编辑/版本管理|管理员|
|
||
|数据统计|/admin/stats|趋势图/健康度/成本|管理员|
|
||
|
||
### 7.2 导航结构
|
||
```
|
||
|
||
[登录页]
|
||
|
||
+-- [用户端]
|
||
|
||
| |-- 左侧导航栏
|
||
|
||
| | |-- 智能问答 -> /chat
|
||
|
||
| | |-- 产品推荐 -> /recommend
|
||
|
||
| | |-- 历史方案 -> /recommend/history
|
||
|
||
| | +-- (销售人员只能看到这三项)
|
||
|
||
||
|
||
|
||
| +-- 右上角:用户信息 + 退出登录
|
||
|
||
+-- [管理端](仅管理员可见)
|
||
|
||
|-- 左侧导航栏
|
||
|
||
| |-- 知识库管理 -> /admin/knowledge-base
|
||
|
||
| |-- 对话日志 -> /admin/logs/chat
|
||
|
||
| |-- 系统日志 -> /admin/logs/system(仅超级管理员)
|
||
|
||
| |-- 用户管理 -> /admin/users(仅超级管理员)
|
||
|
||
| |-- 角色管理 -> /admin/roles(仅超级管理员)
|
||
|
||
| |-- LLM 配置 -> /admin/llm-configs(仅超级管理员)
|
||
|
||
| |-- Prompt 管理 -> /admin/prompts
|
||
|
||
| +-- 数据统计 -> /admin/stats
|
||
|
||
+-- 顶部:管理后台标题 + 切换到用户端
|
||
```
|
||
### 7.3 页面交互细节
|
||
|
||
#### 对话页(/chat)
|
||
```
|
||
|
||
+---------------------------------------------------+
|
||
|
||
|[新建对话] [会话列表侧边栏]|
|
||
||
|
||
|+-----------------------------------------------+|
|
||
||用户: 重疾险等待期多少天?||
|
||
|+-----------------------------------------------+|||
|
||
||
|
||
|+-----------------------------------------------+|
|
||
||AI: 根据 XX 重疾险条款规定...||
|
||
||[来源: XX重疾险条款.md]||
|
||
||[点赞] [点踩] [复制]||
|
||
|+-----------------------------------------------+|||
|
||
||
|
||
|+-----------------------------------------------+|
|
||
||[险种筛选: 全部 v] [保司筛选: 全部 v]||
|
||
||||
|
||
||输入框 [发送]||
|
||
|+-----------------------------------------------+|||
|
||
|
||
+---------------------------------------------------+
|
||
```
|
||
#### 产品推荐页(/recommend)
|
||
```
|
||
|
||
+---------------------------------------------------+
|
||
|
||
|产品推荐 - 新建方案|
|
||
||
|
||
|[客户信息]|
|
||
|姓名: [____] 年龄: [____] 性别: (o)男 ( )女|
|
||
|健康状况: [健康 v] 职业: [__________]|
|
||
|年收入: [____]万 月预算: [____]元|
|
||
||
|
||
|[关注险种] [x]重疾险 [x]医疗险 [ ]意外险 ...|
|
||
||
|
||
|[保障需求]|
|
||
|保额目标: [====o========] 50万|
|
||
|保障期限: [终身 v]|
|
||
||
|
||
|[生成方案]|
|
||
||
|
||
|+-----------------------------------------------+|
|
||
||方案预览区||
|
||
||=== 基础方案 (年保费: 4,800元) ===||
|
||
||1. XX重疾险 - 保额30万 - 年保费2,400元||
|
||
||推荐理由: ...||
|
||
||2. XX医疗险 - 年保费1,200元||
|
||
||...||
|
||
||||
|
||
||[浏览器打印] [重新生成]||
|
||
|+-----------------------------------------------+|||
|
||
|
||
+---------------------------------------------------+
|
||
```
|
||
|
||
---
|
||
|
||
## 八、企微机器人详细规格
|
||
|
||
### 8.1 接入配置
|
||
|
||
|配置项|说明|在哪里获取|
|
||
|--------|------|-----------|
|
||
|CorpID|企业 ID|企微管理后台 -> 我的企业|
|
||
|AgentID|应用 ID|企微管理后台 -> 应用管理 -> 创建应用|
|
||
|AgentSecret|应用密钥|应用详情页|
|
||
|Token|回调消息验证 Token|应用 -> 接收消息 -> 设置 API 接收(自行生成)|
|
||
|EncodingAESKey|消息加密密钥|同上(随机生成 43 位字符串)|
|
||
|
||
### 8.2 回调 URL 验证(GET)
|
||
|
||
企微在配置回调 URL 时会发送 GET 请求验证:
|
||
```
|
||
|
||
GET /api/wecom/callback?msg_signature=xxx×tamp=xxx&nonce=xxx&echostr=xxx
|
||
```
|
||
|
||
验证流程:
|
||
|
||
1. 将 Token、timestamp、nonce、echostr 按字典序排列拼接
|
||
|
||
2. 对拼接字符串做 SHA1 哈希
|
||
|
||
3. 比较哈希结果与 msg_signature
|
||
|
||
4. 验证通过则返回解密后的 echostr
|
||
|
||
### 8.3 消息接收(POST)
|
||
|
||
企微用户发消息时推送的加密 XML:
|
||
```xml
|
||
|
||
<xml>
|
||
|
||
<ToUserName><![CDATA[你的企业ID]]></ToUserName>
|
||
|
||
<Encrypt><![CDATA[加密后的消息]]></Encrypt>
|
||
|
||
</xml>
|
||
```
|
||
|
||
解密后的消息体(text 类型):
|
||
```xml
|
||
|
||
<xml>
|
||
|
||
<ToUserName><![CDATA[你的企业ID]]></ToUserName>
|
||
|
||
<FromUserName><![CDATA[用户UserID]]></FromUserName>
|
||
|
||
<CreateTime>1348831860</CreateTime>
|
||
|
||
<MsgType><![CDATA[text]]></MsgType>
|
||
|
||
<Content><![CDATA[重疾险等待期多少天]]></Content>
|
||
|
||
<MsgId>1234567890123456</MsgId>
|
||
|
||
<AgentID>1000002</AgentID>
|
||
|
||
</xml>
|
||
```
|
||
|
||
群聊消息额外字段:
|
||
```xml
|
||
|
||
<xml>
|
||
|
||
...
|
||
|
||
<ChatId><![CDATA[群聊ID]]></ChatId>
|
||
|
||
<Content><![CDATA[@机器人 重疾险等待期多少天]]></Content>
|
||
|
||
...
|
||
|
||
</xml>
|
||
```
|
||
### 8.4 消息回复
|
||
|
||
通过企微 API 发送文本消息:
|
||
```
|
||
|
||
POST https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=ACCESS_TOKEN
|
||
```
|
||
|
||
请求体:
|
||
```json
|
||
|
||
{
|
||
|
||
"touser": "用户UserID",
|
||
|
||
"msgtype": "text",
|
||
|
||
"agentid": 1000002,
|
||
|
||
"text": {
|
||
|
||
"content": "根据 XX 重疾险条款规定,等待期为90天..."
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
群聊回复到群:
|
||
```
|
||
|
||
POST https://qyapi.weixin.qq.com/cgi-bin/appchat/send?access_token=ACCESS_TOKEN
|
||
```
|
||
|
||
请求体:
|
||
```json
|
||
|
||
{
|
||
|
||
"chatid": "群聊ID",
|
||
|
||
"msgtype": "text",
|
||
|
||
"text": {
|
||
|
||
"content": "根据 XX 重疾险条款规定,等待期为90天..."
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
### 8.5 消息长度限制
|
||
|
||
|场景|限制|处理方式|
|
||
|------|------|---------|
|
||
|单条文本消息|最大 2048 字节|超过时分段发送,每段间隔 500ms|
|
||
|Access Token|有效期 2 小时|代码内缓存,提前 5 分钟刷新|
|
||
|回调响应时间|5 秒内|先返回"success",异步处理消息和回复|
|
||
|
||
### 8.6 非文本消息处理
|
||
|
||
|消息类型|处理方式||
|
||
|---------|---------|--------|
|
||
|图片 (image)|回复"暂不支持图片识别,请用文字描述您的问题"||
|
||
|语音 (voice)|回复"暂不支持语音识别,请用文字描述您的问题"||
|
||
|视频 (video)|回复"暂不支持视频消息"||
|
||
|文件 (file)|回复"暂不支持文件消息"||
|
||
|位置 (location)|忽略不回复||
|
||
|事件 (event)|忽略不回复(如成员加入/退出事件)||
|
||
|
||
---
|
||
|
||
## 九、BaoDan 集成规范
|
||
|
||
> **架构变更**:自研代码直接运行在 BaoDan 进程内(Flask Blueprint),调用 BaoDan 的 RAG 引擎和 LLM 服务时**直接 import Python 函数**,不走 HTTP API。企微回调 URL 直接指向 BaoDan 服务端口。
|
||
|
||
### 9.1 后端调用方式
|
||
|
||
```
|
||
你的 insurance 模块(Flask Blueprint)
|
||
|
|
||
|-- 直接 import BaoDan 的 RAG 引擎
|
||
| from core.rag.retrieval.dataset_retrieval import DatasetRetrieval
|
||
| from core.rag.embedding.model import ModelEmbedding
|
||
|
|
||
|-- 直接 import BaoDan 的 LLM 服务
|
||
| from core.model_runtime.model_manager import ModelManager
|
||
|
|
||
|-- 直接调用 BaoDan 的 Workflow
|
||
| from core.workflow.workflow_engine import WorkflowEngine
|
||
|
|
||
|-- 复用 BaoDan 的数据库连接
|
||
| from extensions.ext_database import db
|
||
| from models.dataset import Dataset, Document
|
||
|
|
||
|-- 复用 BaoDan 的认证
|
||
| from extensions.ext_login import login
|
||
| from models.account import Account
|
||
```
|
||
|
||
| Headers: Authorization: Bearer <BAODAN_APP_API_KEY>
|
||
|
||
|-- 调用 BaoDan Workflow API(产品推荐)
|
||
|
||
| POST http://baodan-api:5001/v1/workflows/run
|
||
|
||
| Headers: Authorization: Bearer <BAODAN_WORKFLOW_API_KEY>
|
||
|
||
|-- 调用 BaoDan Knowledge API(文档管理,可选)
|
||
|
||
| POST http://baodan-api:5001/v1/datasets/{id}/documents
|
||
|
||
| Headers: Authorization: Bearer <BAODAN_DATASET_API_KEY>
|
||
```
|
||
### 9.2 BaoDan Chat API 调用规范
|
||
|
||
**请求**:
|
||
```json
|
||
|
||
POST /v1/chat-messages
|
||
|
||
Headers:
|
||
|
||
Authorization: Bearer app-xxxxxxxxxxxx
|
||
|
||
Content-Type: application/json
|
||
|
||
{
|
||
|
||
"inputs": {},
|
||
|
||
"query": "重疾险等待期多少天?",
|
||
|
||
"response_mode": "blocking",
|
||
|
||
"user": "wecom_zhangsan",
|
||
|
||
"conversation_id": "",
|
||
|
||
"files": []
|
||
|
||
}
|
||
```
|
||
|
||
|参数|类型|说明|
|
||
|------|------|------|
|
||
|inputs|object|自定义变量,与 Prompt 中的占位符对应|
|
||
|query|string|用户的问题文本|
|
||
|response_mode|string|blocking(等完整回答)或 streaming(流式返回)|
|
||
|user|string|用户标识,企微场景用 "wecom_{userid}"|
|
||
|conversation_id|string|空字符串 = 新对话;非空 = 继续已有对话|
|
||
|files|array|附件列表(暂不使用)|
|
||
|
||
**blocking 模式响应**:
|
||
```json
|
||
|
||
{
|
||
|
||
"event": "message",
|
||
|
||
"message_id": "msg-xxx",
|
||
|
||
"conversation_id": "conv-xxx",
|
||
|
||
"mode": "blocking",
|
||
|
||
"answer": "根据 XX 重疾险条款规定,等待期为合同生效之日起90天。",
|
||
|
||
"metadata": {
|
||
|
||
"usage": {
|
||
|
||
"prompt_tokens": 520,
|
||
|
||
"completion_tokens": 180,
|
||
|
||
"total_tokens": 700
|
||
|
||
},
|
||
|
||
"retriever_resources": [
|
||
|
||
{
|
||
|
||
"dataset_name": "重疾险-产品条款",
|
||
|
||
"document_name": "XX重疾险条款.md",
|
||
|
||
"segment_content": "等待期为合同生效之日起90天...",
|
||
|
||
"score": 0.92
|
||
|
||
}
|
||
|
||
]
|
||
|
||
},
|
||
|
||
"created_at": 1700000000
|
||
|
||
}
|
||
```
|
||
### 9.3 BaoDan Workflow API 调用规范
|
||
|
||
**请求**:
|
||
```json
|
||
|
||
POST /v1/workflows/run
|
||
|
||
Headers:
|
||
|
||
Authorization: Bearer app-yyyyyyyyyyyy
|
||
|
||
Content-Type: application/json
|
||
|
||
{
|
||
|
||
"inputs": {
|
||
|
||
"age": "35",
|
||
|
||
"gender": "male",
|
||
|
||
"occupation": "软件工程师",
|
||
|
||
"annual_income": "300000",
|
||
|
||
"monthly_budget": "2000",
|
||
|
||
"insurance_types": "重疾险,医疗险,意外险",
|
||
|
||
"coverage_amount": "500000",
|
||
|
||
"coverage_period": "终身"
|
||
|
||
},
|
||
|
||
"response_mode": "blocking",
|
||
|
||
"user": "wecom_zhangsan"
|
||
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
|
||
{
|
||
|
||
"workflow_run_id": "wf-xxx",
|
||
|
||
"task_id": "task-xxx",
|
||
|
||
"data": {
|
||
|
||
"outputs": {
|
||
|
||
"recommendation": "## 基础方案\n\n..."
|
||
|
||
},
|
||
|
||
"status": "succeeded",
|
||
|
||
"elapsed_time": 15.2,
|
||
|
||
"total_tokens": 2000
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
### 9.4 用户标识映射
|
||
|
||
|场景|BaoDan user 格式|说明|
|
||
|------|---------------|------|
|
||
|企微单聊|`wecom_{userid}`|如 `wecom_zhangsan`|
|
||
|企微群聊|`wecom_{userid}`|同上,按发送人区分|
|
||
|网页登录|`web_{baodan_user_id}`|BaoDan 内部用户 ID|
|
||
|
||
### 9.5 对话上下文管理
|
||
|
||
|场景|conversation_id|说明|
|
||
|------|----------------|------|
|
||
|新对话|空字符串 `""`|BaoDan 返回新的 conversation_id|
|
||
|继续对话|传入上次返回的 conversation_id|BaoDan 自动携带历史上下文|
|
||
|切换会话|传入目标会话的 conversation_id|BaoDan 加载该会话的上下文|
|
||
|新建会话|传入空字符串|开始全新的对话|
|
||
|
||
### 9.6 BaoDan WebApp 嵌入方式
|
||
|
||
对话页面直接嵌入 BaoDan 的 WebApp,通过 iframe 方式:
|
||
```html
|
||
|
||
<!-- 方式一:直接嵌入 BaoDan WebApp -->
|
||
|
||
<iframe
|
||
|
||
src="http://你的BAODAN地址/chat/{app_id}?user=用户ID"
|
||
|
||
style="width:100%; height:100%; border:none;"
|
||
|
||
allow="microphone">
|
||
|
||
</iframe>
|
||
```
|
||
|
||
**BaoDan WebApp URL 格式**:
|
||
|
||
- 对话应用:`{baodan_base_url}/chat/{app_id}`
|
||
|
||
- 工作流应用:`{baodan_base_url}/chat/{app_id}`
|
||
|
||
**iframe 通信**:BaoDan WebApp 通过 `postMessage` 与父页面通信(如需要定制交互,需要监听 message 事件)。
|
||
|
||
---
|
||
|
||
## 十、异常与边界处理
|
||
|
||
### 10.1 智能问答异常处理
|
||
|
||
|异常场景|系统行为|用户看到什么|
|
||
|---------|---------|-------------|
|
||
|BaoDan API 不可达|后端返回 500|"服务暂时不可用,请稍后重试"|
|
||
|BaoDan API 超时(>60秒)|中断请求|"回答生成超时,请重试"|
|
||
|DeepSeek API Key 过期|BaoDan 返回错误|"AI 服务配置异常,请联系管理员"|
|
||
|知识库无匹配结果|BaoDan 正常返回|AI 回复"当前知识库中未找到相关内容"|
|
||
|用户输入为空|前端拦截|发送按钮禁用|
|
||
|用户输入超长(>10000字)|前端拦截|提示"输入内容过长,请缩短后重试"|
|
||
|BaoDan 返回空回答|后端兜底|"抱歉,暂时无法回答您的问题,请换个方式提问"|
|
||
|流式输出中断|前端显示已收到部分|"回答中断,请重新提问"|
|
||
|
||
### 10.2 产品推荐异常处理
|
||
|
||
|异常场景|系统行为|用户看到什么|
|
||
|---------|---------|-------------|
|
||
|必填项未填|前端拦截|对应字段下方红色提示"请填写XX"|
|
||
|无效输入(年龄=-1)|前端校验|"请输入有效的年龄(1-150)"|
|
||
|Workflow 执行失败|记录错误,更新状态|"方案生成失败,请重试"|
|
||
|Workflow 执行超时(>120秒)|任务标记为 failed|"方案生成超时,请重试"|
|
||
|生成结果为空|BaoDan Workflow 内处理|"未找到匹配的保险产品,请调整筛选条件"|
|
||
|网络中断|任务可能仍在后端执行|"网络异常,方案正在生成中,请稍后查看"|
|
||
|
||
### 10.3 企微机器人异常处理
|
||
|
||
|异常场景|系统行为|用户看到什么|
|
||
|---------|---------|-------------|
|
||
|消息解密失败|记录日志,返回 success|不回复(避免企微重试)|
|
||
|用户不在白名单|根据配置决定|"您暂无使用权限"或忽略|
|
||
|BaoDan 处理中超过 5 秒|已返回 success|先不回复,异步处理完后回复|
|
||
|企微 API 回复失败|记录日志|无(用户看不到)|
|
||
|Access Token 获取失败|重试 3 次|机器人不回复|
|
||
|消息内容包含敏感词|正常处理|不做额外过滤(合规由管理员负责)|
|
||
|
||
### 10.4 企微 OAuth 异常处理
|
||
|
||
|异常场景|系统行为|用户看到什么|
|
||
|---------|---------|-------------|
|
||
|用户拒绝授权|跳转回登录页|"授权已取消,请重新登录"|
|
||
|code 已过期(5分钟有效期)|企微返回错误|"授权已过期,请重新登录"|
|
||
|企微用户未在系统中注册|根据配置决定|"您的账号暂未开通,请联系管理员"|
|
||
|JWT Token 过期|前端检测 401|跳转到登录页|
|
||
|刷新 Token 失败|清除本地 Token|跳转到登录页|
|
||
|
||
### 10.5 网络与基础设施异常
|
||
|
||
|异常场景|系统行为|恢复方式|
|
||
|---------|---------|---------|
|
||
|PostgreSQL 连接断开|后端报错日志|数据库恢复后自动重连|
|
||
|Redis 连接断开|Celery 暂停|Redis 恢复后自动恢复|
|
||
|磁盘空间满|知识库上传失败,其他功能正常|清理磁盘后手动重试|
|
||
|服务器重启|所有服务停止|Docker 自动重启 / 手动启动|
|
||
|BaoDan 服务重启|所有对话断开|BaoDan 自动恢复,用户刷新页面即可|
|
||
|
||
---
|
||
|
||
## 十一、术语表
|
||
|
||
### 11.1 保险领域术语
|
||
|
||
|术语|英文|说明|
|
||
|------|------|------|
|
||
|重疾险|Critical Illness Insurance|确诊约定重大疾病后一次性赔付的保险|
|
||
|寿险|Life Insurance|身故后向受益人赔付的保险|
|
||
|医疗险|Medical Insurance|报销医疗费用的保险|
|
||
|意外险|Accident Insurance|因意外事故导致伤害/身故时赔付的保险|
|
||
|年金险|Annuity Insurance|按约定周期领取保险金的保险|
|
||
|储蓄险|Savings Insurance|兼具保障和储蓄功能的保险|
|
||
|等待期|Waiting Period|保单生效后到保障开始的等待天数(通常 90-180 天)|
|
||
|免赔额|Deductible|保险公司不赔付的部分(如医疗险的 1 万免赔额)|
|
||
|保额|Sum Insured|保险赔付的最高金额|
|
||
|保费|Premium|被保险人需缴纳的保险费用|
|
||
|核保|Underwriting|保险公司评估是否承保及承保条件的过程|
|
||
|体况|Health Status|投保人的身体健康状况|
|
||
|条款|Policy Terms|保险合同的详细约定内容|
|
||
|理赔|Claim|向保险公司申请赔付的过程|
|
||
|
||
### 11.2 技术术语
|
||
|
||
|术语|说明||
|
||
|------|------|--------|
|
||
|BaoDan|开源 AI 应用开发平台,提供 RAG、对话管理、工作流等功能||
|
||
|RAG|检索增强生成(Retrieval-Augmented Generation),先从知识库检索相关文档,再让 LLM 基于检索结果生成回答||
|
||
|LLM|大语言模型(Large Language Model),如 DeepSeek、GPT-4o||
|
||
|Embedding|向量嵌入,将文本转换为数学向量以支持语义搜索||
|
||
|Chunk|文档被切分成的小段落,是知识库检索的最小单位||
|
||
|Workflow|BaoDan 的可视化工作流编辑器,可拖拽编排多步骤 AI 流程||
|
||
|SSE|Server-Sent Events,流式传输协议,用于逐字显示 AI 回答||
|
||
|JWT|JSON Web Token,用于用户认证的令牌机制||
|
||
|PTY|伪终端(本项目不涉及,仅 Dinotty 相关)||
|
||
|WebApp|BaoDan 提供的内嵌对话界面,可通过 iframe 嵌入你的系统||
|
||
|
||
---
|
||
|
||
## 十二、数据流转图
|
||
|
||
> 以下用文本/伪代码形式展示系统核心业务的完整数据流转链路,每条链路标注了每一步的 API 调用、数据库读写、外部服务调用、返回数据和错误处理。
|
||
|
||
### 12.1 用户提问 → AI 回答(完整链路)
|
||
|
||
```
|
||
用户(浏览器/企微)
|
||
│
|
||
▼
|
||
[步骤1] 前端发起请求
|
||
│ POST /api/chat/message
|
||
│ Headers: Authorization: Bearer <JWT>
|
||
│ Body: {"session_id":"sess-001", "message":"重疾险等待期多少天?", "filters":{"险种":"重疾险"}}
|
||
│
|
||
▼
|
||
[步骤2] 后端 - JWT 鉴权
|
||
│ ├─ 读取 Redis: GET token:blacklist:<token> → 检查是否被吊销
|
||
│ ├─ 解析 JWT → 提取 user_id、role
|
||
│ ├─ 读取 PostgreSQL: SELECT * FROM wecom_user_mapping WHERE baodan_user_id = ?
|
||
│ │
|
||
│ ├─ [成功] → 继续步骤3
|
||
│ └─ [失败] → 返回 {"code":1002, "message":"未授权,请重新登录"}
|
||
│
|
||
▼
|
||
[步骤3] 后端 - 调用 BaoDan Chat API(blocking 模式)
|
||
│ POST http://baodan-api:5001/v1/chat-messages
|
||
│ Headers: Authorization: Bearer app-xxxxxxxxxxxx
|
||
│ Body: {
|
||
│ "inputs": {"险种": "重疾险", "保司": ""},
|
||
│ "query": "重疾险等待期多少天?",
|
||
│ "response_mode": "blocking",
|
||
│ "user": "wecom_zhangsan",
|
||
│ "conversation_id": "sess-001",
|
||
│ "files": []
|
||
│ }
|
||
│
|
||
│ ├─ [成功] → 继续步骤4
|
||
│ ├─ [超时 >60s] → 返回 {"code":2002, "message":"AI 回答生成超时,请重试"}
|
||
│ └─ [BaoDan 返回错误] → 返回 {"code":2001, "message":"AI 服务暂时不可用,请稍后重试"}
|
||
│
|
||
▼
|
||
[步骤4] BaoDan 内部处理(无需后端干预)
|
||
│ ├─ BaoDan 接收 query
|
||
│ ├─ Embedding: 将 query 向量化
|
||
│ ├─ 知识库检索: Weaviate 向量相似度搜索 → 返回 Top-K 文档片段
|
||
│ ├─ Prompt 组装: System Prompt + 检索结果 + 用户 query
|
||
│ ├─ LLM 调用: DeepSeek API → 流式生成回答
|
||
│ └─ 返回完整回答 + metadata(来源引用)
|
||
│
|
||
▼
|
||
[步骤5] 后端 - 处理 BaoDan 响应并存储
|
||
│ ├─ 提取 answer、conversation_id、retriever_resources
|
||
│ ├─ 写入 PostgreSQL(可选): 记录问答日志
|
||
│ └─ 组装返回数据
|
||
│
|
||
▼
|
||
[步骤6] 后端 → 前端 响应
|
||
│ 返回 SSE 流式数据:
|
||
│ data: {"type":"source", "data":{"doc_name":"XX重疾险条款.md", "chunk":"等待期为90天..."}}
|
||
│ data: {"type":"delta", "data":"根据"}
|
||
│ data: {"type":"delta", "data":"XX重疾险条款规定..."}
|
||
│ ...
|
||
│ data: {"type":"done", "data":{"message_id":"msg-001", "conversation_id":"conv-001"}}
|
||
│
|
||
▼
|
||
[步骤7] 前端渲染
|
||
├─ 逐字显示 AI 回答
|
||
├─ 展示来源引用(可展开查看原文)
|
||
├─ 显示 [点赞] [点踩] [复制] 按钮
|
||
└─ 完成
|
||
```
|
||
|
||
### 12.2 产品推荐(完整链路)
|
||
|
||
```
|
||
用户(浏览器)
|
||
│
|
||
▼
|
||
[步骤1] 前端表单提交
|
||
│ POST /api/proposals/generate
|
||
│ Headers: Authorization: Bearer <JWT>
|
||
│ Body: {
|
||
│ "customer": {"name":"李四", "age":35, "gender":"male", "health_status":"健康",
|
||
│ "occupation":"软件工程师", "annual_income":300000, "monthly_budget":2000},
|
||
│ "insurance_types": ["重疾险","医疗险","意外险"],
|
||
│ "coverage_amount": 500000,
|
||
│ "coverage_period": "终身",
|
||
│ "existing_policies": []
|
||
│ }
|
||
│
|
||
▼
|
||
[步骤2] 后端 - JWT 鉴权
|
||
│ 同 12.1 步骤2
|
||
│
|
||
▼
|
||
[步骤3] 后端 - 参数校验
|
||
│ ├─ 必填字段检查:age/gender/occupation/insurance_types/monthly_budget/coverage_amount
|
||
│ ├─ 类型检查:age 为整数 1-150,gender 为 "male"/"female"
|
||
│ ├─ 业务规则:insurance_types 至少选1项,monthly_budget > 0
|
||
│ │
|
||
│ ├─ [校验通过] → 继续步骤4
|
||
│ └─ [校验失败] → 返回 {"code":1001, "message":"参数错误:年龄必须为1-150之间的整数"}
|
||
│
|
||
▼
|
||
[步骤4] 后端 - 创建推荐记录
|
||
│ ├─ 写入 PostgreSQL: INSERT INTO recommendation_records (user_id, customer_name, ..., status='pending')
|
||
│ └─ 返回 task_id
|
||
│
|
||
▼
|
||
[步骤5] 后端 - 异步调用 BaoDan Workflow API
|
||
│ POST http://baodan-api:5001/v1/workflows/run
|
||
│ Headers: Authorization: Bearer app-yyyyyyyyyyyy
|
||
│ Body: {
|
||
│ "inputs": {
|
||
│ "age": "35", "gender": "male", "occupation": "软件工程师",
|
||
│ "annual_income": "300000", "monthly_budget": "2000",
|
||
│ "insurance_types": "重疾险,医疗险,意外险",
|
||
│ "coverage_amount": "500000", "coverage_period": "终身"
|
||
│ },
|
||
│ "response_mode": "blocking",
|
||
│ "user": "wecom_zhangsan"
|
||
│ }
|
||
│
|
||
│ ├─ [成功] → 继续步骤6
|
||
│ ├─ [超时 >120s] → 更新 PostgreSQL: UPDATE recommendation_records SET status='failed' → 返回 {"code":2004}
|
||
│ └─ [Workflow 失败] → 更新 PostgreSQL: SET status='failed', error_message=... → 返回 {"code":2003}
|
||
│
|
||
▼
|
||
[步骤6] BaoDan Workflow 内部处理
|
||
│ ├─ 节点1: 参数校验(Code Node)
|
||
│ ├─ 节点2: 检索策略生成(Code Node)→ 为每个险种生成检索 query
|
||
│ ├─ 节点3: 知识库检索(Knowledge Retrieval Node)→ Weaviate 检索 Top-5
|
||
│ ├─ 节点4: LLM 方案生成(LLM Node)→ DeepSeek 生成三套方案
|
||
│ ├─ 节点5: 格式化输出(Code Node)→ Markdown 清理
|
||
│ └─ 返回 outputs.recommendation(Markdown 文本)
|
||
│
|
||
▼
|
||
[步骤7] 后端 - 保存结果
|
||
│ ├─ 写入 PostgreSQL: UPDATE recommendation_records SET
|
||
│ │ generated_plan=?, status='done', completed_at=NOW()
|
||
│ └─ 返回完整方案数据
|
||
│
|
||
▼
|
||
[步骤8] 后端 → 前端 响应
|
||
│ 返回 {"code":0, "data":{"task_id":"task-001", "status":"done",
|
||
│ "proposal":{"id":"prop-001", "plans":[...]}}}
|
||
│
|
||
▼
|
||
[步骤9] 前端轮询(如步骤5返回 processing)
|
||
│ GET /api/proposals/generate/{task_id}
|
||
│ ├─ [status=processing] → 继续轮询(间隔 2 秒)
|
||
│ ├─ [status=done] → 展示方案
|
||
│ └─ [status=failed] → 显示错误提示
|
||
│
|
||
▼
|
||
[步骤10] 前端展示方案
|
||
├─ 渲染三套方案(基础/均衡/全面)
|
||
├─ 每套方案包含:产品表格 + 总保费 + 推荐理由
|
||
├─ 显示 [浏览器打印] [重新生成] 按钮
|
||
└─ 完成
|
||
```
|
||
|
||
### 12.3 企微机器人问答(完整链路)
|
||
|
||
```
|
||
企微用户(手机/电脑)
|
||
│
|
||
▼
|
||
[步骤1] 用户发送消息
|
||
│ 用户在企微中 @机器人 或私聊发送:"重疾险等待期多少天?"
|
||
│
|
||
▼
|
||
[步骤2] 企微服务器 → 你的后端 推送加密消息
|
||
│ POST https://your-domain.com/api/wecom/callback
|
||
│ Headers: msg_signature=xxx×tamp=xxx&nonce=xxx
|
||
│ Body: <xml><ToUserName>...</ToUserName><Encrypt>加密消息</Encrypt></xml>
|
||
│
|
||
▼
|
||
[步骤3] 后端 - 立即返回 "success"(5秒内必须响应)
|
||
│ 返回空字符串 "success"
|
||
│ (企微要求5秒内响应,否则会重试推送)
|
||
│
|
||
▼
|
||
[步骤4] 后端 - 异步处理消息
|
||
│ ├─ 验证签名: SHA1(sort([Token, timestamp, nonce])) == msg_signature
|
||
│ │ ├─ [验证失败] → 记录日志,流程结束(不回复)
|
||
│ │ └─ [验证通过] → 继续
|
||
│ ├─ 解密消息: AES-CBC 解密 Encrypt 字段 → 得到明文 XML
|
||
│ ├─ 解析 XML: 提取 FromUserName(用户ID)、Content(消息内容)、MsgType
|
||
│ │
|
||
│ ├─ [MsgType != text] → 回复"暂不支持该消息类型,请用文字描述" → 流程结束
|
||
│ ├─ [用户不在白名单] → 回复"您暂无使用权限" → 流程结束
|
||
│ └─ [正常文本消息] → 继续步骤5
|
||
│
|
||
▼
|
||
[步骤5] 后端 - 查询/创建用户映射
|
||
│ ├─ 读取 PostgreSQL: SELECT * FROM wecom_user_mapping WHERE wecom_userid = ?
|
||
│ │
|
||
│ ├─ [用户不存在 + 系统允许注册] → INSERT 新记录 → 继续步骤6
|
||
│ ├─ [用户不存在 + 系统不允许] → 回复"您的账号暂未开通" → 流程结束
|
||
│ └─ [用户存在] → 继续步骤6
|
||
│
|
||
▼
|
||
[步骤6] 后端 - 调用 BaoDan Chat API
|
||
│ POST http://baodan-api:5001/v1/chat-messages
|
||
│ Headers: Authorization: Bearer app-xxxxxxxxxxxx
|
||
│ Body: {
|
||
│ "inputs": {},
|
||
│ "query": "重疾险等待期多少天?",
|
||
│ "response_mode": "blocking",
|
||
│ "user": "wecom_zhangsan",
|
||
│ "conversation_id": "",
|
||
│ "files": []
|
||
│ }
|
||
│
|
||
│ ├─ [成功] → 继续步骤7
|
||
│ ├─ [超时 >55s] → 回复"处理时间较长,请稍后再试" → 流程结束
|
||
│ └─ [BaoDan 错误] → 回复"AI 服务暂时不可用,请稍后重试" → 流程结束
|
||
│
|
||
▼
|
||
[步骤7] 后端 - 回复企微消息
|
||
│ ├─ 获取企微 Access Token:
|
||
│ │ POST https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=CORPID&corpsecret=SECRET
|
||
│ │ 返回 {"access_token":"xxx", "expires_in":7200}
|
||
│ │ (Token 缓存到 Redis,提前5分钟刷新)
|
||
│ │
|
||
│ ├─ 判断消息长度:
|
||
│ │ ├─ [<=2048字节] → 单条回复
|
||
│ │ └─ [>2048字节] → 分段回复(每段间隔500ms)
|
||
│ │
|
||
│ ├─ 单聊回复:
|
||
│ │ POST https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=TOKEN
|
||
│ │ Body: {"touser":"zhangsan", "msgtype":"text", "agentid":1000002,
|
||
│ │ "text":{"content":"根据XX重疾险条款规定,等待期为90天..."}}
|
||
│ │
|
||
│ ├─ 群聊回复:
|
||
│ │ POST https://qyapi.weixin.qq.com/cgi-bin/appchat/send?access_token=TOKEN
|
||
│ │ Body: {"chatid":"群聊ID", "msgtype":"text",
|
||
│ │ "text":{"content":"根据XX重疾险条款规定,等待期为90天..."}}
|
||
│ │
|
||
│ ├─ [发送成功] → 记录日志 → 流程结束
|
||
│ └─ [发送失败] → 记录错误日志 → 流程结束(用户无感知)
|
||
│
|
||
▼
|
||
[步骤8] 企微用户收到回复
|
||
└─ 完成
|
||
```
|
||
|
||
### 12.4 企微 OAuth 登录(完整链路)
|
||
|
||
```
|
||
用户(企微内置浏览器)
|
||
│
|
||
▼
|
||
[步骤1] 用户点击"企微登录"按钮
|
||
│ 前端构造 OAuth 授权 URL 并跳转:
|
||
│ https://open.weixin.qq.com/connect/oauth2/authorize?
|
||
│ corp_id=CORPID&
|
||
│ redirect_uri=https://your-domain.com/api/auth/wework-callback&
|
||
│ response_type=code&
|
||
│ scope=snsapi_base&
|
||
│ state=RANDOM_STATE_STRING
|
||
│ #wechat_redirect
|
||
│
|
||
▼
|
||
[步骤2] 企微授权页面
|
||
│ ├─ [用户点击"同意"] → 企微携带 code + state 回调 redirect_uri
|
||
│ └─ [用户点击"取消"] → 回调 URL 带 error=access_denied
|
||
│ 前端捕获 → 显示"授权已取消,请重新登录"
|
||
│
|
||
▼
|
||
[步骤3] 企微服务器 → 你的后端 回调
|
||
│ GET https://your-domain.com/api/auth/wework-callback?
|
||
│ code=XXXXX&
|
||
│ state=RANDOM_STATE_STRING
|
||
│
|
||
▼
|
||
[步骤4] 后端 - 验证 state 防 CSRF
|
||
│ ├─ 读取 Redis: GET oauth:state:<session_id>
|
||
│ ├─ 比对 state 值
|
||
│ │
|
||
│ ├─ [state 不匹配] → 返回 403 "登录验证失败,请重试"
|
||
│ └─ [state 匹配] → 继续步骤5
|
||
│
|
||
▼
|
||
[步骤5] 后端 - 用 code 换取企微用户信息
|
||
│ ├─ 获取 Access Token:
|
||
│ │ GET https://qyapi.weixin.qq.com/cgi-bin/gettoken?
|
||
│ │ corpid=CORPID&corpsecret=SECRET
|
||
│ │ 返回 {"access_token":"xxx", "expires_in":7200}
|
||
│ │
|
||
│ ├─ 获取用户信息:
|
||
│ │ GET https://qyapi.weixin.qq.com/cgi-bin/user/get?
|
||
│ │ access_token=TOKEN&userid=USERID
|
||
│ │ 返回 {"userid":"zhangsan", "name":"张三", "department":[1]}
|
||
│ │
|
||
│ ├─ [企微 API 失败] → 返回 500 "企微服务异常,请稍后重试"
|
||
│ └─ [企微 API 成功] → 继续步骤6
|
||
│
|
||
▼
|
||
[步骤6] 后端 - 查找或创建系统用户
|
||
│ ├─ 读取 PostgreSQL: SELECT * FROM wecom_user_mapping WHERE wecom_userid = 'zhangsan'
|
||
│ │
|
||
│ ├─ [用户存在 + status=active] → 继续步骤7
|
||
│ ├─ [用户存在 + status=disabled] → 返回 403 "您的账号已被禁用,请联系管理员"
|
||
│ ├─ [用户不存在 + 允许自动注册] → INSERT 新记录 → 继续步骤7
|
||
│ └─ [用户不存在 + 不允许自动注册] → 返回 403 "您的账号暂未开通,请联系管理员"
|
||
│
|
||
▼
|
||
[步骤7] 后端 - 签发 JWT Token
|
||
│ ├─ 生成 JWT: payload = {user_id, username, role, department, exp=now+2h}
|
||
│ ├─ 用 SECRET_KEY 签名
|
||
│ ├─ 写入 Redis: SET token:user:<user_id> = <token> EX 7200
|
||
│ └─ 更新 PostgreSQL: UPDATE wecom_user_mapping SET last_active_at = NOW()
|
||
│
|
||
▼
|
||
[步骤8] 后端 → 前端 重定向
|
||
│ 302 重定向到前端页面,URL 携带 Token:
|
||
│ https://your-domain.com/app/login/callback?
|
||
│ token=eyJhbGciOiJIUzI1NiIs...&
|
||
│ expires_in=7200&
|
||
│ user={"id":"user-001","username":"张三","role":"sales","department":"上海团队"}
|
||
│
|
||
▼
|
||
[步骤9] 前端 - 处理回调
|
||
│ ├─ 解析 URL 参数,提取 token 和 user 信息
|
||
│ ├─ 存储 Token: localStorage.setItem('token', token)
|
||
│ ├─ 存储用户信息: localStorage.setItem('user', userJSON)
|
||
│ ├─ 设置 Axios 拦截器: 每次请求自动携带 Authorization: Bearer <token>
|
||
│ ├─ 设置 Token 自动刷新: 在 token 过期前 5 分钟调用 /auth/refresh-token
|
||
│ └─ 跳转到主页: router.push('/chat')
|
||
│
|
||
▼
|
||
[步骤10] 登录完成
|
||
└─ 用户进入系统主界面
|
||
```
|
||
|
||
---
|
||
|
||
## 十三、接口字段约束表
|
||
|
||
> 每个接口的所有请求参数和响应字段的完整约束定义。字段验证分为前端验证(即时反馈)和后端验证(安全兜底),两层都必须实现。
|
||
|
||
### 13.1 A1 认证鉴权接口
|
||
|
||
#### A1.1.1 POST /auth/wework-login
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|code|string|是|1-128字符|-|-|非空,去除首尾空格后长度>=1|企微授权码不能为空|
|
||
|string|state|是|1-64字符|-|-|必须与 Redis 中存储的 state 匹配|登录验证失败,请重试|
|
||
|
||
#### A1.1.2 POST /auth/password-login
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|username|是|3-64字符|-|-|仅允许字母、数字、下划线|用户名不能为空|
|
||
|string|password|是|8-128字符|-|-|非空字符串|密码不能为空|
|
||
|
||
#### A1.1.3 POST /auth/refresh-token
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|Authorization (Header)|是|-|-|-|Bearer <token> 格式,token 为有效 JWT|登录已过期,请重新登录|
|
||
|
||
#### A1.1.4 POST /auth/logout
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|Authorization (Header)|是|-|-|-|Bearer <token> 格式|登录已过期,请重新登录|
|
||
|
||
### 13.2 A2 智能问答接口
|
||
|
||
#### A2.1.1 POST /chat/message
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|session_id|是|1-64字符|-|-|非空,必须是当前用户拥有的会话 ID|会话不存在或已删除|
|
||
|string|message|是|1-10000字符|-|-|去除首尾空格后长度>=1|请输入您的问题|
|
||
|object|filters|否|-|{}|-|对象类型,包含险种和保司字段|-|
|
||
|string|filters.险种|否|0-32字符|null|重疾险/寿险/医疗险/意外险/年金险/储蓄险|如果提供,必须是有效险种名|无效的险种筛选条件|
|
||
|string|filters.保司|否|0-64字符|null|-|字符串类型|无效的保司筛选条件|
|
||
|
||
#### A2.1.2 GET /chat/sessions
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|int|page|否|1-10000|1|-|正整数|页码必须为正整数|
|
||
|int|page_size|否|1-100|20|-|正整数,最大100|每页条数不能超过100|
|
||
|
||
#### A2.1.3 POST /chat/sessions
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|title|否|0-128字符|自动根据首条消息生成|-|如果提供则为非空字符串|-|
|
||
|
||
#### A2.1.4 DELETE /chat/sessions/{id}
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|id (Path)|是|1-64字符|-|-|非空,必须是当前用户拥有的会话 ID|会话不存在或已删除|
|
||
|
||
#### A2.1.5 GET /chat/sessions/{id}/messages
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|id (Path)|是|1-64字符|-|-|非空,必须是当前用户拥有的会话 ID|会话不存在或已删除|
|
||
|
||
#### A2.1.6 POST /chat/messages/{id}/feedback
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|id (Path)|是|1-64字符|-|-|非空,必须是当前用户的消息 ID|消息不存在|
|
||
|string|rating|是|-|-|helpful/not_helpful|必须是 helpful 或 not_helpful|请选择评分(有用或无用)|
|
||
|string|comment|否|0-2000字符|null|-|如果 rating 为 not_helpful 且提供 comment,长度>=1|-|
|
||
|
||
#### A2.2.1 POST /retrieval/search
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|query|是|1-1000字符|-|-|非空搜索词|请输入搜索关键词|
|
||
|object|filters|否|-|{}|-|对象类型|-|
|
||
|string|filters.险种|否|0-32字符|null|重疾险/寿险/医疗险/意外险/年金险/储蓄险|有效险种名|-|
|
||
|string|filters.保司|否|0-64字符|null|-|字符串|-|
|
||
|int|top_k|否|1-20|5|-|正整数|返回数量不能超过20|
|
||
|
||
#### A2.2.2 GET /retrieval/suggest
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|query|是|1-500字符|-|-|非空,当前对话上下文|请输入上下文|
|
||
|
||
### 13.3 A3 方案生成接口
|
||
|
||
#### A3.1.1 POST /proposals/generate
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|object|customer|是|-|-|-|非空对象|客户信息不能为空|
|
||
|string|customer.name|是|1-64字符|-|-|非空字符串|请输入客户姓名|
|
||
|int|customer.age|是|1-150|-|-|正整数|请输入有效的年龄(1-150)|
|
||
|string|customer.gender|是|-|-|male/female|必须是 male 或 female|请选择客户性别|
|
||
|string|customer.health_status|否|0-32字符|健康|健康/有既往病史/拒保史|-|
|
||
|string|customer.occupation|是|1-64字符|-|-|非空字符串|请输入客户职业|
|
||
|int|customer.annual_income|否|0-100000000|0|-|非负整数|年收入不能为负数|
|
||
|int|customer.monthly_budget|是|1-1000000|-|-|正整数|月预算必须大于0|
|
||
|array|insurance_types|是|1-10项|-|重疾险/寿险/医疗险/意外险/年金险/储蓄险|非空数组,每项为有效险种名|请至少选择一个关注险种|
|
||
|int|coverage_amount|是|10000-10000000|-|-|正整数,>=10000|保额不能低于1万元|
|
||
|string|coverage_period|是|-|-|10年/20年/30年/至60岁/至70岁/至80岁/终身|必须是有效期限|请选择保障期限|
|
||
|array|existing_policies|否|0-20项|[]|-|数组,每项为对象|-|
|
||
|
||
#### A3.1.2 GET /proposals/generate/{task_id}
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|task_id (Path)|是|1-64字符|-|-|非空,必须是当前用户的任务 ID|任务不存在|
|
||
|
||
#### A3.1.4 POST /proposals/{id}/share
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|id (Path)|是|1-64字符|-|-|非空,必须是当前用户拥有的方案 ID|方案不存在|
|
||
|int|expire_hours|否|1-720|72|-|正整数,最大30天|分享有效期不能超过720小时|
|
||
|
||
### 13.4 A4 知识库接口
|
||
|
||
#### A4.1.1 POST /kb/documents/upload
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|file[]|files|是|1-50个文件|-|md/doc/docx/txt/pdf|仅支持 MD/Word/TXT/PDF 格式|仅支持 MD、Word、TXT、PDF 格式的文件|
|
||
|file|单个文件大小|-|最大 50MB|-|-|单文件不超过 50MB|文件大小不能超过50MB|
|
||
|string|险种|是|1-32字符|-|重疾险/寿险/医疗险/意外险/年金险/储蓄险|必须选择险种分类|请选择险种分类|
|
||
|string|保司|是|1-64字符|-|-|非空字符串|请选择保险公司|
|
||
|
||
#### A4.1.2 GET /kb/documents
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|int|page|否|1-10000|1|-|正整数|-|
|
||
|int|page_size|否|1-100|20|-|正整数|-|
|
||
|string|险种|否|0-32字符|null|重疾险/寿险/医疗险/意外险/年金险/储蓄险|有效险种名|-|
|
||
|string|保司|否|0-64字符|null|-|字符串|-|
|
||
|string|status|否|-|null|processing/completed/failed|有效状态值|-|
|
||
|string|keyword|否|0-128字符|null|-|搜索关键词(文件名/编号匹配)|-|
|
||
|
||
#### A4.1.3 GET /kb/documents/{id}/status
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|id (Path)|是|1-64字符|-|-|非空文档 ID|文档不存在|
|
||
|
||
#### A4.1.4 POST /kb/documents/{id}/retry
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|id (Path)|是|1-64字符|-|-|非空,文档必须是 failed 状态|文档不存在或状态不允许重试|
|
||
|
||
#### A4.1.5 PATCH /kb/documents/{id}
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|id (Path)|是|1-64字符|-|-|非空文档 ID|文档不存在|
|
||
|string|编号|否|0-32字符|当前值|-|字母数字和连字符|-|
|
||
|string|险种|否|1-32字符|当前值|重疾险/寿险/医疗险/意外险/年金险/储蓄险|有效险种名|-|
|
||
|string|保司|否|1-64字符|当前值|-|非空字符串|-|
|
||
|array|tags|否|0-20项|当前值|-|字符串数组,每项 1-32 字符|-|
|
||
|
||
#### A4.1.6 DELETE /kb/documents/{id}
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|id (Path)|是|1-64字符|-|-|非空文档 ID|文档不存在|
|
||
|
||
#### A4.2.1 GET /kb/datasources
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|(无请求参数)| | | | | | | |
|
||
|
||
#### A4.2.2 POST /kb/datasources
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|name|是|1-128字符|-|-|非空字符串|数据源名称不能为空|
|
||
|string|api_url|是|1-512字符|-|-|合法 URL 格式|请输入有效的 API 地址|
|
||
|string|sync_frequency|是|-|-|-|Cron 表达式格式(5字段)|请输入有效的定时表达式|
|
||
|string|险种|是|1-32字符|-|重疾险/寿险/医疗险/意外险/年金险/储蓄险|有效险种名|请选择险种分类|
|
||
|
||
#### A4.2.3 POST /kb/datasources/{id}/sync
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|id (Path)|是|1-64字符|-|-|非空数据源 ID|数据源不存在|
|
||
|
||
#### A4.2.4 GET /kb/datasources/{id}/logs
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|id (Path)|是|1-64字符|-|-|非空数据源 ID|数据源不存在|
|
||
|int|page|否|1-10000|1|-|正整数|-|
|
||
|int|page_size|否|1-100|20|-|正整数|-|
|
||
|
||
### 13.5 A5 用户权限接口
|
||
|
||
#### A5.1.1 GET /admin/users
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|int|page|否|1-10000|1|-|正整数|-|
|
||
|int|page_size|否|1-100|20|-|正整数|-|
|
||
|string|role|否|-|null|super_admin/admin/manager/sales|有效角色值|-|
|
||
|string|department|否|0-128字符|null|-|字符串|-|
|
||
|string|keyword|否|0-128字符|null|-|用户名搜索关键词|-|
|
||
|
||
#### A5.1.1 POST /admin/users
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|username|是|2-64字符|-|-|非空,不能与已有用户名重复|用户名不能为空|
|
||
|string|wecom_userid|是|1-64字符|-|-|非空,不能与已有映射重复|企微用户ID不能为空|
|
||
|string|role|是|-|-|super_admin/admin/manager/sales|必须是有效角色|请选择有效角色|
|
||
|string|department|否|0-128字符|-|-|字符串|-|
|
||
|
||
#### A5.1.1 PUT /admin/users/{id}
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|id (Path)|是|1-64字符|-|-|非空用户 ID|用户不存在|
|
||
|string|role|否|-|当前值|super_admin/admin/manager/sales|有效角色|-|
|
||
|string|department|否|0-128字符|当前值|-|字符串|-|
|
||
|string|status|否|-|当前值|active/disabled|有效状态|-|
|
||
|
||
#### A5.1.1 DELETE /admin/users/{id}
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|id (Path)|是|1-64字符|-|-|非空用户 ID|用户不存在|
|
||
|(注意:禁用用户时吊销其所有 Token)| | | | | | | |
|
||
|
||
#### A5.1.2 POST /admin/users/batch-import
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|file|file|是|1个文件|-|xls/xlsx|必须是 Excel 文件且符合模板格式|请上传符合模板格式的 Excel 文件|
|
||
|(模板列:username, wecom_userid, role, department)| | | | | | | |
|
||
|
||
#### A5.1.3 GET /admin/roles
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|(无请求参数)| | | | | | | |
|
||
|
||
#### A5.1.3 POST /admin/roles
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|name|是|2-32字符|-|-|非空,不能与已有角色名重复|角色名不能为空|
|
||
|array|permissions|是|1-50项|-|-|非空数组,每项为有效权限标识|请至少分配一个权限|
|
||
|
||
#### A5.1.3 PUT /admin/roles/{id}
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|id (Path)|是|1-64字符|-|-|非空角色 ID|角色不存在|
|
||
|string|name|否|2-32字符|当前值|-|非空字符串|-|
|
||
|array|permissions|否|0-50项|当前值|-|权限标识数组|-|
|
||
|
||
#### A5.1.3 DELETE /admin/roles/{id}
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|id (Path)|是|1-64字符|-|-|非空,内置角色不可删除|角色不存在或为内置角色不可删除|
|
||
|
||
### 13.6 A6 系统配置接口
|
||
|
||
#### A6.1.1 GET /admin/llm-configs
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|(无请求参数)| | | | | | | |
|
||
|
||
#### A6.1.1 POST /admin/llm-configs
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|provider|是|1-32字符|-|deepseek/openai/zhipu/-|非空,有效供应商名|请选择模型供应商|
|
||
|string|model|是|1-64字符|-|-|非空,有效模型名|请选择模型|
|
||
|string|api_key|是|1-512字符|-|-|非空字符串,服务端 AES 加密存储|API Key 不能为空|
|
||
|string|base_url|否|0-512字符|供应商默认值|-|合法 URL 格式|请输入有效的 API 地址|
|
||
|bool|is_default|否|-|false|-|布尔值|-|
|
||
|object|params|否|-|{}|-|模型参数对象|-|
|
||
|float|params.temperature|否|0.0-2.0|0.7|-|浮点数范围校验|Temperature 必须在 0-2 之间|
|
||
|int|params.max_tokens|否|1-32768|4096|-|正整数|Max Tokens 必须为正整数|
|
||
|float|params.top_p|否|0.0-1.0|0.9|-|浮点数范围校验|Top P 必须在 0-1 之间|
|
||
|
||
#### A6.1.1 PUT /admin/llm-configs/{id}
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|id (Path)|是|1-64字符|-|-|非空配置 ID|配置不存在|
|
||
|(字段同 POST,均为可选,只更新提供的字段)| | | | | | | |
|
||
|
||
#### A6.1.2 POST /admin/llm-configs/{id}/ping
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|id (Path)|是|1-64字符|-|-|非空配置 ID|配置不存在|
|
||
|
||
#### A6.1.3 GET /admin/prompts
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|(无请求参数)| | | | | | | |
|
||
|
||
#### A6.1.3 POST /admin/prompts
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|name|是|2-128字符|-|-|非空字符串|Prompt 名称不能为空|
|
||
|string|type|是|-|-|system/user|有效类型|请选择 Prompt 类型|
|
||
|string|content|是|1-50000字符|-|-|非空字符串,保存时自动生成版本快照|Prompt 内容不能为空|
|
||
|array|variables|否|0-20项|[]|-|字符串数组,变量名格式|-|
|
||
|
||
#### A6.1.3 PUT /admin/prompts/{id}
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|id (Path)|是|1-64字符|-|-|非空 Prompt ID|Prompt 不存在|
|
||
|(字段同 POST,均为可选)| | | | | | | |
|
||
|
||
#### A6.1.4 POST /admin/prompts/test
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|prompt_id|是|1-64字符|-|-|非空 Prompt ID|Prompt 不存在|
|
||
|string|test_query|是|1-2000字符|-|-|非空测试问题|请输入测试问题|
|
||
|
||
### 13.7 A7 留痕日志接口
|
||
|
||
#### A7.1.1 GET /admin/logs/chat
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|int|page|否|1-10000|1|-|正整数|-|
|
||
|int|page_size|否|1-100|50|-|正整数|-|
|
||
|string|user_id|否|0-64字符|null|-|用户 ID|-|
|
||
|string|start_date|否|10字符|null|-|YYYY-MM-DD 格式日期|日期格式不正确|
|
||
|string|end_date|否|10字符|null|-|YYYY-MM-DD 格式日期,>= start_date|结束日期不能早于开始日期|
|
||
|string|keyword|否|0-128字符|null|-|搜索关键词|-|
|
||
|string|rating|否|-|null|helpful/not_helpful|有效评分值|-|
|
||
|string|export|否|-|null|csv|导出格式|-|
|
||
|
||
#### A7.1.3 GET /admin/logs/system
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|int|page|否|1-10000|1|-|正整数|-|
|
||
|int|page_size|否|1-100|50|-|正整数|-|
|
||
|string|action|否|-|null|login/logout/upload/delete/config_change|有效操作类型|-|
|
||
|string|user_id|否|0-64字符|null|-|用户 ID|-|
|
||
|string|start_date|否|10字符|null|-|YYYY-MM-DD 格式|-|
|
||
|string|end_date|否|10字符|null|-|YYYY-MM-DD 格式|-|
|
||
|
||
### 13.8 A8 统计报表接口
|
||
|
||
#### A8.1.2 GET /stats/trend
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|metric|是|-|-|chat_count/user_count/proposal_count|有效指标名|请选择统计指标|
|
||
|string|start_date|是|10字符|-|-|YYYY-MM-DD 格式|请选择开始日期|
|
||
|string|end_date|是|10字符|-|-|YYYY-MM-DD 格式|请选择结束日期|
|
||
|string|granularity|否|-|day|day/week/month|有效粒度值|-|
|
||
|
||
#### A8.1.3 GET /stats/kb-health
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|int|days|否|1-365|30|-|正整数|统计天数不能超过365|
|
||
|
||
#### A8.1.4 GET /stats/token-cost
|
||
|
||
|字段名|类型|必填|长度/范围约束|默认值|枚举值|验证规则|错误提示文案|
|
||
|------|-----|:---:|------------|------|------|--------|------------|
|
||
|string|start_date|是|10字符|-|-|YYYY-MM-DD 格式|请选择开始日期|
|
||
|string|end_date|是|10字符|-|-|YYYY-MM-DD 格式|请选择结束日期|
|
||
|string|group_by|否|-|model|model/day|有效分组维度|-|
|
||
|
||
---
|
||
|
||
## 十四、产品推荐表单验证规则
|
||
|
||
> 产品推荐页面(/recommend)所有表单字段的完整验证规则,包含前端即时验证和后端兜底验证。
|
||
|
||
### 14.1 客户信息表单
|
||
|
||
|字段名|类型|必填|前端验证规则|后端验证规则|错误提示文案|边界值测试用例|
|
||
|------|-----|:---:|------------|------------|------------|------------|
|
||
|客户姓名|text|是|非空,去除首尾空格后长度 1-64,仅允许中文/英文/空格/·|同前端|请输入客户姓名|空值→"请输入客户姓名";空格→"请输入客户姓名";65字符→"姓名不能超过64个字符"|
|
||
|年龄|number|是|整数,范围 1-150,不支持小数|同前端|请输入有效的年龄(1-150)|空值→"请输入年龄";0→"请输入有效的年龄(1-150)";-1→同上;151→同上;3.5→"年龄必须为整数";1→合法;150→合法|
|
||
|性别|radio|是|必须选择 male 或 female,默认无选中|同前端|请选择客户性别|未选择→"请选择客户性别";male→合法;female→合法|
|
||
|健康状况|select|否|枚举值:健康/有既往病史/拒保史,默认"健康"|同前端|-|空值→默认"健康";"健康"→合法|
|
||
|职业|text|是|非空,1-64字符,去除首尾空格|同前端|请输入客户职业|空值→"请输入客户职业";65字符→"职业不能超过64个字符"|
|
||
|年收入|number|否|非负整数,范围 0-100000000,默认 0|同前端|年收入不能为负数|空值→默认0;-1→"年收入不能为负数";100000001→"年收入不能超过1亿元"|
|
||
|月预算|number|是|正整数,范围 1-1000000|同前端|月预算必须大于0|空值→"请输入月预算";0→"月预算必须大于0";-1→同上;1000001→"月预算不能超过100万元"|
|
||
|
||
### 14.2 保障需求表单
|
||
|
||
|字段名|类型|必填|前端验证规则|后端验证规则|错误提示文案|边界值测试用例|
|
||
|------|-----|:---:|------------|------------|------------|------------|
|
||
|关注险种|checkbox-group|是|至少勾选1项,最多6项;可选项:重疾险/寿险/医疗险/意外险/年金险/储蓄险|同前端|请至少选择一个关注险种|未勾选→"请至少选择一个关注险种";勾选1项→合法;勾选6项→合法|
|
||
|保额目标|slider/number|是|正整数,范围 10000-10000000,步进 10000|同前端|保额不能低于1万元|10000→合法;9999→"保额不能低于1万元";10000001→"保额不能超过1000万元"|
|
||
|保障期限|select|是|枚举值:10年/20年/30年/至60岁/至70岁/至80岁/终身|同前端|请选择保障期限|未选择→"请选择保障期限";"终身"→合法|
|
||
|
||
### 14.3 已有保单信息(可选)
|
||
|
||
|字段名|类型|必填|前端验证规则|后端验证规则|错误提示文案|边界值测试用例|
|
||
|------|-----|:---:|------------|------------|------------|------------|
|
||
|已有保单|dynamic-form|否|每个保单条目包含:产品名称(必填)、保司(必填)、保额(可选)、生效日期(可选)|同前端|-|不填→合法(跳过);填1条→合法;填20条→合法;填21条→"已有保单不能超过20条"|
|
||
|
||
### 14.4 表单提交综合验证
|
||
|
||
|验证场景|触发条件|前端行为|后端行为|错误提示|
|
||
|--------|---------|--------|--------|--------|
|
||
|全部必填项未填 | 点击"生成方案" | 各必填字段下方显示红色行内错误 | 返回 1001 错误码 | 各字段对应错误提示 |
|
||
|部分必填项未填 | 点击"生成方案" | 未填字段标红,已填字段正常 | 返回 1001 错误码 | 缺失字段的错误提示 |
|
||
|所有字段填写正确 | 点击"生成方案" | 按钮变为 loading 状态,显示"方案生成中..." | 调用 BaoDan Workflow | 无 |
|
||
|网络断开 | 提交时 | 弹窗提示"网络异常,请检查网络连接" | 不到达后端 | 网络异常,请检查网络连接 |
|
||
|JWT 过期 | 提交时 | 自动跳转登录页 | 返回 1003 错误码 | 登录已过期,请重新登录 |
|
||
|重复提交 | 快速双击"生成方案" | 第一次点击后按钮立即禁用 | 正常处理第一个请求 | 无 |
|
||
|生成超时(>120s) | 后端响应超时 | 弹窗提示"方案生成超时,请重试" | 标记任务为 failed | 方案生成超时,请重试 |
|
||
|生成失败 | BaoDan Workflow 执行失败 | 弹窗提示"方案生成失败,请重试" | 标记任务为 failed | 方案生成失败,请重试 |
|
||
|
||
### 14.5 前端验证实现规范
|
||
|
||
- 所有字段在用户**离开输入框时**(blur 事件)触发首次验证
|
||
- 在用户**修改输入时**(input 事件)触发实时验证(仅在已触发过首次验证后)
|
||
- 点击"生成方案"按钮时执行**全量验证**
|
||
- 错误提示显示在字段下方,使用红色文字(#F56C6C),字号 12px
|
||
- 验证不通过时输入框边框变为红色(border-color: #F56C6C)
|
||
- 验证通过时恢复默认边框颜色
|
||
- 提交按钮在所有必填项验证通过前保持 disabled 状态(灰色,不可点击)
|
||
|
||
---
|
||
|
||
## 十五、BaoDan 配置操作手册
|
||
|
||
> 从零开始配置 BaoDan 平台的完整操作步骤,面向从未接触过 BaoDan 的开发者。每一步都标注了具体的按钮名称、菜单位置和配置值。
|
||
|
||
### 15.1 BaoDan 初始化(首次访问)
|
||
|
||
**前提**:BaoDan Docker 部署已完成(参见第二十章部署清单)。
|
||
|
||
1. 浏览器打开 `http://你的服务器IP:3000`
|
||
2. 首次访问会看到"BaoDan"欢迎页面,点击 **"设置管理员账号"** 按钮
|
||
3. 填写管理员信息:
|
||
- 邮箱:`admin@your-domain.com`
|
||
- 密码:设置一个强密码(至少8位,含大小写+数字)
|
||
- 名称:`系统管理员`
|
||
4. 点击 **"创建管理员"** 按钮
|
||
5. 自动跳转到 BaoDan 后台首页,初始化完成
|
||
|
||
### 15.2 添加 DeepSeek 模型供应商
|
||
|
||
1. 登录 BaoDan 后台(`http://你的服务器IP:3000`)
|
||
2. 点击左下角 **齿轮图标**(设置)→ 进入设置页面
|
||
3. 点击顶部 **"模型供应商"** 标签页
|
||
4. 点击 **"添加供应商"** 按钮
|
||
5. 在供应商列表中找到 **"DeepSeek"**,点击 **"设置"** 按钮
|
||
6. 填写配置:
|
||
- API Key:`sk-xxxxxxxxxxxxxxxxxxxxxxxx`(从 platform.deepseek.com 获取)
|
||
- API Base URL:`https://api.deepseek.com/v1`(默认已填,无需修改)
|
||
7. 点击 **"保存"** 按钮
|
||
8. 添加 Embedding 模型:
|
||
- 在同一页面找到 **"文本嵌入模型"** 区域
|
||
- 点击 **"设置默认模型"**
|
||
- 选择供应商 `DeepSeek`,模型 `deepseek-embedding`
|
||
- 点击 **"保存"**
|
||
9. 验证:点击 **"测试"** 按钮,显示"连接成功"即可
|
||
|
||
### 15.3 创建知识库(按险种分类)
|
||
|
||
**每个险种创建一个独立知识库**,便于管理和检索筛选。
|
||
|
||
1. 点击左侧导航 **"知识库"** 菜单
|
||
2. 点击 **"创建知识库"** 按钮
|
||
3. 填写信息:
|
||
- 名称:`重疾险-产品条款`(格式:`险种-分类`)
|
||
- 描述:`重疾险相关产品条款、费率表、理赔规则等文档`
|
||
4. 点击 **"创建"** 按钮
|
||
5. 进入知识库设置页面,配置分段规则(见15.4)
|
||
6. 重复以上步骤,依次创建以下知识库:
|
||
- `寿险-产品条款`
|
||
- `医疗险-产品条款`
|
||
- `意外险-产品条款`
|
||
- `年金险-产品条款`
|
||
- `储蓄险-产品条款`
|
||
- `保险法规-政策文件`(可选,存放监管政策类文档)
|
||
|
||
### 15.4 上传文档到知识库
|
||
|
||
1. 进入目标知识库页面(如"重疾险-产品条款")
|
||
2. 点击 **"添加文件"** 按钮
|
||
3. 选择 **"上传文件"** 选项
|
||
4. 拖拽或选择 `.md` 文件(支持批量上传)
|
||
5. 配置分段设置:
|
||
- **分段标识符**:选择 **"Markdown 标题"**(按 `#` / `##` / `###` 分段)
|
||
- **分段最大长度**:`1000` tokens
|
||
- **分段重叠**:`100` tokens(确保上下文连续性)
|
||
- **文本预处理规则**:
|
||
- 勾选 **"替换连续空格/换行/制表符"**
|
||
- 勾选 **"删除所有 URL 和邮箱地址"**(可选)
|
||
6. **索引方式**选择:
|
||
- **高质量模式**(推荐):使用 Embedding 向量 + 全文检索混合模式
|
||
- Embedding 模型:`deepseek-embedding`
|
||
7. 点击 **"保存并处理"** 按钮
|
||
8. 等待处理完成(状态从 "处理中" 变为 "可用")
|
||
9. 重复以上步骤上传所有文档
|
||
|
||
### 15.5 创建聊天应用(智能问答)
|
||
|
||
1. 点击左侧导航 **"工作室"** 菜单
|
||
2. 点击 **"创建应用"** 按钮
|
||
3. 选择 **"聊天助手"** 类型
|
||
4. 填写应用信息:
|
||
- 名称:`保险智能客服`
|
||
- 描述:`基于保险知识库的智能问答助手`
|
||
5. 点击 **"创建"** 按钮
|
||
6. 进入应用编辑页面,配置以下内容:
|
||
|
||
**配置 Prompt**:
|
||
1. 点击 **"编排"** 标签页
|
||
2. 在 **"提示词"** 区域编辑系统提示词:
|
||
|
||
```
|
||
你是一名专业的保险顾问助手,专门为保险代理人提供产品咨询和方案建议服务。
|
||
|
||
## 核心规则
|
||
1. 所有回答必须基于检索到的知识库内容,不可编造产品信息
|
||
2. 如果知识库中没有相关内容,明确告知用户"当前知识库中未找到相关信息,建议咨询相关保险公司"
|
||
3. 回答要专业、准确、简洁,适合保险代理人向客户转述
|
||
4. 涉及具体条款时,必须注明产品名称和出处
|
||
|
||
## 回答格式
|
||
- 使用清晰的分段和编号
|
||
- 重要数据(保额、保费、等待期等)用粗体标注
|
||
- 如果涉及多个产品,使用表格对比展示
|
||
```
|
||
|
||
**关联知识库**:
|
||
1. 点击 **"上下文"** 区域
|
||
2. 点击 **"添加"** 按钮
|
||
3. 选择所有已创建的知识库(全选或按需选择)
|
||
4. 配置检索参数:
|
||
- **Top-K**:`5`(返回最相关的5个文档片段)
|
||
- **Score 阈值**:`0.5`(低于此分数的片段不返回)
|
||
|
||
**配置模型参数**:
|
||
1. 点击 **"模型"** 区域
|
||
2. 选择模型:`deepseek-chat`
|
||
3. 设置参数:
|
||
- Temperature:`0.7`
|
||
- Top P:`0.9`
|
||
- Max Tokens:`4096`
|
||
|
||
### 15.6 设置应用公开访问
|
||
|
||
1. 在应用编辑页面,点击右上角 **"发布"** 按钮
|
||
2. 弹窗中确认发布
|
||
3. 发布后,点击 **"访问 API"** 获取 API 地址
|
||
4. 记录 **API 密钥**(格式:`app-xxxxxxxxxxxx`),后续后端配置使用
|
||
5. 获取 WebApp URL:`http://baodan:3000/chat/{app_id}`
|
||
|
||
### 15.7 创建 Workflow 应用(产品推荐)
|
||
|
||
1. 点击左侧导航 **"工作室"**
|
||
2. 点击 **"创建应用"** → 选择 **"工作流"** 类型
|
||
3. 名称:`产品推荐方案生成`
|
||
4. 点击 **"创建"** 后进入可视化编辑器
|
||
5. 按以下顺序添加节点(拖拽组件到画布):
|
||
|
||
**节点1 - 开始节点**(默认已存在):
|
||
- 添加输入变量:
|
||
- `age`:类型 string,必填
|
||
- `gender`:类型 string,必填
|
||
- `occupation`:类型 string,必填
|
||
- `annual_income`:类型 string,必填
|
||
- `monthly_budget`:类型 string,必填
|
||
- `insurance_types`:类型 string,必填
|
||
- `coverage_amount`:类型 string,必填
|
||
- `coverage_period`:类型 string,必填
|
||
|
||
**节点2 - 代码节点(参数校验)**:
|
||
- 拖入 "代码执行" 组件
|
||
- 输入:所有开始节点变量
|
||
- 代码逻辑:校验 age 为 1-150 整数,gender 为 male/female 等
|
||
|
||
**节点3 - 代码节点(检索策略)**:
|
||
- 输入:校验后的参数
|
||
- 代码:根据 insurance_types 生成检索 query 列表
|
||
|
||
**节点4 - 知识库检索节点**:
|
||
- 拖入 "知识库" 组件
|
||
- 关联所有险种知识库
|
||
- 输入:检索 query
|
||
- Top-K:5
|
||
- Score 阈值:0.6
|
||
|
||
**节点5 - LLM 节点**:
|
||
- 拖入 "LLM" 组件
|
||
- 模型:`deepseek-chat`
|
||
- Temperature:0.7
|
||
- Prompt:参见第十九章完整 Prompt
|
||
|
||
**节点6 - 代码节点(格式化输出)**:
|
||
- 输入:LLM 输出的 Markdown
|
||
- 逻辑:清理格式,确保可读性
|
||
|
||
**节点7 - 结束节点**:
|
||
- 输出变量:`recommendation`(格式化后的方案文本)
|
||
|
||
6. 点击 **"发布"** 按钮保存 Workflow
|
||
7. 记录 API 密钥(Workflow 专用 Key)
|
||
|
||
### 15.8 配置 iframe 嵌入参数
|
||
|
||
1. 进入聊天应用设置
|
||
2. 点击 **"API 访问"** 标签页
|
||
3. 勾选 **"启用 WebApp 访问"**
|
||
4. 配置 **"跨域设置"**:
|
||
- 允许的来源(Allowed Origins):添加 `https://your-domain.com`
|
||
5. 获取 iframe 嵌入代码:
|
||
```
|
||
<iframe
|
||
src="http://baodan:3000/chat/{app_id}?user={user_id}"
|
||
style="width:100%; height:100%; border:none;"
|
||
allow="microphone">
|
||
</iframe>
|
||
```
|
||
|
||
### 15.9 测试对话功能
|
||
|
||
1. 在聊天应用页面,点击右上角 **"预览"** 按钮
|
||
2. 输入测试问题:`重疾险的等待期是多少天?`
|
||
3. 验证以下内容:
|
||
- [ ] 收到基于知识库的回答(非编造内容)
|
||
- [ ] 回答中包含来源引用
|
||
- [ ] 回答在 15 秒内开始输出
|
||
4. 测试边界情况:
|
||
- 输入空消息 → 应提示"请输入问题"
|
||
- 输入无关问题 → 应回答"当前知识库中未找到相关内容"
|
||
- 输入超长文本(>10000字)→ 应正常处理或提示过长
|
||
|
||
### 15.10 测试 Workflow 功能
|
||
|
||
1. 进入 Workflow 应用页面,点击 **"预览"** 按钮
|
||
2. 填写测试输入:
|
||
- age:`35`
|
||
- gender:`male`
|
||
- occupation:`软件工程师`
|
||
- annual_income:`300000`
|
||
- monthly_budget:`2000`
|
||
- insurance_types:`重疾险,医疗险`
|
||
- coverage_amount:`500000`
|
||
- coverage_period:`终身`
|
||
3. 点击 **"运行"** 按钮
|
||
4. 验证以下内容:
|
||
- [ ] 每个节点正常执行(无红色错误标记)
|
||
- [ ] 知识库检索返回了相关产品信息
|
||
- [ ] LLM 生成了三套方案(基础/均衡/全面)
|
||
- [ ] 每套方案包含产品表格、保费、推荐理由
|
||
- [ ] 总保费不超过月预算 x 12(24000元/年)
|
||
- [ ] 整体执行时间 < 120 秒
|
||
|
||
---
|
||
|
||
## 十六、企微应用配置操作手册
|
||
|
||
> 企微管理后台的完整配置步骤,从创建应用到机器人可用的全流程。
|
||
|
||
### 16.1 登录企微管理后台
|
||
|
||
1. 打开浏览器访问 `https://work.weixin.qq.com/`
|
||
2. 使用管理员账号扫码或账密登录
|
||
3. 进入管理后台首页
|
||
|
||
### 16.2 创建自建应用
|
||
|
||
1. 点击左侧导航 **"应用管理"**
|
||
2. 点击 **"自建"** 区域的 **"创建应用"** 按钮
|
||
3. 填写应用信息:
|
||
- **应用名称**:`保险智能客服`
|
||
- **应用 Logo**:上传一个图标(建议 200x200px PNG)
|
||
- **应用介绍**:`基于 AI 的保险知识问答和产品推荐助手`
|
||
- **可见范围**:选择需要使用此应用的部门(如:全部部门,或指定销售部门)
|
||
4. 点击 **"创建应用"** 按钮
|
||
5. 创建成功后,记录以下信息:
|
||
- **AgentId**:在应用详情页顶部显示(如 `1000002`)
|
||
- **Secret**:点击 **"Secret"** 旁边的 **"查看"** 按钮,输入管理员密码后获取
|
||
|
||
### 16.3 配置应用可见范围
|
||
|
||
1. 在应用详情页,点击 **"可见范围"** 区域的 **"编辑"** 按钮
|
||
2. 勾选需要使用此应用的部门
|
||
3. 点击 **"保存"**
|
||
4. **重要**:可见范围决定了哪些用户能在企微中看到此应用
|
||
|
||
### 16.4 配置接收消息(回调 URL)
|
||
|
||
1. 在应用详情页,找到 **"接收消息"** 区域
|
||
2. 点击 **"设置API接收"** 按钮
|
||
3. 填写以下信息:
|
||
- **URL**:`https://your-domain.com/api/wecom/callback`
|
||
(注意:必须是 HTTPS,企微要求)
|
||
- **Token**:点击 **"随机获取"** 按钮自动生成
|
||
- **EncodingAESKey**:点击 **"随机获取"** 按钮自动生成(43位字符串)
|
||
4. 点击 **"保存"** 按钮
|
||
5. 企微会向你填写的 URL 发送验证请求
|
||
6. **此时后端服务必须已部署并运行**,否则保存会失败
|
||
7. 验证通过后,点击 **"接收消息"** 下的 **"设置接收消息"**,选择 **"使用 API 接收消息"**
|
||
8. 将 Token 和 EncodingAESKey 记录下来,配置到后端环境变量:
|
||
```
|
||
WECOM_TOKEN=你复制的Token
|
||
WECOM_ENCODING_AES_KEY=你复制的EncodingAESKey
|
||
```
|
||
|
||
### 16.5 配置企业可信 IP
|
||
|
||
1. 在应用详情页,找到 **"企业可信IP"** 区域
|
||
2. 点击 **"配置"** 按钮
|
||
3. 添加你服务器的**公网 IP 地址**(如 `123.45.67.89`)
|
||
4. 如果有多个出口 IP,全部添加
|
||
5. 点击 **"保存"**
|
||
6. **注意**:如果不配置,企微消息回调会被拒绝
|
||
|
||
### 16.6 获取 CorpID / AgentID / Secret
|
||
|
||
|信息|获取位置|格式示例|
|
||
|------|---------|---------|
|
||
|CorpID|管理后台 → 我的企业 → 企业信息 → 企业ID|`ww1234567890abcdef`|
|
||
|AgentID|应用管理 → 应用详情页顶部|`1000002`|
|
||
|AgentSecret|应用详情 → Secret → 查看|`xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`|
|
||
|
||
配置到后端环境变量:
|
||
```
|
||
WECOM_CORP_ID=ww1234567890abcdef
|
||
WECOM_AGENT_ID=1000002
|
||
WECOM_AGENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||
```
|
||
|
||
### 16.7 配置 OAuth 回调域名
|
||
|
||
1. 管理后台 → **"我的企业"** → **"企业微信授权登录"**
|
||
2. 点击 **"设置授权回调域"**
|
||
3. 填写你的域名:`your-domain.com`
|
||
4. 点击 **"保存"**
|
||
5. **注意**:这里填的是根域名,不是具体路径。回调 URL 为 `https://your-domain.com/api/auth/wework-callback`
|
||
|
||
### 16.8 测试机器人消息收发
|
||
|
||
1. 在企微手机端或电脑端,搜索应用名称 **"保险智能客服"**
|
||
2. 打开应用,发送一条消息:`你好`
|
||
3. 验证以下内容:
|
||
- [ ] 收到 AI 回复(非超时错误)
|
||
- [ ] 回复内容与知识库相关
|
||
- [ ] 回复延迟 < 10 秒
|
||
4. 如果没有收到回复:
|
||
- 检查后端日志是否有企微回调记录
|
||
- 检查可信 IP 配置是否正确
|
||
- 检查 Token/Secret 配置是否正确
|
||
|
||
### 16.9 测试 OAuth 登录
|
||
|
||
1. 在企微内置浏览器中访问:`https://your-domain.com/app/login`
|
||
2. 点击 **"企微登录"** 按钮
|
||
3. 应自动跳转到企微授权页面
|
||
4. 点击 **"同意"** 授权
|
||
5. 验证以下内容:
|
||
- [ ] 成功跳转回系统主页
|
||
- [ ] 右上角显示用户姓名
|
||
- [ ] 可以正常使用各项功能
|
||
6. 如果登录失败:
|
||
- 检查 OAuth 回调域名是否配置正确
|
||
- 检查后端 WECOM_CORP_ID 和 WECOM_AGENT_SECRET 是否正确
|
||
- 查看浏览器控制台网络请求的错误信息
|
||
|
||
### 16.10 配置应用主页 URL(企微 H5)
|
||
|
||
1. 在应用详情页,找到 **"应用主页"** 区域
|
||
2. 点击 **"设置"** 按钮
|
||
3. 选择 **"自定义主页"**
|
||
4. 填写主页 URL:`https://your-domain.com/app`
|
||
5. 设置 **"工作台"** 展示:
|
||
- 勾选 **"在企业微信工作台展示"**
|
||
- 设置展示名称:`保险智能客服`
|
||
6. 点击 **"保存"**
|
||
7. 验证:在企微工作台中应能看到"保险智能客服"入口
|
||
|
||
---
|
||
|
||
## 十七、前端页面交互设计
|
||
|
||
> 每个页面的完整交互设计,包括所有状态、用户操作流程、按钮行为和状态变化。
|
||
|
||
### 17.1 登录页(/login)
|
||
|
||
**页面状态**:
|
||
|
||
|状态|显示内容|触发条件|
|
||
|------|--------|---------|
|
||
|初始状态|登录方式选择界面|页面首次加载|
|
||
|企微授权中|全屏 loading + "正在跳转企微授权..."|点击"企微登录"按钮后|
|
||
|授权回调中|全屏 loading + "正在登录..."|企微回调到达后端时|
|
||
|登录失败|登录界面 + 顶部红色提示条|登录接口返回错误|
|
||
|已登录|自动跳转到 /chat|已存在有效 Token|
|
||
|
||
**用户操作流程**:
|
||
1. 用户访问 /login → 显示登录界面
|
||
2. 选择登录方式:
|
||
- **方式A - 企微登录**:点击"企微登录"按钮 → 跳转企微授权 → 同意授权 → 回调到后端 → 签发 JWT → 跳转 /chat
|
||
- **方式B - 账密登录**:输入用户名 + 密码 → 点击"登录" → 后端校验 → 签发 JWT → 跳转 /chat
|
||
3. 登录成功 → 存储 Token 到 localStorage → 跳转 /chat
|
||
|
||
**按钮行为**:
|
||
- "企微登录"按钮:点击后变为 loading 状态,禁止重复点击,跳转企微授权页
|
||
- "登录"按钮:表单验证通过后可点击,点击后显示 loading + "登录中...",禁止重复点击
|
||
- Enter 键:在密码输入框中按 Enter 等同于点击"登录"按钮
|
||
|
||
### 17.2 对话页(/chat)
|
||
|
||
**页面状态**:
|
||
|
||
|状态|显示内容|触发条件|
|
||
|------|--------|---------|
|
||
|加载中|骨架屏(Skeleton)|页面首次加载|
|
||
|空状态|居中图标 + "开始新的对话吧" + 快捷提问按钮|无任何会话|
|
||
|正常|左侧会话列表 + 右侧对话区域|有会话数据|
|
||
|发送中|用户消息气泡 + AI 回复区域显示"思考中..."动画|发送消息后等待响应|
|
||
|流式输出|AI 回复区域逐字显示文字|SSE 流式响应中|
|
||
|错误|对话区域顶部红色提示条|接口返回错误|
|
||
|会话删除确认|弹窗"确定要删除这个会话吗?"|点击删除按钮|
|
||
|
||
**用户操作流程**:
|
||
1. 进入页面 → 加载会话列表 → 显示最近会话或空状态
|
||
2. 点击"新建对话" → 清空对话区域 → 等待用户输入
|
||
3. 在输入框输入问题 → 点击发送(或按 Enter)→ 显示"思考中..."
|
||
4. 收到流式响应 → 逐字显示 AI 回答
|
||
5. 回答完成 → 显示来源引用 + 操作按钮(复制/点赞/点踩)
|
||
6. 可继续在同一会话中提问
|
||
|
||
**按钮行为**:
|
||
- "新建对话"按钮:清空对话区域,重置 session_id 为空,输入框获得焦点
|
||
- "发送"按钮:输入框为空时禁用(灰色),有内容时启用(蓝色);点击后立即禁用直到回复完成
|
||
- "复制"按钮:复制 AI 回答的纯文本到剪贴板,点击后变为"已复制"状态(2秒后恢复)
|
||
- "点赞/点踩"按钮:点击后调用反馈接口,已评的按钮高亮,可切换
|
||
- "删除会话"按钮:显示确认弹窗,确认后调用 DELETE 接口,从列表移除
|
||
- 筛选下拉框(险种/保司):选择后影响后续对话的知识库检索范围
|
||
|
||
### 17.3 产品推荐页(/recommend)
|
||
|
||
**页面状态**:
|
||
|
||
|状态|显示内容|触发条件|
|
||
|------|--------|---------|
|
||
|初始状态|空白表单,所有字段为默认值|页面首次加载或重置|
|
||
|表单填写中|表单各字段可编辑|用户交互中|
|
||
|表单验证失败|未通过字段标红 + 红色行内错误提示|点击"生成方案"时|
|
||
|提交中|按钮 loading + "方案生成中..." + 禁止重复提交|提交成功后|
|
||
|生成中(轮询)|进度提示 + "正在生成方案,请稍候..."|后端返回 processing|
|
||
|生成完成|方案预览区显示三套方案|轮询返回 done|
|
||
|生成失败|红色错误提示 + "重新生成"按钮|轮询返回 failed|
|
||
|网络错误|弹窗"网络异常,请检查网络连接"|请求超时或网络断开|
|
||
|
||
**用户操作流程**:
|
||
1. 进入页面 → 显示空白表单
|
||
2. 填写客户信息(姓名/年龄/性别/职业/收入/预算)
|
||
3. 选择关注险种(勾选复选框)
|
||
4. 设置保额目标和保障期限
|
||
5. 可选:添加已有保单信息
|
||
6. 点击"生成方案" → 前端全量校验 → 校验通过则提交
|
||
7. 显示 loading → 轮询任务状态 → 显示生成的方案
|
||
8. 可点击"重新生成"回到步骤2
|
||
|
||
**按钮行为**:
|
||
- "生成方案"按钮:全量验证通过后可点击;点击后显示 loading 状态,disabled 直到生成完成或失败
|
||
- "重新生成"按钮:重置表单为上次提交值,允许修改后重新提交
|
||
- "浏览器打印"按钮:调用 window.print() 打印方案预览区
|
||
- "添加保单"按钮:在已有保单区域动态添加一行表单
|
||
- "删除保单"行按钮:移除对应保单行,至少保留0行
|
||
|
||
### 17.4 推荐结果页
|
||
|
||
**页面状态**:
|
||
|
||
|状态|显示内容|触发条件|
|
||
|------|--------|---------|
|
||
|加载中|骨架屏|页面加载时|
|
||
|正常|三套方案(基础/均衡/全面),每套含产品表格|数据加载完成|
|
||
|空状态|暂无方案 + "去生成"按钮|无历史方案|
|
||
|错误|错误提示 + "重试"按钮|接口返回错误|
|
||
|
||
**用户操作流程**:
|
||
1. 生成完成后直接在推荐页下方显示方案
|
||
2. 可切换查看三套方案(Tab 切换:基础方案/均衡方案/全面方案)
|
||
3. 每套方案显示:方案名称、总年保费、产品明细表格、推荐理由
|
||
4. 可点击"浏览器打印"导出
|
||
5. 可点击"重新生成"回到表单页
|
||
|
||
### 17.5 历史方案页(/recommend/history)
|
||
|
||
**页面状态**:
|
||
|
||
|状态|显示内容|触发条件|
|
||
|------|--------|---------|
|
||
|加载中|表格骨架屏|页面加载时|
|
||
|正常|方案列表表格 + 分页器|有历史数据|
|
||
|空状态|居中图标 + "暂无推荐方案"|无历史数据|
|
||
|筛选结果为空|表格显示"暂无匹配数据"|筛选条件无匹配|
|
||
|删除确认|弹窗"确定要删除此方案吗?"|点击删除按钮|
|
||
|
||
**用户操作流程**:
|
||
1. 进入页面 → 加载方案列表(默认按时间倒序)
|
||
2. 可使用筛选条件:客户姓名、险种、日期范围、状态
|
||
3. 点击某条记录 → 查看方案详情
|
||
4. 可对方案进行操作:查看、删除、分享
|
||
|
||
**按钮行为**:
|
||
- "查看"按钮:弹窗展示方案详情(Markdown 渲染)
|
||
- "删除"按钮:显示确认弹窗,确认后删除
|
||
- "分享"按钮:调用分享接口,生成有时效的链接,显示在弹窗中可复制
|
||
- 分页器:翻页加载对应页数据
|
||
- "导出CSV"按钮:调用日志接口 export=csv,触发浏览器下载
|
||
|
||
### 17.6 管理后台页面
|
||
|
||
#### 17.6.1 管理后台框架(/admin)
|
||
|
||
**布局**:左侧导航栏 + 右侧内容区 + 顶部栏
|
||
|
||
**左侧导航菜单**:
|
||
- 知识库管理 → /admin/knowledge-base
|
||
- 对话日志 → /admin/logs/chat
|
||
- 系统日志 → /admin/logs/system(仅超级管理员可见)
|
||
- 用户管理 → /admin/users(仅超级管理员可见)
|
||
- 角色管理 → /admin/roles(仅超级管理员可见)
|
||
- LLM 配置 → /admin/llm-configs(仅超级管理员可见)
|
||
- Prompt 管理 → /admin/prompts
|
||
- 数据统计 → /admin/stats
|
||
|
||
**顶部栏**:管理后台标题 + "切换到用户端"链接 + 用户信息 + 退出登录
|
||
|
||
#### 17.6.2 知识库管理页(/admin/knowledge-base)
|
||
|
||
**页面状态**:
|
||
|
||
|状态|显示内容|触发条件|
|
||
|------|--------|---------|
|
||
|加载中|表格骨架屏|页面加载|
|
||
|正常|文档列表表格 + 上传按钮 + 筛选栏|有文档数据|
|
||
|上传中|上传进度条 + "正在处理..."|上传文件后|
|
||
|处理中|文档状态列显示 spinner + "处理中"|文档正在向量化|
|
||
|
||
**按钮行为**:
|
||
- "上传文档"按钮:打开文件选择器,选择文件后弹出分类配置(险种/保司),确认后上传
|
||
- "重试"按钮(仅 failed 状态文档可见):重新触发处理流水线
|
||
- "查看状态"按钮:弹窗显示处理流水线各阶段状态和耗时
|
||
- "删除"按钮:确认后软删除文档并下线索引
|
||
- "编辑"按钮:弹窗编辑文档元数据(编号/分类/标签)
|
||
|
||
#### 17.6.3 对话日志页(/admin/logs/chat)
|
||
|
||
**按钮行为**:
|
||
- "查询"按钮:根据筛选条件重新加载日志
|
||
- "重置"按钮:清空所有筛选条件,恢复默认
|
||
- "导出CSV"按钮:下载日志数据为 CSV 文件
|
||
|
||
#### 17.6.4 用户管理页(/admin/users)
|
||
|
||
**按钮行为**:
|
||
- "新增用户"按钮:弹窗表单(用户名/企微ID/角色/部门),填写后保存
|
||
- "批量导入"按钮:下载 Excel 模板 → 填写后上传 → 预览导入结果 → 确认导入
|
||
- "编辑"按钮:弹窗修改角色/部门/状态
|
||
- "禁用"按钮:确认后禁用用户,同时吊销其所有 Token
|
||
- "启用"按钮:恢复已禁用的用户
|
||
|
||
#### 17.6.5 角色管理页(/admin/roles)
|
||
|
||
**按钮行为**:
|
||
- "新增角色"按钮:弹窗表单(角色名 + 权限树勾选),保存后生效
|
||
- "编辑"按钮:修改角色名和权限(内置角色仅可编辑权限)
|
||
- "删除"按钮:内置角色不可删除(按钮灰色禁用),自定义角色可删除
|
||
|
||
#### 17.6.6 LLM 配置页(/admin/llm-configs)
|
||
|
||
**按钮行为**:
|
||
- "添加模型"按钮:弹窗表单(供应商/模型/Key/Base URL/参数),保存后自动加密存储 Key
|
||
- "测试连通性"按钮:调用 ping 接口,显示延迟结果(绿色=成功/红色=失败)
|
||
- "设为默认"按钮:将该模型设为默认,其他模型取消默认
|
||
- "删除"按钮:确认后删除配置(已被应用引用的不可删除)
|
||
|
||
---
|
||
|
||
## 十八、错误提示文案清单
|
||
|
||
> 所有面向用户的错误消息完整清单,按错误码组织。每条错误消息都是完整的中文句子,前端直接展示给用户。
|
||
|
||
### 18.1 统一响应格式
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "success",
|
||
"data": { ... }
|
||
}
|
||
```
|
||
|
||
- `code = 0` 表示成功
|
||
- `code != 0` 表示失败,`message` 字段为用户可见的错误文案
|
||
- 前端根据 `code` 值决定展示方式(弹窗/行内/Toast)
|
||
|
||
### 18.2 认证相关错误(1xxx)
|
||
|
||
|错误码|触发场景|显示位置|具体文案|是否可重试|
|
||
|:---:|--------|--------|--------|:---:|
|
||
|1001|请求参数缺少必填项或类型错误|页面顶部 Toast 提示|参数错误:{具体字段}不能为空|修改后重试|
|
||
|1001|请求参数类型不匹配|页面顶部 Toast 提示|参数错误:{字段名}格式不正确|修改后重试|
|
||
|1002|未携带 Token 或 Token 格式无效|自动跳转登录页|登录已过期,请重新登录|否(需重新登录)|
|
||
|1002|Token 不在有效用户中|自动跳转登录页|登录已过期,请重新登录|否(需重新登录)|
|
||
|1003|JWT Token 已超过有效期|自动跳转登录页|登录已过期,请重新登录|否(需重新登录)|
|
||
|1003|refresh-token 接口 Token 过期|自动跳转登录页|登录已过期,请重新登录|否(需重新登录)|
|
||
|1004|用户角色无权访问该接口|页面顶部 Toast 提示|权限不足,您没有访问该功能的权限|否(需管理员授权)|
|
||
|1004|非管理员访问管理接口|页面顶部 Toast 提示|权限不足,您没有管理权限|否|
|
||
|1005|请求的资源 ID 不存在|行内提示或弹窗|请求的资源不存在或已删除|否|
|
||
|1005|访问不存在的会话|行内提示|会话不存在或已删除|否|
|
||
|1005|访问不存在的方案|行内提示|方案不存在或已删除|否|
|
||
|1005|访问不存在的文档|行内提示|文档不存在或已删除|否|
|
||
|1005|访问不存在的用户|行内提示|用户不存在|否|
|
||
|1005|访问不存在的角色|行内提示|角色不存在|否|
|
||
|1005|访问不存在的 Prompt|行内提示|Prompt 不存在|否|
|
||
|1005|访问不存在的数据源|行内提示|数据源不存在|否|
|
||
|1005|访问不存在的任务|行内提示|任务不存在|否|
|
||
|
||
### 18.3 BaoDan 相关错误(2xxx)
|
||
|
||
|错误码|触发场景|显示位置|具体文案|是否可重试|
|
||
|:---:|--------|--------|--------|:---:|
|
||
|2001|BaoDan API 不可达或返回错误|页面顶部 Toast 提示|AI 服务暂时不可用,请稍后重试|是|
|
||
|2001|BaoDan API 返回 500 错误|页面顶部 Toast 提示|AI 服务暂时不可用,请稍后重试|是|
|
||
|2001|BaoDan API Key 过期或无效|页面顶部 Toast 提示|AI 服务配置异常,请联系管理员|否(需管理员修复)|
|
||
|2002|BaoDan Chat API 响应超过 60 秒|页面顶部 Toast 提示|AI 回答生成超时,请重试|是|
|
||
|2003|BaoDan Workflow 执行失败|弹窗提示|方案生成失败,请重试|是|
|
||
|2003|Workflow 节点执行出错|弹窗提示|方案生成失败,请稍后重试|是|
|
||
|2004|BaoDan Workflow 执行超过 120 秒|弹窗提示|方案生成超时,请重试|是|
|
||
|
||
### 18.4 企微相关错误(3xxx)
|
||
|
||
|错误码|触发场景|显示位置|具体文案|是否可重试|
|
||
|:---:|--------|--------|--------|:---:|
|
||
|3001|企微 API 调用失败|服务端日志(用户不可见)|(不展示给用户,记录日志)|自动重试|
|
||
|3002|企微 Access Token 获取失败|服务端日志(用户不可见)|(不展示给用户,3次重试后放弃)|自动重试3次|
|
||
|3003|企微消息回复失败|服务端日志(用户不可见)|(不展示给用户,记录日志)|否|
|
||
|3004|企微 OAuth 授权码已过期(5分钟有效)|登录页顶部 Toast 提示|授权已过期,请重新登录|是(重新发起授权)|
|
||
|3005|企微用户不在系统白名单中|登录页顶部 Toast 提示|您的账号暂未开通,请联系管理员|否|
|
||
|3005|企微用户已被禁用|登录页顶部 Toast 提示|您的账号已被禁用,请联系管理员|否|
|
||
|
||
### 18.5 文件相关错误(4xxx)
|
||
|
||
|错误码|触发场景|显示位置|具体文案|是否可重试|
|
||
|:---:|--------|--------|--------|:---:|
|
||
|4001|文件上传失败(网络中断等原因)|页面顶部 Toast 提示|文件上传失败,请检查网络后重试|是|
|
||
|4001|文件写入磁盘失败|页面顶部 Toast 提示|文件保存失败,请稍后重试|是|
|
||
|4002|文件格式不支持|上传弹窗行内提示|仅支持 MD、Word、TXT、PDF 格式的文件|否(需转换格式)|
|
||
|4003|单个文件超过 50MB 限制|上传弹窗行内提示|文件大小不能超过 50MB|否(需压缩文件)|
|
||
|4003|批量上传总大小超过限制|上传弹窗行内提示|上传文件总大小不能超过 200MB|否(需分批上传)|
|
||
|4004|Excel 导入模板格式错误|弹窗提示|请下载模板后按模板格式填写|否(需使用模板)|
|
||
|
||
### 18.6 系统相关错误(5xxx-9xxx)
|
||
|
||
|错误码|触发场景|显示位置|具体文案|是否可重试|
|
||
|:---:|--------|--------|--------|:---:|
|
||
|5001|PostgreSQL 数据库操作失败|页面顶部 Toast 提示|系统繁忙,请稍后重试|是|
|
||
|5002|Redis 缓存操作失败|页面顶部 Toast 提示|系统繁忙,请稍后重试|是|
|
||
|5003|文件操作失败(读写异常)|页面顶部 Toast 提示|文件处理异常,请稍后重试|是|
|
||
|9999|服务器内部未知错误|页面顶部 Toast 提示|系统异常,请稍后重试|是|
|
||
|
||
### 18.7 前端本地验证错误(非接口返回)
|
||
|
||
|触发场景|显示位置|具体文案|是否可重试|
|
||
|--------|--------|--------|:---:|
|
||
|登录用户名为空|行内错误|用户名不能为空|修改后重试|
|
||
|登录密码为空|行内错误|密码不能为空|修改后重试|
|
||
|登录用户名过短|行内错误|用户名至少3个字符|修改后重试|
|
||
|登录密码过短|行内错误|密码至少8个字符|修改后重试|
|
||
|客户姓名为空|行内错误|请输入客户姓名|修改后重试|
|
||
|年龄为空|行内错误|请输入年龄|修改后重试|
|
||
|年龄不在有效范围|行内错误|请输入有效的年龄(1-150)|修改后重试|
|
||
|年龄非整数|行内错误|年龄必须为整数|修改后重试|
|
||
|性别未选择|行内错误|请选择客户性别|修改后重试|
|
||
|职业为空|行内错误|请输入客户职业|修改后重试|
|
||
|年收入为负数|行内错误|年收入不能为负数|修改后重试|
|
||
|月预算为空|行内错误|请输入月预算|修改后重试|
|
||
|月预算为零或负数|行内错误|月预算必须大于0|修改后重试|
|
||
|未选择关注险种|行内错误|请至少选择一个关注险种|修改后重试|
|
||
|保额低于下限|行内错误|保额不能低于1万元|修改后重试|
|
||
|保额超过上限|行内错误|保额不能超过1000万元|修改后重试|
|
||
|保障期限未选择|行内错误|请选择保障期限|修改后重试|
|
||
|已有保单超过20条|行内错误|已有保单不能超过20条|修改后重试|
|
||
|网络断开时提交|页面弹窗|网络异常,请检查网络连接|检查网络后重试|
|
||
|输入内容过长(>10000字)|行内提示|输入内容过长,请缩短后重试|修改后重试|
|
||
|搜索关键词为空|行内提示|请输入搜索关键词|修改后重试|
|
||
|
||
### 18.8 错误展示规范
|
||
|
||
|展示方式|适用场景|展示位置|持续时间|是否可关闭|
|
||
|--------|--------|--------|--------|:---:|
|
||
|Toast 提示(轻提示)|一般性错误、操作成功提示|页面顶部居中 |3秒后自动消失|否|
|
||
|行内错误|表单字段验证失败|对应字段下方|持续显示直到修正|否|
|
||
|弹窗(Modal)|严重错误、需用户确认的操作|页面居中遮罩层|用户点击关闭或确认|是|
|
||
|错误页面(404/500)|页面级错误|全屏替换内容|持续显示|点击按钮跳转|
|
||
|
||
---
|
||
|
||
## 十九、BaoDan Workflow 节点完整设计
|
||
|
||
> 产品推荐方案 BaoDan Workflow 的每个节点完整配置,包含输入变量、输出变量、完整 Prompt、条件分支逻辑、错误处理和测试数据。
|
||
|
||
### 19.1 Workflow 总览
|
||
|
||
```
|
||
[开始] → [节点1:参数校验] → [节点2:检索策略] → [节点3:知识库检索(循环)]
|
||
↓
|
||
[节点6:格式化输出] ← [节点4:LLM方案生成] ← [节点5:异常处理]
|
||
↓
|
||
[结束]
|
||
```
|
||
|
||
### 19.2 节点 1:参数校验(Code Node)
|
||
|
||
**节点配置**:
|
||
|
||
|属性|值|
|
||
|------|---|
|
||
|节点名称|参数校验|
|
||
|节点类型|Code(代码执行)|
|
||
|编程语言|Python3|
|
||
|
||
**输入变量**:
|
||
|
||
|变量名|类型|来源|必填|
|
||
|------|-----|-----|:---:|
|
||
|age|string|开始节点|是|
|
||
|gender|string|开始节点|是|
|
||
|occupation|string|开始节点|是|
|
||
|annual_income|string|开始节点|是|
|
||
|monthly_budget|string|开始节点|是|
|
||
|insurance_types|string|开始节点|是|
|
||
|coverage_amount|string|开始节点|是|
|
||
|coverage_period|string|开始节点|是|
|
||
|
||
**输出变量**:
|
||
|
||
|变量名|类型|说明|
|
||
|------|-----|-----|
|
||
|validated_params|object|校验通过的参数对象|
|
||
|error_msg|string|校验失败时的错误信息(为空表示通过)|
|
||
|is_valid|bool|是否校验通过|
|
||
|
||
**代码逻辑**:
|
||
|
||
```python
|
||
import json
|
||
|
||
def main(age: str, gender: str, occupation: str, annual_income: str,
|
||
monthly_budget: str, insurance_types: str, coverage_amount: str,
|
||
coverage_period: str) -> dict:
|
||
errors = []
|
||
|
||
# 年龄校验
|
||
try:
|
||
age_int = int(age)
|
||
if not (1 <= age_int <= 150):
|
||
errors.append("年龄必须在1-150之间")
|
||
except ValueError:
|
||
errors.append("年龄必须为整数")
|
||
|
||
# 性别校验
|
||
if gender not in ("male", "female"):
|
||
errors.append("性别必须为 male 或 female")
|
||
|
||
# 职业校验
|
||
if not occupation or not occupation.strip():
|
||
errors.append("职业不能为空")
|
||
|
||
# 月预算校验
|
||
try:
|
||
budget = int(monthly_budget)
|
||
if budget <= 0:
|
||
errors.append("月预算必须大于0")
|
||
except ValueError:
|
||
errors.append("月预算必须为正整数")
|
||
|
||
# 保额校验
|
||
try:
|
||
amount = int(coverage_amount)
|
||
if amount < 10000:
|
||
errors.append("保额不能低于1万元")
|
||
except ValueError:
|
||
errors.append("保额必须为正整数")
|
||
|
||
# 险种校验
|
||
valid_types = ["重疾险", "寿险", "医疗险", "意外险", "年金险", "储蓄险"]
|
||
selected = [t.strip() for t in insurance_types.split(",") if t.strip()]
|
||
if not selected:
|
||
errors.append("请至少选择一个险种")
|
||
for t in selected:
|
||
if t not in valid_types:
|
||
errors.append(f"无效的险种: {t}")
|
||
|
||
if errors:
|
||
return {
|
||
"validated_params": {},
|
||
"error_msg": "; ".join(errors),
|
||
"is_valid": False
|
||
}
|
||
|
||
return {
|
||
"validated_params": {
|
||
"age": age_int,
|
||
"gender": gender,
|
||
"occupation": occupation.strip(),
|
||
"annual_income": int(annual_income) if annual_income else 0,
|
||
"monthly_budget": budget,
|
||
"insurance_types": selected,
|
||
"coverage_amount": amount,
|
||
"coverage_period": coverage_period
|
||
},
|
||
"error_msg": "",
|
||
"is_valid": True
|
||
}
|
||
```
|
||
|
||
**错误处理**:当 `is_valid = False` 时,直接跳转到结束节点,输出 `error_msg` 作为错误提示。
|
||
|
||
### 19.3 节点 2:检索策略生成(Code Node)
|
||
|
||
**输入变量**:
|
||
|
||
|变量名|类型|来源|
|
||
|------|-----|-----|
|
||
|validated_params|object|节点1输出|
|
||
|
||
**输出变量**:
|
||
|
||
|变量名|类型|说明|
|
||
|------|-----|-----|
|
||
|search_queries|array|检索关键词列表,每项含险种和query|
|
||
|
||
**代码逻辑**:
|
||
|
||
```python
|
||
def main(validated_params: dict) -> dict:
|
||
queries = []
|
||
age = validated_params["age"]
|
||
|
||
for insurance_type in validated_params["insurance_types"]:
|
||
query = f"{insurance_type} 产品条款 保额 费率 {age}岁"
|
||
queries.append({
|
||
"险种": insurance_type,
|
||
"query": query
|
||
})
|
||
|
||
return {"search_queries": queries}
|
||
```
|
||
|
||
### 19.4 节点 3:知识库检索(Knowledge Retrieval Node)
|
||
|
||
**节点配置**:
|
||
|
||
|属性|值|
|
||
|------|---|
|
||
|节点名称|知识库检索|
|
||
|节点类型|Knowledge Retrieval(知识库检索)|
|
||
|关联知识库|所有险种知识库(重疾险/寿险/医疗险/意外险/年金险/储蓄险)|
|
||
|检索模式|混合检索(向量 + 全文)|
|
||
|Top-K|每个查询返回 5 个最相关片段|
|
||
|Score 阈值|0.6(低于此分数的片段被过滤)|
|
||
|
||
**输入变量**:
|
||
|
||
|变量名|类型|来源|
|
||
|------|-----|-----|
|
||
|query|string|从 search_queries 数组中提取的检索词|
|
||
|
||
**输出变量**:
|
||
|
||
|变量名|类型|说明|
|
||
|------|-----|-----|
|
||
|retrieved_docs|array|检索到的文档片段列表,含文档名、内容、分数、来源|
|
||
|
||
**节点行为**:
|
||
- 对每个险种分别执行一次检索
|
||
- 每次检索返回 Top-5 相关片段
|
||
- 所有检索结果合并后传递给下游节点
|
||
|
||
### 19.5 节点 4:LLM 方案生成(LLM Node)
|
||
|
||
**节点配置**:
|
||
|
||
|属性|值|
|
||
|------|---|
|
||
|节点名称|方案生成|
|
||
|节点类型|LLM|
|
||
|模型|deepseek-chat|
|
||
|Temperature|0.7|
|
||
|Top P|0.9|
|
||
|Max Tokens|8192|
|
||
|
||
**输入变量**:
|
||
|
||
|变量名|类型|来源|
|
||
|------|-----|-----|
|
||
|validated_params|object|节点1输出|
|
||
|retrieved_docs|array|节点3输出|
|
||
|
||
**输出变量**:
|
||
|
||
|变量名|类型|说明|
|
||
|------|-----|-----|
|
||
|recommendation|string|生成的三套方案(Markdown 格式)|
|
||
|
||
**完整 Prompt**:
|
||
|
||
```
|
||
你是一名资深保险方案规划师,拥有 10 年保险行业经验。你的任务是根据客户的个人信息和检索到的保险产品条款,为客户量身定制保险产品推荐方案。
|
||
|
||
## 客户信息
|
||
|
||
- 年龄:{{validated_params.age}} 岁
|
||
- 性别:{{validated_params.gender}}
|
||
- 职业:{{validated_params.occupation}}
|
||
- 年收入:{{validated_params.annual_income}} 元
|
||
- 月预算:{{validated_params.monthly_budget}} 元
|
||
- 关注险种:{{validated_params.insurance_types}}
|
||
- 保额目标:{{validated_params.coverage_amount}} 元
|
||
- 保障期限:{{validated_params.coverage_period}}
|
||
|
||
## 检索到的产品信息
|
||
|
||
{{retrieved_docs}}
|
||
|
||
## 输出要求
|
||
|
||
请严格按照以下格式生成三套方案(基础方案 / 均衡方案 / 全面方案),每套方案需独立完整:
|
||
|
||
### 基础方案(年保费约 XXXX 元)
|
||
|
||
|产品名称|所属保险公司|险种|保额|年保费|推荐理由|
|
||
|---------|-----------|-----|-----|------|--------|
|
||
|XX重疾险|XX人寿|重疾险|30万|2400元|35岁男性投保,性价比高,覆盖120种重疾|
|
||
|
||
方案总结:(2-3句话说明基础方案的特点和适用人群)
|
||
|
||
### 均衡方案(年保费约 XXXX 元)
|
||
|
||
(同上格式)
|
||
|
||
方案总结:(2-3句话)
|
||
|
||
### 全面方案(年保费约 XXXX 元)
|
||
|
||
(同上格式)
|
||
|
||
方案总结:(2-3句话)
|
||
|
||
## 重要规则
|
||
|
||
1. **保费数据必须来自检索到的产品信息,绝对不可编造**
|
||
2. 如果某险种在知识库中没有匹配到合适产品,在该险种下标注"暂无合适产品推荐,请咨询相关保险公司"
|
||
3. 三套方案的总年保费不得超过客户月预算 x 12
|
||
4. 基础方案侧重核心保障,保费最低;均衡方案保障适中;全面方案覆盖最广
|
||
5. 优先推荐性价比高的产品
|
||
6. 每款产品的推荐理由控制在 1-2 句话,突出核心卖点
|
||
7. 最后附上免责声明:"以上方案仅供参考,具体保障内容以保险合同条款为准。投保前请仔细阅读产品条款。"
|
||
```
|
||
|
||
### 19.6 节点 5:异常处理(Code Node)
|
||
|
||
**输入变量**:
|
||
|
||
|变量名|类型|来源|
|
||
|------|-----|-----|
|
||
|is_valid|bool|节点1输出|
|
||
|error_msg|string|节点1输出|
|
||
|recommendation|string|节点4输出(可能为空)|
|
||
|
||
**输出变量**:
|
||
|
||
|变量名|类型|说明|
|
||
|------|-----|-----|
|
||
|final_output|string|最终输出内容|
|
||
|is_success|bool|是否成功|
|
||
|
||
**代码逻辑**:
|
||
|
||
```python
|
||
def main(is_valid: bool, error_msg: str, recommendation: str) -> dict:
|
||
if not is_valid:
|
||
return {
|
||
"final_output": f"参数校验失败:{error_msg}",
|
||
"is_success": False
|
||
}
|
||
|
||
if not recommendation or not recommendation.strip():
|
||
return {
|
||
"final_output": "未能生成推荐方案,请尝试调整筛选条件后重新提交。",
|
||
"is_success": False
|
||
}
|
||
|
||
return {
|
||
"final_output": recommendation,
|
||
"is_success": True
|
||
}
|
||
```
|
||
|
||
### 19.7 节点 6:格式化输出(Code Node)
|
||
|
||
**输入变量**:
|
||
|
||
|变量名|类型|来源|
|
||
|------|-----|-----|
|
||
|final_output|string|节点5输出|
|
||
|
||
**输出变量**:
|
||
|
||
|变量名|类型|说明|
|
||
|------|-----|-----|
|
||
|recommendation|string|格式化后的方案文本|
|
||
|
||
**代码逻辑**:
|
||
|
||
```python
|
||
import re
|
||
|
||
def main(final_output: str) -> dict:
|
||
# 去除多余空行(保留最多2个连续换行)
|
||
text = re.sub(r'\n{3,}', '\n\n', final_output)
|
||
# 去除首尾空白
|
||
text = text.strip()
|
||
return {"recommendation": text}
|
||
```
|
||
|
||
### 19.8 条件分支逻辑
|
||
|
||
```
|
||
节点1(参数校验)
|
||
├─ is_valid = True → 节点2(检索策略) → 节点3(知识库检索) → 节点4(LLM生成) → 节点5(异常处理) → 节点6(格式化) → 结束
|
||
└─ is_valid = False → 节点5(异常处理) → 节点6(格式化) → 结束
|
||
```
|
||
|
||
### 19.9 节点失败时的行为
|
||
|
||
|失败节点|错误处理|输出内容|
|
||
|---------|--------|--------|
|
||
|节点1(参数校验)|直接跳转节点5|返回 error_msg |
|
||
|节点2(检索策略)|BaoDan 自动重试1次,失败则跳转节点5|返回"检索策略生成失败"|
|
||
|节点3(知识库检索)|BaoDan 自动重试1次,失败则跳转节点5|返回"知识库检索失败,请稍后重试"|
|
||
|节点4(LLM生成)|BaoDan 自动重试1次,失败则跳转节点5|返回"方案生成失败,请稍后重试"|
|
||
|节点5/6(处理节点)|BaoDan 自动重试1次|返回"系统处理异常"|
|
||
|
||
### 19.10 测试数据与预期输出
|
||
|
||
**测试用例 1:标准场景**
|
||
|
||
输入:
|
||
```json
|
||
{
|
||
"age": "35",
|
||
"gender": "male",
|
||
"occupation": "软件工程师",
|
||
"annual_income": "300000",
|
||
"monthly_budget": "2000",
|
||
"insurance_types": "重疾险,医疗险",
|
||
"coverage_amount": "500000",
|
||
"coverage_period": "终身"
|
||
}
|
||
```
|
||
|
||
预期输出:
|
||
- 三套方案(基础/均衡/全面)
|
||
- 每套方案含产品表格(产品名/保司/险种/保额/保费/推荐理由)
|
||
- 基础方案总年保费 <= 24000 元(2000 x 12)
|
||
- 不得包含编造的产品信息
|
||
|
||
**测试用例 2:边界 - 低预算**
|
||
|
||
输入:
|
||
```json
|
||
{
|
||
"age": "25",
|
||
"gender": "female",
|
||
"occupation": "教师",
|
||
"annual_income": "80000",
|
||
"monthly_budget": "500",
|
||
"insurance_types": "意外险",
|
||
"coverage_amount": "50000",
|
||
"coverage_period": "10年"
|
||
}
|
||
```
|
||
|
||
预期输出:基础方案总年保费 <= 6000 元
|
||
|
||
**测试用例 3:异常 - 无效参数**
|
||
|
||
输入:
|
||
```json
|
||
{
|
||
"age": "-1",
|
||
"gender": "unknown",
|
||
"occupation": "",
|
||
"annual_income": "0",
|
||
"monthly_budget": "0",
|
||
"insurance_types": "",
|
||
"coverage_amount": "0",
|
||
"coverage_period": ""
|
||
}
|
||
```
|
||
|
||
预期输出:返回参数校验失败错误信息
|
||
|
||
---
|
||
|
||
## 二十、从零到运行部署清单
|
||
|
||
> 从一台全新的 Linux 服务器到系统完全可用的完整部署步骤,每一步包含具体命令和验证方法。适用于 Ubuntu 22.04 LTS。
|
||
|
||
### 20.1 服务器环境准备
|
||
|
||
**最低配置**:4核CPU / 8GB内存 / 100GB SSD / 公网IP
|
||
|
||
**第一步:系统更新**
|
||
|
||
```bash
|
||
sudo apt update && sudo apt upgrade -y
|
||
sudo apt install -y curl wget git vim ufw
|
||
```
|
||
|
||
**第二步:设置时区**
|
||
|
||
```bash
|
||
sudo timedatectl set-timezone Asia/Shanghai
|
||
```
|
||
|
||
**第三步:配置防火墙**
|
||
|
||
```bash
|
||
sudo ufw allow 22/tcp # SSH
|
||
sudo ufw allow 80/tcp # HTTP
|
||
sudo ufw allow 443/tcp # HTTPS
|
||
sudo ufw enable
|
||
sudo ufw status
|
||
```
|
||
|
||
验证:`sudo ufw status` 应显示 22、80、443 端口已允许。
|
||
|
||
**第四步:安装 Docker**
|
||
|
||
```bash
|
||
# 安装 Docker
|
||
curl -fsSL https://get.docker.com | sudo bash
|
||
|
||
# 启动 Docker 并设置开机自启
|
||
sudo systemctl start docker
|
||
sudo systemctl enable docker
|
||
|
||
# 验证
|
||
docker --version
|
||
# 预期输出:Docker version 24.x.x 或更高
|
||
```
|
||
|
||
**第五步:安装 Docker Compose**
|
||
|
||
```bash
|
||
# 安装 Docker Compose 插件
|
||
sudo apt install -y docker-compose-plugin
|
||
|
||
# 验证
|
||
docker compose version
|
||
# 预期输出:Docker Compose version v2.x.x
|
||
```
|
||
|
||
### 20.2 BaoDan 部署
|
||
|
||
**第一步:克隆 BaoDan 仓库**
|
||
|
||
```bash
|
||
cd /opt
|
||
sudo git clone https://github.com/langgenius/baodan.git
|
||
cd /opt/baodan/docker
|
||
sudo cp .env.example .env
|
||
```
|
||
|
||
**第二步:修改 BaoDan 环境变量**
|
||
|
||
```bash
|
||
sudo vim .env
|
||
```
|
||
|
||
修改以下关键配置:
|
||
|
||
```bash
|
||
# 数据库配置
|
||
DB_USERNAME=postgres
|
||
DB_PASSWORD=your_strong_db_password_here
|
||
DB_HOST=db
|
||
DB_PORT=5432
|
||
DB_DATABASE=baodan
|
||
|
||
# Redis 配置
|
||
REDIS_PASSWORD=your_strong_redis_password_here
|
||
|
||
# 密钥配置(随机生成,勿用默认值)
|
||
SECRET_KEY=随机生成的64位字符串
|
||
INIT_PASSWORD=your_admin_password_here
|
||
|
||
# 外部访问地址
|
||
CONSOLE_WEB_URL=https://your-domain.com
|
||
SERVICE_API_URL=https://your-domain.com
|
||
APP_WEB_URL=https://your-domain.com
|
||
```
|
||
|
||
生成随机密钥:
|
||
```bash
|
||
openssl rand -base64 42
|
||
```
|
||
|
||
**第三步:启动 BaoDan**
|
||
|
||
```bash
|
||
sudo docker compose up -d
|
||
```
|
||
|
||
等待所有容器启动完成(约 2-5 分钟):
|
||
```bash
|
||
sudo docker compose ps
|
||
```
|
||
|
||
验证:所有容器状态应为 `running`。
|
||
|
||
**第四步:验证 BaoDan 访问**
|
||
|
||
浏览器打开 `http://你的服务器IP:3000`,应看到 BaoDan 欢迎页面。
|
||
|
||
### 20.3 PostgreSQL 配置
|
||
|
||
BaoDan 的 Docker Compose 已包含 PostgreSQL。如需在同一实例使用自研表:
|
||
|
||
**第一步:进入 PostgreSQL**
|
||
|
||
```bash
|
||
sudo docker compose exec db psql -U postgres
|
||
```
|
||
|
||
**第二步:创建自研数据库**
|
||
|
||
```sql
|
||
CREATE DATABASE insurance_bot OWNER postgres;
|
||
\c insurance_bot
|
||
```
|
||
|
||
**第三步:创建自研表结构**
|
||
|
||
```sql
|
||
-- 企微用户映射表
|
||
CREATE TABLE wecom_user_mapping (
|
||
id SERIAL PRIMARY KEY,
|
||
wecom_userid VARCHAR(64) NOT NULL UNIQUE,
|
||
baodan_user_id VARCHAR(64) NOT NULL,
|
||
username VARCHAR(128),
|
||
department VARCHAR(128),
|
||
role VARCHAR(32) DEFAULT 'sales',
|
||
status VARCHAR(16) DEFAULT 'active',
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
last_active_at TIMESTAMP
|
||
);
|
||
|
||
CREATE UNIQUE INDEX idx_wecom_userid ON wecom_user_mapping(wecom_userid);
|
||
CREATE INDEX idx_baodan_user_id ON wecom_user_mapping(baodan_user_id);
|
||
|
||
-- 推荐方案记录表
|
||
CREATE TABLE recommendation_records (
|
||
id SERIAL PRIMARY KEY,
|
||
user_id VARCHAR(64) NOT NULL,
|
||
customer_name VARCHAR(64),
|
||
customer_age SMALLINT,
|
||
customer_gender VARCHAR(8),
|
||
health_status VARCHAR(32),
|
||
occupation VARCHAR(64),
|
||
annual_income INTEGER,
|
||
monthly_budget INTEGER,
|
||
insurance_types TEXT,
|
||
coverage_amount INTEGER,
|
||
coverage_period VARCHAR(32),
|
||
existing_policies TEXT,
|
||
generated_plan TEXT,
|
||
plan_variants TEXT,
|
||
baodan_task_id VARCHAR(64),
|
||
status VARCHAR(16) DEFAULT 'pending',
|
||
error_message TEXT,
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
completed_at TIMESTAMP
|
||
);
|
||
|
||
CREATE INDEX idx_rec_user_id ON recommendation_records(user_id);
|
||
CREATE INDEX idx_rec_status ON recommendation_records(status);
|
||
CREATE INDEX idx_rec_created_at ON recommendation_records(created_at);
|
||
|
||
-- 系统操作日志表
|
||
CREATE TABLE system_operation_logs (
|
||
id SERIAL PRIMARY KEY,
|
||
user_id VARCHAR(64) NOT NULL,
|
||
action VARCHAR(32) NOT NULL,
|
||
target_type VARCHAR(32),
|
||
target_id VARCHAR(64),
|
||
detail JSONB,
|
||
ip VARCHAR(45),
|
||
user_agent VARCHAR(256),
|
||
created_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
CREATE INDEX idx_sol_user_id ON system_operation_logs(user_id);
|
||
CREATE INDEX idx_sol_action ON system_operation_logs(action);
|
||
CREATE INDEX idx_sol_created_at ON system_operation_logs(created_at);
|
||
```
|
||
|
||
验证:`\dt` 应显示 3 张表已创建。
|
||
|
||
### 20.4 Redis 配置
|
||
|
||
BaoDan 的 Docker Compose 已包含 Redis。自研后端共用同一个 Redis 实例即可。
|
||
|
||
验证:
|
||
```bash
|
||
sudo docker compose exec redis redis-cli -a your_redis_password ping
|
||
# 预期输出:PONG
|
||
```
|
||
|
||
### 20.5 后端部署
|
||
|
||
**第一步:安装 Python 环境**
|
||
|
||
```bash
|
||
# 安装 Python 3.12
|
||
sudo apt install -y python3.12 python3.12-venv python3-pip
|
||
|
||
# 验证
|
||
python3.12 --version
|
||
# 预期输出:Python 3.12.x
|
||
```
|
||
|
||
**第二步:创建项目目录并配置虚拟环境**
|
||
|
||
```bash
|
||
mkdir -p /opt/insurance-bot/backend
|
||
cd /opt/insurance-bot/backend
|
||
|
||
# 创建虚拟环境
|
||
python3.12 -m venv venv
|
||
source venv/bin/activate
|
||
```
|
||
|
||
**第三步:安装依赖**
|
||
|
||
```bash
|
||
pip install fastapi uvicorn[standard] psycopg2-binary redis python-jose[cryptography] passlib[bcrypt] python-multipart httpx pydantic
|
||
```
|
||
|
||
**第四步:配置环境变量**
|
||
|
||
```bash
|
||
cat > /opt/insurance-bot/backend/.env << 'EOF'
|
||
# 数据库
|
||
DATABASE_URL=postgresql://postgres:your_db_password@db:5432/insurance_bot
|
||
|
||
# Redis
|
||
REDIS_URL=redis://:your_redis_password@redis:6379/0
|
||
|
||
# JWT
|
||
JWT_SECRET_KEY=your_jwt_secret_key_here
|
||
JWT_ALGORITHM=HS256
|
||
JWT_EXPIRE_MINUTES=120
|
||
|
||
# BaoDan
|
||
BAODAN_API_BASE_URL=http://api:5001
|
||
BAODAN_CHAT_API_KEY=app-xxxxxxxxxxxx
|
||
BAODAN_WORKFLOW_API_KEY=app-yyyyyyyyyyyy
|
||
|
||
# 企微
|
||
WECOM_CORP_ID=ww1234567890abcdef
|
||
WECOM_AGENT_ID=1000002
|
||
WECOM_AGENT_SECRET=your_agent_secret_here
|
||
WECOM_TOKEN=your_callback_token
|
||
WECOM_ENCODING_AES_KEY=your_encoding_aes_key
|
||
|
||
# 服务器
|
||
SERVER_HOST=0.0.0.0
|
||
SERVER_PORT=8000
|
||
EOF
|
||
```
|
||
|
||
**第五步:编写 Dockerfile**
|
||
|
||
```dockerfile
|
||
FROM python:3.12-slim
|
||
|
||
WORKDIR /app
|
||
|
||
COPY requirements.txt .
|
||
RUN pip install --no-cache-dir -r requirements.txt
|
||
|
||
COPY . .
|
||
|
||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||
```
|
||
|
||
**第六步:启动后端服务**
|
||
|
||
```bash
|
||
# 如果使用 Docker
|
||
sudo docker build -t insurance-bot-backend .
|
||
sudo docker run -d \
|
||
--name insurance-backend \
|
||
--restart always \
|
||
--env-file .env \
|
||
--network docker_default \
|
||
-p 8000:8000 \
|
||
insurance-bot-backend
|
||
|
||
# 或者直接运行
|
||
cd /opt/insurance-bot/backend
|
||
source venv/bin/activate
|
||
uvicorn main:app --host 0.0.0.0 --port 8000
|
||
```
|
||
|
||
验证:
|
||
```bash
|
||
curl http://localhost:8000/docs
|
||
# 预期输出:Flask Swagger UI 页面
|
||
```
|
||
|
||
### 20.6 前端部署
|
||
|
||
**第一步:安装 Node.js**
|
||
|
||
```bash
|
||
# 安装 Node.js 22
|
||
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo bash -
|
||
sudo apt install -y nodejs
|
||
|
||
# 验证
|
||
node --version # v22.x.x
|
||
npm --version # 10.x.x
|
||
```
|
||
|
||
**第二步:构建前端**
|
||
|
||
```bash
|
||
cd /opt/insurance-bot/frontend
|
||
|
||
# 安装依赖
|
||
npm install
|
||
|
||
# 构建生产版本
|
||
npm run build
|
||
# 产物在 dist/ 目录
|
||
```
|
||
|
||
**第三步:配置 Nginx**
|
||
|
||
```bash
|
||
sudo apt install -y nginx
|
||
```
|
||
|
||
创建 Nginx 配置文件:
|
||
|
||
```bash
|
||
sudo vim /etc/nginx/sites-available/insurance-bot
|
||
```
|
||
|
||
```nginx
|
||
server {
|
||
listen 80;
|
||
server_name your-domain.com;
|
||
|
||
# 重定向到 HTTPS
|
||
return 301 https://$host$request_uri;
|
||
}
|
||
|
||
server {
|
||
listen 443 ssl http2;
|
||
server_name your-domain.com;
|
||
|
||
# SSL 证书(参见 20.8)
|
||
ssl_certificate /etc/ssl/your-domain.com.pem;
|
||
ssl_certificate_key /etc/ssl/your-domain.com.key;
|
||
|
||
# 安全头
|
||
add_header X-Frame-Options SAMEORIGIN;
|
||
add_header X-Content-Type-Options nosniff;
|
||
add_header X-XSS-Protection "1; mode=block";
|
||
|
||
# 你的前端(Vue 3 产品推荐页面)
|
||
location /app {
|
||
alias /opt/insurance-bot/frontend/dist;
|
||
try_files $uri $uri/ /app/index.html;
|
||
}
|
||
|
||
# 你的后端 API
|
||
location /api/ {
|
||
proxy_pass http://127.0.0.1:8000/api/;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
proxy_read_timeout 120s;
|
||
}
|
||
|
||
# BaoDan 后台 + 对话页面
|
||
location / {
|
||
proxy_pass http://127.0.0.1:3000;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
}
|
||
|
||
# BaoDan API
|
||
location /v1/ {
|
||
proxy_pass http://127.0.0.1:5001/v1/;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
}
|
||
}
|
||
```
|
||
|
||
启用配置:
|
||
```bash
|
||
sudo ln -s /etc/nginx/sites-available/insurance-bot /etc/nginx/sites-enabled/
|
||
sudo rm -f /etc/nginx/sites-enabled/default
|
||
sudo nginx -t
|
||
sudo systemctl reload nginx
|
||
```
|
||
|
||
验证:`sudo nginx -t` 应输出 `syntax is ok` 和 `test is successful`。
|
||
|
||
### 20.7 企微后台配置
|
||
|
||
参见第十六章《企微应用配置操作手册》完整步骤。关键信息确认清单:
|
||
|
||
|配置项|获取方式|配置位置|
|
||
|------|---------|--------|
|
||
|CorpID|企微管理后台 → 我的企业 | 后端 .env |
|
||
|AgentID|应用详情页 | 后端 .env |
|
||
|AgentSecret|应用详情 → Secret | 后端 .env |
|
||
|Token|接收消息设置页 | 后端 .env |
|
||
|EncodingAESKey|接收消息设置页 | 后端 .env |
|
||
|回调URL|`https://your-domain.com/api/wecom/callback`|企微后台设置|
|
||
|可信IP|服务器公网IP|企微后台设置|
|
||
|OAuth域名|`your-domain.com`|企微管理后台 → 授权登录|
|
||
|
||
### 20.8 域名 + HTTPS 配置
|
||
|
||
**第一步:购买域名并解析**
|
||
|
||
在域名服务商处添加 DNS 记录:
|
||
|记录类型|主机记录|记录值|
|
||
|--------|--------|------|
|
||
|A|@|服务器公网IP|
|
||
|A|chat|服务器公网IP|
|
||
|A|api|服务器公网IP|
|
||
|
||
**第二步:申请 SSL 证书**
|
||
|
||
使用 Let's Encrypt 免费证书:
|
||
|
||
```bash
|
||
sudo apt install -y certbot python3-certbot-nginx
|
||
|
||
sudo certbot --nginx -d your-domain.com -d chat.your-domain.com -d api.your-domain.com
|
||
```
|
||
|
||
按照提示输入邮箱并同意条款。Certbot 会自动修改 Nginx 配置并安装证书。
|
||
|
||
**第三步:设置自动续期**
|
||
|
||
```bash
|
||
sudo crontab -e
|
||
# 添加以下行(每天凌晨2点检查续期)
|
||
0 2 * * * certbot renew --quiet --post-hook "systemctl reload nginx"
|
||
```
|
||
|
||
验证:浏览器访问 `https://your-domain.com`,应显示安全锁标志。
|
||
|
||
### 20.9 验证清单
|
||
|
||
|步骤|验证命令/操作|预期结果|通过标准|
|
||
|-----|-------------|--------|--------|
|
||
|1. Docker 运行 | `docker compose ps` | 所有容器 running | 无 exited 容器 |
|
||
|2. PostgreSQL 连接 | `docker compose exec db psql -U postgres -c '\l'` | 显示 baodan 和 insurance_bot 数据库 | 两个库都存在 |
|
||
|3. Redis 连接 | `docker compose exec redis redis-cli ping` | PONG | 返回 PONG |
|
||
|4. BaoDan 后台访问 | 浏览器打开 `http://IP:3000` | BaoDan 欢迎页面 | 页面正常加载 |
|
||
|5. BaoDan 管理员登录 | 使用 15.1 设置的账号登录 | 进入 BaoDan 后台 | 登录成功 |
|
||
|6. DeepSeek 模型配置 | BaoDan 设置 → 模型供应商 | 显示 DeepSeek 且状态正常 | 测试连通成功 |
|
||
|7. 知识库文档 | BaoDan 知识库页面 | 文档状态为"可用" | 无 processing/failed |
|
||
|8. 对话功能 | BaoDan 预览对话 | 回答基于知识库内容 | 回答准确 |
|
||
|9. 后端 API | `curl http://localhost:8000/docs` | Swagger UI 页面 | 页面正常加载 |
|
||
|10. 前端页面 | 浏览器打开 `https://your-domain.com/app` | Vue 应用页面 | 页面正常加载 |
|
||
|11. HTTPS | 浏览器访问 `https://your-domain.com` | 安全锁标志 | 证书有效 |
|
||
|12. 企微机器人 | 在企微中发送消息给机器人 | 收到 AI 回复 | 回复在 10 秒内 |
|
||
|13. 企微 OAuth | 在企微内置浏览器访问登录页 | 跳转授权后登录成功 | 进入系统主页 |
|
||
|14. 产品推荐 | 填写表单并提交 | 生成三套方案 | 方案内容合理 |
|
||
|15. 权限验证 | 用销售人员账号访问管理页面 | 被拒绝访问 | 显示权限不足提示 |
|
||
|
||
---
|
||
|
||
## 二十一、角色-功能权限矩阵
|
||
|
||
### 21.1 页面访问权限
|
||
|
||
| 页面 | 超级管理员 | 管理员 | 销售主管 | 销售人员 | 客户 |
|
||
|------|:---:|:---:|:---:|:---:|:---:|
|
||
| /login | Y | Y | Y | Y | Y |
|
||
| /chat | Y | Y | Y | Y | Y |
|
||
| /recommend | Y | Y | Y | Y | Y |
|
||
| /recommend/history | Y | Y | Y | Y | Y |
|
||
| /admin/knowledge-base | Y | Y | - | - | - |
|
||
| /admin/logs/chat | Y | Y | 仅本组 | 仅自己 | - |
|
||
| /admin/logs/system | Y | - | - | - | - |
|
||
| /admin/users | Y | - | - | - | - |
|
||
| /admin/roles | Y | - | - | - | - |
|
||
| /admin/llm-configs | Y | - | - | - | - |
|
||
| /admin/prompts | Y | Y | - | - | - |
|
||
| /admin/stats | Y | Y | 仅本组 | - | - |
|
||
|
||
### 21.2 数据权限规则
|
||
|
||
| 角色 | 可见数据范围 |
|
||
|------|------------|
|
||
| 超级管理员 | 全部数据 |
|
||
| 管理员 | 本部门数据 |
|
||
| 销售主管 | 本组数据 |
|
||
| 销售人员 | 仅自己的数据 |
|
||
| 客户 | 仅自己的对话 |
|
||
|
||
## 二十二、端到端业务流程
|
||
```
|
||
|
||
[开始]
|
||
|
||
v
|
||
|
||
[1. 用户登录]
|
||
|
||
| 企微 OAuth / 账密登录
|
||
|
||
| -> 生成 JWT Token
|
||
|
||
v
|
||
|
||
[2. 进入对话页]
|
||
|
||
| BaoDan WebApp 加载(iframe 嵌入)
|
||
|
||
| 显示会话列表(历史对话)
|
||
|
||
v
|
||
|
||
[3. 选择检索范围](可选)
|
||
|
||
| 选择险种筛选器(重疾/寿险/医疗...)
|
||
|
||
| 选择保司筛选器(XX人寿/太平洋...)
|
||
|
||
v
|
||
|
||
[4. 用户输入问题]
|
||
|
||
| 回车发送 / 点击发送按钮
|
||
|
||
| 前端校验:非空、长度限制
|
||
|
||
v
|
||
|
||
[5. 发送请求]
|
||
|
||
| -> BaoDan Chat API(SSE 流式)
|
||
|
||
| -> BaoDan 执行 RAG 检索(知识库 -> 向量搜索 -> Top-K 段落)
|
||
|
||
| -> LLM 基于检索结果生成回答
|
||
|
||
v
|
||
|
||
[6. 流式展示回答]
|
||
|
||
| 逐字打印 AI 回复
|
||
|
||
| 显示引用来源文档
|
||
|
||
v
|
||
|
||
[7. 用户评价]
|
||
|
||
| 点赞/点踩 -> BaoDan 记录
|
||
|
||
| 或纠错 -> 弹窗输入 -> 提交到后端
|
||
|
||
v
|
||
|
||
[结束]
|
||
```
|
||
### 14.2 产品推荐流程(用户端)
|
||
```
|
||
|
||
[开始]
|
||
|
||
v
|
||
|
||
[1. 进入产品推荐页]
|
||
|
||
| 显示空白表单
|
||
|
||
v
|
||
|
||
[2. 填写客户信息]
|
||
|
||
| 基础信息(姓名/年龄/性别/健康/职业)
|
||
|
||
| 选择关注险种(多选 -> 动态显示对应字段)
|
||
|
||
| 设置保障需求(保额滑块/预算输入)
|
||
|
||
| 选择保障期限
|
||
|
||
| (可选)录入已有保单
|
||
|
||
v
|
||
|
||
[3. 前端校验]
|
||
|
||
| 必填项检查、数值范围检查
|
||
|
||
| 不通过 -> 红色提示
|
||
|
||
| 通过 -> 继续
|
||
|
||
v
|
||
|
||
[4. 点击"生成方案"]
|
||
|
||
| 前端显示 loading + 进度提示
|
||
|
||
| -> POST /api/recommend/generate
|
||
|
||
| -> 后端调 BaoDan Workflow API
|
||
|
||
v
|
||
|
||
[5. BaoDan Workflow 执行]
|
||
|
||
| 参数校验 -> 知识库检索 -> LLM 生成方案
|
||
|
||
| 返回 2-3 套方案(基础/均衡/全面)
|
||
|
||
v
|
||
|
||
[6. 方案展示]
|
||
|
||
| Markdown 渲染方案报告
|
||
|
||
| 产品对比表格 + 推荐理由
|
||
|
||
v
|
||
|
||
[7. 用户操作]
|
||
|
||
| a. 满意 -> 打印导出 / 分享链接
|
||
|
||
| b. 不满意 -> 重新生成
|
||
|
||
| c. 需修改 -> 进入编辑模式
|
||
|
||
v
|
||
|
||
[8. 保存/导出]
|
||
|
||
| 方案自动保存到数据库
|
||
|
||
| 用户可选择浏览器打印为 PDF
|
||
|
||
v
|
||
|
||
[结束]
|
||
```
|
||
### 14.3 知识库文档入库流程(管理员端)
|
||
```
|
||
|
||
[开始]
|
||
|
||
v
|
||
|
||
[1. 管理员进入知识库管理页]
|
||
|
||
| 查看当前文档列表和状态
|
||
|
||
v
|
||
|
||
[2. 上传文档]
|
||
|
||
| 选择文件(MD/Word/TXT)
|
||
|
||
| 选择分类标签(险种/保司)
|
||
|
||
| 可多文件批量上传
|
||
|
||
| -> POST /kb/documents/upload
|
||
|
||
v
|
||
|
||
[3. 异步处理流水线]
|
||
|
||
| a. 格式转换(Word -> 纯文本)
|
||
|
||
| b. 文档分段(按 Markdown 标题切分,800 tokens/段)
|
||
|
||
| c. 向量化(DeepSeek Embedding 调用)
|
||
|
||
| d. 写入向量数据库
|
||
|
||
v
|
||
|
||
[4. 处理完成]
|
||
|
||
| 文档状态 -> completed
|
||
|
||
| 文档可被检索
|
||
|
||
v
|
||
|
||
[5. 异常处理](如失败)
|
||
|
||
| 文档状态 -> failed
|
||
|
||
| 显示错误原因
|
||
|
||
| 管理员可点击"重试"
|
||
|
||
v
|
||
|
||
[结束]
|
||
```
|
||
### 14.4 企微机器人问答流程
|
||
```
|
||
|
||
[用户在企微中操作]
|
||
|
||
v
|
||
|
||
[1. 发送消息]
|
||
|
||
| 单聊:直接发消息给机器人
|
||
|
||
| 群聊:@机器人 + 问题内容
|
||
|
||
v
|
||
|
||
[2. 企微服务器推送]
|
||
|
||
| -> POST /api/wecom/callback
|
||
|
||
| 加密 XML 消息体
|
||
|
||
v
|
||
|
||
[3. 后端接收]
|
||
|
||
| a. 验证签名
|
||
|
||
| b. 解密消息
|
||
|
||
| c. 立即返回 "success"(<5秒)
|
||
|
||
v
|
||
|
||
[4. 异步处理](Background Task)
|
||
|
||
| a. 解析消息类型
|
||
|
||
| - 文本 -> 继续
|
||
|
||
| - 非文本 -> 回复"暂不支持"
|
||
|
||
| b. 提取问题内容
|
||
|
||
| - 群聊:去掉"@机器人"前缀
|
||
|
||
| c. 调用 BaoDan Chat API
|
||
|
||
| d. 等待 AI 回复
|
||
|
||
v
|
||
|
||
[5. 回复消息]
|
||
|
||
| 单聊 -> wecom send message API
|
||
|
||
| 群聊 -> wecom appchat send API
|
||
|
||
| 超长消息 -> 自动分段
|
||
|
||
v
|
||
|
||
[结束]
|
||
```
|
||
|
||
---
|
||
|
||
## 二十三、状态机定义
|
||
|
||
### 15.1 推荐方案状态机
|
||
```
|
||
|
||
+----------+
|
||
|
||
| pending | <-- 用户提交请求
|
||
|
||
+----+-----+
|
||
|
||
| Workflow 开始执行
|
||
|
||
v
|
||
|
||
+----------+
|
||
|
||
|processing| <-- BaoDan Workflow 运行中
|
||
|
||
+----+-----+
|
||
|
||
+--------+--------+
|
||
|
||
||
|
||
|
||
v v
|
||
|
||
+--------+ +--------+
|
||
|
||
| done | | failed | <-- 超时或错误
|
||
|
||
+--------+ +--------+
|
||
|
||
|||
|
||
|
||
| | 用户点击"重新生成"
|
||
|
||
+--------+--------+
|
||
|
||
v
|
||
|
||
+----------+
|
||
|
||
| pending | <-- 重新提交
|
||
|
||
+----------+
|
||
```
|
||
|
||
状态转换规则:
|
||
|
||
|当前状态|触发条件|目标状态|操作|
|
||
|---------|---------|---------|------|
|
||
|pending|Workflow 开始执行|processing|开始异步任务|
|
||
|processing|Workflow 返回成功|done|保存 generated_plan|
|
||
|processing|Workflow 返回失败|failed|记录 error_message|
|
||
|processing|超时(>120秒)|failed|记录 "生成超时"|
|
||
|failed|用户点击重新生成|pending|重置状态,重新提交|
|
||
|done|用户点击重新生成|pending|重置状态,重新提交|
|
||
|
||
### 15.2 知识库文档处理状态机
|
||
```
|
||
|
||
+------------+
|
||
|
||
| uploaded | <-- 文件上传完成
|
||
|
||
+-----+------+
|
||
|
||
v
|
||
|
||
+------------+
|
||
|
||
| converting | <-- 格式转换中(Word->文本)
|
||
|
||
+-----+------+
|
||
|
||
v
|
||
|
||
+------------+
|
||
|
||
| chunking | <-- 文档分段中
|
||
|
||
+-----+------+
|
||
|
||
v
|
||
|
||
+------------+
|
||
|
||
| embedding | <-- 向量化中(调 DeepSeek Embedding)
|
||
|
||
+-----+------+
|
||
|
||
+--------+--------+
|
||
|
||
||
|
||
|
||
v v
|
||
|
||
+----------+ +--------+
|
||
|
||
|completed | | failed | <-- 任一步骤出错
|
||
|
||
+----------+ +--------+
|
||
|
||
|||
|
||
|
||
| | 管理员点击"重试"
|
||
|
||
+--------+--------+
|
||
|
||
v
|
||
|
||
+------------+
|
||
|
||
| uploaded | <-- 回到起点
|
||
|
||
+------------+
|
||
```
|
||
|
||
状态转换规则:
|
||
|
||
|当前状态|触发条件|目标状态|操作|
|
||
|---------|---------|---------|------|
|
||
|uploaded|开始处理|converting|调用格式转换|
|
||
|converting|转换成功|chunking|开始分段|
|
||
|chunking|分段完成|embedding|开始向量化|
|
||
|embedding|向量化完成|completed|文档可被检索|
|
||
|任一环节出错|异常|failed|记录错误原因|
|
||
|failed|点击"重试"|uploaded|重新开始处理|
|
||
|
||
### 15.3 企微消息处理状态机
|
||
```
|
||
|
||
+-----------+
|
||
|
||
| received | <-- 收到企微推送
|
||
|
||
+-----+-----+
|
||
|
||
v
|
||
|
||
+-----------+
|
||
|
||
| decrypting| <-- 解密消息
|
||
|
||
+-----+-----+
|
||
|
||
+-----+-----+
|
||
|
||
||
|
||
|
||
v v
|
||
|
||
+---------+ +----------+
|
||
|
||
| success | | error | <-- 解密失败
|
||
|
||
+---------+ +----------+
|
||
|
||
v
|
||
|
||
+-----------+
|
||
|
||
| processing| <-- 异步调用 BaoDan
|
||
|
||
+-----+-----+
|
||
|
||
+----+----+
|
||
|
||
||
|
||
|
||
v v
|
||
|
||
+------+ +--------+
|
||
|
||
|done||failed|
|
||
|
||
+------+ +--------+
|
||
|
||
|||
|
||
|
||
v v
|
||
|
||
+---------+ +----------+
|
||
|
||
| replied | | no_reply | <-- 回复失败时记录日志
|
||
|
||
+---------+ +----------+
|
||
```
|
||
|
||
---
|
||
|
||
## 二十四、数据安全与合规
|
||
|
||
### 16.1 敏感数据分类
|
||
|
||
|数据类型|敏感级别|包含字段|保护措施|
|
||
|---------|:---:|---------|---------|
|
||
|客户个人信息|高|姓名、年龄、健康状况、收入|不在日志中明文记录;数据库加密存储|
|
||
|企微用户信息|中|userid、姓名、部门|仅在后端使用,不暴露给前端|
|
||
|API Key|高|DeepSeek Key、企微 Secret|AES 加密存储,前端脱敏显示|
|
||
|对话内容|中|用户问题、AI 回答|存储在 BaoDan 数据库,有访问权限控制|
|
||
|JWT Token|高|用户认证令牌|HTTPS 传输,2 小时过期|
|
||
|
||
### 16.2 数据保护规则
|
||
|
||
|规则|说明|实现方式||
|
||
|------|------|---------|--------|
|
||
|传输加密|所有数据传输走 HTTPS|Nginx 配置 SSL 证书||
|
||
|存储加密|API Key 等敏感配置加密|AES-256 加密后入库||
|
||
|日志脱敏|日志中不记录完整敏感信息|日志截断:问题/回答记录前 50 字符||
|
||
|前端脱敏|API Key 在页面上显示为 `sk-xxxx****xxxx`|后端返回时自动脱敏||
|
||
|访问控制|不同角色只能访问授权数据|接口层权限校验||
|
||
|数据隔离|销售人员只能看自己的数据|查询时自动注入 user_id 过滤条件||
|
||
|
||
### 16.3 合规要求
|
||
|
||
|要求|说明|
|
||
|------|------|
|
||
|个人信息保护法|收集客户个人信息需告知目的,最小必要原则|
|
||
|保险行业监管|AI 推荐方案需附免责声明,不可替代专业建议|
|
||
|对话记录留存|保险行业建议保留至少 10 年对话记录|
|
||
|知识库版本溯源|修改知识库需保留版本记录,可追溯||
|
||
|
||
---
|
||
|
||
## 二十五、备份与恢复策略
|
||
|
||
### 17.1 备份范围
|
||
|
||
|备份对象|存储位置|重要性|备份频率|
|
||
|---------|---------|:---:|---------|
|
||
|PostgreSQL 数据库|远程存储|高|每日凌晨 2 点|
|
||
|BaoDan 向量数据库|远程存储|高|每日凌晨 2 点|
|
||
|知识库原始文件(9GB MD)|远程存储|高|每周一次(数据变化少)|
|
||
|用户上传的文件|本地 + 远程|中|每日增量|
|
||
|系统配置文件|Git 仓库|中|代码提交即备份|
|
||
|日志文件|本地|低|保留 90 天后自动清理|
|
||
|
||
### 17.2 恢复指标
|
||
|
||
|指标|目标值|说明||
|
||
|------|--------|------|--------|
|
||
|RPO(恢复点目标)|< 24 小时|最多丢失一天的数据||
|
||
|RTO(恢复时间目标)|< 2 小时|从故障到服务恢复的时间||
|
||
|备份验证|每月一次|恢复到测试环境验证备份可用||
|
||
|
||
### 17.3 恢复流程
|
||
```
|
||
|
||
[数据库恢复]
|
||
|
||
1. 从备份存储下载最近的数据库备份
|
||
|
||
2. 停止 BaoDan 服务
|
||
|
||
3. 恢复 PostgreSQL 数据库
|
||
|
||
4. 启动 BaoDan 服务
|
||
|
||
5. 验证知识库检索正常
|
||
|
||
6. 通知管理员恢复完成
|
||
|
||
[全量恢复]
|
||
|
||
1. 重新部署 BaoDan(Docker 或源码)
|
||
|
||
2. 恢复 PostgreSQL 数据库
|
||
|
||
3. 恢复知识库文件
|
||
|
||
4. 重新启动所有服务
|
||
|
||
5. 全面功能验证
|
||
```
|
||
|
||
---
|
||
|
||
## 二十六、部署架构
|
||
|
||
### 18.1 服务器拓扑
|
||
```
|
||
|
||
[互联网]
|
||
|
||
[域名 + HTTPS]
|
||
|
||
+------v------+
|
||
|
||
| Nginx | 端口: 80/443
|
||
|
||
|反向代理|
|
||
|
||
+------+------+
|
||
|
||
+--------------+--------------+
|
||
|
||
|||
|
||
|
||
+------v------+ +----v----+ +-------v-------+
|
||
|
||
|BaoDan Web||BaoDan||你的前端|
|
||
|(Next.js)||API||(Vue 3)|
|
||
|端口:3000||端口||端口:3000|
|
||
|
||
+-------------+ | :5001 | +---------------+
|
||
|
||
+---------+
|
||
|
||
||||
|
||
|
||
+------v--------------v--------------v------+
|
||
|
||
|PostgreSQL||
|
||
|端口: 5432|
|
||
|(BaoDan 数据 + 你的自研表)|
|
||
|
||
+---------------------------------------------+
|
||
|
||
|||
|
||
|
||
+------v------+ +----v----+ +-------v-------+
|
||
|
||
|Redis||Weaviate||Celery Worker|
|
||
|端口:6379||端口||(后台任务)|
|
||
|
||
+-------------+ | :8080 | +---------------+
|
||
|
||
+---------+
|
||
|
||
+-------------------+
|
||
|
||
|BaoDan 服务(含 insurance 模块)|||||
|
||
|端口: 8000|
|
||
|(企微回调/推荐接口)|
|
||
|
||
+-------------------+
|
||
```
|
||
### 18.2 端口规划
|
||
|
||
|服务|端口|对外暴露|说明|
|
||
|------|:---:|:---:|------|
|
||
|Nginx|80, 443|是|HTTPS 入口|
|
||
|BaoDan Web|3000|否(Nginx 转发)|管理后台 + 对话 WebApp|
|
||
|BaoDan API|5001|否(内部)|BaoDan 核心 API|
|
||
|BaoDan 服务(含 insurance 模块)|5001|是(Nginx 转发)|企微回调 + 推荐接口|
|
||
|PostgreSQL|5432|否(内部)|数据库|
|
||
|Redis|6379|否(内部)|缓存|
|
||
|Weaviate|8080|否(内部)|向量数据库|
|
||
|你的 Vue 前端|5173|否(Nginx 转发)|产品推荐页面|
|
||
|
||
### 18.3 Nginx 配置要点
|
||
```nginx
|
||
|
||
# HTTPS 证书
|
||
|
||
ssl_certificate /etc/ssl/your-domain.com.pem;
|
||
|
||
ssl_certificate_key /etc/ssl/your-domain.com.key;
|
||
|
||
# BaoDan 后台 + 对话页面
|
||
|
||
location / {
|
||
|
||
proxy_pass http://127.0.0.1:3000;
|
||
|
||
}
|
||
|
||
# BaoDan API
|
||
|
||
location /v1/ {
|
||
|
||
proxy_pass http://127.0.0.1:5001/v1/;
|
||
|
||
}
|
||
|
||
# 你的前端(产品推荐页面等)
|
||
|
||
location /app/ {
|
||
|
||
proxy_pass http://127.0.0.1:5173/;
|
||
|
||
}
|
||
|
||
# 你的后端 API(企微回调 + 推荐接口)
|
||
|
||
location /api/ {
|
||
|
||
proxy_pass http://127.0.0.1:8000/api/;
|
||
|
||
}
|
||
|
||
# 企微回调路径(需要较长超时)
|
||
|
||
location /api/wecom/callback {
|
||
|
||
proxy_pass http://127.0.0.1:8000/api/wecom/callback;
|
||
|
||
proxy_read_timeout 120s; # 企微回调需要较长超时
|
||
|
||
}
|
||
```
|
||
### 18.4 域名规划
|
||
|
||
|域名/路径|指向|说明|
|
||
|----------|------|------|
|
||
|chat.your-domain.com|Nginx -> BaoDan Web|对话页面入口|
|
||
|chat.your-domain.com/admin|Nginx -> BaoDan Web|管理后台入口|
|
||
|chat.your-domain.com/app|Nginx -> Vue 前端|产品推荐页面|
|
||
|chat.your-domain.com/api/wecom/callback|Nginx -> BaoDan|企微机器人回调 URL|
|
||
|api.your-domain.com|Nginx -> BaoDan|后端 API 入口|
|
||
|
||
---
|
||
|
||
## 二十七、BaoDan 功能映射表
|
||
|
||
> 每项需求对应 BaoDan 的哪个功能,开发时可直接对照。
|
||
|
||
|需求编号|功能点|BaoDan 对应功能|配置位置|
|
||
|---------|--------|-------------|---------|
|
||
|1.1.1|文字输入框|WebApp 内置输入框|默认|
|
||
|1.1.2|流式输出|WebApp Streaming 模式|应用设置 -> 对话速度 -> 流式|
|
||
|1.1.3|多轮对话上下文|WebApp 会话管理|应用设置 -> 上下文窗口长度|
|
||
|1.1.4|会话管理|WebApp 侧边栏|默认|
|
||
|1.1.5|会话标题自动命名|WebApp 自动标题|应用设置 -> 自动话题转换|
|
||
|1.2.1|Markdown 渲染|WebApp 内置 Markdown 渲染|默认|
|
||
|1.2.3|来源引用标注|WebApp 引用显示|应用设置 -> 引用与归属|
|
||
|1.3.1-3|检索范围控制|知识库选择器|多知识库配置|
|
||
|1.4.1|点赞/点踩|WebApp 反馈功能|应用设置 -> 对话反馈|
|
||
|3.1.1|批量上传文档|知识库上传|知识库 -> 上传文档|
|
||
|3.1.2|文档列表|知识库文档管理|知识库 -> 文档列表|
|
||
|3.2.1|处理流水线状态|文档处理状态|知识库 -> 文档状态|
|
||
|3.2.2|失败原因与重试|文档错误详情|知识库 -> 文档详情|
|
||
|3.4.1|检索效果测试|召回测试|知识库 -> 召回测试|
|
||
|4.1.1|对话记录查看|对话日志|日志 -> 对话日志|
|
||
|6.1.1|多模型配置|模型供应商设置|设置 -> 模型供应商|
|
||
|6.2.1|Prompt 编辑|提示词编排|应用 -> 提示词编排|
|
||
|2.2.1|AI 匹配产品组合|Workflow 编排|应用 -> 工作室 -> 创建工作流|
|
||
|
||
---
|
||
|
||
## 二十八、全局错误码定义
|
||
|
||
### 19.1 统一响应格式
|
||
|
||
所有自研后端接口返回统一 JSON 格式:
|
||
```json
|
||
|
||
{
|
||
|
||
"code": 0,
|
||
|
||
"message": "success",
|
||
|
||
"data": { ... }
|
||
|
||
}
|
||
```
|
||
### 19.2 错误码表
|
||
|
||
|错误码|说明|HTTP 状态码|处理建议|
|
||
|:---:|------|:---:|---------|
|
||
|0|成功|200|-|
|
||
|1001|参数错误(缺少必填项/类型错误)|200|检查请求参数|
|
||
|1002|未授权(无 Token 或 Token 无效)|200|重新登录|
|
||
|1003|Token 已过期|200|调用 refresh-token 接口|
|
||
|1004|权限不足(角色无权访问)|200|联系管理员提升权限|
|
||
|1005|资源不存在(如用户/方案/文档 ID 不存在)|200|检查资源 ID|
|
||
|2001|BaoDan API 调用失败|200|检查 BaoDan 服务状态和 API Key|
|
||
|2002|BaoDan API 调用超时(>60 秒)|200|重试或检查 BaoDan 服务负载|
|
||
|2003|BaoDan Workflow 执行失败|200|检查 Workflow 配置|
|
||
|2004|BaoDan Workflow 执行超时(>120 秒)|200|简化输入参数或检查 Workflow|
|
||
|3001|企微 API 调用失败|200|检查企微 CorpID/Secret 配置|
|
||
|3002|企微 Access Token 获取失败|200|检查网络和企微配置|
|
||
|3003|企微消息回复失败|200|检查 AgentID 和用户权限|
|
||
|3004|企微 OAuth code 已过期|200|让用户重新授权|
|
||
|3005|企微用户未授权/不在白名单|200|联系管理员开通权限|
|
||
|4001|文件上传失败|200|检查文件格式和大小|
|
||
|4002|文件格式不支持|200|仅支持 MD/Word/TXT|
|
||
|4003|文件大小超限|200|单文件最大 50MB|
|
||
|5001|数据库操作失败|200|检查数据库连接|
|
||
|5002|Redis 操作失败|200|检查 Redis 连接|
|
||
|9999|服务器内部错误|500|检查服务器日志|
|
||
|
||
### 19.3 说明
|
||
|
||
- 业务异常(1xxx-5xxx)统一使用 HTTP 200,通过 `code` 字段区分成功/失败
|
||
|
||
- 系统异常(9xxx)使用对应 HTTP 状态码
|
||
|
||
- `message` 字段为人类可读的错误描述,前端直接展示给用户
|
||
|
||
- 生产环境不返回详细的错误堆栈,只在服务端日志中记录
|
||
|
||
---
|
||
|
||
## 二十九、页面归属与构建方式
|
||
|
||
> 明确每个页面由谁提供,开发者知道哪些需要自己写代码,哪些直接用。
|
||
|
||
### 20.1 页面归属清单
|
||
|
||
|页面|路径|提供方|构建方式|说明|
|
||
|
||
|------|------|:---:|---------|------|--------
|
||
|
||
|登录页|/login|自研|Vue 3 组件|企微 OAuth 跳转 + 账密登录表单|
|
||
|对话页|/chat|BaoDan WebApp|iframe 嵌入|直接嵌入 BaoDan 的 Chat WebApp|
|
||
|产品推荐页|/recommend|自研|Vue 3 页面|表单 + 结果展示,调后端 API|
|
||
|方案历史页|/recommend/history|自研|Vue 3 页面|方案列表 + 筛选,调后端 API|
|
||
|知识库管理页|/admin/kb|BaoDan 后台|BaoDan 原生|直接用 BaoDan 知识库管理界面|
|
||
|对话日志页|/admin/logs/chat|BaoDan 后台|BaoDan 原生|直接用 BaoDan 日志界面|
|
||
|系统日志页|/admin/logs/system|自研|Vue 3 页面|BaoDan 没有系统操作日志|
|
||
|用户管理页|/admin/users|自研|Vue 3 页面|企微用户绑定 + 角色分配|
|
||
|角色管理页|/admin/roles|自研|Vue 3 页面|权限树勾选配置|
|
||
|LLM 配置页|/admin/llm-configs|BaoDan 后台|BaoDan 原生|直接用 BaoDan 模型配置|
|
||
|Prompt 管理页|/admin/prompts|BaoDan 后台|BaoDan 原生|直接用 BaoDan 提示词编辑|
|
||
|数据统计页|/admin/stats|自研|Vue 3 页面|图表展示(ECharts/Chart.js)|
|
||
|管理后台框架|/admin|自研|Vue 3 组件|左侧导航 + 顶部栏 + 路由|
|
||
|
||
### 20.2 统计
|
||
|
||
|类型|页面数|说明|||
|
||
|------|:---:|------|--------|--------|
|
||
|**BaoDan 直接提供**|4 个|对话页、知识库管理、对话日志、LLM 配置、Prompt 管理|||
|
||
|**自研前端**|9 个|登录页、推荐页、历史页、系统日志、用户管理、角色管理、统计页、管理框架|||
|
||
|**合计**|13 个|-|||
|
||
|
||
### 20.3 BaoDan 页面嵌入方式
|
||
|
||
|BaoDan 页面|嵌入方式|URL 格式|
|
||
|----------|---------|---------|
|
||
|对话 WebApp|iframe|`http://baodan:3000/chat/{app_id}?user={user_id}`|
|
||
|知识库管理|新窗口打开 BaoDan 后台|`http://baodan:3000/datasets`|
|
||
|对话日志|新窗口打开 BaoDan 后台|`http://baodan:3000/logs`|
|
||
|LLM 配置|新窗口打开 BaoDan 后台|`http://baodan:3000/settings/model`|
|
||
|Prompt 管理|新窗口打开 BaoDan 后台|`http://baodan:3000/apps/{app_id}/prompt-engine`|
|
||
|
||
> 说明:知识库管理、日志、LLM 配置、Prompt 管理这 4 个页面目前通过新窗口跳转到 BaoDan 后台操作。如果后续需要深度定制(如加标签管理、编号规则),需要改为自研前端 + 调 BaoDan API。
|
||
|
||
---
|
||
|
||
## 三十、BaoDan Workflow 节点详细设计
|
||
|
||
> 产品推荐方案的 BaoDan Workflow 每个节点的输入、输出、Prompt 定义。
|
||
|
||
### 21.1 Workflow 总览
|
||
```
|
||
|
||
[开始] -> [节点1:参数校验] -> [节点2:检索策略] -> [节点3:知识库检索]
|
||
|
||
-> [节点4:LLM方案生成] -> [节点5:格式化输出] -> [结束]
|
||
```
|
||
### 21.2 各节点定义
|
||
|
||
#### 节点 1:参数校验
|
||
|
||
|属性|值|
|
||
|------|---|
|
||
|节点类型|代码节点(Code)|
|
||
|输入|用户提交的 JSON 参数|
|
||
|输出|validated_params(校验后的参数)或 error_msg(错误信息)|
|
||
|
||
校验规则:
|
||
|
||
- age: 必填,整数,1-150
|
||
|
||
- gender: 必填,"male" 或 "female"
|
||
|
||
- occupation: 必填,非空字符串
|
||
|
||
- insurance_types: 必填,非空数组
|
||
|
||
- monthly_budget: 必填,正整数
|
||
|
||
- coverage_amount: 必填,正整数
|
||
|
||
#### 节点 2:检索策略
|
||
|
||
|属性|值|
|
||
|------|---|
|
||
|节点类型|代码节点(Code)|
|
||
|输入|validated_params|
|
||
|输出|search_queries(检索关键词列表)|
|
||
|
||
逻辑:根据用户选择的险种,为每个险种生成一条检索 query:
|
||
```python
|
||
|
||
def generate_search_queries(params):
|
||
|
||
queries = []
|
||
|
||
for insurance_type in params["insurance_types"]:
|
||
|
||
query = f"{insurance_type} 产品条款 保额 费率 {params['age']}岁"
|
||
|
||
queries.append({"险种": insurance_type, "query": query})
|
||
|
||
return queries
|
||
```
|
||
#### 节点 3:知识库检索
|
||
|
||
|属性|值|
|
||
|------|---|
|
||
|节点类型|知识库检索节点(Knowledge Retrieval)|
|
||
|输入|search_queries|
|
||
|输出|retrieved_docs(检索到的文档片段)|
|
||
|知识库|关联所有险种相关知识库|
|
||
|Top-K|每个险种检索 Top 5|
|
||
|相似度阈值|0.6|
|
||
|
||
#### 节点 4:LLM 方案生成
|
||
|
||
|属性|值|
|
||
|------|---|
|
||
|节点类型|LLM 节点|
|
||
|输入|retrieved_docs + validated_params|
|
||
|输出|recommendation(Markdown 格式方案)|
|
||
|模型|deepseek-chat|
|
||
|Temperature|0.7|
|
||
|
||
**节点 Prompt**:
|
||
```
|
||
你是一名专业的保险方案规划师。根据以下客户信息和检索到的产品条款,生成保险产品推荐方案。
|
||
|
||
## 客户信息
|
||
|
||
- 年龄:{{age}} 岁
|
||
|
||
- 性别:{{gender}}
|
||
|
||
- 职业:{{occupation}}
|
||
|
||
- 年收入:{{annual_income}} 元
|
||
|
||
- 月预算:{{monthly_budget}} 元
|
||
|
||
- 关注险种:{{insurance_types}}
|
||
|
||
- 保额目标:{{coverage_amount}} 元
|
||
|
||
- 保障期限:{{coverage_period}}
|
||
|
||
## 检索到的产品信息
|
||
|
||
{{retrieved_docs}}
|
||
|
||
## 输出要求
|
||
|
||
请生成三套方案(基础方案 / 均衡方案 / 全面方案),每套方案包含:
|
||
|
||
1. 方案名称和总年保费
|
||
|
||
2. 每款推荐产品的表格:
|
||
|
||
|产品名称|所属保司|险种|保额|年保费|推荐理由(1-2句话)|
|
||
|
||
3. 方案总结(2-3句话说明方案特点)
|
||
|
||
4. 免责声明:"以上方案仅供参考,具体保障内容以保险合同条款为准。"
|
||
|
||
## 注意事项
|
||
|
||
- 保费数据必须来自检索到的产品信息,不可编造
|
||
|
||
- 如果某险种在知识库中没有匹配产品,标注"暂无合适产品推荐"
|
||
|
||
- 总保费不得超过客户月预算 * 12
|
||
|
||
- 优先推荐性价比高的产品
|
||
```
|
||
#### 节点 5:格式化输出
|
||
|
||
|属性|值|||||
|
||
|------|---|--------|--------|--------|--------|
|
||
|节点类型|代码节点(Code)|||||
|
||
|输入|recommendation(LLM 输出的 Markdown)|||||
|
||
|
||
|
||
逻辑:对 LLM 输出做基本格式清理(去除多余空行、确保 Markdown 格式正确),直接输出给前端渲染。
|
||
|
||
|模块|总数|高优先级|中优先级|低优先级|
|
||
|
||
|------|:---:|:---:|:---:|:---:|
|
||
|
||
|M1 智能问答(用户前端)|16|9|5|2|
|
||
|M2 产品推荐(用户前端)|19|9|7|3|
|
||
|M3 知识库管理(管理前端)|17|9|5|3|
|
||
|M4 留痕与日志(管理前端)|9|4|4|1|
|
||
|M5 用户与权限(管理前端)|8|5|2|1|
|
||
|M6 系统配置(管理前端)|14|6|5|3|
|
||
|M7 数据统计(管理前端)|9|3|4|2|
|
||
|企微机器人|5|3|1|1|
|
||
|A1 认证鉴权(后端接口)|4|2|2|0|
|
||
|A2 智能问答(后端接口)|8|5|2|1|
|
||
|A3 方案生成(后端接口)|4|3|0|1|
|
||
|A4 知识库(后端接口)|10|7|2|1|
|
||
|A5 用户权限(后端接口)|3|2|0|1|
|
||
|A6 系统配置(后端接口)|4|2|2|0|
|
||
|A7 留痕日志(后端接口)|3|2|1|0|
|
||
|A8 统计报表(后端接口)|4|1|3|0|
|
||
|**合计**|**137**|**77**|**45**|**15**|
|
||
|
||
---
|
||
|
||
## 三十一、功能清单统计
|
||
|
||
|模块|功能项数|高优先级|中优先级|低优先级|
|
||
|
||
|------|:---:|:---:|:---:|:---:|
|
||
|
||
|M1 智能问答(用户前端)|16|9|5|2|
|
||
|M2 产品推荐(用户前端)|19|9|7|3|
|
||
|M3 知识库管理(管理前端)|17|9|5|3|
|
||
|M4 留痕与日志(管理前端)|9|4|4|1|
|
||
|M5 用户与权限(管理前端)|8|5|2|1|
|
||
|M6 系统配置(管理前端)|14|6|5|3|
|
||
|M7 数据统计(管理前端)|9|3|4|2|
|
||
|企微机器人|5|3|1|1|
|
||
|A1 认证鉴权(后端接口)|4|2|2|0|
|
||
|A2 智能问答(后端接口)|8|5|2|1|
|
||
|A3 方案生成(后端接口)|4|3|0|1|
|
||
|A4 知识库(后端接口)|10|7|2|1|
|
||
|A5 用户权限(后端接口)|3|2|0|1|
|
||
|A6 系统配置(后端接口)|4|2|2|0|
|
||
|A7 留痕日志(后端接口)|3|2|1|0|
|
||
|A8 统计报表(后端接口)|4|1|3|0|
|
||
|**合计**|**137**|**77**|**45**|**15**|
|
||
|
||
---
|
||
|
||
## 三十二、验收标准
|
||
|
||
|验收项|标准|验证方式|||
|
||
|--------|------|---------|--------|--------|
|
||
|智能问答准确率|30 个测试问题,准确率 >= 85%|人工测试|||
|
||
|产品推荐质量|10 组客户数据,方案合理且产品信息准确|人工评审|||
|
||
|企微单聊|10 轮连续对话无错误|手动测试|||
|
||
|企微群聊|3 人同时 @机器人 不混淆|手动测试|||
|
||
|企微 OAuth|登录流程完整走通|手动测试|||
|
||
|H5 适配|手机端可正常使用|真机测试|||
|
||
|知识库覆盖|9GB MD 文档全部入库且可检索|BaoDan 后台检查|||
|
||
|响应时间|AI 回答 < 15 秒|计时测试|||
|
||
|并发能力|5 人同时使用无明显延迟|手动测试|||
|
||
|方案导出|PDF/PPT/Word 可正常下载且排版正确|下载验证|||
|
||
|角色权限|不同角色看到不同功能和数据范围|切换账号验证|||
|
||
|日志完整性|问答记录和操作日志可查询导出|后台验证||| |