From b80e298b4fe65135fc5d14865c77d1cd37b6d618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E9=87=91=E5=AE=9D?= Date: Mon, 1 Jun 2026 10:39:07 +0800 Subject: [PATCH] docs: update frontend api handoff docs --- README.md | 41 +- docs/03_api_design.md | 737 +++++++++++++++----------- docs/06_demo_testing_guide.md | 157 +++--- docs/07_demo_function_traceability.md | 51 +- 4 files changed, 577 insertions(+), 409 deletions(-) diff --git a/README.md b/README.md index b9c8345..13a7449 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@ # 医疗问诊 Agent 第一版 Demo -这是大系统中的“医疗问诊 Agent”子功能 Demo。系统不做独立注册登录,宿主系统进入时通过请求头传入 `X-User-Id`,后端按该用户隔离会话、检查申请、诊断治疗提交、评价报告和历史记录。 +这是大系统中的“医疗问诊 Agent”子功能 Demo。系统不做独立注册登录,宿主系统进入本 Agent 时通过请求头传入 `X-User-Id`,后端按该用户标识隔离训练会话、检查申请、诊断治疗提交、AI 评价报告和历史记录。 ## 当前功能 ```text 病例列表 -> 病例详情 +-> 病例 SQL 导入/删除病例 -> 创建训练会话 -> 多轮问诊 Chat / SSE 流式 Chat --> 提示辅助(练习模式中手动点击) +-> 练习提示 -> 检查/检验申请 -> 完成问诊 -> 提交诊断 @@ -34,7 +35,7 @@ ## 核心数据表 -当前功能只依赖新表: +当前功能依赖以下表: ```text case_base @@ -54,7 +55,7 @@ user_learning_profiles audit_logs ``` -旧表 `cases`、`case_exam_items`、`training_sessions`、`session_orders`、`session_submissions`、`evaluation_records`、`evaluation_report_exports`、`rubric_templates` 已清理。 +旧表已不参与运行。 ## 启动后端 @@ -85,31 +86,27 @@ cd D:\Code\newfounder\medical-consultation-agent\backend ## 导入接口解析后的病例 SQL -接口提供的 SQL dump 不能直接在正式库执行。项目提供安全导入脚本,只解析病例数据并按当前新表字段映射写入,不执行源 SQL 中的 `DROP TABLE`、`CREATE TABLE`、`ALTER TABLE`。 +后端提供安全导入能力,只解析源 SQL 中的 `case_base`、`traditional_case`、`teaching_case`、`scoring_rule` 四类病例数据,不执行源 SQL 中的删表、建表或锁表语句。 -先检查不写库: +命令行预检: ```powershell cd D:\Code\newfounder\medical-consultation-agent\backend .\.venv\Scripts\python.exe scripts\import_source_case_sql.py "C:\path\to\case.sql" ``` -确认检查通过后再写入: +确认预检通过后写入: ```powershell .\.venv\Scripts\python.exe scripts\import_source_case_sql.py "C:\path\to\case.sql" --apply ``` -如果源 SQL 缺少 `case_exam_item`,脚本会根据病例描述生成基础检查项目,保障练习模式可继续申请检查。源 SQL 存在乱码、字段数量不匹配或损坏的 `INSERT` 时,脚本会拒绝导入。 - -前端也提供同一套安全导入能力: +前端导入页: ```text http://127.0.0.1:5173/#/import ``` -页面流程为“选择 SQL 文件 -> 解析检查 -> 确认导入 -> 刷新病例库”。后端接口只接受 `.sql` 文件,最大 5MB,只解析 `case_base`、`traditional_case`、`teaching_case`、`scoring_rule` 四类源表数据;`case_exam_item` 仍由后端按病例内容自动补齐。确认导入成功后,病例列表会重新请求后端,新病例可以直接进入练习模式或教学互动模式。 - ## 启动前端 ```powershell @@ -143,6 +140,8 @@ http://127.0.0.1:5173 ## 验证命令 +后端: + ```powershell cd D:\Code\newfounder\medical-consultation-agent\backend .\.venv\Scripts\python.exe -m compileall app scripts tests @@ -162,13 +161,27 @@ npm.cmd run build | 文档 | 内容 | |---|---| -| [docs/00_development_log.md](docs/00_development_log.md) | 开发过程和本轮变更记录 | +| [docs/00_development_log.md](docs/00_development_log.md) | 开发过程和变更记录 | | [docs/01_functional_scope.md](docs/01_functional_scope.md) | 当前功能范围 | | [docs/02_database_design.md](docs/02_database_design.md) | 数据库总体设计 | | [docs/02_database_table_dictionary.md](docs/02_database_table_dictionary.md) | 表字段字典 | -| [docs/03_api_design.md](docs/03_api_design.md) | 前端对接 API 文档 | +| [docs/03_api_design.md](docs/03_api_design.md) | 前端 API 对接文档 | | [docs/04_data_collection.md](docs/04_data_collection.md) | 数据采集和存储边界 | | [docs/05_agent_prompt_design.md](docs/05_agent_prompt_design.md) | Agent 和提示词模板调用说明 | | [docs/06_demo_testing_guide.md](docs/06_demo_testing_guide.md) | 前端测试指南 | | [docs/07_demo_function_traceability.md](docs/07_demo_function_traceability.md) | 功能到代码和数据表追踪 | | [docs/08_pediatric_case_demo_script.md](docs/08_pediatric_case_demo_script.md) | 儿科病例演示脚本 | + +## Git 管理说明 + +仓库上传范围包含 `backend`、`frontend`、`docs`、`scripts` 和项目说明文件。 + +不上传: + +- `demo_frontend/` +- `.env` +- `backend/.venv/` +- `frontend/node_modules/` +- `frontend/dist/` +- `storage/` +- 本地 PDF、日志、数据库文件和临时 SQL 文件 diff --git a/docs/03_api_design.md b/docs/03_api_design.md index e05a508..fbfc4da 100644 --- a/docs/03_api_design.md +++ b/docs/03_api_design.md @@ -1,29 +1,45 @@ -# 后端 API 对接文档 +# 前端 API 对接文档 + +本文档面向 Vue 前端与后续正式前端开发,描述当前第一版 Demo 已实现的后端接口、请求头、状态流转、字段结构和错误处理规则。 ## 1. 通用约定 -Base URL: +### 1.1 Base URL + +本地开发默认地址: ```text http://127.0.0.1:8000/api/v1 ``` -必传 Header: +前端默认配置: -| Header | 说明 | -|---|---| -| `X-User-Id` | 宿主系统传入的用户 ID,所有业务隔离依据 | -| `X-Entry-Scene` | 入口场景,前端 Demo 默认 `vue_demo` | +```text +Vite dev server: http://127.0.0.1:5173 +API base: http://127.0.0.1:8000/api/v1 +``` -可传 Header: +### 1.2 必传 Header -| Header | 说明 | -|---|---| -| `X-Tenant-Id` | 宿主系统租户/机构 ID | -| `X-Class-Id` | 教学班级 ID | -| `X-Role` | 用户角色 | +所有业务接口都必须携带以下 Header: -统一响应: +| Header | 类型 | 说明 | +|---|---:|---| +| `X-User-Id` | string | 宿主系统传入的用户标识。后端按该字段隔离会话、提交、评价和历史记录。 | +| `X-Entry-Scene` | string | 入口场景。Demo 前端默认 `vue_demo`。 | + +可选 Header: + +| Header | 类型 | 说明 | +|---|---:|---| +| `X-Tenant-Id` | string | 租户、机构或项目 ID。第一版只透传和审计。 | +| `X-User-Role` | string | 用户角色。第一版只透传和审计。 | +| `X-Class-Id` | string | 班级或课程 ID。第一版只透传和审计。 | +| `X-Request-Id` | string | 请求链路 ID。前端有链路追踪需求时传入。 | + +### 1.3 统一响应结构 + +除 SSE 流式接口外,后端统一返回: ```json { @@ -33,34 +49,92 @@ http://127.0.0.1:8000/api/v1 } ``` -常见错误码: +前端处理规则: -| code | 含义 | -|---|---| -| `USER_ID_REQUIRED` | 缺少 `X-User-Id` | -| `CASE_NOT_FOUND` | 病例不存在或未启用 | -| `SESSION_NOT_FOUND` | 会话不存在或不属于当前用户 | -| `SESSION_STATUS_INVALID` | 当前阶段不允许该操作 | -| `INQUIRY_REQUIRED` | 完成问诊前至少需要一轮医生提问 | -| `DIAGNOSIS_REQUIRED` | 提交治疗前需要先提交诊断 | -| `TREATMENT_REQUIRED` | 生成评价前需要先提交治疗 | -| `ORDER_ITEM_NOT_FOUND` | 检查项目不存在 | -| `LLM_CALL_TIMEOUT` | LLM 普通调用超时 | -| `LLM_STREAM_TIMEOUT` | LLM 流式调用超时 | -| `LLM_STREAM_FAILED` | LLM 流式调用失败 | -| `CASE_SQL_FILE_INVALID` | 上传文件不是 `.sql` | -| `CASE_SQL_FILE_EMPTY` | 上传 SQL 文件为空 | -| `CASE_SQL_FILE_TOO_LARGE` | 上传 SQL 文件超过 5MB | -| `CASE_SQL_IMPORT_INVALID` | SQL 解析或字段映射校验失败 | +- `code === "OK"`:读取 `data`。 +- `code !== "OK"`:展示 `message`,不要直接展示底层异常。 +- HTTP 401 通常表示缺少 `X-User-Id`。 +- HTTP 404 通常表示资源不存在或不属于当前 `user_id`。 +- HTTP 400 通常表示当前状态不允许操作或入参不合法。 -## 2. Agent Hello +### 1.4 常见错误码 -| 项 | 内容 | -|---|---| -| Method | `GET` | -| Path | `/agent/hello` | -| Router | `agent.hello` | -| 用途 | 校验后端连接,返回当前用户上下文和功能开关 | +| code | 场景 | 前端处理 | +|---|---|---| +| `USER_ID_REQUIRED` | 缺少 `X-User-Id` | 回到入口设置页,提示填写 user_id。 | +| `CASE_NOT_FOUND` | 病例不存在、未启用或已删除 | 刷新病例列表。 | +| `CASE_DELETE_CONFIRM_REQUIRED` | 删除病例未传 `confirm=true` | 保持确认弹窗,不执行删除。 | +| `CASE_DELETE_TRAINING_DATA_EXISTS` | 病例存在训练数据但未允许删除训练数据 | 当前前端固定传 `delete_training_data=true`。 | +| `SESSION_NOT_FOUND` | 会话不存在或不属于当前用户 | 回到病例页重新创建会话。 | +| `SESSION_STATUS_INVALID` | 当前阶段不允许执行该操作 | 根据 `status` 禁用按钮并提示原因。 | +| `INQUIRY_REQUIRED` | 完成问诊前没有医生提问 | 提示至少完成一轮问诊。 | +| `DIAGNOSIS_REQUIRED` | 提交治疗前未提交诊断 | 引导到诊断表单。 | +| `TREATMENT_REQUIRED` | 生成评价前未提交治疗 | 引导到治疗表单。 | +| `ORDER_ITEM_NOT_FOUND` | 检查项不存在 | 刷新检查项列表。 | +| `LLM_CALL_TIMEOUT` | 非流式模型调用超时 | 提示重试。 | +| `LLM_STREAM_TIMEOUT` | 流式模型调用超时 | 结束 pending,提示重试或关闭流式。 | +| `LLM_STREAM_FAILED` | 流式模型调用失败 | 结束 pending,提示模型服务异常。 | +| `CASE_SQL_FILE_INVALID` | 上传文件不是 `.sql` | 提示选择 SQL 文件。 | +| `CASE_SQL_FILE_EMPTY` | SQL 文件为空 | 提示重新导出文件。 | +| `CASE_SQL_FILE_TOO_LARGE` | 文件超过 5MB | 提示压缩或拆分。 | +| `CASE_SQL_IMPORT_INVALID` | SQL 解析或字段映射失败 | 展示 `errors`,不允许确认导入。 | + +## 2. 前端主流程 + +```text +入口页 + -> GET /agent/hello +病例页 + -> GET /cases + -> GET /cases/{case_id} + -> GET /cases/{case_id}/delete-preview + -> DELETE /cases/{case_id} +训练配置页 + -> POST /sessions +Chat 页 + -> POST /sessions/{session_id}/chat 或 /chat/stream + -> POST /sessions/{session_id}/hints + -> GET /sessions/{session_id}/order-items + -> POST /sessions/{session_id}/orders +提交页 + -> POST /sessions/{session_id}/complete-inquiry + -> POST /sessions/{session_id}/diagnosis + -> POST /sessions/{session_id}/treatment +报告页 + -> POST /sessions/{session_id}/evaluation + -> POST /evaluations/{evaluation_id}/export-pdf +历史页 + -> GET /evaluations + -> GET /evaluations/{evaluation_id} +导入页 + -> POST /imports/case-sql/preview + -> POST /imports/case-sql/apply +LLM 测试页 + -> POST /llm/test/deepseek-fast + -> POST /llm/test/deepseek-reason +``` + +训练会话状态流转: + +```text +inquiry -> diagnosis -> treatment -> evaluating -> evaluated +``` + +前端按钮启用规则: + +| 阶段 | 允许操作 | +---|---| +| `inquiry` | 问诊、查看提示、申请检查、完成问诊 | +| `diagnosis` | 提交诊断 | +| `treatment` | 提交治疗方案 | +| `evaluating` | 生成评价报告 | +| `evaluated` | 查看报告、导出 PDF、查看历史 | + +## 3. Agent Hello + +### `GET /agent/hello` + +用途:检查后端连接,返回当前用户上下文和 Demo 能力开关。 Response `data`: @@ -77,28 +151,38 @@ Response `data`: "pdf_export": true, "knowledge_search": true, "llm_mock_enabled": false, - "llm_fallback_to_mock": false + "llm_mode": "real", + "llm_fallback_to_mock": false, + "llm_fast_model": "deepseek-v4-pro", + "llm_reason_model": "deepseek-v4-pro", + "runtime_memory_backend": "redis" } } ``` -## 3. 病例列表 +前端展示说明: -| 项 | 内容 | -|---|---| -| Method | `GET` | -| Path | `/cases` | -| Router | `cases.list_cases` | -| Service | `CaseService.list_cases` | -| 表 | `case_base` | +- `stream_chat`:是否支持 SSE 流式问诊。 +- `score_types`:评分输出类型,`percentage` 为百分制,`five_point` 为五分制。 +- `pdf_export`:是否支持 PDF 导出。 +- `knowledge_search`:是否已接入评分参考指南检索。 +- `llm_mock_enabled`:是否强制使用 mock 模型。 +- `llm_fallback_to_mock`:真实模型失败时是否回退 mock。 +- `runtime_memory_backend`:短期记忆后端,通常为 `redis` 或 `memory`。 + +## 4. 病例接口 + +### `GET /cases` + +用途:获取病例列表。不会返回标准答案、隐藏病史、评分细则。 Query: -| 参数 | 说明 | -|---|---| -| `department_id` | 科室筛选 | -| `training_type` | 训练类型筛选 | -| `mode` | `practice` 或 `teaching` | +| 参数 | 类型 | 必填 | 说明 | +|---|---:|---:|---| +| `department_id` | number | 否 | 科室筛选。 | +| `training_type` | string | 否 | 训练类别筛选:`case_analysis`、`diagnosis_treatment`、`consultation`。 | +| `mode` | string | 否 | 模式筛选:`practice`、`teaching`。 | Response `data`: @@ -106,72 +190,142 @@ Response `data`: { "items": [ { - "id": 1, - "title": "支气管肺炎 - 6岁男性患儿", + "id": 2, + "case_code": "SRC_2", "department_id": 1, - "department_name": "儿科", + "title": "支气管肺炎 - 6岁男性患儿", "difficulty": "medium", "chief_complaint": "发热、咳嗽4天,喘息1天。", - "patient_age": 6, - "patient_gender": "male", - "training_type": "case_analysis", - "supported_modes": ["practice", "teaching"], + "supported_training_type": "diagnosis_treatment", + "supported_mode": "free_chat", "has_teaching_video": false, - "has_knowledge_points": true + "has_knowledge_points": true, + "has_quiz": false } ] } ``` -## 4. 病例详情 +### `GET /cases/{case_id}` -| 项 | 内容 | -|---|---| -| Method | `GET` | -| Path | `/cases/{case_id}` | -| Router | `cases.get_case_detail` | -| Service | `CaseService.get_case_detail` | -| 表 | `case_base`、`traditional_case`、`teaching_case`、`case_exam_item` | +用途:获取病例训练入口详情。不会返回标准诊断、标准治疗、隐藏病史。 Response `data`: ```json { - "id": 1, + "id": 2, + "case_code": "SRC_2", "title": "支气管肺炎 - 6岁男性患儿", - "department_name": "儿科", + "department": "儿科", + "difficulty": "medium", + "patient": { + "name": null, + "age": 6, + "gender": "male", + "occupation": null + }, "chief_complaint": "发热、咳嗽4天,喘息1天。", - "patient_age": 6, - "patient_gender": "male", - "supported_modes": ["practice", "teaching"], - "exam_item_count": 5, - "has_knowledge_points": true + "supported_training_type": "diagnosis_treatment", + "supported_mode": "free_chat", + "has_teaching_video": false, + "has_knowledge_points": true, + "has_quiz": false, + "order_item_types": ["lab", "imaging", "vital_sign"] } ``` -病例详情不返回标准答案、隐藏病史和完整评分细则。 +### `GET /cases/{case_id}/delete-preview` -## 5. 创建训练会话 +用途:删除病例前统计影响范围。前端删除弹窗先调用该接口。 -| 项 | 内容 | -|---|---| -| Method | `POST` | -| Path | `/sessions` | -| Router | `sessions.create_session` | -| Service | `SessionService.create_session` | -| 表 | `training_session` | +Response `data`: + +```json +{ + "case_id": 2, + "case_title": "支气管肺炎 - 6岁男性患儿", + "can_delete": true, + "affected": { + "case_base": 1, + "traditional_case": 1, + "teaching_case": 0, + "scoring_rule": 1, + "case_exam_item": 6, + "training_session": 1, + "training_order": 6, + "training_submission": 1, + "training_record": 1 + } +} +``` + +### `DELETE /cases/{case_id}` + +用途:确认后删除病例及关联业务数据。审计日志只记录删除行为,不反删。 Request: ```json { - "case_id": 1, - "training_type": "case_analysis", + "confirm": true, + "delete_training_data": true +} +``` + +Response `data`: + +```json +{ + "deleted": true, + "case_id": 2, + "deleted_counts": { + "training_order": 6, + "training_submission": 1, + "training_record": 1, + "training_session": 1, + "case_exam_item": 6, + "scoring_rule": 1, + "traditional_case": 1, + "teaching_case": 0, + "case_base": 1 + } +} +``` + +前端交互规则: + +- 删除按钮只放在病例详情区,不放在病例卡片上。 +- 点击删除后先调用 `delete-preview`。 +- 弹窗展示 `affected`。 +- 用户输入固定确认文案后再调用 `DELETE`。 +- 删除成功后清空当前病例、会话、检查结果、报告缓存,并刷新 `/cases`。 + +## 5. 会话与问诊接口 + +### `POST /sessions` + +用途:创建训练会话,初始化短期 memory。 + +Request: + +```json +{ + "case_id": 2, + "training_type": "diagnosis_treatment", "mode": "practice", "score_type": "percentage" } ``` +字段说明: + +| 字段 | 允许值 | 说明 | +|---|---|---| +| `training_type` | `case_analysis`、`diagnosis_treatment`、`consultation` | 训练类别。 | +| `mode` | `practice`、`teaching` | 训练模式。后端兼容旧值 `novice`,会归一为 `practice`。 | +| `score_type` | `percentage`、`five_point` | 评分输出类型。 | + Response `data`: ```json @@ -179,25 +333,13 @@ Response `data`: "session_id": 10, "session_code": "sess_20260528100000_abcd1234", "status": "inquiry", - "patient_opening": "家长:医生,孩子发烧咳嗽好几天了..." + "patient_opening": "家长:医生,孩子发热咳嗽好几天了,昨天开始喘得厉害,精神也不太好。" } ``` -校验: +### `POST /sessions/{session_id}/chat` -- `case_id` 必须存在并启用。 -- `mode` 当前使用 `practice` 或 `teaching`。 -- 创建会话时初始化短期 memory。 - -## 6. 普通问诊 - -| 项 | 内容 | -|---|---| -| Method | `POST` | -| Path | `/sessions/{session_id}/chat` | -| Router | `sessions.chat` | -| Service | `SessionService.chat` | -| Agent | `PatientAgent.reply` | +用途:普通非流式问诊。 Request: @@ -211,24 +353,22 @@ Response `data`: ```json { - "reply": "发热有4天了,最高烧到39度多。", + "reply": "发热有4天了,最高烧到39度多,吃了退烧药能降下来,但过几个小时又会烧。", "latency_ms": 2500, "model": "deepseek-v4-pro", "fallback_used": false } ``` -校验:会话必须属于当前 `X-User-Id`,且状态为 `inquiry`。 +校验: -## 7. 流式问诊 +- 会话必须属于当前 `X-User-Id`。 +- 当前状态必须为 `inquiry`。 +- `message` 长度 1 到 2000。 -| 项 | 内容 | -|---|---| -| Method | `POST` | -| Path | `/sessions/{session_id}/chat/stream` | -| Router | `sessions.chat_stream` | -| Service | `SessionService.stream_chat` | -| Agent | `PatientAgent.stream_reply` | +### `POST /sessions/{session_id}/chat/stream` + +用途:SSE 流式问诊。当前 Chat 页面优先使用该接口。 Request 同普通问诊。 @@ -236,7 +376,7 @@ SSE 事件: ```text event: message_delta -data: {"delta":"发热有"} +data: {"delta":"发热有4天了,"} event: message_done data: {"latency_ms":3200,"first_token_ms":800,"model":"deepseek-v4-pro","fallback_used":false} @@ -245,23 +385,22 @@ event: error data: {"code":"LLM_STREAM_TIMEOUT","message":"AI 病人回复超时,请重试"} ``` -前端收到 `message_done` 或 `error` 后必须结束 pending。 +前端处理规则: -## 8. 查看提示 +- 收到 `message_delta`:追加到当前 AI 病人气泡。 +- 收到 `message_done`:结束 pending,启用发送按钮。 +- 收到 `error`:结束 pending,展示错误。 +- fetch abort 或 reader done 但未收到 `message_done`:结束 pending,提示“AI 病人回复超时或失败,请重试”。 -| 项 | 内容 | -|---|---| -| Method | `POST` | -| Path | `/sessions/{session_id}/hints` | -| Router | `sessions.generate_hints` | -| Service | `SessionService.generate_hints` | -| Agent | `HintAgent.generate` | +### `POST /sessions/{session_id}/hints` + +用途:练习模式下手动生成提示。提示基于病例、当前对话和已申请检查动态生成。 Request: ```json { - "last_user_message": "孩子发热几天了?", + "last_user_message": "孩子发热几天了?最高体温多少?", "scope": "current_conversation" } ``` @@ -270,26 +409,38 @@ Response `data`: ```json { - "hints": ["可以继续追问最高体温、热型和退热药反应。"], - "missing_dimensions": ["既往史", "严重程度评估"], - "next_questions": ["孩子以前有没有喘息或哮喘史?"], + "hints": [ + "可以继续追问最高体温、热型和退热药反应。" + ], + "missing_dimensions": [ + "既往史", + "严重程度评估" + ], + "next_questions": [ + "孩子以前有没有喘息、哮喘或过敏史?", + "现在血氧是多少?有没有呼吸困难?" + ], "recommended_orders": [ - {"item_code": "oxygen_saturation", "reason": "用于判断缺氧和病情严重程度"} + { + "item_code": "oxygen_saturation", + "reason": "用于判断低氧和病情严重程度" + } ] } ``` -校验:当前仅允许 `practice` 模式且会话状态为 `inquiry`。 +前端处理规则: -## 9. 检查项目列表 +- 提示不自动弹出。 +- 只在练习模式中显示“查看提示”按钮。 +- 练习模式中是否点击提示不影响评分链路。 +- 教学互动模式当前不显示提示入口。 -| 项 | 内容 | -|---|---| -| Method | `GET` | -| Path | `/sessions/{session_id}/order-items` | -| Router | `sessions.list_order_items` | -| Service | `OrderService.list_order_items` | -| 表 | `case_exam_item` | +## 6. 检查/检验接口 + +### `GET /sessions/{session_id}/order-items` + +用途:获取当前病例可申请的检查项目。只返回名称和类型,不返回检查结果。 Response `data`: @@ -299,23 +450,15 @@ Response `data`: { "item_code": "complete_blood_count", "item_name": "血常规", - "item_type": "lab", - "category": "实验室检查", - "ordered": false + "item_type": "lab" } ] } ``` -## 10. 申请检查 +### `POST /sessions/{session_id}/orders` -| 项 | 内容 | -|---|---| -| Method | `POST` | -| Path | `/sessions/{session_id}/orders` | -| Router | `sessions.create_order` | -| Service | `OrderService.create_order` | -| 表 | `training_order` | +用途:申请检查/检验,结果必须来自数据库 `case_exam_item`,不允许 LLM 编造。 Request: @@ -329,26 +472,30 @@ Response `data`: ```json { - "order_id": 1, "item_code": "complete_blood_count", "item_name": "血常规", + "item_type": "lab", "result_text": "WBC 12.4×10^9/L,中性粒细胞72%。", "result_structured": {}, - "already_ordered": false, - "context_written": true + "is_key": true, + "is_abnormal": true, + "context_written": true, + "already_ordered": false } ``` -同一 `session_id + item_code` 幂等,重复申请返回已有记录,不重复写入 memory。 +幂等规则: -## 11. 完成问诊 +- 同一 `session_id + item_code` 只写入一次。 +- 重复申请返回已有结果,`already_ordered=true`。 +- 重复申请不重复写入 runtime memory。 +- 前端按 `item_code` 去重展示。 -| 项 | 内容 | -|---|---| -| Method | `POST` | -| Path | `/sessions/{session_id}/complete-inquiry` | -| Router | `sessions.complete_inquiry` | -| Service | `SessionService.complete_inquiry` | +## 7. 阶段提交接口 + +### `POST /sessions/{session_id}/complete-inquiry` + +用途:完成问诊,进入诊断阶段。 Response `data`: @@ -361,23 +508,17 @@ Response `data`: 校验:至少存在一轮医生提问。 -## 12. 提交诊断 +### `POST /sessions/{session_id}/diagnosis` -| 项 | 内容 | -|---|---| -| Method | `POST` | -| Path | `/sessions/{session_id}/diagnosis` | -| Router | `sessions.submit_diagnosis` | -| Service | `SessionService.submit_diagnosis` | -| 表 | `training_submission` | +用途:提交诊断。 Request: ```json { "primary_diagnosis": "支气管肺炎", - "differential_diagnoses": ["毛细支气管炎", "哮喘急性发作"], - "diagnosis_basis": "发热、咳嗽、喘息,结合肺部体征、炎症指标、胸片和血氧情况。" + "differential_diagnoses": ["毛细支气管炎", "支气管哮喘急性发作", "上呼吸道感染"], + "diagnosis_basis": "结合发热、咳嗽、喘息、肺部体征、炎症指标升高、胸片异常和血氧情况,符合儿童支气管肺炎表现。" } ``` @@ -389,25 +530,19 @@ Response `data`: } ``` -## 13. 提交治疗 +### `POST /sessions/{session_id}/treatment` -| 项 | 内容 | -|---|---| -| Method | `POST` | -| Path | `/sessions/{session_id}/treatment` | -| Router | `sessions.submit_treatment` | -| Service | `SessionService.submit_treatment` | -| 表 | `training_submission` | +用途:提交治疗方案。 Request: ```json { - "treatment_principle": "抗感染、平喘、改善氧合、严密观察。", - "treatment_measures": "根据病情选择抗感染治疗,必要时雾化吸入,监测体温、呼吸和血氧。", - "risk_plan": "关注低氧、呼吸困难加重、持续高热、精神反应差。", - "communication": "向家属说明病情、用药注意事项和复诊/住院指征。", - "follow_up": "治疗后复查体温、呼吸、血氧和炎症指标。" + "treatment_principle": "抗感染、止咳平喘、改善氧合、严密观察病情变化。", + "treatment_measures": "根据病情选择抗感染治疗,必要时雾化吸入缓解喘息,监测体温、呼吸、血氧和精神反应。", + "risk_plan": "关注低氧、呼吸困难加重、持续高热、精神反应差、脱水等情况。", + "communication": "向家属说明肺炎病情、用药注意事项、观察指标和复诊/住院指征。", + "follow_up": "治疗后复查体温、呼吸、血氧和必要炎症指标,症状加重时及时就诊。" } ``` @@ -419,16 +554,11 @@ Response `data`: } ``` -## 14. 生成评价 +## 8. 评价与报告接口 -| 项 | 内容 | -|---|---| -| Method | `POST` | -| Path | `/sessions/{session_id}/evaluation` | -| Router | `sessions.create_evaluation` | -| Service | `EvaluationService.create_evaluation` | -| Agent | `ScoringAgent.score`、`ReportAgent` | -| 表 | `scoring_rule`、`knowledge_chunks`、`training_record` | +### `POST /sessions/{session_id}/evaluation` + +用途:生成 AI 评价报告。后端读取评分规则、知识检索结果、检查申请、问诊过程和提交内容后调用 Scoring Agent。 Request: @@ -445,26 +575,34 @@ Response `data`: "evaluation_id": 1, "score_type": "percentage", "total_score": 82, - "dimension_scores": [], + "dimension_scores": [ + { + "dimension": "信息采集", + "score": 18, + "max_score": 25, + "comment": "已覆盖发热和咳嗽,但既往喘息史和家族过敏史追问不足。", + "evidence": ["询问发热天数和最高体温", "申请血常规和CRP"], + "deductions": ["未充分询问既往喘息史"], + "improvement": "补充既往史、过敏史、严重程度评估相关问题。" + } + ], "errors": [], - "improvement_plan": [], - "evidence_summary": [], + "improvement_plan": ["加强儿童肺炎严重程度评估训练。"], + "evidence_summary": ["检查结果已写入评分依据。"], "guideline_refs": [], - "overall_comment": "完成了主要诊断链路,但检查利用和沟通细节仍需加强。" + "overall_comment": "诊断方向正确,检查利用和沟通细节仍需加强。" } ``` -评价完成后释放短期 memory,并写入 `training_record`。 +评价完成后: -## 15. 历史评价列表 +- 写入 `training_record`。 +- 释放当前会话短期 memory。 +- 历史记录只保存评价报告,不长期保存完整聊天记录。 -| 项 | 内容 | -|---|---| -| Method | `GET` | -| Path | `/evaluations` | -| Router | `evaluations.list_evaluations` | -| Service | `EvaluationService.list_history` | -| 表 | `training_record` | +### `GET /evaluations` + +用途:按当前 `X-User-Id` 查询历史评价。 Response `data`: @@ -476,34 +614,32 @@ Response `data`: "case_title": "支气管肺炎 - 6岁男性患儿", "score_type": "percentage", "total_score": 82, - "created_at": "2026-05-28T10:00:00", + "created_at": "2026-05-29T10:00:00", "pdf_exported": true } ] } ``` -## 16. 评价详情 +### `GET /evaluations/{evaluation_id}` -| 项 | 内容 | -|---|---| -| Method | `GET` | -| Path | `/evaluations/{evaluation_id}` | -| Router | `evaluations.get_evaluation_detail` | -| Service | `EvaluationService.get_detail` | -| 表 | `training_record` | +用途:获取评价详情。只能读取当前用户自己的评价。 -Response `data` 为完整评价报告,并包含 `session_id`、`case_id`、`case_title`、`created_at`、`pdf_file_path`。 +Response `data`:继承评价报告字段,并额外包含: -## 17. 导出 PDF +```json +{ + "session_id": 10, + "case_id": 2, + "case_title": "支气管肺炎 - 6岁男性患儿", + "created_at": "2026-05-29T10:00:00", + "pdf_file_path": "storage/reports/training_record_1_percentage_xxx.pdf" +} +``` -| 项 | 内容 | -|---|---| -| Method | `POST` | -| Path | `/evaluations/{evaluation_id}/export-pdf` | -| Router | `evaluations.export_pdf` | -| Service | `PdfExportService.export` | -| 表 | `training_record` | +### `POST /evaluations/{evaluation_id}/export-pdf` + +用途:生成本地 PDF 报告并保存文件路径。 Response `data`: @@ -514,35 +650,11 @@ Response `data`: } ``` -## 18. 知识检索 +## 9. 病例 SQL 导入接口 -| 项 | 内容 | -|---|---| -| Method | `GET` | -| Path | `/knowledge/search` | -| Router | `knowledge.search_knowledge` | -| Service | `KnowledgeService.search_guidelines` | -| 表 | `knowledge_sources`、`knowledge_documents`、`knowledge_chunks` | +### `POST /imports/case-sql/preview` -Query: - -| 参数 | 说明 | -|---|---| -| `department_id` | 科室 ID | -| `training_type` | 训练类型 | -| `q` | 关键词,逗号分隔 | - -用途:评价生成前检索科室/类型下的评分指南。前端第一版不需要主动调用。 - -## 19. 病例 SQL 导入预检 - -| 项 | 内容 | -|---|---| -| Method | `POST` | -| Path | `/imports/case-sql/preview` | -| Router | `imports.preview_case_sql` | -| Service | `CaseSqlImportService.preview` | -| 用途 | 前端上传接口解析后的 SQL 文件,后端只解析并校验,不写入数据库 | +用途:上传接口解析后的 SQL 文件,只解析和校验,不写入数据库。 Request: @@ -560,48 +672,37 @@ Response `data`: "tables": { "case_base": 1, "traditional_case": 1, - "teaching_case": 1, - "scoring_rule": 5 + "teaching_case": 0, + "scoring_rule": 1 }, "can_import": true, - "warnings": [], + "warnings": [ + "源 SQL 未包含 case_exam_item,导入器会按当前业务规则处理。" + ], "errors": [], "preview_cases": [ { - "id": 1001, - "title": "儿童支气管肺炎", - "case_type": "diagnosis_treatment", + "id": 2, + "title": "支气管肺炎 - 6岁男性患儿", + "case_type": "traditional", "difficulty": "medium" } ] } ``` -校验逻辑: -- 必须携带 `X-User-Id`。 -- 文件后缀必须为 `.sql`,大小不超过 5MB。 -- 只解析源 SQL 中的 `case_base`、`traditional_case`、`teaching_case`、`scoring_rule`。 -- 预检接口不执行源 SQL,不写入数据库。 -- 源 SQL 中出现 `DROP TABLE`、`CREATE TABLE`、`ALTER TABLE` 等语句时只作为警告展示,导入器不会执行这些语句。 -- 字段数量不匹配、非法字符串、JSON 字段非法时返回 `can_import=false` 和 `errors`。 +规则: -## 20. 病例 SQL 确认导入 +- 只识别 `case_base`、`traditional_case`、`teaching_case`、`scoring_rule`。 +- 不执行源 SQL 中的 `DROP TABLE`、`CREATE TABLE`、`ALTER TABLE`、`LOCK TABLES`。 +- 字段数量不匹配、JSON 损坏、字符串未闭合时返回 `can_import=false`。 +- `preview` 不写库。 -| 项 | 内容 | -|---|---| -| Method | `POST` | -| Path | `/imports/case-sql/apply` | -| Router | `imports.apply_case_sql` | -| Service | `CaseSqlImportService.apply` | -| 用途 | 预检通过后,将 SQL 中的病例源表数据映射写入当前数据库 | -| 表 | `case_base`、`traditional_case`、`teaching_case`、`scoring_rule`、`case_exam_item` | +### `POST /imports/case-sql/apply` -Request: +用途:确认导入,将 SQL 中的病例源表数据映射写入当前数据库。 -```text -Content-Type: multipart/form-data -file: case.sql -``` +Request 同预检接口。 Response `data`: @@ -612,30 +713,48 @@ Response `data`: "encoding": "utf-8", "inserted_or_updated_cases": 1, "imported_traditional_cases": 1, - "imported_teaching_cases": 1, - "imported_scoring_rules": 5, - "generated_exam_items": 4, + "imported_teaching_cases": 0, + "imported_scoring_rules": 1, + "generated_exam_items": 6, "warnings": [] } ``` -校验逻辑: -- 必须携带 `X-User-Id`。 -- 导入过程使用事务,任意表映射失败则整体回滚。 -- `case_base` 按 `id` 更新或插入。 -- `traditional_case` 和 `teaching_case` 按 `case_id` 更新或插入。 -- `scoring_rule` 按 `case_id` 先删除旧规则再写入源规则。 -- 源 SQL 缺少 `case_exam_item` 时,由后端根据病例文本生成基础检查项目,保障问诊训练链路可继续使用。 -- 导入成功后前端刷新 `/cases`,新增病例即可进入训练。 +前端处理: -## 21. LLM Fast 测试 +- 先调用 `preview`,只有 `can_import=true` 才允许点击“确认导入”。 +- 导入成功后刷新病例列表。 +- 如果源 SQL 缺少 `case_exam_item`,后端会生成基础检查项,保证新病例可训练。 -| 项 | 内容 | -|---|---| -| Method | `POST` | -| Path | `/llm/test/deepseek-fast` | -| Router | `llm_test.test_deepseek_fast` | -| Service | `OpenAICompatibleLLMClient.chat` | +## 10. 知识检索接口 + +### `GET /knowledge/search` + +用途:按科室、训练类别和关键词检索评分参考指南。第一版主要供评价链路和调试使用。 + +Query: + +| 参数 | 类型 | 必填 | 说明 | +|---|---:|---:|---| +| `department_id` | number | 是 | 科室 ID。 | +| `training_type` | string | 是 | 训练类别。 | +| `q` | string | 否 | 关键词,多个关键词用英文逗号分隔。 | + +Response `data`: + +```json +{ + "matched_chunks": [], + "source_refs": [], + "no_match": true +} +``` + +## 11. LLM 测试接口 + +### `POST /llm/test/deepseek-fast` + +用途:测试快速模型耗时。 Request: @@ -660,13 +779,31 @@ Response `data`: } ``` -## 22. LLM Reason 测试 +### `POST /llm/test/deepseek-reason` -| 项 | 内容 | +用途:测试 reason 模型耗时。接口内部会优先按配置执行,流式不兼容时降级为非流式,不影响问诊主链路。 + +Request 同 Fast 测试。 + +Response 字段同 Fast 测试。 + +## 12. 前端字段枚举 + +| 字段 | 允许值 | |---|---| -| Method | `POST` | -| Path | `/llm/test/deepseek-reason` | -| Router | `llm_test.test_deepseek_reason` | -| Service | `OpenAICompatibleLLMClient.stream_chat`,流式不兼容时降级到 `chat` | +| `score_type` | `percentage`、`five_point` | +| `mode` | `practice`、`teaching` | +| `training_type` | `case_analysis`、`diagnosis_treatment`、`consultation` | +| `session.status` | `inquiry`、`diagnosis`、`treatment`、`evaluating`、`evaluated` | +| `patient.gender` | `male`、`female`、`null` | -Request 同 Fast 测试。Response 字段同 Fast 测试。 +## 13. 前端联调注意事项 + +1. 所有请求必须带 `X-User-Id`,否则后端返回 `USER_ID_REQUIRED`。 +2. 当前 Demo 不做登录注册,`user_id` 由宿主系统或测试页传入。 +3. 病例详情不返回标准答案,避免前端泄露训练答案。 +4. 检查结果只来自数据库,不来自 LLM。 +5. 聊天记录只在 runtime memory 中短期保存,评价完成后释放。 +6. 历史页读取的是 `training_record`,只展示完整训练结束后的评价。 +7. 删除病例会级联删除该病例训练数据,前端必须保留二次确认。 +8. `.env` 不进入 Git,前端不能写死 API Key。 diff --git a/docs/06_demo_testing_guide.md b/docs/06_demo_testing_guide.md index 0ee99e2..ac5cf0d 100644 --- a/docs/06_demo_testing_guide.md +++ b/docs/06_demo_testing_guide.md @@ -1,5 +1,7 @@ # Demo 前端测试指南 +本文档用于本地演示和前后端联调。前端页面覆盖当前第一版 Demo 的完整训练闭环。 + ## 1. 启动服务 后端: @@ -17,7 +19,7 @@ cd D:\Code\newfounder\medical-consultation-agent\frontend npm.cmd run dev -- --host 127.0.0.1 --port 5173 ``` -浏览器访问: +访问: ```text http://127.0.0.1:5173 @@ -30,49 +32,11 @@ cd D:\Code\newfounder\medical-consultation-agent\backend .\.venv\Scripts\python.exe scripts\migrate_to_new_schema.py ``` -当前数据库为 `medical_consultation_agent`。核心表为 `case_base`、`traditional_case`、`teaching_case`、`scoring_rule`、`case_exam_item`、`training_session`、`training_order`、`training_submission`、`training_record`。 +当前数据库为 `medical_consultation_agent`。 -## 3. 导入接口解析后的病例 SQL +## 3. 入口页测试 -接口提供的 SQL dump 先走安全检查,不直接执行原始 SQL: - -```powershell -cd D:\Code\newfounder\medical-consultation-agent\backend -.\.venv\Scripts\python.exe scripts\import_source_case_sql.py "C:\path\to\case.sql" -``` - -检查通过后再写入当前库: - -```powershell -.\.venv\Scripts\python.exe scripts\import_source_case_sql.py "C:\path\to\case.sql" --apply -``` - -导入脚本规则: - -- 忽略源 SQL 中的 `DROP TABLE`、`CREATE TABLE`、`ALTER TABLE`、`LOCK TABLES`。 -- 按字段名映射导入 `case_base`、`traditional_case`、`teaching_case`、`scoring_rule`。 -- 源 SQL 缺少 `case_exam_item` 时,根据病例描述生成基础检查项目。 -- 源 SQL 缺少 `teaching_case` 时,只影响教学互动模式入口,不影响练习模式。 -- 源 SQL 存在乱码、字段数量不匹配或损坏字符串时拒绝导入。 - -前端上传测试: - -1. 进入 `http://127.0.0.1:5173/#/import`。 -2. 选择接口解析后的 `.sql` 文件。 -3. 点击“解析检查”。 -4. 查看识别到的表、病例预览、警告和错误。 -5. `can_import=true` 后点击“确认导入”。 -6. 导入成功后点击“刷新病例库”,或直接进入病例页查看新增病例。 - -预期结果: -- 预检阶段不写数据库。 -- 确认导入阶段写入 `case_base`、`traditional_case`、`teaching_case`、`scoring_rule`,并自动补齐基础检查项目。 -- 新增病例出现在病例列表中,可继续创建训练会话。 -- 损坏 SQL 会显示错误,不会写入任何表。 - -## 4. 入口页测试 - -1. 打开入口页。 +1. 打开前端首页。 2. 确认 `user_id` 为 `demo_user_001`。 3. 确认 API Base 为 `http://127.0.0.1:8000/api/v1`。 4. 点击连接或进入 Agent。 @@ -80,22 +44,67 @@ cd D:\Code\newfounder\medical-consultation-agent\backend 预期结果: - 页面显示后端地址、当前 user_id、入口场景和最近连接时间。 -- “已连接”只在 `GET /api/v1/agent/hello` 成功返回后显示。 +- “已连接”只在 `GET /api/v1/agent/hello` 成功后显示。 - 功能卡片显示流式问诊、PDF 导出、知识检索、评分类型、LLM mock/fallback 状态。 -## 5. 病例库测试 +## 4. 病例 SQL 导入测试 + +进入: + +```text +http://127.0.0.1:5173/#/import +``` + +步骤: + +1. 选择接口解析后的 `.sql` 文件。 +2. 点击“解析检查”。 +3. 查看识别到的表、病例预览、警告和错误。 +4. `can_import=true` 后点击“确认导入”。 +5. 导入成功后刷新病例库。 + +预期结果: + +- 预检阶段不写数据库。 +- 后端只解析 `case_base`、`traditional_case`、`teaching_case`、`scoring_rule`。 +- 源 SQL 中的 `DROP TABLE`、`CREATE TABLE`、`ALTER TABLE`、`LOCK TABLES` 不会执行。 +- 缺少 `case_exam_item` 时,后端按当前业务规则生成基础检查项。 +- 新病例出现在病例列表中,可继续创建训练会话。 + +## 5. 病例列表与详情测试 1. 进入病例页。 -2. 点击“支气管肺炎 - 6岁男性患儿”病例卡片。 +2. 点击病例卡片。 3. 查看病例详情。 4. 点击“开始训练”。 预期结果: -- 点击病例卡片只展开详情,不直接创建会话。 -- 详情展示科室、年龄、性别、主诉、训练类型、检查项目数量和是否有教学知识。 +- 点击病例卡片只选中病例,不直接创建会话。 +- 详情展示科室、年龄、性别、主诉、训练类型、教学资源和可申请检查类型。 +- 详情不展示标准答案、隐藏病史和评分细则。 -## 6. 创建会话 +## 6. 删除病例测试 + +该功能用于联调阶段清理导入错误或不再需要的病例。 + +步骤: + +1. 在病例详情中点击“删除病例”。 +2. 弹窗中查看删除影响范围。 +3. 输入 `确认删除`。 +4. 点击“确认删除”。 +5. 删除成功后刷新病例列表。 + +预期结果: + +- 前端先调用 `GET /api/v1/cases/{case_id}/delete-preview`。 +- 确认后调用 `DELETE /api/v1/cases/{case_id}`。 +- 后端删除病例主表、扩展表、评分规则、检查项、训练会话、检查申请、提交内容和训练评价记录。 +- `audit_logs` 只记录删除操作,不被反删。 +- 删除后访问病例详情返回 `CASE_NOT_FOUND`。 + +## 7. 创建训练会话 1. 选择训练模式:`练习模式` 或 `教学互动模式`。 2. 选择评分类型:`百分制` 或 `五分制`。 @@ -104,14 +113,10 @@ cd D:\Code\newfounder\medical-consultation-agent\backend 预期结果: - 后端创建 `training_session`。 -- Redis/进程内 memory 生成 `memory_key`。 -- Chat 页面显示 AI 病人首句。 +- Redis 或进程内 memory 生成短期记忆。 +- Chat 页显示 AI 病人开场白。 -## 7. 多轮问诊 - -1. 在 Chat 输入框提问。 -2. 保持流式开关开启。 -3. 点击“发送问诊”。 +## 8. 多轮问诊测试 推荐问题: @@ -125,23 +130,23 @@ cd D:\Code\newfounder\medical-consultation-agent\backend 预期结果: -- 前端显示流式增量内容。 +- 流式模式下逐步显示 AI 病人回复。 - 收到 `message_done` 后停止“正在生成”。 - 出错时显示错误,不会无限 pending。 -## 8. 查看提示 +## 9. 查看提示测试 练习模式中点击“查看提示”。 预期结果: - 调用 `POST /api/v1/sessions/{session_id}/hints`。 -- 显示缺失问诊维度、下一步可问问题、推荐检查。 +- 展示缺失问诊维度、下一步问题和推荐检查。 - 提示不自动弹出,不写入长期历史。 -## 9. 检查/检验申请 +## 10. 检查/检验申请测试 -在检查面板依次申请: +建议申请: - 血常规 - CRP @@ -152,18 +157,18 @@ cd D:\Code\newfounder\medical-consultation-agent\backend 预期结果: - 检查结果来自 `case_exam_item`。 -- 同一检查重复点击不重复写入,只显示“已申请”。 -- 页面提示“该检查结果已写入本次会话上下文和评分依据”。 +- 同一 `item_code` 重复点击不会重复写入。 +- 页面提示检查结果已写入本次会话上下文和评分依据。 -## 10. 诊断与治疗提交 +## 11. 诊断与治疗提交测试 1. 点击“完成问诊”。 -2. 在提交页填写诊断。 +2. 填写诊断。 3. 点击“提交诊断”。 4. 填写治疗方案。 5. 点击“提交治疗方案”。 -演示模板可通过“填入演示模板”按钮填充,默认表单为空。 +演示时可以点击“填入演示模板”,正常测试时表单默认为空。 预期结果: @@ -171,7 +176,7 @@ cd D:\Code\newfounder\medical-consultation-agent\backend - 诊断提交后进入治疗阶段。 - 治疗提交后进入评价阶段。 -## 11. 评价、PDF 与历史 +## 12. 评价、PDF 与历史记录测试 1. 点击“生成 AI 评价报告”。 2. 查看维度评分、证据摘要、扣分点和改进计划。 @@ -185,7 +190,7 @@ cd D:\Code\newfounder\medical-consultation-agent\backend - PDF 路径写入 `training_record.pdf_file_path`。 - 历史记录按当前 `user_id` 查询。 -## 12. LLM 测试 +## 13. LLM 测试 进入 LLM 测试页: @@ -195,16 +200,16 @@ cd D:\Code\newfounder\medical-consultation-agent\backend 预期结果: - 页面展示模型名、总耗时、是否流式、是否 mock、是否 fallback。 -- 真实模型异常时显示错误,不静默伪装为真实回复。 +- 真实模型异常时显示错误,不静默伪装为真实响应。 -## 13. 常见问题 +## 14. 常见问题 | 问题 | 排查 | |---|---| -| 页面显示未连接 | 检查后端是否在 `127.0.0.1:8000` 启动 | -| 缺少 `X-User-Id` | 入口页填写 user_id 后重新进入 | -| 病例为空 | 运行 `scripts\migrate_to_new_schema.py` | -| Chat 卡在生成中 | 查看浏览器控制台 SSE 是否收到 `message_done` 或 `error` | -| 检查重复出现 | 刷新页面后复测,同一 `item_code` 应只出现一次 | -| 评价无法生成 | 确认已完成问诊、提交诊断、提交治疗 | -| Redis 中有 TTL | 正常行为,短期 memory 使用 TTL 自动过期 | +| 页面显示未连接 | 检查后端是否在 `127.0.0.1:8000` 启动。 | +| 缺少 `X-User-Id` | 入口页填写 user_id 后重新进入。 | +| 病例为空 | 运行 `scripts\migrate_to_new_schema.py` 或导入病例 SQL。 | +| Chat 卡在生成中 | 查看浏览器控制台 SSE 是否收到 `message_done` 或 `error`。 | +| 检查重复出现 | 刷新页面后复测,同一 `item_code` 应只出现一次。 | +| 评价无法生成 | 确认已完成问诊、提交诊断、提交治疗。 | +| Redis 中有 TTL | 正常行为,短期 memory 使用 TTL 自动过期。 | diff --git a/docs/07_demo_function_traceability.md b/docs/07_demo_function_traceability.md index fb2ce1e..650fc7e 100644 --- a/docs/07_demo_function_traceability.md +++ b/docs/07_demo_function_traceability.md @@ -1,26 +1,30 @@ # Demo 功能追踪表 +本文档用于说明当前 Demo 功能、前端入口、后端接口、核心函数和数据表之间的对应关系。 + ## 1. 功能到代码映射 | 功能 | 前端页面/按钮 | API | Router 函数 | Service / Agent | 数据表 | 状态 | |---|---|---|---|---|---|---| -| Agent 入口连接 | 入口页“连接/进入” | `GET /api/v1/agent/hello` | `agent.hello` | `AuditService.log` | `audit_logs` | 已实现 | -| 病例列表 | 病例页刷新 | `GET /api/v1/cases` | `cases.list_cases` | `CaseService.list_cases` | `case_base` | 已实现 | +| Agent 连接检查 | 入口页“连接/进入” | `GET /api/v1/agent/hello` | `agent.hello` | `AuditService.log` | `audit_logs` | 已实现 | +| 病例列表 | 病例页“刷新” | `GET /api/v1/cases` | `cases.list_cases` | `CaseService.list_cases` | `case_base` | 已实现 | | 病例详情 | 点击病例卡片 | `GET /api/v1/cases/{case_id}` | `cases.get_case_detail` | `CaseService.get_case_detail` | `case_base`、`traditional_case`、`teaching_case`、`case_exam_item` | 已实现 | +| 删除病例预览 | 病例详情“删除病例” | `GET /api/v1/cases/{case_id}/delete-preview` | `cases.get_case_delete_preview` | `CaseService.get_delete_preview` | 病例表、训练表统计 | 已实现 | +| 删除病例 | 删除弹窗“确认删除” | `DELETE /api/v1/cases/{case_id}` | `cases.delete_case` | `CaseService.delete_case`、`CaseRepository.delete_case_cascade` | `case_base`、`traditional_case`、`teaching_case`、`scoring_rule`、`case_exam_item`、`training_session`、`training_order`、`training_submission`、`training_record` | 已实现 | | 病例 SQL 预检 | 导入页“解析检查” | `POST /api/v1/imports/case-sql/preview` | `imports.preview_case_sql` | `CaseSqlImportService.preview` | 不写库 | 已实现 | | 病例 SQL 导入 | 导入页“确认导入” | `POST /api/v1/imports/case-sql/apply` | `imports.apply_case_sql` | `CaseSqlImportService.apply`、`scripts.import_source_case_sql` | `case_base`、`traditional_case`、`teaching_case`、`scoring_rule`、`case_exam_item` | 已实现 | -| 创建训练会话 | “创建训练会话” | `POST /api/v1/sessions` | `sessions.create_session` | `SessionService.create_session` | `training_session`、runtime memory | 已实现 | -| 普通问诊 | Chat“发送问诊” | `POST /api/v1/sessions/{session_id}/chat` | `sessions.chat` | `SessionService.chat`、`PatientAgent.reply` | runtime memory | 已实现 | -| 流式问诊 | Chat 流式开关开启 | `POST /api/v1/sessions/{session_id}/chat/stream` | `sessions.chat_stream` | `SessionService.stream_chat`、`PatientAgent.stream_reply` | runtime memory | 已实现 | -| 查看提示 | Chat“查看提示” | `POST /api/v1/sessions/{session_id}/hints` | `sessions.generate_hints` | `SessionService.generate_hints`、`HintAgent.generate` | runtime memory、`training_order` | 已实现 | +| 创建训练会话 | 训练配置页“创建训练会话” | `POST /api/v1/sessions` | `sessions.create_session` | `SessionService.create_session` | `training_session`、runtime memory | 已实现 | +| 普通问诊 | Chat 页“发送问诊” | `POST /api/v1/sessions/{session_id}/chat` | `sessions.chat` | `SessionService.chat`、`PatientAgent.reply` | runtime memory | 已实现 | +| 流式问诊 | Chat 页流式开关开启 | `POST /api/v1/sessions/{session_id}/chat/stream` | `sessions.chat_stream` | `SessionService.stream_chat`、`PatientAgent.stream_reply` | runtime memory | 已实现 | +| 查看提示 | Chat 页“查看提示” | `POST /api/v1/sessions/{session_id}/hints` | `sessions.generate_hints` | `SessionService.generate_hints`、`HintAgent.generate` | runtime memory、`training_order` | 已实现 | | 检查项目列表 | 检查面板刷新 | `GET /api/v1/sessions/{session_id}/order-items` | `sessions.list_order_items` | `OrderService.list_order_items` | `case_exam_item` | 已实现 | -| 申请检查 | “申请该检查” | `POST /api/v1/sessions/{session_id}/orders` | `sessions.create_order` | `OrderService.create_order` | `case_exam_item`、`training_order` | 已实现 | -| 完成问诊 | “完成问诊” | `POST /api/v1/sessions/{session_id}/complete-inquiry` | `sessions.complete_inquiry` | `SessionService.complete_inquiry` | `training_session` | 已实现 | -| 提交诊断 | “提交诊断” | `POST /api/v1/sessions/{session_id}/diagnosis` | `sessions.submit_diagnosis` | `SessionService.submit_diagnosis` | `training_submission` | 已实现 | -| 提交治疗 | “提交治疗方案” | `POST /api/v1/sessions/{session_id}/treatment` | `sessions.submit_treatment` | `SessionService.submit_treatment` | `training_submission` | 已实现 | -| 生成评价 | “生成 AI 评价报告” | `POST /api/v1/sessions/{session_id}/evaluation` | `sessions.create_evaluation` | `EvaluationService.create_evaluation`、`ScoringAgent.score`、`ReportAgent` | `scoring_rule`、`knowledge_chunks`、`training_record` | 已实现 | -| 历史列表 | 历史页刷新 | `GET /api/v1/evaluations` | `evaluations.list_evaluations` | `EvaluationService.list_history` | `training_record` | 已实现 | -| 报告详情 | 历史详情/报告页 | `GET /api/v1/evaluations/{evaluation_id}` | `evaluations.get_evaluation_detail` | `EvaluationService.get_detail` | `training_record` | 已实现 | +| 申请检查 | 检查面板“申请该检查” | `POST /api/v1/sessions/{session_id}/orders` | `sessions.create_order` | `OrderService.create_order` | `case_exam_item`、`training_order` | 已实现 | +| 完成问诊 | Chat 页“完成问诊” | `POST /api/v1/sessions/{session_id}/complete-inquiry` | `sessions.complete_inquiry` | `SessionService.complete_inquiry` | `training_session` | 已实现 | +| 提交诊断 | 提交页“提交诊断” | `POST /api/v1/sessions/{session_id}/diagnosis` | `sessions.submit_diagnosis` | `SessionService.submit_diagnosis` | `training_submission` | 已实现 | +| 提交治疗 | 提交页“提交治疗方案” | `POST /api/v1/sessions/{session_id}/treatment` | `sessions.submit_treatment` | `SessionService.submit_treatment` | `training_submission` | 已实现 | +| 生成评价 | 报告页“生成 AI 评价报告” | `POST /api/v1/sessions/{session_id}/evaluation` | `sessions.create_evaluation` | `EvaluationService.create_evaluation`、`ScoringAgent.score`、`ReportAgent` | `scoring_rule`、`knowledge_chunks`、`training_record` | 已实现 | +| 历史评价列表 | 历史页刷新 | `GET /api/v1/evaluations` | `evaluations.list_evaluations` | `EvaluationService.list_history` | `training_record` | 已实现 | +| 评价详情 | 历史页详情/报告页 | `GET /api/v1/evaluations/{evaluation_id}` | `evaluations.get_evaluation_detail` | `EvaluationService.get_detail` | `training_record` | 已实现 | | PDF 导出 | 报告页“导出 PDF” | `POST /api/v1/evaluations/{evaluation_id}/export-pdf` | `evaluations.export_pdf` | `PdfExportService.export` | `training_record` | 已实现 | | 知识检索 | 后端预留/评价内部使用 | `GET /api/v1/knowledge/search` | `knowledge.search_knowledge` | `KnowledgeService.search_guidelines` | `knowledge_sources`、`knowledge_documents`、`knowledge_chunks` | 已实现 | | LLM Fast 测试 | LLM 测试页“测试 Fast” | `POST /api/v1/llm/test/deepseek-fast` | `llm_test.test_deepseek_fast` | `OpenAICompatibleLLMClient.chat` | 无长期表 | 已实现 | @@ -28,7 +32,7 @@ ## 2. 当前数据表状态 -当前功能只依赖新表: +当前运行只依赖新表: ```text case_base @@ -48,16 +52,25 @@ user_learning_profiles audit_logs ``` -旧表已不参与运行。旧表删除后,后端测试和前端构建均已通过。 +旧表不参与运行。旧表删除后,后端测试和前端构建均已通过。 -## 3. 核心状态流 +## 3. 关键业务边界 + +- `X-User-Id` 是会话、提交、评价和历史记录的隔离依据。 +- Chat 消息只作为短期 memory 使用,不作为长期历史保存。 +- 检查/检验结果只来自 `case_exam_item`,不由 LLM 编造。 +- 完整训练结束后只沉淀 `training_record`。 +- 删除病例会删除病例相关业务数据和训练记录,但保留 `audit_logs`。 +- 病例详情接口不返回标准答案和隐藏病史。 + +## 4. 核心状态流 ```text -inquiry -> diagnosis -> treatment -> evaluating -> completed +inquiry -> diagnosis -> treatment -> evaluating -> evaluated ``` -- `inquiry`:允许 Chat、提示、申请检查。 +- `inquiry`:允许 Chat、查看提示、申请检查、完成问诊。 - `diagnosis`:允许提交诊断。 - `treatment`:允许提交治疗方案。 - `evaluating`:允许生成 AI 评价。 -- `completed`:允许查询历史、查看详情、导出 PDF。 +- `evaluated`:允许查看报告、导出 PDF、查看历史。