版本: v3.0
日期: 2026-05-16
项目路径: C:\Project\Code\HealthAgent
HealthAgent 是一款智慧健康助手应用,旨在为用户提供便捷的保单查询、体检预约和健康咨询服务。系统集成了大语言模型(LLM)能力,通过意图识别自动理解用户需求,智能分发至对应业务模块,实现"对话即服务"的交互体验。
| 功能模块 | 描述 |
|---|---|
| 智能对话 | 基于意图识别 + ReAct Agent 的 AI 对话,支持工具调用、多轮推理 |
| 保单查询 | 按用户ID/保单号/投保人/身份证号等多维度查询保单信息 |
| 体检预约 | 多轮对话收集信息,自动预约体检,支持自然语言日期解析(明天/下周一等) |
| 预约记录查询 | 查询用户体检预约记录,展示医院详情(等级/地址/电话)及套餐说明 |
| 健康咨询 | 通用健康问答与生活方式建议 |
| 语音输入 | 浏览器原生 Web Speech API 语音识别 |
| 数据脱敏 | 保单号/身份证/手机号等敏感信息自动脱敏展示 |
- 保险客户:查询个人保单信息、了解保障详情
- 体检用户:在线预约体检、查看预约记录
- 健康关注者:获取健康咨询与建议
graph TB
subgraph "前端层 Frontend"
UI[Vue 3 + TypeScript]
UI --> Router[Vue Router]
UI --> AuthComp[useAuth 组合式函数]
UI --> ChatUI[ChatPage 对话界面]
UI --> PolicyUI[PolicyPage 保单页面]
UI --> ExamUI[ExaminationPages 体检页面]
UI --> DashUI[DashboardPage 仪表盘]
end
subgraph "网关层 Gateway"
Nginx[Vite Dev Proxy / Nginx]
Nginx --> |"/api/*"| Backend
end
subgraph "后端层 Backend — Spring Boot 3.4.1"
Controller[REST Controllers]
Controller --> SmartChatCtrl[SmartChatController]
Controller --> PolicyCtrl[PolicyController]
Controller --> ExamCtrl[ExaminationController]
Controller --> AuthCtrl[AuthController]
Service[业务服务层]
Service --> SmartChatSvc[SmartChatService 编排]
Service --> IntentSvc[IntentRecognitionService]
Service --> ExamIntentSvc[ExaminationIntentService]
Service --> PolicySvc[PolicyService]
Service --> ExamSvc[ExaminationService]
Service --> AuthSvc[AuthService]
Service --> SessionMgr[SessionManager]
Service --> DataMaskSvc[DataMaskingService]
Service --> SkillExecSvc[SkillExecutionService]
ChatClient[LLM 客户端层]
ChatClient --> AbstractCC[AbstractChatClient 模板方法]
AbstractCC --> GlmCC[GlmChatClient 智谱GLM]
AbstractCC --> QwenCC[QwenChatClient 通义千问]
ChatClient --> Factory[ChatClientFactory 工厂]
Interceptor[AuthInterceptor 认证拦截]
Config[WebConfig CORS + 拦截器注册]
end
subgraph "数据层 Data"
MySQL[(MySQL 8.0)]
Redis[(Redis)]
MyBatisPlus[MyBatis-Plus 3.5.5]
end
subgraph "外部服务 External"
GLMAPI[智谱AI GLM-4.6v API]
QwenAPI[通义千问 DashScope API]
end
UI --> Nginx
Controller --> Service
Service --> ChatClient
Service --> MyBatisPlus
MyBatisPlus --> MySQL
AuthSvc --> Redis
GlmCC --> GLMAPI
QwenCC --> QwenAPI
Interceptor --> AuthSvc
Config --> Interceptor
sequenceDiagram
participant U as 用户
participant F as 前端 ChatPage
participant C as SmartChatController
participant S as SmartChatService
participant SM as SessionManager
participant IR as IntentRecognitionService
participant LLM as GLM/Qwen API
participant PS as PolicyService
participant ES as ExaminationService
participant EIS as ExaminationIntentService
U->>F: 输入消息(文本/语音)
F->>C: POST /api/smart-chat/send
C->>S: chat(request)
S->>SM: getCachedIntent(userId)
alt 缓存命中
SM-->>S: 返回缓存意图
else 缓存未命中
S->>IR: recognizeIntent(message)
IR->>LLM: 意图识别 Prompt
LLM-->>IR: intent code
IR-->>S: IntentType
S->>SM: cacheIntent(userId, intent)
end
alt 意图 = QUERY_POLICY
S->>PS: getUserPolicies(userId)
PS-->>S: 保单列表
S->>LLM: 生成友好回复
LLM-->>S: AI 回复
else 意图 = BOOK_EXAMINATION
S->>EIS: recognizeExaminationIntent(message)
EIS->>LLM: 提取预约信息
LLM-->>EIS: ExaminationIntentData
S->>SM: updateCachedExaminationIntent
SM-->>S: 合并后的 IntentData
alt 信息完整(bookingReady)
S->>ES: bookExamination(request)
ES-->>S: 预约成功
else 信息不完整
S-->>S: 构建缺失信息提示
end
else 意图 = HEALTH_CONSULTATION / GENERAL
S->>LLM: 通用对话
LLM-->>S: AI 回复
end
S-->>C: SmartChatResponse
C-->>F: Result<SmartChatResponse>
F-->>U: 展示回复
flowchart TD
A[用户消息] --> B{SessionManager<br/>缓存命中?}
B -->|是| C[复用缓存意图]
B -->|否| D[调用 LLM 意图识别]
D --> E{LLM 返回有效意图?}
E -->|是| F[映射到 IntentType]
E -->|否| G[默认 GENERAL_CONVERSATION]
F --> H[缓存意图到 SessionManager]
G --> H
C --> I{意图类型}
H --> I
I -->|query_policy| J[保单查询分支]
I -->|book_examination| K[体检预约分支]
I -->|health_consultation| L[健康咨询分支]
I -->|general_conversation| M[通用对话分支]
J --> N[查询用户保单]
N --> O[AI 友好化回复]
K --> P[提取预约参数]
P --> Q{信息完整?}
Q -->|是| R[创建预约]
Q -->|否| S[追问缺失信息]
L --> T[调用 LLM 健康咨询]
M --> U[调用 LLM 通用对话]
flowchart TD
A[用户:我想预约体检] --> B[意图识别 -> book_examination]
B --> C[ExaminationIntentService.recognizeExaminationIntent]
C --> D[调用 GLM 提取参数]
D --> E[ExaminationIntentData]
E --> F[SessionManager.updateCachedExaminationIntent]
F --> G{hospitalName + examinationDate<br/>均已填写?}
G -->|否| H[构建缺失信息提示]
H --> I[返回:请告诉我医院和日期]
I --> J[用户补充信息]
J --> K[再次提取参数]
K --> L[与缓存数据 merge]
L --> G
G -->|是| M[构建 ExaminationBookingRequestDTO]
M --> N[ExaminationService.bookExamination]
N --> O[生成预约号 EXM+时间戳+随机数]
O --> P[插入 examination_booking 表]
P --> Q[返回预约成功消息]
Q --> R[清除体检预约缓存]
| 类别 | 技术 | 版本 | 说明 |
|---|---|---|---|
| 运行时 | Java | 21 | LTS 版本 |
| 框架 | Spring Boot | 3.4.1 | 主框架 |
| ORM | MyBatis-Plus | 3.5.5 | 数据访问增强 |
| 数据库 | MySQL | 8.0 | 主数据存储 |
| 缓存 | Redis | — | Refresh Token 存储 |
| JSON | Fastjson2 | — | JSON 序列化 |
| JWT | jjwt | — | Token 生成与验证 |
| API 文档 | SpringDoc OpenAPI | — | Swagger UI |
| 构建工具 | Maven | — | 项目构建 |
| 类别 | 技术 | 说明 |
|---|---|---|
| 框架 | Vue 3 + Composition API | 响应式前端 |
| 语言 | TypeScript | 类型安全 |
| 构建 | Vite | 开发服务器 :5173 |
| 路由 | Vue Router 4 | SPA 路由管理 |
| HTTP | Fetch API | 原生请求 |
| 图标 | Lucide Vue Next | 图标库 |
| 样式 | Tailwind CSS | 原子化 CSS |
| 提供商 | 模型 | API 端点 | 用途 |
|---|---|---|---|
| 智谱AI | glm-4.6v | /api/paas/v4/chat/completions | 意图识别、保单回复、体检参数提取、通用对话 |
| 通义千问 | 可配置 | /api/v1/services/aigc/text-generation/generation | 备选对话模型 |
erDiagram
pol_info {
BIGINT id PK
VARCHAR pol_no UK "保单号"
VARCHAR policy_holder_name "投保人姓名"
VARCHAR insured_name "被保险人姓名"
VARCHAR id_card_no "身份证号"
VARCHAR insurance_company "保险公司"
VARCHAR product_name "产品名称"
VARCHAR insurance_type "保险类型"
DECIMAL premium_amount "保费金额"
DECIMAL insured_amount "保额"
VARCHAR status "保单状态"
DATETIME effective_date "生效日期"
DATETIME expiry_date "到期日期"
DATETIME create_time "创建时间"
DATETIME update_time "更新时间"
TINYINT deleted "逻辑删除"
}
examination_hospital {
BIGINT id PK
VARCHAR hospital_code UK "医院编码"
VARCHAR hospital_name "医院名称"
VARCHAR hospital_level "医院等级"
VARCHAR address "地址"
VARCHAR phone "电话"
VARCHAR department "科室"
INT available_slots "可预约名额"
TINYINT status "状态"
DATETIME create_time "创建时间"
DATETIME update_time "更新时间"
TINYINT deleted "逻辑删除"
}
examination_package {
BIGINT id PK
VARCHAR package_code UK "套餐编码"
VARCHAR package_name "套餐名称"
TEXT package_desc "套餐描述"
DECIMAL price "价格"
VARCHAR duration "预计时长"
TINYINT status "状态"
DATETIME create_time "创建时间"
DATETIME update_time "更新时间"
TINYINT deleted "逻辑删除"
}
examination_booking {
BIGINT id PK
VARCHAR booking_no UK "预约编号"
BIGINT hospital_id FK "医院ID"
BIGINT package_id FK "套餐ID"
DATE schedule_date "预约日期"
VARCHAR user_id "用户ID"
VARCHAR booker_name "登记人姓名"
VARCHAR booker_phone "电话"
VARCHAR id_card_no "身份证号"
TEXT notes "备注"
VARCHAR status "预约状态"
DATETIME create_time "创建时间"
DATETIME update_time "更新时间"
TINYINT deleted "逻辑删除"
}
examination_hospital ||--o{ examination_booking : "hospital_id"
examination_package ||--o{ examination_booking : "package_id"
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | BIGINT | PK, AUTO_INCREMENT | 主键 |
| pol_no | VARCHAR(64) | UK, NOT NULL | 保单号 |
| policy_holder_name | VARCHAR(128) | NOT NULL, INDEX | 投保人姓名 |
| insured_name | VARCHAR(128) | NOT NULL | 被保险人姓名 |
| id_card_no | VARCHAR(18) | NOT NULL | 身份证号 |
| insurance_company | VARCHAR(128) | NOT NULL, INDEX | 保险公司 |
| product_name | VARCHAR(256) | NOT NULL | 产品名称 |
| insurance_type | VARCHAR(64) | — | 保险类型(健康险/寿险/意外险/医疗险) |
| premium_amount | DECIMAL(10,2) | — | 保费金额 |
| insured_amount | DECIMAL(12,2) | — | 保额 |
| status | VARCHAR(32) | INDEX | 状态(active/expired/cancelled) |
| effective_date | DATETIME | — | 生效日期 |
| expiry_date | DATETIME | — | 到期日期(NULL = 终身) |
| create_time | DATETIME | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
| update_time | DATETIME | ON UPDATE CURRENT_TIMESTAMP | 更新时间 |
| deleted | TINYINT | DEFAULT 0 | 逻辑删除 |
初始数据:15 条保单记录,涵盖 7 位投保人(张三/李四/王五/赵六/孙八/周九/吴十/郑十一),保险类型包括健康险、寿险、意外险、医疗险、财产险。
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | BIGINT | PK, AUTO_INCREMENT | 主键 |
| hospital_code | VARCHAR(64) | UK, NOT NULL | 医院编码 |
| hospital_name | VARCHAR(128) | NOT NULL | 医院名称 |
| hospital_level | VARCHAR(64) | — | 医院等级 |
| address | VARCHAR(256) | — | 地址 |
| phone | VARCHAR(32) | — | 联系电话 |
| department | VARCHAR(128) | — | 体检科室 |
| available_slots | INT | DEFAULT 0 | 可预约名额 |
| status | TINYINT | DEFAULT 1 | 状态(0-停用 1-启用) |
初始数据:8 家三甲医院
| 编码 | 名称 | 等级 | 可预约名额 |
|---|---|---|---|
| PEKING_UNION | 北京协和医院 | 三级甲等 | 15 |
| PEKING_301 | 301医院 | 三级甲等 | 20 |
| PEKING_FIRST | 北京大学第一医院 | 三级甲等 | 12 |
| SHANGHAI_ZHONGSHAN | 复旦大学附属中山医院 | 三级甲等 | 18 |
| SHANGHAI_RUIJIN | 上海交大医学院附属瑞金医院 | 三级甲等 | 16 |
| GUANGDONG_GENERAL | 广东省人民医院 | 三级甲等 | 22 |
| WEST_CHINA | 四川大学华西医院 | 三级甲等 | 25 |
| WUHAN_TONGJI | 武汉同济医院 | 三级甲等 | 19 |
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | BIGINT | PK, AUTO_INCREMENT | 主键 |
| package_code | VARCHAR(64) | UK, NOT NULL | 套餐编码 |
| package_name | VARCHAR(128) | NOT NULL | 套餐名称 |
| package_desc | TEXT | — | 套餐描述 |
| price | DECIMAL(10,2) | — | 价格 |
| duration | VARCHAR(32) | — | 预计时长(分钟) |
| status | TINYINT | DEFAULT 1 | 状态 |
初始数据:5 个体检套餐
| 编码 | 名称 | 价格 | 时长 |
|---|---|---|---|
| PKG_BASIC | 基础体检套餐 | ¥299 | 60分钟 |
| PKG_FULL | 全身体检套餐 | ¥899 | 120分钟 |
| PKG_EMPLOYMENT | 入职体检套餐 | ¥199 | 45分钟 |
| PKG_SENIOR | 老年体检套餐 | ¥1,299 | 150分钟 |
| PKG_WOMAN | 女性专项体检套餐 | ¥799 | 90分钟 |
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | BIGINT | PK, AUTO_INCREMENT | 主键 |
| booking_no | VARCHAR(64) | UK, NOT NULL | 预约编号 |
| hospital_id | BIGINT | FK, NOT NULL, INDEX | 医院ID |
| package_id | BIGINT | FK, NOT NULL, INDEX | 套餐ID |
| schedule_date | DATE | NOT NULL, INDEX | 预约日期 |
| user_id | VARCHAR(64) | NOT NULL, INDEX | 用户ID |
| booker_name | VARCHAR(128) | NOT NULL | 登记人姓名 |
| booker_phone | VARCHAR(32) | NOT NULL | 电话 |
| id_card_no | VARCHAR(18) | — | 身份证号 |
| notes | TEXT | — | 备注 |
| status | VARCHAR(32) | DEFAULT 'confirmed' | 状态(confirmed/cancelled/completed) |
com.healthagent/
├── HealthAgentApplication.java # 启动类
├── common/
│ ├── IntentType.java # 意图类型枚举
│ └── Result.java # 统一响应结果
├── config/
│ ├── WebConfig.java # CORS + 拦截器配置
│ ├── JwtSecretProvider.java # JWT 密钥管理
│ ├── SkillConfigLoader.java # 技能配置加载
│ ├── MyMetaObjectHandler.java # MyBatis-Plus 自动填充
│ └── ConfigValidationRunner.java # 配置校验
├── controller/
│ ├── SmartChatController.java # 智能对话
│ ├── PolicyController.java # 保单管理
│ ├── ExaminationController.java # 体检预约
│ └── AuthController.java # 认证
├── dto/
│ ├── SmartChatRequest.java # 对话请求
│ ├── SmartChatResponse.java # 对话响应
│ ├── ExaminationIntentData.java # 体检意图数据
│ ├── ExaminationBookingDTO.java # 预约 DTO
│ ├── ExaminationBookingRequestDTO.java
│ ├── ExaminationHospitalDTO.java
│ ├── ExaminationPackageDTO.java
│ ├── PolicyInfo.java # 保单信息 DTO
│ ├── PolicyQueryRequest.java # 保单查询请求
│ ├── LoginRequest.java
│ ├── LoginResponse.java
│ └── RefreshTokenRequest.java
├── entity/
│ ├── PolInfoEntity.java
│ ├── ExaminationHospitalEntity.java
│ ├── ExaminationPackageEntity.java
│ ├── ExaminationPlanEntity.java
│ └── ExaminationBookingEntity.java
├── interceptor/
│ └── AuthInterceptor.java # JWT 认证拦截器
├── mapper/
│ ├── PolInfoMapper.java
│ ├── ExaminationHospitalMapper.java
│ ├── ExaminationPackageMapper.java
│ ├── ExaminationPlanMapper.java
│ └── ExaminationBookingMapper.java
└── service/
├── SmartChatService.java # 核心编排服务
├── IntentRecognitionService.java # 意图识别
├── ExaminationIntentService.java # 体检意图提取
├── ExaminationService.java # 体检预约业务
├── PolicyService.java # 保单查询业务
├── AuthService.java # 认证服务
├── SessionManager.java # 会话管理
├── DataMaskingService.java # 数据脱敏
├── SkillExecutionService.java # 技能执行
└── chat/
├── AbstractChatClient.java # LLM 客户端抽象类
├── GlmChatClient.java # GLM 实现
├── QwenChatClient.java # 通义千问实现
└── ChatClientFactory.java # 客户端工厂
职责:接收用户消息,通过 SessionManager 获取/识别意图,按意图类型分发至对应处理器。
核心方法:
public SmartChatResponse chat(SmartChatRequest request)处理流程:
- 从 SessionManager 获取缓存意图,若未命中则调用 IntentRecognitionService
- 根据 IntentType 分发:
QUERY_POLICY→handleInsuranceQuery()BOOK_EXAMINATION→handleExaminationBooking()HEALTH_CONSULTATION/GENERAL_CONVERSATION→handleGeneralConversation()
设计特点:
- 意图缓存机制避免重复识别,同一用户同一会话内意图不变
- 保单查询后调用 LLM 做友好化处理(
generatePolicyResponse()) - 体检预约使用多轮对话模式,通过 SessionManager 合并信息
职责:将用户自然语言消息分类为四种意图之一。
识别 Prompt 结构:
- System: "你是一个意图识别助手,请只返回意图代码"
- User: 包含用户消息 + 四种意图说明 + 关键词提示
意图映射:
| IntentType | Code | 触发关键词 |
|---|---|---|
| QUERY_POLICY | query_policy | 保单、保险、理赔 |
| BOOK_EXAMINATION | book_examination | 体检、预约、检查 |
| QUERY_BOOKING | query_booking | 预约记录、我的预约、预约情况、预约列表 |
| HEALTH_CONSULTATION | health_consultation | 健康、症状、疾病 |
| GENERAL_CONVERSATION | general_conversation | 闲聊、问候 |
容错:LLM 返回无效意图时,IntentType.fromCode() 支持模糊匹配(code.contains(type.code)),兜底返回 GENERAL_CONVERSATION。
职责:从用户消息中提取体检预约所需的结构化参数。
提取参数:
{
"hospitalName": "医院名称",
"hospitalCode": "医院代码",
"examinationDate": "YYYY-MM-DD",
"examinationTime": "上午9点",
"packageType": "全身体检",
"notes": "备注",
"confidence": 0.8
}双重策略:
- LLM 优先:调用 GLM API 提取 JSON 格式参数
- 规则兜底:LLM 失败时使用正则匹配 + 关键词匹配
- 医院名:遍历 8 家医院关键词 + 正则
(.+?医院) - 日期:匹配
YYYY-MM-DD格式 + "明天/后天/下周" - 时间:匹配
\d{1,2}[点时]+ "上午/下午"
- 医院名:遍历 8 家医院关键词 + 正则
JSON 清理:自动去除 LLM 返回的 markdown 代码块标记(json ... )。
职责:管理用户会话状态,维护三类 ConcurrentHashMap 缓存。
缓存结构:
| 缓存 Map | Key | Value | 用途 |
|---|---|---|---|
| sessionHistory | sessionId | List<ChatMessage> | 对话历史 |
| userIntentCache | userId | IntentType | 意图缓存 |
| examinationBookingCache | userId | ExaminationIntentData | 体检预约信息 |
关键方法:
updateCachedExaminationIntent():合并新旧意图数据,非空字段覆盖mergeExaminationIntentData():字段级别 merge,新值优先isNotBlank()判空后设置bookingReady标志
双模式设计:
- MyBatis-Plus DB 模式:通过 PolInfoMapper 查询 examination_hospital 数据库
getByPolNo()/getByPolicyHolderName()/getByIdCardNo()queryPolicies()/queryPoliciesPage()createPolicy()/updatePolicy()/deleteByPolNo()
- Mock 模式(兼容旧接口):静态 HashMap,按 userId 映射
getUserPolicies()— 仅返回 active 保单getAllUserPolicies()— 返回全部含 expiredformatPoliciesAsText()— 格式化为文本
注意:SmartChatService 中对话走的是 Mock 模式,PolicyController 中走的是 DB 模式。
核心方法:
| 方法 | 说明 |
|---|---|
getAvailableHospitals() |
查询启用状态的医院列表 |
searchHospitals(keyword) |
按名称/编码/等级/科室模糊搜索 |
getAvailablePackages() |
查询启用状态的套餐列表 |
bookExamination(request) |
创建预约(生成预约号 + 插入 DB) |
getUserBookings(userId) |
查询用户预约列表 |
getBookingByNo(bookingNo) |
按预约号查详情 |
cancelBooking(bookingNo, userId) |
取消预约(改状态为 cancelled) |
预约号生成规则:EXM + yyyyMMddHHmmss + 4位随机数
JWT 双 Token 机制:
| Token 类型 | 生成方式 | 有效期 | 存储 |
|---|---|---|---|
| Access Token | JJWT HMAC-SHA 签名 | 120 分钟 | 客户端内存 |
| Refresh Token | UUID 去横线 | 168 小时(7天) | Redis |
认证流程:
- 用户提交用户名密码 →
login()验证(目前仅支持默认账户 admin/admin123) - 签发 Access Token + Refresh Token
- Refresh Token 存入 Redis(key:
auth:refresh:{token}) - Access Token 过期后用 Refresh Token 换取新 Token 对
- 登出时删除 Redis 中的 Refresh Token
密钥管理:JwtSecretProvider 动态提供签名密钥,未配置 JWT_SECRET 时自动生成临时强密钥。
脱敏规则:
| 数据类型 | 规则 | 示例 |
|---|---|---|
| 保单号 | 保留前2后2 | POL2024****01 |
| 身份证 | 保留前6后4 | 110101****1234 |
| 手机号 | 保留前3后4 | 138****8000 |
| 银行卡 | 保留前4后4 | 6222****1234 |
| 邮箱 | 保留首字符+@后 | z****@example.com |
| 姓名 | 保留首字 | 张* / 张** |
v3.0 引入 ReAct(Reasoning + Acting)Agent 框架,将智能对话从"单轮意图识别 → 业务服务调用"升级为"多轮思考 → 工具调用 → 观察结果 → 继续推理 → 最终回复"的循环模式。
graph LR
subgraph "ReAct Agent"
Orchestrator[ReActAgentOrchestrator] --> Loop[ReActLoop]
Loop --> Registry[ToolRegistry]
Loop --> Executor[ToolExecutor]
Registry --> Tools[工具集合]
Executor --> Tools
end
subgraph "意图识别"
IR[IntentRecognitionService] --> |"query_policy"| Orchestrator
IR --> |"book_examination"| Orchestrator
IR --> |"query_booking"| Orchestrator
IR --> |"health_consultation"| Simple[简单对话模式]
end
subgraph "工具实现"
Tools --> PQ[PolicyQueryTool]
Tools --> BE[BookExaminationTool]
Tools --> QB[QueryBookingTool]
Tools --> LP[ListPackagesTool]
Tools --> LH[ListHospitalsTool]
end
| 组件 | 职责 | 文件路径 |
|---|---|---|
| ReActAgentOrchestrator | 智能对话编排器,根据意图类型选择 ReAct 或简单模式 | agent/ReActAgentOrchestrator.java |
| ReActLoop | Thought-Action-Observation 循环引擎 | agent/ReActLoop.java |
| ToolRegistry | 工具注册中心,管理所有可用工具 | agent/tool/ToolRegistry.java |
| ToolExecutor | 工具执行器,将 JSON 参数分发到对应工具 | agent/tool/ToolExecutor.java |
| ConversationState | 会话状态(对话历史 + 当前意图 + 任务步骤) | agent/ConversationState.java |
| SessionManager | 会话持久化 + 意图缓存 | service/SessionManager.java |
Thought → Action → Observation → Thought → Action → Observation → ... → Finish
- Thought(思考):分析用户问题 + 上下文,决定下一步操作
- Action(行动):选择合适的工具并调用
- Observation(观察):读取工具返回的结果
- 循环执行直到获得足够信息,输出 Finish(最终回复)
最大循环步数:10 步,超时自动终止并返回兜底回复。
| 工具名称 | 描述 | 必填参数 | 可选参数 |
|---|---|---|---|
query_policy |
查询用户保单信息 | userId | policyNo, status |
book_examination |
创建体检预约 | userId, date, name, phone | hospitalName/hospitalId, packageName/packageId |
query_booking |
查询用户体检预约记录 | userId | status |
list_hospitals |
查询可用医院列表 | 无 | 无 |
list_packages |
查询体检套餐列表 | 无 | 无 |
userId 自动注入:当 LLM 未提供 userId 参数时,系统自动从当前用户上下文注入,确保工具始终使用正确的用户 ID。
自然语言日期解析:BookExaminationTool 内置日期解析器,支持以下表达:
| 表达 | 解析结果 |
|---|---|
| 今天/明天/后天 | ±N 天 |
| 这周末/本周末 | 本周六 |
| 上周末 | 上周日 |
| 下周一~日 | 对应下周星期 |
| 月底/月末 | 本月最后一天 |
| 下月初 | 下月 1 号 |
2026-05-17(明天) |
正则提取 yyyy-MM-dd |
医院/套餐名称模糊匹配:支持别名解析(如"协和"→"北京协和医院"、"标准"→"基础体检套餐"),未匹配时返回可用选项列表引导用户选择。
防占位符验证:拦截 AI 生成的默认值(如 name=用户),强制要求真实信息。
预约记录详情展示:查询预约记录时展示医院等级、地址、联系电话、套餐说明等完整信息。
classDiagram
class AbstractChatClient {
<<abstract>>
#apiKey: String
#baseUrl: String
#model: String
+chat(userMessage, systemPrompt, userId) String
#buildMessages(systemPrompt, userMessage, userId) List~ChatMessage~
#getDefaultSystemPrompt() String
#buildErrorMessage(Exception) String
#buildRequestBody(messages)* String
#callApi(requestBody)* String
#parseResponse(rawResponse)* String
}
class GlmChatClient {
+buildRequestBody(messages) String
+callApi(requestBody) String
+parseResponse(rawResponse) String
}
class QwenChatClient {
+buildRequestBody(messages) String
+callApi(requestBody) String
+parseResponse(rawResponse) String
+getDefaultSystemPrompt() String
}
class ChatClientFactory {
+createClient(provider, apiKey, baseUrl, model)$ AbstractChatClient
}
AbstractChatClient <|-- GlmChatClient
AbstractChatClient <|-- QwenChatClient
ChatClientFactory ..> AbstractChatClient : creates
模板方法 chat():
buildMessages()— 构建 [system, user] 消息列表buildRequestBody()— 子类实现,构建 JSON 请求体callApi()— 子类实现,发送 HTTP 请求parseResponse()— 子类实现,解析响应 JSON
GLM 与 Qwen 差异:
| 项目 | GLM | Qwen |
|---|---|---|
| API 路径 | /api/paas/v4/chat/completions | /api/v1/services/aigc/text-generation/generation |
| 请求格式 | messages 直接在根级 | messages 嵌套在 input 对象中 |
| 认证头 | Authorization: Bearer {key} | Authorization: Bearer {key} + X-DashScope-SSE: disable |
| 响应路径 | choices[0].message.content | output.choices[0].message.content |
| 路由 | 组件 | 需认证 | 说明 |
|---|---|---|---|
| /login | LoginPage | 否 | 登录页 |
| /dashboard | DashboardPage | 是 | 仪表盘首页 |
| /chat | ChatPage | 是 | AI 对话(默认首页) |
| /policy | PolicyPage | 是 | 保单查询 |
| /examination/packages | ExaminationPackagesPage | 是 | 体检套餐列表 |
| /examination/detail | ExaminationDetailPage | 是 | 体检详情/预约 |
| /examination/bookings | ExaminationBookingsPage | 是 | 预约记录 |
路由守卫:router.beforeEach 检查 isAuthenticated,未认证重定向至 /login。
布局结构:
- 顶栏:渐变蓝→青背景,健康助手标题 + HeartPulse 图标,首页/菜单按钮,用户头像
- 消息区:滚动容器,日期标签 + 欢迎消息 + 对话气泡
- 用户消息:蓝→青渐变气泡,右对齐
- AI 回复:白色气泡,左对齐
- 加载状态:三点弹跳动画
- 输入区:
- 快捷按钮:保单查询 / 体检预约
- 语音输入按钮(Mic 图标,录音时红色脉冲动画)
- 文本输入框 + 发送按钮
语音输入:
- 使用 Web Speech API(
SpeechRecognition/webkitSpeechRecognition) - 语言:zh-CN
- 支持中间结果(interimResults = true)
- 错误处理:浏览器不支持提示 / 麦克风权限拒绝提示
API 调用:
POST /api/smart-chat/send
Body: { message, userId }
Response: { code: 200, data: { message, intent, action, ... } }
布局:
- 顶栏:健康助手 Logo + 用户信息 + 退出按钮
- 功能卡片(3列网格):
- 保单查询(蓝色,Shield 图标)→ /policy
- 体检预约(青色,Stethoscope 图标)→ /examination/packages
- AI助手(紫色,Bot 图标)→ /chat
- 今日健康提示:渐变背景卡片
功能:展示用户保单列表,支持按保单号/投保人/身份证号查询。
- ExaminationPackagesPage:体检套餐列表
- ExaminationDetailPage:套餐详情 + 预约表单
- ExaminationBookingsPage:预约记录查看/取消
useAuth 组合式函数:
isAuthenticated— 认证状态响应式变量user— 当前用户信息login(username, password)— 登录logout()— 登出checkAuth()— 检查认证状态
Token 存储:localStorage 存储 Access Token 和 Refresh Token。
graph LR
subgraph "对话引擎"
Input[用户输入] --> IR[意图识别层]
IR --> |query_policy| PQ[保单查询处理器]
IR --> |book_examination| EB[体检预约处理器]
IR --> |health_consultation| HC[健康咨询处理器]
IR --> |general_conversation| GC[通用对话处理器]
PQ --> LLM1[LLM 友好化]
EB --> LLM2[LLM 参数提取]
HC --> LLM3[LLM 咨询回复]
GC --> LLM4[LLM 通用回复]
LLM1 --> Output[智能回复]
LLM2 --> Output
LLM3 --> Output
LLM4 --> Output
end
请分析以下用户消息,判断用户的意图。
用户消息: {userMessage}
可选意图类型:
- query_policy: 用户想查询保单信息(包含"保单"、"保险"、"理赔"等关键词)
- book_examination: 用户想预约体检(包含"体检"、"预约"、"检查"等关键词)
- query_booking: 用户想查询体检预约记录(包含"预约记录"、"我的预约"、"预约情况"、"预约列表"等关键词)
- health_consultation: 用户想进行健康咨询(包含"健康"、"症状"、"疾病"、"怎么办"等关键词)
- general_conversation: 一般对话、闲聊、问候等
请只返回一个意图代码,不需要其他解释。
System Prompt: "你是一个意图识别助手,请只返回意图代码,不需要其他解释。"
请分析用户消息,提取体检预约相关信息。
用户消息: {userMessage}
请以JSON格式返回以下信息:
{
"hospitalName": "医院名称(如果没有明确提到,请填null)",
"hospitalCode": "医院代码(如果没有明确提到,请填null)",
"examinationDate": "体检日期,格式YYYY-MM-DD(如果没有明确提到,请填null)",
"examinationTime": "体检时间,如'上午9点'(如果没有明确提到,请填null)",
"packageType": "套餐类型(如果没有明确提到,请填null)",
"notes": "其他备注信息(如果没有,请填null)",
"confidence": 0.0到1.0之间的置信度
}
只返回JSON,不要有其他内容。
用户询问保单相关问题,以下是查询到的保单信息:
{policyInfo}
请根据以上信息,用友好的方式回复用户,可以:
1. 总结保单的主要特点
2. 提醒用户关注的事项
3. 询问是否需要了解更多信息
用户原问题:{userMessage}
回复要简洁,自然,像一个专业的保险顾问。
stateDiagram-v2
[*] --> Idle: 用户进入对话
Idle --> IntentRecognized: 发送首条消息
IntentRecognized --> PolicyQuery: intent=query_policy
IntentRecognized --> ExamBooking: intent=book_examination
IntentRecognized --> QueryBooking: intent=query_booking
IntentRecognized --> HealthConsult: intent=health_consultation
IntentRecognized --> GeneralChat: intent=general_conversation
PolicyQuery --> Idle: 返回保单信息
ExamBooking --> CollectingInfo: 信息不完整
CollectingInfo --> CollectingInfo: 追问缺失字段
CollectingInfo --> BookingConfirmed: 信息完整
BookingConfirmed --> Idle: 预约成功
QueryBooking --> Idle: 返回预约记录列表
HealthConsult --> Idle: 返回咨询回复
GeneralChat --> Idle: 返回通用回复
sequenceDiagram
participant C as 客户端
participant S as AuthController
participant A as AuthService
participant R as Redis
C->>S: POST /api/auth/login {username, password}
S->>A: login(username, password)
A->>A: 验证默认账户
A->>A: 签发 Access Token (HMAC-SHA)
A->>A: 生成 Refresh Token (UUID)
A->>R: SET auth:refresh:{token} → username (TTL 168h)
A-->>S: AuthTokens
S-->>C: {accessToken, refreshToken, userInfo}
Note over C: Access Token 过期
C->>S: POST /api/auth/refresh {refreshToken}
S->>A: refresh(refreshToken)
A->>R: GET auth:refresh:{token}
R-->>A: username
A->>R: DEL auth:refresh:{token}
A->>A: 签发新 Token 对
A-->>S: 新 AuthTokens
S-->>C: {新accessToken, 新refreshToken}
C->>S: POST /api/auth/logout {refreshToken}
S->>A: logout(refreshToken)
A->>R: DEL auth:refresh:{token}
- 拦截路径:
/api/** - 排除路径:
/api/auth/login,/api/auth/refresh,/api/auth/current,/api/chat/**,/swagger-ui/**,/v3/api-docs/** - Token 提取:
Authorization: Bearer {token}或?token={token} - 开关:
healthagent.auth.interceptor-enabled(默认 false,仅调试用)
- 允许来源:
http://localhost:5173,http://127.0.0.1:5173,http://localhost:5174,http://127.0.0.1:5174 - 允许方法:GET, POST, PUT, DELETE, OPTIONS
- 允许凭证:true
- 预检缓存:3600s
SkillExecutionService 在格式化保单信息时,自动对以下字段进行脱敏:
- 保单号:
maskPolicyId()→ 保留前2后2 - 身份证号:
maskIdCard()→ 保留前6后4 - 姓名脱敏:
maskName()→ 保留首字
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/auth/login | 用户登录 |
| POST | /api/auth/logout | 用户登出 |
| POST | /api/auth/refresh | 刷新令牌 |
| GET | /api/auth/current | 获取当前用户 |
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/smart-chat/send | 发送对话消息 |
请求体:
{
"message": "我想查询保单",
"userId": "admin"
}响应体:
{
"code": 200,
"message": "操作成功",
"data": {
"intent": "query_policy",
"message": "您共有2份有效保单...",
"action": "query_policy_success",
"messageType": "policy_info",
"needsMoreInfo": false,
"data": [...]
}
}| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/policy/query | 条件查询保单 |
| POST | /api/policy/page?pageNum=1&pageSize=10 | 分页查询 |
| GET | /api/policy/{polNo} | 按保单号查询 |
| GET | /api/policy/holder/{name} | 按投保人姓名查询 |
| GET | /api/policy/idcard/{idCardNo} | 按身份证号查询 |
| GET | /api/policy/all | 查询所有保单 |
| POST | /api/policy | 新增保单 |
| PUT | /api/policy | 更新保单 |
| DELETE | /api/policy/{polNo} | 删除保单 |
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/examinations/hospitals | 获取医院列表 |
| GET | /api/examinations/hospitals?keyword=协和 | 搜索医院 |
| GET | /api/examinations/hospitals/{code} | 医院详情 |
| GET | /api/examinations/packages | 套餐列表 |
| POST | /api/examinations/book | 创建预约 |
| GET | /api/examinations/bookings/{bookingNo} | 预约详情 |
| GET | /api/examinations/users/{userId}/bookings | 用户预约列表 |
| DELETE | /api/examinations/bookings/{bookingNo}?userId=xxx | 取消预约 |
| GET | /api/examinations/requirements | 预约须知 |
以下截图来自系统实际运行效果
Dashboard 仪表盘:
展示三大核心功能入口卡片:保单查询、体检预约、AI助手,以及每日健康提示。

主对话界面:
语音输入:
点击麦克风图标启动浏览器原生语音识别,录音时显示红色脉冲动画,识别结果自动填入输入框。

上下文记忆:
系统通过 SessionManager 维护对话上下文,已识别的意图在会话内持续有效,体检预约多轮对话自动合并历史信息。

系统自动将用户消息分类为四种意图之一,意图识别结果影响后续处理路径。

对话式查询:
用户发送"我想查询我的保单",系统识别为 query_policy 意图,自动查询用户保单并以友好格式展示。

页面式查询:
PolicyPage 提供表单化查询界面,支持按保单号、投保人、身份证号等条件精确查询。

预约入口:通过快捷按钮或自然语言触发
项目使用 Trae IDE 进行开发,支持热重载和实时预览。

| 组件 | 最低版本 | 说明 |
|---|---|---|
| JDK | 21+ | 后端运行时 |
| Node.js | 18+ | 前端构建 |
| MySQL | 8.0+ | 数据库 |
| Redis | 6.0+ | Token 存储 |
| 配置项 | 默认值 | 说明 |
|---|---|---|
| server.port | 8084 | 后端端口 |
| MYSQL_HOST | localhost | MySQL 地址 |
| MYSQL_PORT | 3306 | MySQL 端口 |
| MYSQL_DATABASE | healthagent | 数据库名 |
| MYSQL_USERNAME | root | 数据库用户名 |
| MYSQL_PASSWORD | 123456 | 数据库密码 |
| REDIS_HOST | localhost | Redis 地址 |
| REDIS_PORT | 6379 | Redis 端口 |
| JWT_SECRET | (自动生成) | JWT 签名密钥 |
| APP_AUTH_DEFAULT_USERNAME | admin | 默认用户名 |
| APP_AUTH_DEFAULT_PASSWORD | admin123 | 默认密码 |
| AUTH_INTERCEPTOR_ENABLED | false | 认证拦截器开关 |
| healthagent.chat.model | glm-4.6v | LLM 模型 |
| healthagent.chat.provider | glm | LLM 提供商 |
| healthagent.glm.api-key | (需配置) | GLM API 密钥 |
- 初始化数据库:
CREATE DATABASE healthagent CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; SOURCE sql/pol_info.sql; SOURCE sql/examination.sql;
- 启动后端:
后端启动于
cd backend mvn spring-boot:run # 或 java -jar target/HealthAgent.jar
http://localhost:8084 - 启动前端:
前端开发服务器启动于
cd frontend npm install npm run devhttp://localhost:5173,自动代理/api到后端 - 访问:打开
http://localhost:5173,使用 admin/admin123 登录
启动后访问 Swagger UI:http://localhost:8084/swagger-ui.html
| 项目 | 当前状态 | 改进方向 |
|---|---|---|
| 用户注册 | 仅默认账户 | 增加注册功能 + 用户表 |
| 保单对话 | 使用 Mock 数据 | 统一走 DB 查询 |
| 意图缓存 | 全会话不变 | 支持意图切换/超时重识别 |
| 体检预约 | 部分信息硬编码 | 对话中动态获取姓名/手机号 |
| Token 存储 | 内存 ConcurrentHashMap | 迁移到 Redis 持久化 |
| examination_plan | Mapper 存在但未使用 | 实现排期管理功能 |
| 前端状态 | 无 Pinia/Vuex | 引入状态管理 |
| 文件上传 | 不支持 | 支持图片/文档上传 |
文档结束 | HealthAgent v3.0 | 2026-05-16
| 更新项 | 说明 |
|---|---|
| ReAct Agent 框架 | 引入 Thought-Action-Observation 循环推理引擎,支持工具调用 |
| 意图识别增强 | 新增 query_booking 意图(查询预约记录) |
| 保单表增加 user_id | pol_info 表新增 user_id 列,支持按用户ID查询保单 |
| 体检预约智能解析 | 支持自然语言日期(明天/下周一/月底等)、医院/套餐名称模糊匹配 |
| userId 自动注入 | ReAct 工具调用时自动注入当前用户 ID,避免 LLM 遗漏 |
| 预约记录详情展示 | 展示医院等级/地址/电话、套餐说明等完整信息 |
| 防占位符验证 | 拦截 AI 生成的默认值(如 name=用户),强制要求真实信息 |
| 工具集合 | query_policy / book_examination / query_booking / list_hospitals / list_packages |




