baodan/docs/保险智能客服系统_需求文档.md

185 KiB
Raw Permalink Blame History

保险智能客服系统 — 需求文档

版本V1.0

日期2026-05-31

技术基础BaoDanAI 引擎)+ 自研企微后端

开发人数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 企微登录接口详细设计

请求:


POST /auth/wework-login

{

    "code": "企微授权回调code",

    "state": "随机状态值"

}

响应:


{

    "code": 0,

    "message": "success",

    "data": {

        "token": "eyJhbGciOiJIUzI1NiIs...",

        "expires_in": 7200,

        "user": {

            "id": "user-001",

            "username": "张三",

            "wecom_userid": "zhangsan",

            "role": "sales",

            "department": "上海团队"

        }

    }

}

A1.1.2 账密登录接口详细设计

请求:


POST /auth/password-login

{

    "username": "admin",

    "password": "hashed_password"

}

响应:


{

    "code": 0,

    "message": "success",

    "data": {

        "token": "eyJhbGciOiJIUzI1NiIs...",

        "expires_in": 7200

    }

}

A1.1.3 刷新 Token 接口详细设计

请求:


POST /auth/refresh-token

Headers: { "Authorization": "Bearer <old_token>" }

响应:


{

    "code": 0,

    "message": "success",

    "data": {

        "token": "eyJhbGciOiJIUzI1NiIs...",

        "expires_in": 7200

    }

}

A1.1.4 退出登录接口详细设计

请求:


POST /auth/logout

Headers: { "Authorization": "Bearer <token>" }

响应:


{

    "code": 0,

    "message": "success"

}

A2智能问答接口

A2.1 对话接口

接口编号 方法 URL 说明 优先级 实现方式
A2.1.1 POST /chat/messageSSE 流式) 接收问题 + 会话 ID调用 RAG 检索 + LLMSSE 流式返回回答与引用来源 自研(调 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 发送消息接口详细设计

请求:


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 获取会话列表接口详细设计

请求:


GET /chat/sessions?page=1&page_size=20

Headers: { "Authorization": "Bearer <token>" }

响应:


{

    "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 获取消息记录接口详细设计

请求:


GET /chat/sessions/{id}/messages

Headers: { "Authorization": "Bearer <token>" }

响应:


{

    "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 反馈接口详细设计

请求:


POST /chat/messages/{id}/feedback

Headers: { "Authorization": "Bearer <token>" }

{

    "rating": "helpful",

    "comment": null

}

或纠错:


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 检索接口详细设计

请求:


POST /retrieval/search

Headers: { "Authorization": "Bearer <token>" }

{

    "query": "重疾险等待期",

    "filters": {

        "险种": "重疾险",

        "保司": null

    },

    "top_k": 5

}

响应:


{

    "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 生成方案接口详细设计

请求:


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": []

}

响应:


{

    "code": 0,

    "data": {

        "task_id": "task-001",

        "status": "processing"

    }

}

A3.1.2 轮询任务状态接口详细设计

请求:


GET /proposals/generate/{task_id}

Headers: { "Authorization": "Bearer <token>" }

响应(生成中):


{

    "code": 0,

    "data": {

        "task_id": "task-001",

        "status": "processing"

    }

}

响应(完成):


{

    "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 方案导出接口详细设计

请求:


POST /proposals/{id}/export

Headers: { "Authorization": "Bearer <token>" }

{

    "format": "pdf"

}

响应:


{

    "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 分享链接接口详细设计

请求:


POST /proposals/{id}/share

Headers: { "Authorization": "Bearer <token>" }

{

    "expire_hours": 72

}

响应:


{

    "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 上传文档接口详细设计

请求:


POST /kb/documents/upload

Content-Type: multipart/form-data

Headers: { "Authorization": "Bearer <token>" }

Form Fields:

  files: [file1.md, file2.md, file3.md]

  险种: "重疾险"

  保司: "XX人寿"

响应:


{

    "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 文档列表接口详细设计

请求:


GET /kb/documents?page=1&page_size=20&险种=重疾险&status=completed&keyword=等待期

Headers: { "Authorization": "Bearer <token>" }

响应:


{

    "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 文档状态接口详细设计

请求:


GET /kb/documents/{id}/status

Headers: { "Authorization": "Bearer <token>" }

响应:


{

    "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 数据源列表接口详细设计

请求:


GET /kb/datasources

Headers: { "Authorization": "Bearer <token>" }

响应:


{

    "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 用户管理接口详细设计

列表:


GET /admin/users?page=1&page_size=20&role=sales&department=上海团队

Headers: { "Authorization": "Bearer <admin_token>" }

响应:


{

    "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"

            }

        ]

    }

}

新增:


POST /admin/users

{

    "username": "李四",

    "wecom_userid": "lisi",

    "role": "sales",

    "department": "上海团队"

}

A5.1.3 角色管理接口详细设计

列表:


GET /admin/roles

响应:


{

    "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 配置 CRUDKey 写入时服务端加密 自研(调 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 配置接口详细设计

请求:


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

    }

}

响应:


{

    "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 连通性测试接口详细设计

请求:


POST /admin/llm-configs/{id}/ping

Headers: { "Authorization": "Bearer <admin_token>" }

响应:


{

    "code": 0,

    "data": {

        "reachable": true,

        "latency_ms": 320,

        "model": "deepseek-chat"

    }

}

A6.1.3 Prompt 管理接口详细设计

列表:


GET /admin/prompts

新增/更新:


POST /admin/prompts

{

    "name": "保险顾问系统提示词",

    "type": "system",

    "content": "你是一名专业的保险顾问助手...",

    "variables": ["险种", "保司"]

}

响应(保存时自动生成版本快照):


{

    "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 测试接口详细设计

请求:


POST /admin/prompts/test

{

    "prompt_id": "prompt-001",

    "test_query": "重疾险等待期多少天?"

}

响应:


{

    "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 问答日志接口详细设计

请求:


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 模式):


{

    "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.1export=csv 时返回 CSV 文件流

A7.1.3 系统日志接口详细设计

请求:


GET /admin/logs/system?page=1&page_size=50&action=login&user_id=user-001

Headers: { "Authorization": "Bearer <admin_token>" }

响应:


{

    "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 使用概览接口详细设计

请求:


GET /stats/overview

Headers: { "Authorization": "Bearer <admin_token>" }

响应:


{

    "code": 0,

    "data": {

        "today_chats": 45,

        "active_users": 12,

        "kb_hit_rate": 0.87,

        "total_documents": 1500

    }

}

A8.1.2 趋势数据接口详细设计

请求:


GET /stats/trend?metric=chat_count&start_date=2026-05-01&end_date=2026-05-31&granularity=day

Headers: { "Authorization": "Bearer <admin_token>" }

响应:


{

    "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 知识库健康度接口详细设计

请求:


GET /stats/kb-health?days=30

Headers: { "Authorization": "Bearer <admin_token>" }

响应:


{

    "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 消耗接口详细设计

请求:


GET /stats/token-cost?start_date=2026-05-01&end_date=2026-05-31&group_by=model

Headers: { "Authorization": "Bearer <admin_token>" }

响应:


{

    "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 自研表结构

表 1wecom_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

表 2recommendation_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

表 3system_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&timestamp=xxx&nonce=xxx&echostr=xxx

验证流程:

  1. 将 Token、timestamp、nonce、echostr 按字典序排列拼接

  2. 对拼接字符串做 SHA1 哈希

  3. 比较哈希结果与 msg_signature

  4. 验证通过则返回解密后的 echostr

8.3 消息接收POST

企微用户发消息时推送的加密 XML


<xml>

   <ToUserName><![CDATA[你的企业ID]]></ToUserName>

   <Encrypt><![CDATA[加密后的消息]]></Encrypt>

</xml>

解密后的消息体text 类型):


<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>

   ...

   <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

请求体:


{

    "touser": "用户UserID",

    "msgtype": "text",

    "agentid": 1000002,

    "text": {

        "content": "根据 XX 重疾险条款规定等待期为90天..."

    }

}

群聊回复到群:


POST https://qyapi.weixin.qq.com/cgi-bin/appchat/send?access_token=ACCESS_TOKEN

请求体:


{

    "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 模式响应


{

    "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 调用规范

请求


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"

}

响应


{

    "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 方式:


<!-- 方式一:直接嵌入 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 APIblocking 模式)
    │  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-150gender 为 "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.recommendationMarkdown 文本)
    │
    ▼
[步骤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&timestamp=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 为有效 JWT 登录已过期,请重新登录

A1.1.4 POST /auth/logout

字段名 类型 必填 长度/范围约束 默认值 枚举值 验证规则 错误提示文案
string Authorization (Header) - - - Bearer 格式 登录已过期,请重新登录

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 Keysk-xxxxxxxxxxxxxxxxxxxxxxxx(从 platform.deepseek.com 获取)
    • API Base URLhttps://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-K5返回最相关的5个文档片段
    • Score 阈值0.5(低于此分数的片段不返回)

配置模型参数

  1. 点击 "模型" 区域
  2. 选择模型:deepseek-chat
  3. 设置参数:
    • Temperature0.7
    • Top P0.9
    • Max Tokens4096

15.6 设置应用公开访问

  1. 在应用编辑页面,点击右上角 "发布" 按钮
  2. 弹窗中确认发布
  3. 发布后,点击 "访问 API" 获取 API 地址
  4. 记录 API 密钥(格式:app-xxxxxxxxxxxx),后续后端配置使用
  5. 获取 WebApp URLhttp://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-K5
  • Score 阈值0.6

节点5 - LLM 节点

  • 拖入 "LLM" 组件
  • 模型:deepseek-chat
  • Temperature0.7
  • Prompt参见第十九章完整 Prompt

节点6 - 代码节点(格式化输出)

  • 输入LLM 输出的 Markdown
  • 逻辑:清理格式,确保可读性

节点7 - 结束节点

  • 输出变量:recommendation(格式化后的方案文本)
  1. 点击 "发布" 按钮保存 Workflow
  2. 记录 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. 填写测试输入:
    • age35
    • gendermale
    • occupation软件工程师
    • annual_income300000
    • monthly_budget2000
    • insurance_types重疾险,医疗险
    • coverage_amount500000
    • coverage_period终身
  3. 点击 "运行" 按钮
  4. 验证以下内容:
    • 每个节点正常执行(无红色错误标记)
    • 知识库检索返回了相关产品信息
    • LLM 生成了三套方案(基础/均衡/全面)
    • 每套方案包含产品表格、保费、推荐理由
    • 总保费不超过月预算 x 1224000元/年)
    • 整体执行时间 < 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. 填写以下信息:
    • URLhttps://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. 填写主页 URLhttps://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 统一响应格式

{
    "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 是否校验通过

代码逻辑

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

代码逻辑

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 节点 4LLM 方案生成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 是否成功

代码逻辑

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 格式化后的方案文本

代码逻辑

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 返回"知识库检索失败,请稍后重试"
节点4LLM生成 BaoDan 自动重试1次失败则跳转节点5 返回"方案生成失败,请稍后重试"
节点5/6处理节点 BaoDan 自动重试1次 返回"系统处理异常"

19.10 测试数据与预期输出

测试用例 1标准场景

输入:

{
    "age": "35",
    "gender": "male",
    "occupation": "软件工程师",
    "annual_income": "300000",
    "monthly_budget": "2000",
    "insurance_types": "重疾险,医疗险",
    "coverage_amount": "500000",
    "coverage_period": "终身"
}

预期输出:

  • 三套方案(基础/均衡/全面)
  • 每套方案含产品表格(产品名/保司/险种/保额/保费/推荐理由)
  • 基础方案总年保费 <= 24000 元2000 x 12
  • 不得包含编造的产品信息

测试用例 2边界 - 低预算

输入:

{
    "age": "25",
    "gender": "female",
    "occupation": "教师",
    "annual_income": "80000",
    "monthly_budget": "500",
    "insurance_types": "意外险",
    "coverage_amount": "50000",
    "coverage_period": "10年"
}

预期输出:基础方案总年保费 <= 6000 元

测试用例 3异常 - 无效参数

输入:

{
    "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

第一步:系统更新

sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget git vim ufw

第二步:设置时区

sudo timedatectl set-timezone Asia/Shanghai

第三步:配置防火墙

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

# 安装 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

# 安装 Docker Compose 插件
sudo apt install -y docker-compose-plugin

# 验证
docker compose version
# 预期输出Docker Compose version v2.x.x

20.2 BaoDan 部署

第一步:克隆 BaoDan 仓库

cd /opt
sudo git clone https://github.com/langgenius/baodan.git
cd /opt/baodan/docker
sudo cp .env.example .env

第二步:修改 BaoDan 环境变量

sudo vim .env

修改以下关键配置:

# 数据库配置
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

生成随机密钥:

openssl rand -base64 42

第三步:启动 BaoDan

sudo docker compose up -d

等待所有容器启动完成(约 2-5 分钟):

sudo docker compose ps

验证:所有容器状态应为 running

第四步:验证 BaoDan 访问

浏览器打开 http://你的服务器IP:3000,应看到 BaoDan 欢迎页面。

20.3 PostgreSQL 配置

BaoDan 的 Docker Compose 已包含 PostgreSQL。如需在同一实例使用自研表

第一步:进入 PostgreSQL

sudo docker compose exec db psql -U postgres

第二步:创建自研数据库

CREATE DATABASE insurance_bot OWNER postgres;
\c insurance_bot

第三步:创建自研表结构

-- 企微用户映射表
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 实例即可。

验证:

sudo docker compose exec redis redis-cli -a your_redis_password ping
# 预期输出PONG

20.5 后端部署

第一步:安装 Python 环境

# 安装 Python 3.12
sudo apt install -y python3.12 python3.12-venv python3-pip

# 验证
python3.12 --version
# 预期输出Python 3.12.x

第二步:创建项目目录并配置虚拟环境

mkdir -p /opt/insurance-bot/backend
cd /opt/insurance-bot/backend

# 创建虚拟环境
python3.12 -m venv venv
source venv/bin/activate

第三步:安装依赖

pip install fastapi uvicorn[standard] psycopg2-binary redis python-jose[cryptography] passlib[bcrypt] python-multipart httpx pydantic

第四步:配置环境变量

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

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"]

第六步:启动后端服务

# 如果使用 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

验证:

curl http://localhost:8000/docs
# 预期输出Flask Swagger UI 页面

20.6 前端部署

第一步:安装 Node.js

# 安装 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

第二步:构建前端

cd /opt/insurance-bot/frontend

# 安装依赖
npm install

# 构建生产版本
npm run build
# 产物在 dist/ 目录

第三步:配置 Nginx

sudo apt install -y nginx

创建 Nginx 配置文件:

sudo vim /etc/nginx/sites-available/insurance-bot
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;
    }
}

启用配置:

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 oktest 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 免费证书:

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 配置并安装证书。

第三步:设置自动续期

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 APISSE 流式)

  |  -> 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. 重新部署 BaoDanDocker 或源码)

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 配置要点


# 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 格式:


{

    "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


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

节点 4LLM 方案生成

属性
节点类型 LLM 节点
输入 retrieved_docs + validated_params
输出 recommendationMarkdown 格式方案)
模型 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
输入 recommendationLLM 输出的 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 可正常下载且排版正确 下载验证
角色权限 不同角色看到不同功能和数据范围 切换账号验证
日志完整性 问答记录和操作日志可查询导出 后台验证