- index.md: 迁移 123 个, 提交 577 次, 新增 AI 分析症状导航 4 条 - erp-ai.md: 新增 §4 Ollama 本地模型对接、已知限制、已修复 bug - erp-ai.md: 更新 SSE 流程图(预校验 + 缓存回放修复) - erp-ai.md: 3 个管理前端页面已实现
188 lines
7.1 KiB
Markdown
188 lines
7.1 KiB
Markdown
---
|
||
title: erp-ai AI 分析模块
|
||
updated: 2026-05-05
|
||
status: in-development
|
||
tags: [ai, llm, sse, health-analysis, ollama]
|
||
---
|
||
|
||
# erp-ai AI 分析模块
|
||
|
||
> 从 [[index]] 导航。关联: [[erp-health]] [[erp-core]] [[erp-server]]
|
||
>
|
||
> 设计规格: `docs/superpowers/specs/2026-04-25-erp-ai-module-design.md`
|
||
|
||
## 1. 设计决策
|
||
|
||
### 为什么需要 AI 模块?
|
||
|
||
健康管理平台需要为医护人员提供智能辅助分析能力:化验单解读、健康趋势分析、体检方案推荐、报告摘要生成。这些能力需要一个独立的模块来管理 AI 提供商、Prompt 模板和用量追踪。
|
||
|
||
### 核心架构选择
|
||
|
||
- **原生 Rust crate** — 与 erp-health 同级,直接调用数据库和事件总线
|
||
- **ErpModule trait 实现** — `AiModule` 在 erp-server 注册,路由挂载到 `/api/v1/ai`
|
||
- **SSE 流式响应** — 所有分析端点使用 Server-Sent Events 实时推送
|
||
- **多提供商支持** — AiProvider trait 抽象,已实现 Claude (Anthropic)、Ollama (本地)、OpenAI
|
||
- **数据脱敏** — 发送前自动移除患者 PII(姓名、身份证、手机号)
|
||
- **分析预校验** — 触发分析前校验数据完整性,items/sections 为空时直接返回 400
|
||
- **非对话化 Prompt** — 迁移 000123 更新所有 prompt 为结构化输出格式
|
||
|
||
## 2. 关键文件 + 数据流
|
||
|
||
### 目录结构
|
||
|
||
```
|
||
crates/erp-ai/
|
||
├── src/
|
||
│ ├── lib.rs ← 模块导出 (AiModule, AiState, AiError)
|
||
│ ├── module.rs ← ErpModule trait 实现, 6 权限码, 路由定义
|
||
│ ├── error.rs ← AiError → AppError (59 行)
|
||
│ ├── state.rs ← AiState { db, event_bus, provider }
|
||
│ ├── dto.rs ← 请求/响应 DTO (102 行)
|
||
│ ├── provider/
|
||
│ │ ├── mod.rs ← AiProvider trait 定义
|
||
│ │ └── claude.rs ← Claude SSE 流式实现 (227 行)
|
||
│ ├── service/
|
||
│ │ ├── analysis.rs ← AnalysisService 核心编排 (183 行)
|
||
│ │ ├── prompt.rs ← PromptService 模板渲染 (67 行)
|
||
│ │ └── usage.rs ← UsageService 用量追踪 (45 行)
|
||
│ ├── handler/
|
||
│ │ └── mod.rs ← 6 个 SSE + REST 端点 (340 行)
|
||
│ ├── prompt/
|
||
│ │ └── mod.rs ← Prompt 模板引擎 (25 行)
|
||
│ └── entity/
|
||
│ ├── ai_prompt.rs ← Prompt 模板 Entity
|
||
│ ├── ai_analysis.rs ← 分析结果 Entity
|
||
│ └── ai_usage.rs ← 用量日志 Entity
|
||
```
|
||
|
||
### 实体模型(6 个实体)
|
||
|
||
| 实体 | 用途 |
|
||
|------|------|
|
||
| ai_prompt | Prompt 模板(版本管理、激活/回滚) |
|
||
| ai_analysis | 分析结果记录(输入参数、输出摘要、token 用量) |
|
||
| ai_usage | 用量追踪(按租户/用户聚合) |
|
||
| ai_suggestion | AI 建议记录(风险分级、建议内容、状态跟踪) |
|
||
| ai_risk_threshold | 风险阈值配置(按指标定义临界值) |
|
||
| ai_action | AI 行动分发(从建议到行动的闭环跟踪) |
|
||
|
||
### 权限码(6 个)
|
||
|
||
| 权限码 | 说明 |
|
||
|--------|------|
|
||
| `ai.analysis.list` / `ai.analysis.manage` | 查看分析历史 / 发起分析 |
|
||
| `ai.prompt.list` / `ai.prompt.manage` | 查看 Prompt / 管理 Prompt |
|
||
| `ai.usage.list` | 查看用量统计 |
|
||
| `ai.provider.manage` | 管理提供商配置 |
|
||
|
||
### API 端点: `/api/v1/ai/`
|
||
|
||
| 端点 | 方法 | 说明 |
|
||
|------|------|------|
|
||
| `/ai/analyze/lab-report` | POST | 化验单智能解读(SSE) |
|
||
| `/ai/analyze/trends` | POST | 健康趋势分析(SSE) |
|
||
| `/ai/analyze/checkup-plan` | POST | 体检方案推荐(SSE) |
|
||
| `/ai/analyze/report-summary` | POST | 报告摘要生成(SSE) |
|
||
| `/ai/analysis/history` | GET | 分析历史列表 |
|
||
| `/ai/analysis/{id}` | GET | 分析结果详情 |
|
||
|
||
### 集成契约
|
||
|
||
| 方向 | 模块 | 接口 | 触发时机 |
|
||
|------|------|------|---------|
|
||
| 依赖 → | [[erp-health]] | 健康数据查询 | 分析时读取患者数据 |
|
||
| 依赖 → | [[erp-core]] | EventBus, AppError | 模块注册 |
|
||
| 注册 → | [[erp-server]] | `AiModule` | 启动时注册 |
|
||
|
||
## 3. 代码逻辑
|
||
|
||
### SSE 流式分析流程
|
||
|
||
```
|
||
客户端 POST → handler::stream_*()
|
||
→ 预校验: items/sections 为空 → 400 直接拒绝
|
||
→ sanitization: 移除 PII (姓名→患者, 身份证→***)
|
||
→ PromptService: 加载激活模板 + 渲染变量
|
||
→ AnalysisService: 编排分析流程
|
||
→ 检查缓存: 相同输入 + prompt 版本 → 复用已有结果(直接回放纯文本)
|
||
→ AiProvider::stream(): 调用 LLM API(Ollama/Claude/OpenAI)
|
||
→ 逐 chunk SSE 推送: data: { "content": "..." }
|
||
→ AnalysisService: 保存结果到 ai_analysis(已完成的跳过)
|
||
→ 后处理: 解析建议、发布事件
|
||
→ SSE done
|
||
```
|
||
|
||
### 数据脱敏
|
||
|
||
发送给 LLM 的数据经过脱敏处理:
|
||
- 患者姓名 → "患者"
|
||
- 身份证号 → 全掩码
|
||
- 手机号 → `138****1234` 格式
|
||
|
||
### 模块依赖
|
||
|
||
`AiModule` 声明依赖 `["health"]`,确保健康模块先于 AI 模块初始化。
|
||
|
||
⚡ **不变量**: 所有发送到外部 LLM 的数据必须经过 PII 脱敏
|
||
⚡ **不变量**: SSE 连接超时必须设置,避免挂起
|
||
⚡ **不变量**: 用量追踪必须记录,用于计费和审计
|
||
|
||
## 4. Ollama 本地模型对接
|
||
|
||
### 配置方式
|
||
|
||
`crates/erp-server/config/default.toml`:
|
||
|
||
```toml
|
||
[ai]
|
||
default_provider = "ollama"
|
||
model = "qwen3:4b"
|
||
|
||
[ai.providers.ollama]
|
||
provider_type = "ollama"
|
||
base_url = "http://localhost:11434"
|
||
default_model = "qwen3:4b"
|
||
max_tokens = 2048
|
||
temperature = 0.3
|
||
is_enabled = true
|
||
```
|
||
|
||
### 已知限制
|
||
|
||
| 问题 | 说明 |
|
||
|------|------|
|
||
| qwen3-vl:4b 无法运行 | Ollama 已知 bug(#13101),VL 模型内存估算异常(3.3GB 模型要求 38.3 GiB) |
|
||
| qwen3:4b 思考模式 | 模型内置 `<think/>` 标签,首次响应可能延迟 |
|
||
| 无图像识别能力 | 当前所有分析使用结构化数据库数据,不需要 VL 模型 |
|
||
| Prompt 模板 model_config | DB 中 `ai_prompt.model_config.model` 优先级高于全局配置,需同步更新 |
|
||
|
||
### 已修复的 bug
|
||
|
||
| Bug | 修复 |
|
||
|-----|------|
|
||
| 缓存回放 JSON 嵌套 | `replay_cached` 直接回放纯文本;`complete_analysis` 跳过已完成记录 |
|
||
| 分析返回对话式回复 | 迁移 000123 更新 prompt 为非对话、结构化输出 |
|
||
| 空数据触发无效分析 | handler 层预校验 items/sections 非空 |
|
||
|
||
## 5. 活跃问题 + 陷阱
|
||
|
||
### 当前状态: 🔧 开发中
|
||
|
||
6 个 API 端点已实现,SSE 流式分析可用,已对接本地 Ollama。前端 3 个管理页面已实现(Prompt 管理、分析历史、用量统计)。当前为 Phase 1 MVP。
|
||
|
||
### 待完善
|
||
|
||
| 问题 | 级别 | 说明 |
|
||
|------|------|------|
|
||
| 小程序端集成 | P2 | 患者端查看 AI 分析结果 |
|
||
| 用量配额限制 | P1 | 按租户设置用量上限 |
|
||
| 化验报告必填项 | P1 | 后端校验已加,前端录入表单也应对应设置必填 |
|
||
|
||
## 6. 变更记录
|
||
|
||
| 日期 | 变更 |
|
||
|------|------|
|
||
| 2026-05-05 | 对接本地 Ollama qwen3:4b;修复缓存回放 JSON 嵌套 bug;预校验 + prompt 非对话化 |
|
||
| 2026-04-25 | 创建 erp-ai wiki 页面 |
|