Files
hms/docs/superpowers/specs/2026-05-18-ai-agent-breakthrough-design.md
iven 31771168dd docs(ai): Spec Review R2 修复 — 复用 HealthDataProvider + 新增 generate_with_tools
CRITICAL:
- 移除重复的 HealthDataQuery trait,扩展现有 HealthDataProvider(新增 2 方法)
- Provider 适配改为新增 generate_with_tools 方法,不破坏现有 generate 路径

IMPORTANT:
- 修复章节编号(全文重排为连续编号)
- ai_tool_call_logs 补充 created_by + 说明省略原因(append-only)
- ai_user_profiles 说明省略 created_by/updated_by 原因(Agent 自动维护)
- ToolContext 改为持有 Arc<dyn HealthDataProvider> 而非裸 db
- SSE 语义明确:仅流式输出最终回复
- 5 轮上限强制终止逻辑:追加总结指令让 LLM 正常结束
- GenerateRequest 不再破坏性修改,新旧路径并行
2026-05-18 01:57:16 +08:00

624 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 个 ProviderClaude/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<Uuid>,
pub db: DatabaseConnection, // erp-ai 本地表sessions/messages/logs
pub health_provider: Arc<dyn HealthDataProvider>, // erp-health 数据(已有注入)
}
pub struct ToolResult {
pub output: String,
pub display_hint: Option<DisplayHint>,
}
```
### 3.2 跨 Crate 数据访问架构
erp-ai 不直接依赖 erp-health保持模块边界。数据查询类 Tool 通过已有的 `HealthDataProvider` trait 访问健康数据。
**现有 `HealthDataProvider` traiterp-core已注入 AiState**
```rust
// 已有的方法 — 直接复用
get_lab_report(tenant_id, report_id) LabReportDto
get_vital_signs(tenant_id, patient_id, metrics, range) Vec<VitalSignDto>
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<AppointmentSummaryDto>
get_medication_list(tenant_id, patient_id) Vec<MedicationSummaryDto>
```
**优势**:现有的 DTO 已做 PII 脱敏PatientSummaryDto 用 age_group/sex 而非姓名/身份证Tool 无需额外脱敏处理。
**注册机制**`AiState` 已持有 `Arc<dyn HealthDataProvider>`state.rs:25Tool 通过 `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<Pin<Box<dyn Stream<Item = AiResult<String>> + Send>>>;
async fn generate(&self, req: GenerateRequest)
-> AiResult<GenerateResponse>;
fn name(&self) -> &str;
async fn health_check(&self) -> AiResult<bool>;
// 新增 — Agent 专用,支持 Function Calling
async fn generate_with_tools(
&self,
messages: Vec<ChatMessage>,
tools: Vec<ToolDefinition>,
options: GenerateOptions,
) -> AiResult<AgentGenerateResponse> {
// 默认实现:不支持 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<Vec<ToolCall>>,
pub tool_call_id: Option<String>,
}
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<String>,
pub tool_calls: Option<Vec<ToolCall>>,
pub usage: Option<TokenUsage>,
}
```
**各 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 1Tool 扩展 + 策略 Prompt5-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 天 |
| WebAI 客服页面从零构建(会话列表 + 聊天界面 + 富消息) | 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 层返回 UnsupportedOperationOrchestrator 将 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() |