# AI Agent 突破口设计规格 > **日期:** 2026-05-18 | **状态:** Draft | **范围:** erp-ai Agent 改造 > **总工期:** 20-27 天 | **方案:** ReAct Agent + Function Calling ## 1. 背景与动机 ### 1.1 当前状态 HMS 健康管理平台综合评分 6.8/10,功能完整度 87%,但 AI 能力"有弹药没上膛": - erp-ai 有 3 个 Provider(Claude/OpenAI/Ollama)+ `AiProvider` trait 抽象 - SSE 流式分析已实现(化验解读、趋势分析、报告摘要),但 4 个 SSE 端点无 UI 入口 - Copilot 引擎(风险评分、规则引擎、洞察服务)已实现但未集成到用户触达层 - 知识库框架(structured_source、KDIGO 规则)已搭建 - 成本/配额管控(usage、quota、cache)已就绪 - v2 架构设计已规划 RAG、事件驱动管线、两级缓存 - `erp-core` 已有 `HealthDataProvider` trait(含 `get_lab_report`/`get_vital_signs`/`get_patient_summary`/`get_trend_analysis_data`),已注入 `AiState` **AI 客服"小华"现状**:一个硬编码 system prompt + 最近 10 条历史拼成上下文的简单问答,无法识别意图、无法查询数据、无法触发分析。 ### 1.2 核心问题 1. **AI 能力断裂** — 后端分析能力和前端用户触达之间没有桥接 2. **AI 客服能力单调** — 简单问答无法胜任真实客户服务场景(情绪安抚、医疗科普、引导到院) 3. **缺少差异化竞争力** — 当前系统是工具,不是"主动关怀引擎" ### 1.3 设计目标 - **打通现有 AI 链路** — 通过 Agent Tool 机制将 SSE 分析、Copilot 引擎、知识库串联到对话入口 - **升级 AI 客服为多策略 Agent** — 意图识别→策略选择(安抚/科普/推荐/预警/引导)→ Tool 调用→自然回复 - **纯自研实现** — 在 erp-ai 内用 ReAct Agent 模式,不依赖 Dify 等外部平台 - **复用现有基础设施** — Provider 抽象、配额管控、缓存、知识库全部复用 --- ## 2. 整体架构 ### 2.1 Agent 核心循环 ``` ┌─────────────────────────────────────────────────────┐ │ Chat Handler │ │ 接收用户消息 + 会话历史(DB 持久化) │ └──────────────────────┬──────────────────────────────┘ │ ┌──────────────────────▼──────────────────────────────┐ │ Agent Orchestrator │ │ 1. 组装 System Prompt(角色 + 可用 Tools 描述) │ │ 2. 发送给 LLM,获取 Tool Call 或最终回复 │ │ 3. 如果是 Tool Call → 执行 Tool → 结果加入上下文 │ │ 4. 重复 2-3,直到 LLM 给出最终回复 │ │ 5. 安全循环上限:最多 5 轮 Tool Call │ └──────────────────────┬──────────────────────────────┘ │ ┌──────────────────────▼──────────────────────────────┐ │ Tool Registry │ │ 注册所有可用 Tool,按权限/场景过滤 │ │ ┌──────────┐ ┌───────────┐ ┌───────────────┐ │ │ │ 健康数据 │ │ AI 分析 │ │ 知识库 │ │ │ │ 查询工具 │ │ 触发工具 │ │ 检索工具 │ │ │ ├──────────┤ ├───────────┤ ├───────────────┤ │ │ │ 预约工具 │ │ 服务推荐 │ │ 风险预警 │ │ │ │ │ │ 工具 │ │ 工具 │ │ │ └──────────┘ └───────────┘ └───────────────┘ │ └─────────────────────────────────────────────────────┘ ``` ### 2.2 关键设计决策 | 决策 | 选择 | 原因 | |------|------|------| | Agent 状态管理 | Orchestrator 无状态,会话由 Handler 管理 | 简化 Orchestrator 职责,便于测试 | | Tool 执行模型 | 同步阻塞,单轮内多个 Tool Call 并行(`futures::join_all`,单 Tool 超时 10s) | LLM 返回多个 call 时并行执行,减少延迟 | | Provider 扩展 | 在 `AiProvider` trait 新增 `generate_with_tools` 方法,保留原 `generate` 不变 | 不破坏现有分析端点调用,新旧路径并行 | | 跨 crate 数据访问 | 扩展现有 `HealthDataProvider` trait,新增 `get_appointments`/`get_medication` 方法 | erp-core 已有该 trait 且已注入 AiState,避免重复 | | 安全循环上限 | 单次对话最多 5 轮 Tool Call,达到上限时强制 LLM 生成最终回复 | 防止无限循环,控制成本 | | 分析调用模式 | Agent 内走非流式同步调用 | Agent 需要拿到完整结果再决策 | | SSE 语义 | SSE 仅流式输出 Agent 最终回复,Tool Call 过程不在 SSE 中传输 | 前端实现简单,用户体验清晰 | --- ## 3. Tool 系统 ### 3.1 AgentTool Trait ```rust #[async_trait] pub trait AgentTool: Send + Sync { fn name(&self) -> &str; fn description(&self) -> &str; fn parameters_schema(&self) -> serde_json::Value; // JSON Schema async fn execute(&self, ctx: &ToolContext, params: serde_json::Value) -> ToolResult; } pub struct ToolContext { pub tenant_id: Uuid, pub user_id: Uuid, pub patient_id: Option, pub db: DatabaseConnection, // erp-ai 本地表(sessions/messages/logs) pub health_provider: Arc, // erp-health 数据(已有注入) } pub struct ToolResult { pub output: String, pub display_hint: Option, } ``` ### 3.2 跨 Crate 数据访问架构 erp-ai 不直接依赖 erp-health(保持模块边界)。数据查询类 Tool 通过已有的 `HealthDataProvider` trait 访问健康数据。 **现有 `HealthDataProvider` trait(erp-core,已注入 AiState)**: ```rust // 已有的方法 — 直接复用 get_lab_report(tenant_id, report_id) → LabReportDto get_vital_signs(tenant_id, patient_id, metrics, range) → Vec get_patient_summary(tenant_id, patient_id) → PatientSummaryDto get_trend_analysis_data(tenant_id, patient_id, metrics, range) → TrendAnalysisDto // 新增方法 — Phase 0 扩展 get_appointments(tenant_id, patient_id) → Vec get_medication_list(tenant_id, patient_id) → Vec ``` **优势**:现有的 DTO 已做 PII 脱敏(PatientSummaryDto 用 age_group/sex 而非姓名/身份证),Tool 无需额外脱敏处理。 **注册机制**:`AiState` 已持有 `Arc`(state.rs:25),Tool 通过 `ToolContext.health_provider` 访问。 ### 3.3 DisplayHint 定义 `DisplayHint` 用于给前端提供渲染提示,让 Tool 返回的数据不仅作为 LLM 上下文,还能以富消息形式展示给用户: ```rust pub enum DisplayHint { /// 体征数据卡片 — 前端渲染为小图表 VitalCard { indicator_type: String, values: Vec<(DateTime, f64)>, unit: String }, /// 化验报告摘要卡片 LabReportCard { report_date: DateTime, abnormal_count: usize }, /// 操作确认 — 前端渲染为确认按钮 ActionConfirm { action_type: String, summary: String, confirm_payload: serde_json::Value }, /// 风险等级提示 RiskAlert { level: String, message: String }, /// 纯文本(默认) Text, } ``` ### 3.4 Tool 清单 #### 第一类:数据查询(只读,通过 HealthDataProvider 访问) | Tool 名称 | 功能 | 对接现有能力 | |-----------|------|-------------| | `query_patient_vitals` | 查询患者最近体征数据(血压/血糖/心率等) | `get_vital_signs` | | `query_lab_reports` | 查询患者最近化验报告及指标 | `get_lab_report` | | `query_patient_profile` | 查询患者基本信息、病史、过敏史 | `get_patient_summary` | | `query_appointments` | 查询患者预约记录 | `get_appointments`(**新增方法**) | | `query_medication` | 查询患者当前用药情况 | `get_medication_list`(**新增方法**) | #### 第二类:AI 分析触发(调用 erp-ai 现有能力) | Tool 名称 | 功能 | 对接现有能力 | |-----------|------|-------------| | `analyze_lab_report` | 分析指定化验报告,返回异常指标解读 | `analysis_service`(非流式调用) | | `analyze_health_trends` | 分析体征趋势变化,识别异常模式 | `get_trend_analysis_data` + `analysis_service` | | `get_health_insights` | 获取患者当前风险洞察和 AI 建议 | `copilot_engine` + `insight_service` | #### 第三类:知识与服务(对话策略支撑) | Tool 名称 | 功能 | 对接现有能力 | |-----------|------|-------------| | `search_medical_knowledge` | 检索医疗知识库(疾病科普、指标解释) | `knowledge_structured_source` | | `recommend_services` | 根据症状/需求推荐科室或服务 | 新增,基于规则 + 知识库 | | `check_alert_rules` | 检查是否触发告警阈值 | `local_rules_engine` + `ai_risk_threshold` | #### 第四类:行动(写入操作,需更高权限) | Tool 名称 | 功能 | 对接现有能力 | |-----------|------|-------------| | `create_appointment` | 帮用户预约挂号 | `appointment_service` | | `transfer_to_human` | 转接人工客服/值班医生 | 新增,WebSocket 通知 | ### 3.5 权限与安全 - **数据查询 Tool**:自动注入 `tenant_id` + `patient_id` 过滤,LLM 无法绕过多租户隔离 - **分析触发 Tool**:走现有配额管控(`QuotaService`) - **行动 Tool**:需额外权限标记,System Prompt 约束 LLM 只在用户明确请求时调用 - **数据脱敏**:`HealthDataProvider` 返回的 DTO 已做 PII 脱敏(用 age_group/sex 而非真名/身份证),Tool 层无需额外处理 - **审计日志**:每次 Tool Call 记录到 `ai_tool_call_logs` 表 ### 3.6 权限码声明 现有权限码 `ai.chat.send` 保留用于发送消息。新增以下权限码: | 权限码 | 说明 | 适用端点 | |--------|------|----------| | `ai.chat.session.list` | 查看会话列表 | `GET /ai/chat/sessions` | | `ai.chat.session.manage` | 创建/关闭会话 | `POST/DELETE /ai/chat/sessions` | | `ai.chat.session.history` | 查看会话消息历史 | `GET /ai/chat/sessions/{id}/messages` | | `ai.chat.send` | 发送消息(触发 Agent) | `POST /ai/chat/sessions/{id}/messages`(已存在) | 行动类 Tool(`create_appointment`、`transfer_to_human`)不单独声明权限,由 Agent 内部根据用户角色判断。 --- ## 4. 多策略对话流 ### 4.1 策略引导机制 Agent 通过 System Prompt 定义 5 种策略方向,LLM 根据用户表达的内容和情绪自主选择和切换: 1. **【情绪安抚】** 用户焦虑/恐惧/沮丧时 → 先共情,用通俗语言解释,分享积极案例 2. **【医疗科普】** 用户询问指标/疾病知识时 → 调用 `search_medical_knowledge` 获取准确信息 3. **【服务推荐】** 用户有就医需求时 → 调用 `recommend_services` 推荐科室,主动提议预约 4. **【风险预警】** 症状或数据异常时 → 调用分析 Tool 评估风险,明确告知风险等级 5. **【引导到院】** 明确就诊意向或高风险时 → 帮助预约,提供科室信息,必要时转接人工 策略之间不互斥,一轮对话中可自然切换。 ### 4.2 System Prompt 结构 ``` 你是 HMS 健康管理平台的 AI 健康顾问"小华"。 ## 核心策略 根据用户表达的内容和情绪,自然地采用以下策略方向: 1. 【情绪安抚】当用户表达焦虑、恐惧、沮丧时: - 先共情认可感受,不急于给建议 - 用通俗语言解释,避免医学术语 - 分享积极案例,降低恐惧感 2. 【医疗科普】当用户询问指标含义、疾病知识时: - 调用 search_medical_knowledge 获取准确信息 - 用比喻和类比让老年患者也能理解 - 强调"具体请以医生诊断为准" 3. 【服务推荐】当用户表达就医需求或身体不适时: - 调用 recommend_services 推荐合适科室 - 调用 query_appointments 查看已有预约 - 主动提出帮用户预约 4. 【风险预警】当用户描述的症状或数据异常时: - 调用 analyze_health_trends 或 get_health_insights 评估风险 - 明确告知风险等级和需要注意的事项 - 高风险时建议尽快就医 5. 【引导到院】当用户有明确就诊意向或高风险预警时: - 调用 create_appointment 帮助预约 - 提供科室位置、出诊医生信息 - 必要时调用 transfer_to_human 转接值班医生 ## 策略不是互斥的,你可以在一轮对话中自然切换。 ## 永远不要:推荐具体药物、给出明确诊断、替代医生建议。 ``` ### 4.3 对话流示例 **场景:老年患者说"最近头有点晕"** ``` [用户] 最近头有点晕,不知道怎么回事 [Agent 思考] 用户描述头晕症状,可能是高血压或其他问题。 应该:1) 查看最近体征数据 2) 安抚情绪 [Agent 调用] query_patient_vitals → 获取最近血压数据 [Tool 返回] 最近7天平均血压 155/95,较上月升高 10mmHg [Agent 思考] 血压偏高且趋势上升,存在风险。 应该:1) 分析趋势 2) 安抚 + 预警 [Agent 调用] analyze_health_trends → 分析血压趋势 [Tool 返回] 血压持续升高趋势,建议关注,风险等级:中等 [Agent 回复] 张爷爷,我看了您最近的血压记录,确实比之前高了一些。 头晕可能和血压升高有关。不过您先别担心,这个趋势我们 可以通过调整生活方式来改善。我建议您先来看看医生, 要不要我帮您预约个心内科的号? ``` ### 4.4 会话记忆 - **短期记忆**:当前会话完整对话历史,DB 持久化 `ai_chat_messages` 表 - **长期记忆**:用户画像摘要(偏好、常见问题、健康关注点),存储在 `ai_user_profiles` 表,每次新会话加载 - **上下文窗口管理**:历史消息按重要性截断,保留最近 10 轮 + 关键上下文摘要 --- ## 5. 系统集成 ### 5.1 后端集成点 ``` 现有能力 Agent 集成方式 ───────── ────────────── HealthDataProvider trait → Tool 数据查询层(已有注入,新增 2 方法) analysis_service (SSE) → Tool: analyze_lab_report / analyze_health_trends 非 SSE 模式调用,直接拿结果返回给 Agent copilot_engine (风险评分) → Tool: get_health_insights 调用 scoring + rules,返回结构化风险信息 knowledge (structured) → Tool: search_medical_knowledge 查询 KDIGO 规则、科室指南、科普文章 local_rules_engine → Tool: check_alert_rules 评估当前数据是否触发告警 quota_service → Agent Orchestrator 内部调用 每轮 Tool Call 前检查配额 usage_service → Agent Orchestrator 内部调用 记录每轮 token 消耗 cache_service → 分析类 Tool 内部复用 相同参数的重复分析走缓存 ``` ### 5.2 数据模型新增 #### ai_chat_sessions — AI 会话表 > 与现有 `copilot_chat_logs` 表的关系:`copilot_chat_logs` 记录 Copilot 内部对话(AI 分析引擎之间的通信),服务于风险洞察和自动分析。`ai_chat_sessions` / `ai_chat_messages` 记录用户与 AI 客服的面向用户对话。两者并存,数据不迁移。 ```sql CREATE TABLE ai_chat_sessions ( id UUID PRIMARY KEY, tenant_id UUID NOT NULL, user_id UUID NOT NULL, patient_id UUID, title VARCHAR(255), status VARCHAR(20) NOT NULL DEFAULT 'active', -- active / closed metadata JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID, updated_by UUID, deleted_at TIMESTAMPTZ, version INTEGER NOT NULL DEFAULT 1 ); ``` #### ai_chat_messages — AI 聊天消息表 ```sql CREATE TABLE ai_chat_messages ( id UUID PRIMARY KEY, tenant_id UUID NOT NULL, session_id UUID NOT NULL REFERENCES ai_chat_sessions(id), role VARCHAR(20) NOT NULL, -- user / assistant / tool content TEXT, tool_calls JSONB, -- assistant 消息中的 tool call 列表 tool_call_id VARCHAR(100), -- tool 消息的关联 ID token_count INTEGER, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID, updated_by UUID, deleted_at TIMESTAMPTZ, version INTEGER NOT NULL DEFAULT 1 ); ``` #### ai_tool_call_logs — AI 工具调用日志 > 此表为仅追加日志(append-only),记录后不更新,因此省略 `updated_at`/`updated_by`/`version`/`deleted_at`。 ```sql CREATE TABLE ai_tool_call_logs ( id UUID PRIMARY KEY, tenant_id UUID NOT NULL, session_id UUID NOT NULL, message_id UUID NOT NULL, tool_name VARCHAR(100) NOT NULL, parameters JSONB, result_summary TEXT, execution_ms INTEGER, success BOOLEAN NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID ); ``` #### ai_user_profiles — 用户长期画像(长期记忆) > `created_by`/`updated_by` 省略,因为此表由 Agent 自动维护而非用户手动创建。 ```sql CREATE TABLE ai_user_profiles ( id UUID PRIMARY KEY, tenant_id UUID NOT NULL, user_id UUID NOT NULL, preferences JSONB, -- 用户偏好(如:偏好简洁回复、关心血压问题) health_interests TEXT[], -- 健康关注点(如:高血压、糖尿病) frequent_topics TEXT[], -- 常见咨询主题 personality_summary TEXT, -- AI 生成的用户画像摘要 last_updated_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ, version INTEGER NOT NULL DEFAULT 1, UNIQUE(tenant_id, user_id) ); ``` 每次会话结束时,Agent 自动更新用户画像摘要。新会话开始时加载注入 System Prompt。 ### 5.3 PII 脱敏规范 `HealthDataProvider` 返回的 DTO 已做 PII 脱敏(第 8 行注释:"返回的 DTO 已脱去 PII"),Tool 层无需额外处理。 对于 Tool 中可能出现的补充数据,统一通过 `sanitize_for_llm()` 函数处理: | 字段类型 | 脱敏方式 | 示例 | |----------|----------|------| | 患者姓名 | 保留姓氏 + 称呼 | "张爷爷"(Agent 用称呼,不用真名) | | 身份证号 | 不传给 LLM | Tool 层过滤 | | 手机号 | 不传给 LLM | 同上 | | 出生日期 | 转为年龄 | "68 岁" | | 医疗数据 | 正常传递 | 血压值、化验指标等不脱敏 | ### 5.4 Provider Function Calling 适配 现有 `AiProvider` trait 的 `generate()` 方法保持不变(现有分析端点继续使用)。新增 `generate_with_tools()` 方法: ```rust #[async_trait] pub trait AiProvider: Send + Sync { // 保留 — 现有分析端点继续使用 async fn stream_generate(&self, req: GenerateRequest) -> AiResult> + Send>>>; async fn generate(&self, req: GenerateRequest) -> AiResult; fn name(&self) -> &str; async fn health_check(&self) -> AiResult; // 新增 — Agent 专用,支持 Function Calling async fn generate_with_tools( &self, messages: Vec, tools: Vec, options: GenerateOptions, ) -> AiResult { // 默认实现:不支持 FC 的 Provider 返回错误 Err(AiError::UnsupportedOperation("Function Calling not supported".into())) } } pub struct ChatMessage { pub role: MessageRole, // User / Assistant / Tool pub content: String, pub tool_calls: Option>, pub tool_call_id: Option, } pub struct ToolDefinition { pub name: String, pub description: String, pub parameters: serde_json::Value, } pub struct ToolCall { pub id: String, pub name: String, pub arguments: serde_json::Value, } pub struct AgentGenerateResponse { pub content: Option, pub tool_calls: Option>, pub usage: Option, } ``` **各 Provider 适配工作量**: - **Claude**:Anthropic API 使用 `tool_use`/`tool_result` 内容块(1 天) - **OpenAI**:使用 `function` 或 `tool` 类型消息,相对标准(0.5 天) - **Ollama**:若模型不支持 FC,返回 `UnsupportedOperation`,Orchestrator 降级为纯 Prompt 模式(0.5 天) ### 5.5 API 设计 ``` POST /api/v1/ai/chat/sessions — 创建会话 GET /api/v1/ai/chat/sessions — 会话列表 GET /api/v1/ai/chat/sessions/{id} — 会话详情 DELETE /api/v1/ai/chat/sessions/{id} — 关闭会话(软删除) POST /api/v1/ai/chat/sessions/{id}/messages — 发送消息(触发 Agent) GET /api/v1/ai/chat/sessions/{id}/messages — 消息历史 ``` 发送消息端点支持 SSE 流式输出(仅 Agent 最终回复)和 JSON 响应两种模式。 --- ## 6. 前端演进 ### 6.1 消息类型扩展 - **文本消息** — 正常对话内容 - **数据卡片** — "这是您最近的血压趋势" + 小图表(通过 `DisplayHint::VitalCard` 触发) - **操作确认** — "帮您预约了周三上午心内科,确认吗?" + 确认/取消按钮(通过 `DisplayHint::ActionConfirm` 触发) - **转接通知** — "正在为您转接值班医生..."(通过 `DisplayHint::RiskAlert` 触发) ### 6.2 会话管理 - 会话列表页(历史对话) - 新建会话 / 继续会话 - 历史从本地 Storage 迁移到 DB 持久化 ### 6.3 小程序 + Web 实现 **小程序**: - SSE 兼容:Taro 原生不支持 SSE,使用 `requestTask` 长连接或降级为轮询(Phase 2 明确分配 1 天处理) - 富消息渲染:基于 `DisplayHint` 类型分发到不同渲染组件 - 旧数据迁移:`ai-chat.ts` 中 `getLocalHistory()` 的本地 Storage 数据,首次打开新版本时一键上传到 DB **Web**: - AI 客服页面从零构建(无现有 UI),包含会话列表 + 聊天界面 + 富消息渲染 - 复用小程序的 API 模块(`services/ai-chat.ts`),UI 适配 Ant Design --- ## 7. 分阶段实施计划 ### Phase 0:基础设施(5-6 天) > 目标:Agent 核心循环跑通,能用一个 Tool 完成完整对话 | 任务 | 工作量 | |------|--------| | `AiProvider` trait 新增 `generate_with_tools` + Claude Provider 适配 | 1 天 | | OpenAI + Ollama Provider 适配 | 1 天 | | `AgentTool` trait + `ToolRegistry` + `ToolContext` + `DisplayHint` | 0.5 天 | | `AgentOrchestrator` ReAct 循环(含 5 轮上限强制终止逻辑) | 1 天 | | `HealthDataProvider` trait 扩展:新增 `get_appointments`/`get_medication_list` | 1 天 | | 数据库迁移:`ai_chat_sessions` + `ai_chat_messages` + `ai_tool_call_logs` + `ai_user_profiles` | 0.5 天 | | 实现 1 个 Tool:`query_patient_vitals`(验证端到端链路) | 0.5 天 | | 改造 `chat_handler`:接入 Orchestrator,替换原有简单逻辑 | 0.5 天 | | 单元测试 + 集成测试 | 0.5 天 | **交付标准**:Postman 调用 `/api/v1/ai/chat/sessions/{id}/messages`,Agent 能查到患者体征数据并自然回复。 ### Phase 1:Tool 扩展 + 策略 Prompt(5-7 天) > 目标:覆盖全部核心 Tool,多策略对话流生效 | 任务 | 工作量 | |------|--------| | 数据查询类 Tool(`query_lab_reports`、`query_patient_profile`、`query_appointments`、`query_medication`) | 1.5 天 | | AI 分析类 Tool(`analyze_lab_report`、`analyze_health_trends`、`get_health_insights`) | 2 天 | | 知识类 Tool(`search_medical_knowledge`、`recommend_services`、`check_alert_rules`) | 1.5 天 | | 多策略 System Prompt 设计 + 调优 | 1 天 | | 每轮 Tool Call 配额检查 + token 计量 | 0.5 天 | | 测试覆盖 | 1 天 | **交付标准**:模拟 5 种典型场景(安抚/科普/推荐/预警/引导到院),Agent 均能自主选择正确策略和 Tool。 ### Phase 2:前端升级 + 流式输出(7-9 天) > 目标:小程序 + Web 都有完整 AI 客服体验 | 任务 | 工作量 | |------|--------| | 后端:会话 CRUD API(创建/列表/历史消息) | 1 天 | | 后端:Agent 最终回复走 SSE 流式输出 | 1 天 | | 小程序:SSE 兼容层(Taro `requestTask` 或轮询适配) | 1 天 | | 小程序:会话列表页 + 消息历史页 + 富消息渲染 | 2 天 | | Web:AI 客服页面从零构建(会话列表 + 聊天界面 + 富消息) | 2 天 | | 数据卡片渲染(体征趋势小图表) | 1 天 | | 前端迁移:本地 Storage → DB 持久化 + 旧数据迁移脚本 | 0.5 天 | | 端到端测试 | 0.5 天 | **交付标准**:小程序打开 AI 客服,能自然对话,能看到数据卡片,能看到流式输出。 ### Phase 3:行动类 Tool + 人机协作(3-5 天) > 目标:AI 客服能帮用户预约、转接人工 | 任务 | 工作量 | |------|--------| | `create_appointment` Tool(带二次确认机制) | 1 天 | | `transfer_to_human` Tool + WebSocket 通知值班医护 | 2 天 | | 操作确认 UI(预约确认卡片、转接状态提示) | 1 天 | | 安全边界加固(行动类 Tool 权限标记、审计日志) | 0.5 天 | | 端到端测试 | 0.5 天 | **交付标准**:用户对 AI 说"帮我预约个号",AI 查时段→推荐→确认→创建预约,全流程跑通。 ### 总工期 ``` Phase 0 ████████████████ (5-6天) Phase 1 ████████████████ (5-7天) Phase 2 ████████████████████ (7-9天) Phase 3 ██████████ (3-5天) ──────────────────────── 合计 20-27 天 ``` 每个 Phase 结束后都有可演示的交付物。 --- ## 8. 故障处理与降级 | 故障场景 | 用户看到什么 | 处理方式 | |----------|-------------|----------| | 所有 Provider 不可用 | "小华暂时无法回复,请稍后再试" | 返回固定降级消息,记录到 usage_service | | Agent 循环超时(60s) | 已生成的部分回复 + "回复被中断,请重新提问" | SSE 断流 + 超时日志 | | Agent 达到 5 轮上限 | 正常回复(Orchestrator 追加 "请基于已有信息总结回复" 指令强制 LLM 结束) | 用户无感知,回复可能不够完整 | | 单个 Tool 执行超时(10s) | Agent 跳过该 Tool 继续推理 | ToolResult 返回错误摘要,Agent 可选择其他路径 | | Ollama 不支持 Function Calling | 自动降级为纯文本 Prompt 模式 | Provider 层返回 UnsupportedOperation,Orchestrator 将 Tool 描述注入 System Prompt | | LLM 返回无效 Tool Call | "抱歉,我刚才思考有误,请再说一次" | Orchestrator 捕获解析错误,返回重试提示 | --- ## 9. 风险与缓解 | 风险 | 概率 | 影响 | 缓解措施 | |------|------|------|----------| | Function Calling 格式跨 Provider 不统一 | 中 | 高 | Phase 0 就在 3 个 Provider 上验证,Ollama 降级方案已设计 | | LLM 幻觉(编造数据/错误诊断) | 高 | 严重 | System Prompt 强约束 + Tool 返回数据做事实校验 + 免责声明 | | Token 成本超预期 | 中 | 中 | 每轮配额检查 + 缓存复用 + 5 轮上限 | | Tool 执行超时 | 低 | 中 | 单个 Tool 超时 10s,总轮次超时 60s | | PII 泄露给 LLM | 低 | 严重 | HealthDataProvider DTO 已脱敏,Tool 层补充 sanitize_for_llm() |