--- doc_id: PRD-202606-001 feature_id: FEAT-202606-001-unified-entry-client type: prd title: 统一入口客户端 v0.1 设计方案 status: approved owner: 医梦研发团队 created_at: 2026-06-17 updated_at: 2026-06-17 reviewers: [] related_docs: - PRD-202605-001 - ODS-202606-001 related_modules: - emoon-openplatform - emoon-ai-agent - emoon-ai-card - emoon-ai-mcp tags: - 统一入口客户端 - 挂号演示 - HIS Mock - Dify --- ## 结论:方向可行,但第 3 步必须收敛 你的 4 步整体方向是合理的,但要做三个关键修正: | 你原来的想法 | 判断 | 修正方案 | | --------------------------------- | ----------------- | ------------------------------------------------------------------------------------- | | 部署极简 HIS Mock 服务 | 合理 | 必须模拟真实 HIS 的关键语义:患者、科室、医生、排班、号源锁定、订单、支付、预约确认 | | AI 中台通过 MCP 注册 HIS 接口 | 合理 | 下周演示不必完整实现标准 MCP 协议,可先实现 `MCP Tool Service / HTTP Tool Facade`,但接口形态要按未来 MCP 治理设计 | | DeepSeek 意图识别后转发 Dify 不同 workflow | **部分合理,但不能直接这么做** | DeepSeek 只能作为“无活跃任务时”的兜底分类器;优先级必须是:任务状态机 > 卡片状态 > 确定性规则 > DeepSeek 分类 > Dify Workflow | | Web 端类似蚂蚁阿福完成挂号闭环 | 合理 | Web 端只负责对话和卡片交互,不直连 Dify、不直连 HIS、不直接完成业务写操作 | 这个边界和你们现有中台技术方案一致:OpenPlatform 负责入口、权限、会话、卡片状态、审计和事实记录;Dify 负责流程编排;MCP/HIS Adapter 负责院内系统工具调用;统一入口客户端负责终端交互,而不是业务事实判断。 --- # 一、最终建议方案 下周演示建议只做一个清晰闭环: > **患者通过统一入口 Web 客户端自然语言发起挂号诉求 → AI 中台识别任务 → Dify 完成问诊/导诊/话术编排 → MCP 调用 Mock HIS 查询科室、医生、号源 → 前端卡片让患者选择 → Card Runtime 执行建档、锁号、支付、预约确认 → 返回挂号成功卡片。** 不要做成“Dify 直接把所有事情干完”。原因是挂号、建档、支付、锁号都属于确定性业务动作,必须经过卡片确认、幂等、状态机和后端工具调用。你们对外接口文档也明确:普通终端不得直接调用 HIS Tool,不直接持有 Dify Key;写操作必须幂等;设备和客户端只能走 Agent API、Card Action API、Device API 等受控入口。 --- # 二、系统分层 ## 2.1 演示版系统组成 ```text 统一入口 Web 客户端 ↓ AI 中台 OpenPlatform ↓ AgentRouter / TaskStateService / Card Runtime ↓ Dify 挂号智能体 Workflow ↓ MCP Tool Service ↓ Mock HIS Service ``` ## 2.2 各层职责 | 层 | 负责 | 不负责 | | ------------------------ | ---------------------------- | ------------------- | | 统一入口 Web 客户端 | 聊天 UI、虚拟助手、业务卡片、二维码展示、用户点击确认 | 不做业务判断,不直连 Dify/HIS | | OpenPlatform | 鉴权、会话、任务状态、路由、SSE、卡片实例、审计 | 不写复杂自然语言流程 | | AgentRouter | 判断当前消息应进入哪个业务流程 | 不直接生成 AI 回复 | | DeepSeekIntentClassifier | 无活跃任务时做意图识别和槽位抽取 | 不直接调用 HIS,不决定业务写操作 | | Dify Workflow | 问诊追问、导诊话术、科室推荐解释、卡片输出建议 | 不直接写 HIS,不直接完成支付 | | Card Runtime | 创建卡片实例、处理用户动作、幂等、推进任务状态 | 不做自然语言理解 | | MCP Tool Service | 封装 HIS 工具、审计、幂等、错误码映射 | 不做 UI,不做对话 | | Mock HIS Service | 模拟真实 HIS 业务接口 | 不包装成真实支付/医保/医院接口 | 当前 Demo 已经有类似“前端负责统一交互入口,后端负责对话意图路由与业务流程编排”的雏形,但它现在仍是 Demo 级:会话状态在内存里,建档写 CSV,医生排班模拟,卡片协议和任务状态机不完整。下周演示可以借鉴这个交互资产,但不能继续沿用 Demo 后端架构。 --- # 三、关键流程是否合理 ## 3.1 Step 1:极简 HIS Mock 服务 **合理,而且必须先做。** 否则 Dify 和前端都只能“假装挂号成功”,甲方追问“科室、医生、号源、支付、预约号从哪里来”时会露馅。 ### Mock HIS 最小接口 | 模块 | 接口 | 说明 | | -- | -------------------------------------------- | ---------------- | | 患者 | `POST /mock-his/patients/search` | 根据身份证号/手机号查询患者 | | 患者 | `POST /mock-his/patients` | 创建患者档案 | | 科室 | `GET /mock-his/departments` | 查询科室列表 | | 医生 | `GET /mock-his/doctors?departmentId=` | 查询科室医生 | | 排班 | `GET /mock-his/schedules?doctorId=&date=` | 查询医生排班 | | 号源 | `POST /mock-his/slots/lock` | 锁定号源,返回 `lockId` | | 号源 | `POST /mock-his/slots/release` | 释放号源 | | 支付 | `POST /mock-his/payments/orders` | 创建挂号支付订单,返回二维码 | | 支付 | `POST /mock-his/payments/mock-paid` | 演示用:模拟支付成功 | | 支付 | `GET /mock-his/payments/{orderId}` | 查询支付状态 | | 挂号 | `POST /mock-his/appointments` | 支付成功后创建预约 | | 挂号 | `GET /mock-his/appointments/{appointmentId}` | 查询预约详情 | ### Mock HIS 数据表 | 表 | 用途 | | ------------------- | ------- | | `his_patient` | 模拟患者主索引 | | `his_department` | 科室字典 | | `his_doctor` | 医生信息 | | `his_schedule` | 医生排班 | | `his_slot` | 号源库存 | | `his_slot_lock` | 号源锁定记录 | | `his_payment_order` | 支付订单 | | `his_appointment` | 挂号记录 | ### 关键实现约束 1. **号源锁定必须有过期时间**:例如 5 分钟。 2. **创建预约必须校验支付成功**。 3. **重复提交必须幂等**:同一个 `idempotencyKey` 不能创建多条预约。 4. **支付必须明确是模拟支付**:二维码可以是 `demo-payment://orderId=xxx`,不要伪装成真实微信/支付宝/医保支付。 5. **号源扣减必须真实发生**:甲方反复演示时,应能看到剩余号源减少。 --- ## 3.2 Step 2:AI 中台通过 MCP 注册 HIS 接口 **合理,但下周演示不要追求完整 MCP 协议。** 建议做一个“符合未来 MCP 思路的 HTTP Tool Service”: ```text AI 中台 MCP Tool Service ├── his.searchPatient ├── his.createPatient ├── his.listDepartments ├── his.listDoctors ├── his.listSchedules ├── his.lockSlot ├── his.releaseSlot ├── his.createPaymentOrder ├── his.queryPaymentStatus └── his.createAppointment ``` 你们中台方案本身也写清楚了:MCP 是医梦内部 AI 工具网关设计,MVP 阶段可以先用 HTTP/JSON Tool Service 实现,不必一次性实现完整 Model Context Protocol 生态;核心目的是让 Dify 工具调用、卡片动作和 HIS Adapter 都进入同一个可授权、可审计、可幂等的工具出口。 ### 工具注册结构 每个工具至少包含: ```json { "toolName": "his.lockSlot", "riskLevel": "BUSINESS_WRITE", "method": "POST", "path": "/mock-his/slots/lock", "timeoutMs": 3000, "idempotent": true, "requiredContext": ["projectId", "conversationId", "patientId"], "inputSchema": {}, "outputSchema": {} } ``` ### 风险分级 | 风险级别 | 工具 | 控制策略 | | ------ | ----------- | ----------------------------- | | 查询 | 查询科室、医生、排班 | 可由 Dify 调用 | | 敏感查询 | 查询患者档案 | 必须有患者身份上下文 | | 业务写入 | 建档、锁号、预约 | 必须通过 Card Action,不能由 Dify 直接写 | | 财务动作 | 创建支付订单、确认支付 | 必须幂等,必须卡片确认 | | 医疗正式写入 | 暂无 | 下周演示不做 | --- ## 3.3 Step 3:Dify 挂号智能体与 DeepSeek 意图识别 你的想法里最危险的是这一句: > “调用 DeepSeek 进行意图识别,然后再转发给 Dify 的不同 workflow。” 这句话如果直接照做,会出现几个问题: 1. 用户已经在选时间,输入“下午”,DeepSeek 可能无法判断上下文。 2. 用户正在建档,突然问“医保能报销吗”,系统可能错误切换流程。 3. Dify Workflow 太多,状态分散,难以保证挂号闭环。 4. 写操作如果由 Dify 直接触发,会绕开幂等、卡片确认和审计。 5. 多轮对话中最重要的是任务状态,不是每句话重新识别意图。 ### 正确方案 ```text 优先级 1:是否有 activeTask 优先级 2:是否有 waitingCard 优先级 3:确定性规则 优先级 4:DeepSeek 意图分类 优先级 5:Dify Workflow ``` 也就是: > **DeepSeek 只做“新任务入口”和“意图兜底”,不做整个挂号流程的主控。** ### DeepSeek 分类输出 只输出结构化 JSON: ```json { "intent": "REGISTRATION", "subIntent": "SYMPTOM_TRIAGE", "confidence": 0.91, "slots": { "symptom": "头疼三天,有点恶心", "departmentName": null, "doctorName": null, "dateText": null, "timePreference": null }, "risk": { "emergencyLevel": "NORMAL", "needEmergencyWarning": false }, "shouldAskClarification": false } ``` ### 不允许 DeepSeek 做的事 | 禁止项 | 原因 | | ------------ | ------------ | | 直接返回“挂号成功” | 它没有真实 HIS 结果 | | 直接决定扣号源 | 必须走 MCP 和锁号 | | 直接创建支付订单 | 财务动作必须幂等 | | 当前任务中每轮都重新路由 | 会破坏多轮流程 | | 替代任务状态机 | 无法保证流程可恢复 | --- ## 3.4 Step 4:统一入口 Web 客户端 **合理。下周演示只做 Web,不要扩展机器人、自助机硬件。** 统一入口客户端的真实目标是多终端运行时,但下周甲方演示只需要一个 Web 端先证明“蚂蚁阿福式入口 + 挂号闭环”成立。门诊方案里,统一入口本来就是门诊入口控制点,导诊大屏、自助机、机器人等都属于 P0 入口,但下周不必全部硬件落地。 ### Web 端必须包含 | 页面/组件 | 说明 | | ------- | --------------------- | | 虚拟助手区域 | 类似蚂蚁阿福的人机对话入口 | | 聊天消息流 | 支持用户输入、AI 回复、SSE 流式 | | 快捷入口 | 我要挂号、我要建档、科室导诊、查医生 | | 建档卡片 | 姓名、身份证、手机号、性别、出生日期 | | 科室推荐卡片 | 推荐科室、推荐理由、备选科室 | | 医生选择卡片 | 医生、职称、专长、可预约日期 | | 时间选择卡片 | 上午/下午/具体时段、剩余号源 | | 支付二维码卡片 | 二维码、金额、倒计时、模拟支付按钮 | | 挂号成功卡片 | 预约号、医生、科室、时间、诊区、二维码凭证 | | 异常提示卡片 | 无号源、支付失败、信息缺失、流程取消 | --- # 四、确定的端到端流程 ## 4.1 启动前准备 ```text 1. 启动 Mock HIS 2. 导入科室、医生、排班、号源、示例患者 3. AI 中台注册 HIS 工具 4. AI 中台配置 DifyAgentEngine 5. Dify 配置挂号相关 Workflow 6. Web 端配置默认 agentCode = outpatient-registration-agent ``` --- ## 4.2 用户发起挂号 用户输入: ```text 我头疼三天,还有点恶心,想挂号 ``` 流程: ```text Web Client → POST /agent/chat/stream → OpenPlatform 创建 conversation → TaskStateService 判断无 activeTask → 规则未完全命中 → DeepSeekIntentClassifier 分类为 REGISTRATION + SYMPTOM_TRIAGE → 创建 REGISTRATION task → 调用 Dify triage workflow → Dify 追问或推荐科室 → 创建 department-selection 卡片 → SSE 返回文本 + 卡片 ``` --- ## 4.3 建档检查 系统发现当前没有患者身份: ```text TaskStateService → currentStep = CHECK_PATIENT_IDENTITY → MCP Tool: his.searchPatient → 未找到 patient → 创建 patient-record 卡片 ``` 用户填写: ```text 姓名:张三 身份证:620xxxxxxxxxxxxx 手机号:13800000000 ``` 点击确认: ```text Web Client → POST /cards/{cardInstanceId}/actions/confirm_create_patient → Card Runtime 校验状态 + 幂等 → MCP Tool: his.createPatient → Mock HIS 创建 patientId → TaskStateService 写入 patientId → 继续挂号任务 ``` --- ## 4.4 症状分诊与科室推荐 ```text Dify triage workflow → 读取 symptom slots → 判断是否需要追问 → 若信息足够,输出推荐科室 → MCP Tool: his.listDepartments → Card Runtime 创建 department-selection 卡片 ``` 卡片示例: ```json { "cardKey": "department-selection", "data": { "title": "为您推荐以下科室", "departments": [ { "departmentId": "D010", "name": "神经内科", "reason": "头痛伴恶心,建议优先排查神经系统相关问题" }, { "departmentId": "D001", "name": "普通内科", "reason": "可作为综合初筛科室" } ] } } ``` --- ## 4.5 选择医生 用户点击“神经内科”。 ```text Web Client → Card Action: select_department → Card Runtime 校验 → MCP Tool: his.listDoctors(departmentId) → Mock HIS 返回医生列表 → 创建 doctor-selection 卡片 ``` 医生卡片: ```json { "cardKey": "doctor-selection", "data": { "departmentName": "神经内科", "doctors": [ { "doctorId": "DOC001", "name": "李明", "title": "主任医师", "specialties": ["头痛", "眩晕", "脑血管病"], "availableDateCount": 3 } ] } } ``` --- ## 4.6 选择时间与锁号 用户选择医生和日期: ```text Card Action: select_doctor → MCP Tool: his.listSchedules → 返回可用日期和时段 → 创建 time-slot-selection 卡片 ``` 用户选择时段: ```text Card Action: select_time_slot → MCP Tool: his.lockSlot → Mock HIS 创建 lockId,锁定 5 分钟 → 创建 confirm-registration 卡片 ``` 这里必须有锁号,否则演示逻辑不真实。真实 HIS 场景里,号源并发和过期释放是挂号流程的核心风险点;你们技术方案里也明确排班号源工具需要幂等、短锁、超时释放和并发控制。 --- ## 4.7 支付二维码 用户确认挂号: ```text Card Action: confirm_registration → MCP Tool: his.createPaymentOrder → Mock HIS 创建 orderId → 返回 qrCodeBase64 / qrContent → 创建 payment-qrcode 卡片 ``` 支付卡片: ```json { "cardKey": "payment-qrcode", "data": { "orderId": "PAY202606020001", "amount": 25.0, "qrContent": "demo-payment://PAY202606020001", "expireSeconds": 300, "debugAction": "mock_paid" } } ``` 演示中可以提供一个“模拟支付完成”按钮,但必须向团队明确:这只是 Mock 支付,不是实际支付。 --- ## 4.8 支付成功后创建预约 ```text 用户点击模拟支付完成 → MCP Tool: his.mockPaymentPaid → 前端轮询或主动触发 queryPaymentStatus → 支付成功 → MCP Tool: his.createAppointment(lockId, orderId) → Mock HIS 消耗号源 → 生成 appointmentId → 创建 appointment-success 卡片 ``` 挂号成功卡片: ```json { "cardKey": "appointment-success", "data": { "appointmentId": "APT202606020001", "patientName": "张三", "departmentName": "神经内科", "doctorName": "李明", "visitDate": "2026-06-05", "timeSlot": "09:30-09:45", "room": "门诊三楼 302 诊室", "queueNo": "A023", "tips": "请提前 15 分钟到诊区签到候诊" } } ``` --- # 五、推荐的 Dify Workflow 设计 ## 5.1 不建议一开始拆太多 Workflow 为了下周演示稳定,建议先做 **1 个 Dify App + 3 个 Workflow 场景分支**,而不是做一堆互相跳转的 App。 ### Dify App ```text outpatient-registration-agent ``` ### 内部分支 | 分支 | 负责 | | ----------------------- | ------------------------- | | `registration_intake` | 判断是否挂号、建档、查医生、查科室 | | `triage_recommendation` | 症状追问、科室推荐、风险提示 | | `registration_reply` | 围绕卡片动作生成自然语言说明、失败解释、下一步引导 | ## 5.2 Dify 不负责的内容 | 内容 | 归属 | | ------- | ------------------------ | | 是否有患者身份 | OpenPlatform / TaskState | | 患者建档写入 | Card Runtime + MCP | | 查询医生排班 | MCP Tool | | 锁号 | Card Runtime + MCP | | 支付订单 | Card Runtime + MCP | | 预约创建 | Card Runtime + MCP | | 幂等 | Card Runtime / MCP | | 审计 | OpenPlatform | | 任务状态 | TaskStateService | ## 5.3 Dify 输出必须结构化 Dify 每次输出必须包含: ```json { "answer": "我先为您推荐合适的科室。", "taskUpdate": { "taskType": "REGISTRATION", "currentStep": "RECOMMEND_DEPARTMENT" }, "cards": [ { "cardKey": "department-selection", "cardData": {} } ], "safety": { "riskLevel": "LOW", "needEmergencyWarning": false } } ``` OpenPlatform 必须对这个输出做校验。不能让 Dify 随便输出一个 JSON,前端就直接渲染。 --- # 六、AgentRouter 的确定实现 ## 6.1 路由优先级 ```text 1. 当前是否有 activeTask 2. 当前是否有 waitingCard 3. 用户是否明确取消/切换任务 4. 确定性规则:挂号、建档、医生、科室、支付、取消 5. DeepSeek 意图分类 6. 低置信度澄清 ``` ## 6.2 路由示例 | 用户输入 | 当前状态 | 路由结果 | | ------ | ------------- | ----------------------------- | | 我要挂号 | 无任务 | REGISTRATION_START | | 头疼三天 | 无任务 | SYMPTOM_TRIAGE | | 下午 | 正在选时间 | SELECT_TIME_SLOT,不调用 DeepSeek | | 换个医生 | 已选择医生 | 回退到 SELECT_DOCTOR | | 取消 | 任意 activeTask | CANCEL_TASK,释放号源 | | 支付了吗 | 等待支付 | QUERY_PAYMENT_STATUS | | 医保能报销吗 | 挂号中 | 临时 FAQ,不中断挂号;回答后提示继续 | | 胸痛喘不上气 | 任意 | EMERGENCY_WARNING,提示急诊/人工 | ## 6.3 DeepSeek 分类 Prompt 重点 DeepSeek 分类器不要写成聊天助手,而要写成严格分类器: ```text 你是医院统一入口客户端的意图分类器。 只输出 JSON。 不要回答用户问题。 不要编造医院信息。 不要判断挂号成功。 如果用户处于 activeTask,优先根据 currentStep 解释用户输入。 ``` --- # 七、核心 Case 验证清单 下周要经得起使用,至少要覆盖这些 case。 ## 7.1 正向链路 | Case | 输入 | 预期 | | ----- | --------- | ----------- | | 直接挂号 | 我要挂号 | 追问症状或科室 | | 症状挂号 | 我头疼三天想看病 | 推荐科室 | | 指定科室 | 我要挂神经内科 | 展示神经内科医生 | | 指定医生 | 我要挂李明医生 | 查询该医生排班 | | 指定时间 | 我要明天上午 | 筛选明天上午号源 | | 已建档患者 | 输入手机号/身份证 | 识别已有患者,跳过建档 | | 新患者 | 未查到患者 | 引导建档 | | 支付成功 | 扫码/模拟支付 | 创建预约并返回成功卡 | ## 7.2 异常链路 | Case | 风险 | 处理 | | ----------- | ------------ | ---------------------- | | 无号源 | 医生无可用号 | 推荐其他医生/日期 | | 号源锁过期 | 用户支付太慢 | 释放号源,提示重新选择 | | 重复点击确认 | 创建多条预约 | 幂等返回同一结果 | | 支付失败 | 订单未支付 | 提示重试/重新生成二维码 | | 建档信息缺失 | 无法创建 patient | 返回字段校验卡片 | | 用户中途取消 | 锁号未释放 | 释放 lockId,任务 cancelled | | 用户切换科室 | 状态混乱 | 回退到科室选择并清理后续状态 | | 急症描述 | 医疗风险 | 不走普通挂号,提示急诊/人工 | | Dify 输出异常 | 卡片无法解析 | 降级文本提示 + traceId | | Mock HIS 超时 | 工具失败 | 返回可解释错误,不假装成功 | --- # 八、演示范围必须冻结 ## 8.1 下周必须做 | 模块 | 必做内容 | | ---------------- | --------------------------------------- | | Mock HIS | 独立极简服务,使用 SQLite,覆盖患者、科室、医生、排班、号源、锁号、Mock 支付、预约 | | MCP Tool Service | 注册并调用 Mock HIS 工具,写操作带幂等键和 traceId | | OpenPlatform | `/agent/chat/stream`、会话、任务状态、路由、Dify 调用、卡片创建 | | AgentRouter | activeTask / waitingCard 优先,规则兜底,必要时 DeepSeek 分类 | | Card Runtime MVP | 卡片实例、卡片动作、幂等、状态更新、下一张卡片返回 | | Dify | 一个挂号智能体 App,支持症状追问、科室推荐和失败解释,不直接写 HIS | | Web 客户端 | 对话、卡片、二维码、模拟支付、成功页,所有业务按钮走 Card Action | | 测试脚本 | 至少 20 个 case,覆盖正向和异常 | ## 8.2 下周不要做 | 暂缓项 | 原因 | | ------------------ | ------------------------- | | 真实 HIS 对接 | 你们现在没有接口资料 | | 真实支付 | 周期和合规风险高 | | 医保结算 | 复杂度远超演示需要 | | 机器人硬件联调 | 会拖慢挂号闭环 | | 自助机读卡/打印 | 可用 Web 表单和二维码模拟 | | 完整 Device Registry | 下周只需固定演示设备和默认 deviceContext | | 完整计费账本 | 先记录 usage/audit,不做真实能力值扣费 | | 多院多租户 | 演示只做单医院单项目 | | 舌诊/报告解读混入 | 容易稀释挂号主线 | --- # 九、最终架构流程 ```mermaid flowchart TD A["患者在统一入口 Web 输入诉求"] --> B["OpenPlatform /agent/chat/stream"] B --> C["加载 conversation 和 activeTask"] C --> D{"是否有活跃任务"} D -->|是| E["按任务状态继续流程"] D -->|否| F["规则 + DeepSeek 意图分类"] F --> G["创建 REGISTRATION task"] E --> H["调用 Dify 挂号智能体"] G --> H H --> I["Dify 输出 answer + card suggestion"] I --> J["OpenPlatform 校验输出"] J --> K["Card Runtime 创建卡片实例"] K --> L["Web 客户端展示卡片"] L --> M["用户点击卡片动作"] M --> N["Card Runtime 校验幂等和状态"] N --> O["MCP Tool Service 调用 Mock HIS"] O --> P["Mock HIS 返回业务结果"] P --> Q["更新 task/card/conversation"] Q --> R{"流程是否完成"} R -->|否| H R -->|是| S["返回挂号成功卡片"] ``` --- # 十、推荐技术实现 ## 10.1 Mock HIS 建议: ```text Spring Boot 3 + SQLite ``` 这里明确改为 SQLite,不使用 MySQL。原因: 1. 下周演示目标是验证真实业务语义,不是验证数据库运维能力。 2. SQLite 单文件部署,方便 Claude/DeepSeek/交付人员本地启动和重置演示数据。 3. Mock HIS 是可替换的演示服务,不进入 AI 中台后端 Maven reactor,也不作为长期生产 HIS 数据库。 4. 后续真实 HIS 接入通过 `HospitalAdapter` 替换,不依赖 Mock HIS 的数据库选型。 SQLite 必须仍然模拟真实 HIS 语义:号源锁定、锁过期释放、支付状态、预约创建、幂等键、剩余号源扣减都要真实落库,不能用内存随机数据。 ### 服务名 ```text emoon-mock-his ``` ### 部署 ```text docker-compose: - emoon-mock-his - emoon-ai-platform - dify - emoon-terminal-web ``` `emoon-mock-his` 挂载一个 SQLite 数据文件,例如 `./data/mock-his.db`。演示前提供 seed 脚本初始化科室、医生、排班、号源和示例患者;需要重置演示环境时删除或重新生成该 db 文件即可。 ### SQLite 表和约束 | 表 | 必要约束 | | --- | --- | | `his_patient` | `identity_no`、`phone` 至少一个唯一索引,避免重复建档 | | `his_department` | `department_code` 唯一 | | `his_doctor` | 关联 `department_id`,包含职称、专长、诊室 | | `his_schedule` | 关联医生和日期,包含午别、价格 | | `his_slot` | 关联排班,包含 `total_count`、`remaining_count`、`version` | | `his_slot_lock` | `lock_id` 唯一,包含 `expires_at`、`status`、`idempotency_key` | | `his_payment_order` | `order_id` 唯一,包含 `mock:true`、`status`、`qr_content` | | `his_appointment` | `appointment_id` 唯一,`idempotency_key` 唯一 | 号源锁定和预约创建要用 SQLite 事务包住。`his_slot.remaining_count` 扣减必须和锁记录/预约记录在同一事务中完成,避免重复点击或并发演示时生成多条预约。 ### Mock 支付口径 Mock 支付只允许返回 `demo-payment://orderId=...` 或二维码图片内容,不允许使用微信、支付宝、医保等真实品牌或真实支付文案。前端必须展示“联调演示 / Mock 支付”标识。 --- ## 10.2 AI 中台模块 建议最小新增/改造: | 模块 | 类 | | ----------- | ----------------------------------------- | | 路由 | `AgentRouterService` | | 分类 | `DeepSeekIntentClassifier` | | 任务 | `TaskStateService` | | Dify | `DifyAgentEngine` | | 输出校验 | `DifyOutputNormalizer` | | 卡片 | `CardInstanceService`、`CardActionService` | | 工具 | `McpToolRegistry`、`McpToolInvokeService` | | HIS Adapter | `MockHisAdapter` | | 支付 | `MockPaymentTool` | 你们现有中台工程已经具备 `AgentEngine` 抽象、Mock 引擎、OpenPlatform 对话入口、项目签名鉴权、会话记录、初步卡片表等基础,但缺口是 `DifyAgentEngine` 尚未进入统一主链路、MCP 工具协议未形成生产工具集、卡片动作闭环不足。下周工作应优先补这几项,而不是扩展业务场景。 --- # 十一、演示脚本建议 ## 11.1 标准演示链路 ```text 用户:我头疼三天,还有点恶心,想挂号。 AI:我先了解一下情况……是否伴随发热、视物模糊? 用户:没有发热,就是头痛恶心。 AI:建议优先选择神经内科,也可选择普通内科。 用户点击:神经内科。 AI:以下是神经内科可预约医生。 用户点击:李明 主任医师。 AI:以下是可预约时间。 用户点击:明天 09:30。 AI:请确认挂号信息。 用户点击:确认挂号。 AI:请扫码支付挂号费 25 元。 用户点击:模拟支付完成。 AI:挂号成功,预约号 A023,请明天 09:15 到门诊三楼神经内科候诊区签到。 ``` ## 11.2 甲方可能追问 | 甲方问题 | 建议回答 | | ------------- | ------------------------------------------------------------ | | 这是真实 HIS 吗? | 目前是 Mock HIS,用于演示完整接口闭环;真实项目会替换为医院 HIS Adapter,AI 中台和前端不需要大改 | | 支付是真的吗? | 当前是模拟支付二维码,演示业务状态流转;真实支付会接医院现有支付平台 | | AI 会不会乱挂号? | 不会。AI 只负责理解诉求和生成建议,挂号、锁号、支付都必须经过卡片确认和后端工具调用 | | 患者信息安全吗? | 建档信息不进入 Dify,由后端卡片动作链路提交到 HIS;Dify 只处理必要上下文 | | Dify 出错怎么办? | OpenPlatform 有输出校验和降级;卡片必须通过 schema 校验后才会落库和展示 | | 后续能接真实 HIS 吗? | 可以。Mock HIS 和真实 HIS 都通过同一套 MCP Tool / HospitalAdapter 抽象接入 | --- # 十二、最终判定 这个需求可以做,而且下周演示可行。但必须按下面的边界执行: 1. **Mock HIS 先行**,覆盖患者、科室、医生、排班、号源、支付、预约。 2. **AI 中台作为唯一入口**,前端不直连 Dify,不直连 HIS。 3. **DeepSeek 只做兜底意图识别和槽位抽取**,不做主流程控制。 4. **Dify 负责编排和话术**,不直接完成建档、锁号、支付、预约写操作。 5. **Card Runtime 承接所有关键业务动作**,点击卡片后再通过 MCP 调 Mock HIS。 6. **MCP Tool Service 是 HIS 唯一工具出口**,查询可以给 Dify 用,写操作必须卡片确认。 7. **支付明确为 Mock**,只演示二维码、订单状态和预约确认流程。 8. **演示范围只做挂号闭环**,不要混入舌诊、报告、机器人硬件、自助机读卡等支线。 冻结后的方案一句话: > **统一入口 Web 端负责像蚂蚁阿福一样与患者对话;OpenPlatform 负责会话、任务、路由、卡片和审计;DeepSeek 负责新任务意图分类;Dify 负责挂号流程的话术和分支编排;Card Runtime 负责所有确认动作;MCP Tool Service 负责调用 Mock HIS;Mock HIS 负责模拟真实挂号业务状态。**