diff --git a/README.md b/README.md index d9bd07b..16b10d7 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ fastapi/ │ ├── schemas/ # Pydantic 入参/出参 │ ├── services/ # 业务服务 │ └── tasks/ # Celery 异步任务预留 -├── docs/ # API、架构、数据库、部署和交接文档 +├── docs/ # API、架构、数据库、部署和项目文档 ├── scripts/ # 初始化和维护脚本 ├── tests/ # 自动化测试 ├── Dockerfile @@ -90,8 +90,8 @@ fastapi/ ```powershell cd D:\Code\newfounder\medical-consultation-agent -python -m venv .venv -.\.venv\Scripts\activate +python -m venv backend\.venv +.\backend\.venv\Scripts\activate pip install -r requirements.txt copy .env.example .env uvicorn app.main:app --host 127.0.0.1 --port 9000 @@ -209,17 +209,20 @@ event: answer_done .\backend\.venv\Scripts\python.exe tests\test_api_contract.py ``` -## 9. 交接文档 +## 9. 项目文档 | 文档 | 用途 | |---|---| +| `docs/00_project_overview.md` | 项目总览和功能讲解顺序 | | `docs/01_architecture.md` | 系统架构、调用链路和模块边界 | | `docs/02_database.md` | 核心数据库表和读写边界 | | `docs/03_api_design.md` | 前端联调 API 文档 | | `docs/04_deployment.md` | 云服务器部署、更新和回滚 | | `docs/05_modules.md` | 功能模块说明 | -| `docs/06_handover.md` | 离职交接说明和风险清单 | +| `docs/06_maintenance_guide.md` | 开发维护说明和风险清单 | | `docs/07_troubleshooting.md` | 常见故障排查 | +| `docs/08_feature_code_map.md` | 功能到接口、代码和数据表的映射表 | +| `docs/09_prompt_template_catalog.md` | 提示词模板目录和调用说明 | ## 10. 重要约定 diff --git a/app/api/evaluations.py b/app/api/evaluations.py index 13ade26..24e1a6c 100644 --- a/app/api/evaluations.py +++ b/app/api/evaluations.py @@ -1,6 +1,6 @@ from pathlib import Path -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, Query from fastapi.responses import FileResponse from sqlalchemy.orm import Session @@ -15,9 +15,14 @@ router = APIRouter() @router.get("", response_model=ApiResponse[EvaluationListResponse]) -def list_evaluations(ctx: UserContext = Depends(get_user_context), db: Session = Depends(get_db)): +def list_evaluations( + page: int = Query(default=1, ge=1, description="页码,从 1 开始"), + page_size: int = Query(default=10, ge=1, le=100, description="每页数量,最大 100"), + ctx: UserContext = Depends(get_user_context), + db: Session = Depends(get_db), +): """历史评价:基于 user_id 查询完整训练后的评价记录。""" - return ok(EvaluationService(db).list_history(ctx.user_id)) + return ok(EvaluationService(db).list_history(ctx.user_id, page=page, page_size=page_size)) @router.get("/{evaluation_id}", response_model=ApiResponse[EvaluationDetailResponse]) diff --git a/app/repositories/evaluation_repository.py b/app/repositories/evaluation_repository.py index 18eda20..6a64191 100644 --- a/app/repositories/evaluation_repository.py +++ b/app/repositories/evaluation_repository.py @@ -1,4 +1,4 @@ -from sqlalchemy import delete, select +from sqlalchemy import delete, func, select from sqlalchemy.orm import Session from app.models.training_record import TrainingRecord, TrainingScoreDetail @@ -46,12 +46,19 @@ class EvaluationRepository: ) return self.db.scalar(stmt) - def list_by_user(self, user_id: str) -> list[TrainingRecord]: + def count_by_user(self, user_id: str) -> int: + """历史评价计数:按外部 user_id 统计完整训练后的评价记录总数。""" + stmt = select(func.count()).select_from(TrainingRecord).where(TrainingRecord.external_user_id == user_id) + return int(self.db.scalar(stmt) or 0) + + def list_by_user(self, user_id: str, limit: int, offset: int) -> list[TrainingRecord]: """历史评价:按外部 user_id 查询完整训练后的评价记录。""" stmt = ( select(TrainingRecord) .where(TrainingRecord.external_user_id == user_id) .order_by(TrainingRecord.created_at.desc()) + .limit(limit) + .offset(offset) ) return list(self.db.scalars(stmt).all()) diff --git a/app/schemas/evaluation.py b/app/schemas/evaluation.py index 8779328..43454b3 100644 --- a/app/schemas/evaluation.py +++ b/app/schemas/evaluation.py @@ -61,10 +61,22 @@ class EvaluationListItem(BaseModel): pdf_exported: bool +class PaginationMeta(BaseModel): + """分页信息:用于历史训练记录列表的前端分页展示。""" + + page: int + page_size: int + total: int + total_pages: int + has_next: bool + has_prev: bool + + class EvaluationListResponse(BaseModel): """历史评价列表响应。""" items: list[EvaluationListItem] + pagination: PaginationMeta class ExportPdfResponse(BaseModel): diff --git a/app/services/evaluation_service.py b/app/services/evaluation_service.py index a19b763..6a9775a 100644 --- a/app/services/evaluation_service.py +++ b/app/services/evaluation_service.py @@ -19,6 +19,7 @@ from app.schemas.evaluation import ( EvaluationListItem, EvaluationListResponse, EvaluationResponse, + PaginationMeta, ScoreDetailItem, ) from app.services.audit_service import AuditService @@ -235,9 +236,12 @@ class EvaluationService: """用户 ID 兼容:Django 返回的 id 写入 external_user_id,纯数字时同步写入源库 user_id。""" return int(user_id) if str(user_id).isdigit() else None - def list_history(self, user_id: str) -> EvaluationListResponse: + def list_history(self, user_id: str, page: int = 1, page_size: int = 10) -> EvaluationListResponse: """历史评价:按 Django 用户中心 ID 查询完整训练后的 training_record。""" - records = self.eval_repo.list_by_user(user_id) + total = self.eval_repo.count_by_user(user_id) + offset = (page - 1) * page_size + records = self.eval_repo.list_by_user(user_id, limit=page_size, offset=offset) + total_pages = (total + page_size - 1) // page_size if total else 0 return EvaluationListResponse( items=[ EvaluationListItem( @@ -249,7 +253,15 @@ class EvaluationService: pdf_exported=bool(record.pdf_file_path), ) for record in records - ] + ], + pagination=PaginationMeta( + page=page, + page_size=page_size, + total=total, + total_pages=total_pages, + has_next=page < total_pages, + has_prev=page > 1 and total_pages > 0, + ), ) def get_detail(self, evaluation_id: int, user_id: str) -> EvaluationDetailResponse: diff --git a/docs/00_project_overview.md b/docs/00_project_overview.md new file mode 100644 index 0000000..c26d41d --- /dev/null +++ b/docs/00_project_overview.md @@ -0,0 +1,135 @@ +# 项目总览 + +本文档用于快速说明医疗问诊 Agent FastAPI 后端的项目边界、功能现状、代码入口、部署方式、测试方法和后续维护重点。团队成员优先阅读本文,再按链接进入详细文档。 + +## 1. 项目定位 + +本项目是医疗教学平台中的 FastAPI 后端子服务,负责医疗问诊训练、教学互动、AI 评价、PDF 报告、AI 学习助手和机构知识库预留能力。 + +本项目不负责: + +- 用户注册登录 +- 用户管理后台 +- 病例 PDF 解析入库 +- 病例增删改后台 +- 多租户权限后台 +- HIS/LIS/PACS 对接 +- 前端最终 UI + +用户身份来自 Django 用户中心。前端携带 `Authorization: Bearer ` 调用 FastAPI,FastAPI 转发 token 到 Django `/api/user/users/me/`,以 Django 返回的 `id` 作为本服务统一 `user_id`。 + +## 2. 当前已实现功能 + +| 模块 | 状态 | 主要入口 | +|---|---|---| +| 用户鉴权 | 已实现 | `GET /api/v1/auth/me` | +| 病例读取 | 已实现 | `GET /api/v1/cases`、`GET /api/v1/cases/{case_id}` | +| 训练配置 | 已实现 | `GET /api/v1/training-config/recommended`、`GET /api/v1/training-config/options` | +| 训练会话 | 已实现 | `POST /api/v1/sessions` | +| 流式问诊 | 已实现 | `POST /api/v1/sessions/{session_id}/chat/stream` | +| 王主任练习提示 | 已实现 | `POST /api/v1/sessions/{session_id}/hints/stream` | +| 体格检查 / 辅助检查 | 已实现 | `physical-exams`、`auxiliary-exams` 相关接口 | +| 诊断 / 治疗提交 | 已实现 | `complete-inquiry`、`diagnosis`、`treatment` | +| AI 评价 | 已实现 | `POST /api/v1/sessions/{session_id}/evaluation` | +| 个人中心训练记录 | 已实现,支持分页 | `GET /api/v1/evaluations?page=1&page_size=10` | +| PDF 下载 | 已实现 | `GET /api/v1/evaluations/{evaluation_id}/download-pdf` | +| 教学互动 | 已实现 | `GET /api/v1/teaching/cases/{case_id}/items`、`POST /api/v1/teaching/evaluation` | +| AI 学习助手 | 已实现流式问答 | `POST /api/v1/learning-assistant/chat/stream` | +| 内容管理员知识库上传 | 基础链路已实现 | `POST /api/v1/knowledge-admin/documents/upload` | +| 健康检查 | 已实现 | `/health/live`、`/health/ready` | + +## 3. 推荐讲解顺序 + +1. 项目边界:FastAPI 是后端子服务,不做登录、病例管理和最终 UI。 +2. 认证链路:前端 token -> FastAPI -> Django `/me` -> 统一 `user_id`。 +3. 训练链路:病例 -> 配置 -> 会话 -> 问诊 -> 检查 -> 诊断 -> 治疗 -> 评价 -> PDF -> 历史记录。 +4. 教学互动链路:题目列表 -> 答题 -> 评价 -> PDF。 +5. AI 学习助手链路:机构知识库检索 -> LLM 流式回答;无知识库时降级通用回答。 +6. 数据库边界:平台基础数据由 Django/平台维护,FastAPI 主要写训练过程和训练结果。 +7. 部署和验证:Docker、`.env`、`/fastapi/docs`、测试命令。 +8. 后续生产化工作:索引补齐、任务队列、知识库构建、权限细化、日志监控。 + +## 4. 重点代码入口 + +| 目录/文件 | 作用 | +|---|---| +| `app/main.py` | FastAPI 应用工厂、CORS、路由挂载、异常处理 | +| `app/api/router.py` | 所有业务路由聚合 | +| `app/core/user_context.py` | 从请求中解析当前用户上下文 | +| `app/services/external_auth_service.py` | 调用 Django 用户中心 `/me` | +| `app/api/sessions.py` | 训练链路接口 | +| `app/services/session_service.py` | 训练会话、问诊、诊断治疗状态流转 | +| `app/services/order_service.py` | 体格检查和辅助检查 | +| `app/services/evaluation_service.py` | 训练评价生成和训练记录查询 | +| `app/services/teaching_service.py` | 教学互动题目与评价 | +| `app/services/pdf_export_service.py` | PDF 报告生成与下载 | +| `app/services/learning_assistant_service.py` | AI 学习助手 RAG + LLM 编排 | +| `app/services/document_ingestion_service.py` | 内容管理员 PDF 上传入库 | +| `app/agents` | Patient、Hint、Scoring、Report、Learning Assistant 等 LLM Agent | +| `app/prompts` | Markdown 提示词模板 | + +完整功能映射见 [08_feature_code_map.md](08_feature_code_map.md)。 + +## 5. 核心文档索引 + +| 文档 | 用途 | +|---|---| +| [01_architecture.md](01_architecture.md) | 系统架构、核心链路、模块边界 | +| [02_database.md](02_database.md) | 数据库表、读写边界、表含义 | +| [03_api_design.md](03_api_design.md) | 前端联调用 API 文档 | +| [04_deployment.md](04_deployment.md) | 云服务器部署、更新、回滚 | +| [05_modules.md](05_modules.md) | 模块职责、接口、代码入口 | +| [06_maintenance_guide.md](06_maintenance_guide.md) | 开发维护、风险、发布检查清单 | +| [07_troubleshooting.md](07_troubleshooting.md) | 常见故障排查 | +| [08_feature_code_map.md](08_feature_code_map.md) | 功能到接口、代码、数据表的映射表 | +| [09_prompt_template_catalog.md](09_prompt_template_catalog.md) | 提示词模板目录和调用说明 | + +## 6. 本地测试命令 + +```powershell +cd D:\Code\newfounder\medical-consultation-agent + +.\backend\.venv\Scripts\python.exe -m compileall app scripts tests +.\backend\.venv\Scripts\python.exe tests\test_core_logic.py +.\backend\.venv\Scripts\python.exe tests\test_demo_flow.py +.\backend\.venv\Scripts\python.exe tests\test_api_contract.py +``` + +## 7. 数据库与 Redis 检查 + +```powershell +.\backend\.venv\Scripts\python.exe scripts\check_final_schema.py +.\backend\.venv\Scripts\python.exe scripts\check_final_demo_readiness.py +``` + +推荐索引缺失不会阻断当前功能,但生产并发前需要补齐。 + +## 8. 云端部署验证 + +```bash +cd /home/code/medical-ai/fastapi +git pull origin main + +cd /home/code/medical-ai +docker compose build fastapi +docker compose up -d fastapi +docker compose logs --tail=200 fastapi +curl http://127.0.0.1:9000/health/ready +``` + +公网访问: + +```text +http://8.160.178.88/fastapi/docs +``` + +## 9. 发布前检查 + +- `git status --short` 无未确认改动,或已明确哪些改动尚未提交。 +- `.env` 不提交 Git。 +- `docs/03_api_design.md` 是前端联调依据。 +- `docs/02_database.md` 与当前 ORM 表名一致。 +- 自动化测试全部通过。 +- 云端 `/fastapi/docs` 可访问。 +- `GET /api/v1/auth/me` 可用。 +- 训练链路、教学互动、PDF、AI 学习助手各跑通一次。 diff --git a/docs/01_architecture.md b/docs/01_architecture.md index 46b0a75..c563620 100644 --- a/docs/01_architecture.md +++ b/docs/01_architecture.md @@ -1,6 +1,6 @@ # 系统架构说明 -本文档用于交接医疗问诊 Agent FastAPI 后端的系统边界、核心链路和外部依赖。 +本文档用于说明医疗问诊 Agent FastAPI 后端的系统边界、核心链路和外部依赖。 ## 1. 项目定位 diff --git a/docs/02_database.md b/docs/02_database.md index 8404135..89416e0 100644 --- a/docs/02_database.md +++ b/docs/02_database.md @@ -1,6 +1,6 @@ # 数据库说明 -本文档说明医疗问诊 Agent FastAPI 后端依赖的核心数据表、读写边界和交接注意事项。 +本文档说明医疗问诊 Agent FastAPI 后端依赖的核心数据表、读写边界和维护注意事项。 ## 1. 数据库边界 @@ -156,35 +156,35 @@ FastAPI 使用 Django 返回的 `id` 作为业务 `user_id`。 ## 5. 知识库预留表 -### `kb_knowledge_space` +### `kb_spaces` | 项 | 说明 | |---|---| | 用途 | 机构知识空间和 Milvus collection 映射 | | 关键字段 | `institution_id`、`collection_name`、`embedding_model`、`embedding_dim`、`status` | -### `kb_knowledge_document` +### `kb_documents` | 项 | 说明 | |---|---| | 用途 | 内容管理员上传 PDF 的元数据 | | 关键字段 | `institution_id`、`uploaded_by`、`file_name`、`file_sha256`、`status`、`parse_status`、`embedding_status`、`chunk_count` | -### `kb_knowledge_chunk` +### `kb_chunks` | 项 | 说明 | |---|---| | 用途 | PDF 分片文本和页码来源 | | 关键字段 | `document_id`、`institution_id`、`chunk_uid`、`page_start`、`page_end`、`chunk_text` | -### `kb_knowledge_ingestion_task` +### `kb_ingestion_tasks` | 项 | 说明 | |---|---| | 用途 | PDF 入库任务进度 | | 关键字段 | `document_id`、`institution_id`、`status`、`progress`、`current_step`、`error_message` | -### `kb_knowledge_query_log` +### `kb_query_logs` | 项 | 说明 | |---|---| @@ -204,7 +204,7 @@ Redis 用于短期会话 memory: Redis 不作为长期训练历史存储。 -## 7. 数据库交接注意事项 +## 7. 数据库维护注意事项 - FastAPI 不维护用户注册登录。 - FastAPI 不直接修改病例基础数据。 diff --git a/docs/03_api_design.md b/docs/03_api_design.md index c732452..070b02e 100644 --- a/docs/03_api_design.md +++ b/docs/03_api_design.md @@ -201,7 +201,7 @@ data: {"latency_ms":1200,"first_token_ms":300,"model":"deepseek-chat","fallback_ | 接口名称 | url | api | methods | params(入参) | response(返回参数) | |---|---|---|---|---|---| -| 训练记录列表 | `http://8.160.178.88/fastapi/api/v1/evaluations` | `/api/v1/evaluations` | `GET` | Query:`limit` 可选,默认20;`offset` 可选,默认0 | `data.items[]` 当前用户完整训练后的评价记录 | +| 训练记录列表 | `http://8.160.178.88/fastapi/api/v1/evaluations?page=1&page_size=10` | `/api/v1/evaluations` | `GET` | Query:`page` 选填,页码,从1开始,默认1;`page_size` 选填,每页数量,1-100,默认10 | `data.items[]` 当前用户完整训练后的评价记录;`data.pagination` 分页信息 | | 训练记录详情 / 评价详情 | `http://8.160.178.88/fastapi/api/v1/evaluations/{evaluation_id}` | `/api/v1/evaluations/{evaluation_id}` | `GET` | Path:`evaluation_id` 必填 | 完整评价详情,训练和教学互动共用 | | 导出 PDF | `http://8.160.178.88/fastapi/api/v1/evaluations/{evaluation_id}/export-pdf` | `/api/v1/evaluations/{evaluation_id}/export-pdf` | `POST` | Path:`evaluation_id` 必填 | `data.file_path`;`data.exported_at` | | 下载 PDF | `http://8.160.178.88/fastapi/api/v1/evaluations/{evaluation_id}/download-pdf` | `/api/v1/evaluations/{evaluation_id}/download-pdf` | `GET` | Path:`evaluation_id` 必填 | `application/pdf` 文件流,浏览器可直接下载 | diff --git a/docs/04_deployment.md b/docs/04_deployment.md index 54c1c2d..3b4a5d0 100644 --- a/docs/04_deployment.md +++ b/docs/04_deployment.md @@ -1,6 +1,6 @@ # 部署说明 -本文档用于交接云服务器部署、更新、回滚和验证流程。 +本文档用于说明云服务器部署、更新、回滚和验证流程。 ## 1. 服务器目录 diff --git a/docs/05_modules.md b/docs/05_modules.md index 4628be4..5fec579 100644 --- a/docs/05_modules.md +++ b/docs/05_modules.md @@ -111,11 +111,11 @@ |---|---| | 主要作用 | 查询当前用户训练记录和评价详情 | | 当前状态 | 已实现 | -| 相关接口 | `GET /api/v1/evaluations`、`GET /api/v1/evaluations/{evaluation_id}` | +| 相关接口 | `GET /api/v1/evaluations?page=1&page_size=10`、`GET /api/v1/evaluations/{evaluation_id}` | | 相关代码 | `app/api/evaluations.py`、`app/services/evaluation_service.py` | | 相关表 | `training_record`、`training_score_detail` | | 重要规则 | 必须按 `user_id` 隔离 | -| 后续优化 | 增加分页筛选、统计图表和能力画像 | +| 后续优化 | 增加筛选条件、统计图表和能力画像 | ## 11. AI 学习助手模块 @@ -125,7 +125,7 @@ | 当前状态 | 已实现正式流式接口;无知识库时自动降级为通用 LLM 回答 | | 相关接口 | `POST /api/v1/learning-assistant/chat/stream` | | 相关代码 | `app/api/learning_assistant.py`、`app/services/learning_assistant_service.py`、`app/agents/learning_assistant_agent.py` | -| 相关表 | `kb_knowledge_space`、`kb_knowledge_chunk`、`kb_knowledge_query_log` | +| 相关表 | `kb_spaces`、`kb_chunks`、`kb_query_logs` | | 外部依赖 | LLM、Embedding、Milvus | | 后续优化 | 查询改写、rerank、多轮记忆、来源引用格式优化、成本统计 | @@ -137,6 +137,6 @@ | 当前状态 | 接口和数据结构已预留,生产级大规模入库仍需压测 | | 相关接口 | `POST /api/v1/knowledge-admin/documents/upload`、文档列表、文档详情 | | 相关代码 | `app/api/knowledge_admin.py`、`app/services/document_ingestion_service.py`、`app/integrations/*` | -| 相关表 | `kb_knowledge_space`、`kb_knowledge_document`、`kb_knowledge_chunk`、`kb_knowledge_ingestion_task` | +| 相关表 | `kb_spaces`、`kb_documents`、`kb_chunks`、`kb_ingestion_tasks` | | 外部依赖 | Milvus、Embedding 服务、Celery | | 后续优化 | 任务队列监控、失败重试、分片策略、文件去重、权限后台 | diff --git a/docs/06_handover.md b/docs/06_handover.md deleted file mode 100644 index 803dd1a..0000000 --- a/docs/06_handover.md +++ /dev/null @@ -1,182 +0,0 @@ -# 交接文档 - -本文档用于说明医疗问诊 Agent FastAPI 后端当前状态、接手重点、风险和后续工作。 - -## 1. 当前项目状态 - -当前 FastAPI 后端已经完成第一阶段核心功能,并已接入 Django 用户中心、MySQL、Redis、LLM、PDF 下载和 AI 学习助手。 - -已完成能力: - -- Django access token 鉴权 -- 训练配置和推荐配置 -- 新建训练会话 -- AI 病人流式问诊 -- 王主任练习提示 -- 体格检查和辅助检查 -- 完成问诊、提交诊断、提交治疗 -- AI 评价和评分明细 -- PDF 报告导出和下载 -- 教学互动题目和评价 -- 个人中心训练记录和详情 -- AI 学习助手流式问答 -- 后台知识库上传和 RAG 架构预留 - -当前不是最终生产级完整系统,后续上线后仍需要逐模块做稳定性、监控、权限、性能和运维增强。 - -## 2. 优先关注 - -接手后建议按顺序确认: - -1. 能否本地启动 FastAPI。 -2. 能否连接云端 MySQL 和 Redis。 -3. `GET /api/v1/auth/me` 是否能通过 Django token 返回用户信息。 -4. 训练全流程是否能跑通。 -5. 教学互动是否能跑通。 -6. PDF 下载是否正常。 -7. AI 学习助手流式接口是否正常。 -8. 云服务器 `docker compose build fastapi` 是否成功。 -9. `.env` 是否与云服务器 docker-compose 服务名一致。 - -## 3. 当前重要约定 - -- FastAPI 不负责登录注册。 -- 用户身份来自 Django `/api/user/users/me/`。 -- FastAPI 使用 Django 返回的 `id` 作为 `user_id`。 -- 普通业务接口需要 `Authorization: Bearer `。 -- 训练记录只有完整流程完成后写入。 -- 问诊过程主要存在 Redis 短期 memory。 -- 检查结果只来自数据库。 -- AI 学习助手正式接口只保留流式接口。 -- 知识库未初始化时,学习助手仍应正常回答。 - -## 4. 发布前必须检查 - -本地检查: - -```powershell -cd D:\Code\newfounder\medical-consultation-agent - -.\backend\.venv\Scripts\python.exe -m compileall app scripts tests -.\backend\.venv\Scripts\python.exe tests\test_core_logic.py -.\backend\.venv\Scripts\python.exe tests\test_demo_flow.py -.\backend\.venv\Scripts\python.exe tests\test_api_contract.py -``` - -Git 检查: - -```bash -git status --short -git grep -n "<<<<<<<\|=======\|>>>>>>>" -git diff --check -``` - -敏感信息检查: - -```bash -git grep -n "sk-" -git grep -n "api_key" -git grep -n "password" -git grep -n "access_token" -git grep -n "secret" -``` - -服务器检查: - -```bash -cd /home/code/medical-ai -docker compose build fastapi -docker compose up -d fastapi -docker compose logs --tail=200 fastapi -curl http://127.0.0.1:9000/health/ready -``` - -## 5. 已知风险 - -| 风险 | 当前处理 | 后续建议 | -|---|---|---| -| LLM 调用超时或失败 | 已有异常返回和 mock/fallback 配置 | 增加限流、重试、熔断、成本统计 | -| 知识库真实大规模入库 | 当前为生产预留能力 | 压测 PDF 解析、embedding、Milvus 写入 | -| Celery 任务监控 | 已预留任务模块 | 增加 worker 部署、重试、任务状态看板 | -| 部分宽异常处理 | 保证 Demo 稳定 | 逐步收窄异常类型并补充结构化日志 | -| PDF 报告样式 | 当前可下载 | 后续按医院模板优化 | -| 前端最终样式 | 不在本仓库维护 | 以后按 API 文档继续联调 | - -## 6. 后续生产级优化建议 - -### 6.1 工程与质量 - -- 增加 CI/CD。 -- 增加 lint 和格式化。 -- 增加数据库 migration 管理。 -- 增加更多 service 单元测试。 -- 增加接口压测。 - -### 6.2 安全与权限 - -- 完善内容管理员角色来源。 -- 对后台知识库接口增加更细权限。 -- 增加审计日志检索。 -- 增加敏感信息脱敏。 - -### 6.3 AI 与 Agent - -- 优化 Patient Agent 回答稳定性。 -- 优化 Scoring Agent JSON 结构校验。 -- 增加评分重试和人工复核。 -- 增加 LLM 调用耗时、token、费用统计。 -- 增加提示词版本管理。 - -### 6.4 知识库 - -- 完善 PDF 解析质量检测。 -- 优化分片策略。 -- 接入真实 embedding 批量任务。 -- 增加 Milvus collection 生命周期管理。 -- 增加 RAG 命中率和用户反馈统计。 - -## 7. 常用命令 - -本地测试: - -```powershell -.\backend\.venv\Scripts\python.exe tests\test_api_contract.py -``` - -服务器日志: - -```bash -docker compose logs -f fastapi -``` - -服务器重启: - -```bash -docker compose up -d fastapi -``` - -拉取最新代码: - -```bash -cd /home/code/medical-ai/fastapi -git pull origin main -``` - -## 8. 交接清单 - -- [ ] `main` 分支是最新代码 -- [ ] `git status` 干净 -- [ ] 无 Git 冲突标记 -- [ ] 无真实密钥提交 -- [ ] `.env.example` 和 `.env.production.example` 已更新 -- [ ] README 已更新 -- [ ] API 文档已更新 -- [ ] 架构、数据库、部署、模块、排障文档已补齐 -- [ ] 本地测试全部通过 -- [ ] Docker build 通过 -- [ ] 云端 `/fastapi/docs` 可访问 -- [ ] `auth/me` 可用 -- [ ] 训练链路可用 -- [ ] 教学互动可用 -- [ ] PDF 下载可用 -- [ ] AI 学习助手流式可用 diff --git a/docs/06_maintenance_guide.md b/docs/06_maintenance_guide.md new file mode 100644 index 0000000..cc18435 --- /dev/null +++ b/docs/06_maintenance_guide.md @@ -0,0 +1,125 @@ +# 开发维护指南 + +本文档面向后续开发和运维人员,说明当前 FastAPI 后端的维护重点、发布检查、风险项和排查路径。 + +## 1. 维护目标 + +当前项目已经具备训练页面、教学互动、个人中心训练记录、PDF 报告、AI 学习助手和知识库预留链路。后续维护应优先保持以下原则: + +- 不改变已稳定 API 的语义。 +- 不把业务逻辑写入 API router。 +- 不在 FastAPI 中实现登录注册。 +- 不让 LLM 编造检查检验结果。 +- 不长期保存完整问诊聊天记录。 +- 不提交 `.env`、token、API Key、日志、PDF 生成物。 + +## 2. 重点维护模块 + +| 模块 | 代码入口 | 维护重点 | +|---|---|---| +| 用户鉴权 | `app/services/external_auth_service.py` | Django `/me` 地址、Authorization 透传、用户字段标准化 | +| 训练会话 | `app/services/session_service.py` | 状态流转、Redis memory、Patient Agent 调用 | +| 检查申请 | `app/services/order_service.py` | 数据库检查项、幂等写入、结果写入 memory | +| 评价生成 | `app/services/evaluation_service.py` | 评分规则、评分明细、PDF 数据结构 | +| 教学互动 | `app/services/teaching_service.py` | 教学题读取、答案评分、训练记录复用 | +| 个人中心 | `app/api/evaluations.py` | 分页、user_id 隔离、PDF 下载 | +| 学习助手 | `app/services/learning_assistant_service.py` | RAG 检索、来源引用、流式输出 | +| 知识库 | `app/services/document_ingestion_service.py` | 内容管理员权限、PDF 解析、embedding、Milvus | + +## 3. 发布前检查清单 + +```powershell +.\backend\.venv\Scripts\python.exe -m compileall app scripts tests +.\backend\.venv\Scripts\python.exe tests\test_core_logic.py +.\backend\.venv\Scripts\python.exe tests\test_demo_flow.py +.\backend\.venv\Scripts\python.exe tests\test_api_contract.py +.\backend\.venv\Scripts\python.exe scripts\check_final_demo_readiness.py +``` + +通过标准: + +- Python 编译无错误。 +- 核心逻辑测试通过。 +- Demo 流程测试通过。 +- API 契约测试通过。 +- 就绪检查 `ready=true`。 + +## 4. 数据库维护说明 + +FastAPI 当前依赖平台合并后的数据库,核心表包括: + +- `user` +- `institution` +- `department` +- `case_base` +- `traditional_case` +- `teaching_case` +- `case_exam_item` +- `scoring_rule` +- `training_session` +- `training_order` +- `training_submission` +- `training_record` +- `training_score_detail` +- `audit_logs` +- `prompt_templates` +- `kb_spaces` +- `kb_documents` +- `kb_chunks` +- `kb_query_logs` + +生产并发前需要重点确认推荐索引: + +- `case_base.case_type` +- `case_base.difficulty` +- `case_base.publish_status` +- `case_base.status` +- `scoring_rule.dimension` +- `scoring_rule.competency_dimension` +- `training_score_detail.dimension` + +## 5. Redis 维护说明 + +Redis 用于训练会话短期 memory,不作为长期训练记录保存。 + +生命周期: + +1. 创建训练会话时写入开场消息。 +2. 流式问诊时写入医生问题和 AI 病人回复。 +3. 申请检查时写入工具结果。 +4. 生成评价后释放当前会话 memory。 +5. 未完成训练依赖 TTL 自动过期。 + +## 6. LLM 和提示词维护说明 + +提示词模板说明见 [09_prompt_template_catalog.md](09_prompt_template_catalog.md)。 + +维护规则: + +- Patient Agent 只回答患者或家属视角,不输出诊断和治疗方案。 +- Hint Agent 用于练习提示,不能直接给完整答案。 +- Scoring Agent 必须输出可解析 JSON。 +- Report Agent 只整理报告,不重新评分。 +- Learning Assistant 命中知识库时必须引用 PDF 来源和页码;未命中时必须声明未检索到机构知识库参考。 + +## 7. 常见风险 + +| 风险 | 表现 | 处理 | +|---|---|---| +| Django 鉴权失败 | `AUTH_CREDENTIAL_REQUIRED` 或 `AUTH_INVALID` | 检查 token、`AUTH_USER_ME_URL`、Django 服务 | +| 问诊流式卡住 | 前端一直等待 SSE | 检查 LLM Key、Base URL、模型名、超时配置 | +| 检查项为空 | 前端无体格/辅助检查 | 检查 `case_exam_item.case_id` 和 `item_type` | +| 评价失败 | 无法生成报告 | 检查 `scoring_rule`、提交内容、LLM JSON 输出 | +| PDF 下载失败 | 文件不存在或 404 | 检查 `REPORT_STORAGE_DIR` 和文件权限 | +| 学习助手无来源 | `retrieval_hit=false` | 检查知识库表、Milvus、embedding 配置 | + +## 8. 开发规范 + +- API 层只做参数接收、用户上下文获取和响应封装。 +- Service 层承载业务流程。 +- Repository 层只做数据库读写。 +- Agent 层只做提示词拼接、LLM 调用和输出校验。 +- Model 层只定义 ORM。 +- Schema 层只定义 Pydantic 入参/出参。 +- 新增接口必须同步更新 `docs/03_api_design.md` 和 `docs/08_feature_code_map.md`。 +- 新增提示词必须同步更新 `docs/09_prompt_template_catalog.md`。 diff --git a/docs/07_troubleshooting.md b/docs/07_troubleshooting.md index af76037..c5b8573 100644 --- a/docs/07_troubleshooting.md +++ b/docs/07_troubleshooting.md @@ -1,6 +1,6 @@ # 常见故障排查 -本文档用于接手人快速定位本地和云服务器常见问题。 +本文档用于开发人员快速定位本地和云服务器常见问题。 ## 1. `/fastapi/docs` 打不开 @@ -155,7 +155,7 @@ retrieval_error=当前机构知识库暂未初始化或检索不可用... 排查知识库: - 用户是否有 `institution_id` -- 是否已有 `kb_knowledge_space` +- 是否已有 `kb_spaces` - Milvus 是否运行 - Embedding 是否配置 diff --git a/docs/08_feature_code_map.md b/docs/08_feature_code_map.md new file mode 100644 index 0000000..99f1050 --- /dev/null +++ b/docs/08_feature_code_map.md @@ -0,0 +1,99 @@ +# 功能-接口-代码-数据表映射表 + +本文档说明每个功能对应的 API、Router、Service、Repository、Model/数据表和关键逻辑。开发人员排查问题时优先从本表定位代码入口。 + +## 1. 通用调用规则 + +除健康检查外,业务接口都需要携带: + +```http +Authorization: Bearer +X-Entry-Scene: vue_frontend +X-Request-Id: <可选> +``` + +普通 JSON 接口统一返回: + +```json +{ + "code": "OK", + "message": "success", + "data": {} +} +``` + +SSE 流式接口返回 `event + data`,不包裹上述 JSON 结构。 + +## 2. 功能映射总表 + +| 功能模块 | 功能点 | API | Router | Service | Repository | Model/表 | 关键说明 | +|---|---|---|---|---|---|---|---| +| 用户鉴权 | 当前用户信息 | `GET /api/v1/auth/me` | `app/api/auth.py::auth_me` | `app/services/external_auth_service.py::authenticate` | 无本地仓储 | Django `user`、`institution`、`department` | FastAPI 转发 token 到 Django `/api/user/users/me/`,使用 Django `id` 作为统一 `user_id` | +| Agent 状态 | Hello / 功能开关 | `GET /api/v1/agent/hello` | `app/api/agent.py::agent_hello` | `app/core/config.py::as_public_dict` | `app/services/audit_service.py` | `audit_logs` | 给前端展示当前模型、PDF、知识库等能力开关 | +| 病例读取 | 病例列表 | `GET /api/v1/cases` | `app/api/cases.py::list_cases` | `app/services/case_service.py::list_cases` | `app/repositories/case_repository.py` | `case_base`、`traditional_case`、`teaching_case` | 只读已发布启用病例,不做病例新增/删除 | +| 病例读取 | 病例详情 | `GET /api/v1/cases/{case_id}` | `app/api/cases.py::get_case_detail` | `app/services/case_service.py::get_case_detail` | `app/repositories/case_repository.py` | `case_base`、`traditional_case`、`teaching_case` | 前端展示病例基础信息,不作为病例管理后台 | +| 训练配置 | 推荐配置信息 | `GET /api/v1/training-config/recommended?case_id=1` | `app/api/training_config.py::get_recommended_training_config` | `app/services/training_config_service.py::get_recommended` | `app/repositories/case_repository.py` | `case_base`、`traditional_case` | 返回默认就诊环境、年龄段、文化程度、性格等病人初始化信息 | +| 训练配置 | 可选配置信息 | `GET /api/v1/training-config/options?case_id=1` | `app/api/training_config.py::get_training_config_options` | `app/services/training_config_service.py::get_options` | `app/repositories/case_repository.py` | `case_base` | 返回前端可选配置项,用于自定义病人信息 | +| 训练页面 | 新建会话 | `POST /api/v1/sessions` | `app/api/sessions.py::create_session` | `app/services/session_service.py::create_session` | `app/repositories/session_repository.py`、`case_repository.py` | `training_session`、Redis memory | 创建会话、写入 user_id、初始化短期 memory 和病人开场白 | +| 训练页面 | 流式问诊 | `POST /api/v1/sessions/{session_id}/chat/stream` | `app/api/sessions.py::chat_stream` | `app/services/session_service.py::stream_chat` | `session_repository.py`、`case_repository.py` | `training_session`、Redis memory | Patient Agent SSE 输出 `message_delta`、`message_done`、`error` | +| 训练页面 | 王主任练习提示 | `POST /api/v1/sessions/{session_id}/hints/stream` | `app/api/sessions.py::stream_hints` | `app/services/session_service.py::stream_hints` | `session_repository.py`、`case_repository.py` | `training_session`、Redis memory | 根据病例和当前会话生成一句话练习提示 | +| 训练页面 | 结构化提示 | `POST /api/v1/sessions/{session_id}/hints` | `app/api/sessions.py::generate_hints` | `app/services/session_service.py::generate_hints` | `session_repository.py`、`case_repository.py` | `training_session`、Redis memory | 返回缺失维度、下一步问题、推荐检查;当前前端主用流式提示 | +| 训练页面 | 检查项列表 | `GET /api/v1/sessions/{session_id}/order-items` | `app/api/sessions.py::list_order_items` | `app/services/order_service.py::list_order_items` | `case_repository.py`、`session_repository.py` | `case_exam_item` | 返回当前病例全部可申请检查项,不返回结果 | +| 训练页面 | 体格检查列表 | `GET /api/v1/sessions/{session_id}/physical-exams` | `app/api/sessions.py::list_physical_exam_items` | `app/services/order_service.py::list_physical_exam_items` | `case_repository.py`、`session_repository.py` | `case_exam_item` | 过滤 `item_type=physical_exam` | +| 训练页面 | 辅助检查列表 | `GET /api/v1/sessions/{session_id}/auxiliary-exams` | `app/api/sessions.py::list_auxiliary_exam_items` | `app/services/order_service.py::list_auxiliary_exam_items` | `case_repository.py`、`session_repository.py` | `case_exam_item` | 过滤非体格检查项 | +| 训练页面 | 体格检查结果 | `POST /api/v1/sessions/{session_id}/physical-exams/{item_code}` | `app/api/sessions.py::create_physical_exam_order` | `app/services/order_service.py::create_physical_exam_order` | `case_repository.py`、`session_repository.py` | `case_exam_item`、`training_order`、Redis memory | 检查结果只来自数据库;同一会话同一 `item_code` 幂等 | +| 训练页面 | 辅助检查结果 | `POST /api/v1/sessions/{session_id}/auxiliary-exams/{item_code}` | `app/api/sessions.py::create_auxiliary_exam_order` | `app/services/order_service.py::create_auxiliary_exam_order` | `case_repository.py`、`session_repository.py` | `case_exam_item`、`training_order`、Redis memory | 检查结果写入短期 memory,供后续评价使用 | +| 训练页面 | 完成问诊 | `POST /api/v1/sessions/{session_id}/complete-inquiry` | `app/api/sessions.py::complete_inquiry` | `app/services/session_service.py::complete_inquiry` | `session_repository.py` | `training_session`、Redis memory | 校验至少有医生问诊后进入诊断阶段 | +| 训练页面 | 提交诊断 | `POST /api/v1/sessions/{session_id}/diagnosis` | `app/api/sessions.py::submit_diagnosis` | `app/services/session_service.py::submit_diagnosis` | `session_repository.py` | `training_submission`、`training_session` | 保存主诊断、鉴别诊断、诊断依据 | +| 训练页面 | 提交治疗 | `POST /api/v1/sessions/{session_id}/treatment` | `app/api/sessions.py::submit_treatment` | `app/services/session_service.py::submit_treatment` | `session_repository.py` | `training_submission`、`training_session` | 保存治疗原则、措施、风险预案、沟通和随访 | +| 训练页面 | 生成评价 | `POST /api/v1/sessions/{session_id}/evaluation` | `app/api/sessions.py::create_evaluation` | `app/services/evaluation_service.py::create_evaluation` | `evaluation_repository.py`、`source_case_repository.py`、`session_repository.py` | `training_record`、`training_score_detail`、`scoring_rule` | 读取评分规则、短期 memory、诊断治疗提交内容,调用 Scoring Agent | +| 个人中心 | 训练记录列表 | `GET /api/v1/evaluations?page=1&page_size=10` | `app/api/evaluations.py::list_evaluations` | `app/services/evaluation_service.py::list_history` | `app/repositories/evaluation_repository.py` | `training_record` | 按 Django user_id 隔离,支持分页 | +| 个人中心 | 评价详情 | `GET /api/v1/evaluations/{evaluation_id}` | `app/api/evaluations.py::get_evaluation_detail` | `app/services/evaluation_service.py::get_detail` | `evaluation_repository.py` | `training_record`、`training_score_detail` | 训练评价和教学互动评价共用详情接口 | +| 个人中心 | 导出 PDF | `POST /api/v1/evaluations/{evaluation_id}/export-pdf` | `app/api/evaluations.py::export_pdf` | `app/services/pdf_export_service.py::export` | `evaluation_repository.py` | `training_record` | 生成本地 PDF 并写入 `pdf_file_path` | +| 个人中心 | 下载 PDF | `GET /api/v1/evaluations/{evaluation_id}/download-pdf` | `app/api/evaluations.py::download_pdf` | `app/services/pdf_export_service.py::export` | `evaluation_repository.py` | `training_record` | 校验 user_id 后返回 `application/pdf` 文件流 | +| 教学互动 | 获取教学列表 | `GET /api/v1/teaching/cases/{case_id}/items` | `app/api/teaching.py::get_teaching_items` | `app/services/teaching_service.py::list_items` | `teaching_repository.py`、`case_repository.py` | `case_base`、`teaching_case` | 返回题目、选项、答案、解析文本、视频 | +| 教学互动 | 生成评价 | `POST /api/v1/teaching/evaluation` | `app/api/teaching.py::create_teaching_evaluation` | `app/services/teaching_service.py::create_evaluation` | `evaluation_repository.py`、`teaching_repository.py` | `training_record`、`training_score_detail`、`teaching_case` | 根据作答结果生成教学互动评价 | +| AI 学习助手 | 流式问答 | `POST /api/v1/learning-assistant/chat/stream` | `app/api/learning_assistant.py::learning_assistant_stream_chat` | `app/services/learning_assistant_service.py::stream_chat` | `knowledge_base_repository.py` | `kb_spaces`、`kb_chunks`、`kb_query_logs` | 优先 RAG 检索;无知识库时返回 `retrieval_hit=false` 后继续 LLM 回答 | +| AI 学习助手 | 非流式调试问答 | `POST /api/v1/learning-assistant/chat` | `app/api/learning_assistant.py::learning_assistant_chat` | `app/services/learning_assistant_service.py::chat` | `knowledge_base_repository.py` | `kb_spaces`、`kb_chunks`、`kb_query_logs` | `include_in_schema=False`,正式前端不使用 | +| 知识库管理 | 上传 PDF | `POST /api/v1/knowledge-admin/documents/upload` | `app/api/knowledge_admin.py::upload_knowledge_document` | `app/services/document_ingestion_service.py::upload_pdf` | `knowledge_base_repository.py` | `kb_spaces`、`kb_documents`、`kb_ingestion_tasks`、`kb_chunks` | 仅内容管理员使用;学生端不展示入口 | +| 健康检查 | 存活检查 | `GET /health/live` | `app/api/health.py::live` | 无 | 无 | 无 | 检查 FastAPI 进程可响应 | +| 健康检查 | 就绪检查 | `GET /health/ready` | `app/api/health.py::ready` | `settings.deployment_config_errors` | 数据库连接 | MySQL、Redis 配置 | 用于部署验证 | + +## 3. 核心流程说明 + +### 3.1 训练链路 + +1. 前端调用 `GET /api/v1/auth/me` 验证用户。 +2. 前端读取病例和训练配置。 +3. 前端调用 `POST /api/v1/sessions` 创建 `training_session`,后端初始化 Redis 短期 memory。 +4. 前端调用 `POST /api/v1/sessions/{session_id}/chat/stream` 进行流式问诊。 +5. 用户申请体格检查或辅助检查,后端从 `case_exam_item` 返回固定结果并写入 `training_order` 和 Redis memory。 +6. 用户完成问诊、提交诊断、提交治疗。 +7. 后端读取 `scoring_rule`、问诊 memory、检查结果、诊断治疗提交内容,调用 Scoring Agent。 +8. 完成评价后写入 `training_record` 和 `training_score_detail`,释放 Redis memory。 +9. 用户通过评价详情或 PDF 下载查看结果。 + +### 3.2 教学互动链路 + +1. 前端调用 `GET /api/v1/teaching/cases/{case_id}/items` 获取题目、选项、答案、解析、视频。 +2. 前端提交作答到 `POST /api/v1/teaching/evaluation`。 +3. 后端生成评价并写入 `training_record`、`training_score_detail`。 +4. 评价详情和 PDF 下载复用个人中心接口。 + +### 3.3 AI 学习助手链路 + +1. 前端调用 `POST /api/v1/learning-assistant/chat/stream`。 +2. 后端根据当前用户 `institution_id` 查找知识空间。 +3. 有知识库时:问题 embedding -> Milvus 检索 -> MySQL 读取 chunk 元数据 -> 拼接来源给 LLM。 +4. 无知识库或检索失败时:返回 `retrieval_hit=false`,继续给出通用 LLM 学习回答。 +5. 后端写入 `kb_query_logs`,记录命中、来源和耗时。 + +## 4. 重要边界 + +- FastAPI 不维护用户注册登录。 +- FastAPI 不负责病例 PDF 解析入库。 +- 检查结果必须来自 `case_exam_item`,不允许 LLM 编造。 +- 问诊聊天记录只作为 Redis 短期 memory,评价完成后释放。 +- 长期训练结果只写入 `training_record` 和 `training_score_detail`。 +- 教学互动评价和训练评价共用评价详情、PDF 下载、个人中心记录列表。 +- 知识库上传是内容管理员能力,学生端不展示入口。 diff --git a/docs/09_prompt_template_catalog.md b/docs/09_prompt_template_catalog.md new file mode 100644 index 0000000..c466aec --- /dev/null +++ b/docs/09_prompt_template_catalog.md @@ -0,0 +1,161 @@ +# 提示词模板目录 + +本文档说明当前项目中 Markdown 提示词模板的用途、调用位置、输入数据和输出要求。新增或修改提示词时,需要同步更新本文档。 + +## 1. 提示词加载规则 + +当前提示词分为两类: + +1. 文件模板:存放在 `app/prompts`,由对应 Agent 或服务读取。 +2. 代码内系统提示:部分 Agent 会在代码中拼接系统提示,同时结合病例、会话、检查结果和评分规则构造输入。 + +维护要求: + +- 医疗问诊训练类提示词不得替代真实临床诊疗。 +- AI 病人不得主动泄露病例隐藏信息。 +- 检查检验结果只能来自数据库。 +- 评分提示词必须要求结构化 JSON,不能返回纯长文本。 +- 学习助手命中知识库时必须引用来源;未命中时必须说明未检索到机构知识库参考。 + +## 2. 模板清单 + +| 提示词模板 | 文件路径 | 使用场景 | 调用模块 | 触发接口 | 输入数据 | 输出格式 | 说明 | +|---|---|---|---|---|---|---|---| +| 新手病例提示 | `app/prompts/hint/novice_case_hint.md` | 生成结构化练习提示 | `app/agents/hint_agent.py::HintAgent.generate` | `POST /api/v1/sessions/{session_id}/hints` | 病例标题、主诉、关键症状、关键检查、当前会话 memory、已申请检查、最后一句用户问题 | JSON | 返回 `hints`、`missing_dimensions`、`next_questions`、`recommended_orders`;LLM 失败时使用兜底提示 | +| 新手提示旧模板 | `app/prompts/hint/novice_hint.md` | 早期新手提示模板保留 | 当前主流程不直接调用 | 无主入口 | 无 | 文本 | 保留用于兼容和后续提示词对比,当前推荐使用 `novice_case_hint.md` | +| AI 病人通用模板 | `app/prompts/patient/free_chat.md` | AI 病人通用问诊风格参考 | 当前 `PatientAgent` 主要在代码内拼接系统提示 | `POST /api/v1/sessions/{session_id}/chat/stream` | 病例、隐藏信息、初始化配置、短期 memory、医生问题 | 纯文本 | 文件作为模板资产保留;实际回复规则见 `PatientAgent._build_messages` | +| AI 病人新手模板 | `app/prompts/patient/novice.md` | 新手/练习模式风格参考 | 当前 `PatientAgent` 主要在代码内拼接系统提示 | `POST /api/v1/sessions/{session_id}/chat/stream` | 同上 | 纯文本 | 新手与练习模式已合并为训练模式,提示功能由王主任练习提示承担 | +| AI 病人练习模板 | `app/prompts/patient/practice.md` | 练习模式风格参考 | 当前 `PatientAgent` 主要在代码内拼接系统提示 | `POST /api/v1/sessions/{session_id}/chat/stream` | 同上 | 纯文本 | 练习时只回答被问到的信息,不主动给诊断建议 | +| AI 病人教学模板 | `app/prompts/patient/teaching.md` | 教学风格参考 | 当前 `PatientAgent` 主要在代码内拼接系统提示 | 训练流式问诊相关接口 | 同上 | 纯文本 | 用于后续教学模式对话扩展 | +| 医生问题润色 | `app/prompts/polish/doctor_question_polish.md` | 医学生提问润色能力预留 | 当前主流程不直接调用 | 无主入口 | 医生原始问题、病例场景 | JSON 或文本 | 后续用于前端输入优化时启用 | +| 百分制评分 | `app/prompts/scoring/default_percentage.md` | 训练评价百分制评分模板 | 当前 `ScoringAgent` 在代码内拼接评分系统提示,文件作为模板资产 | `POST /api/v1/sessions/{session_id}/evaluation` | 病例、问诊 memory、检查、诊断治疗、评分规则、指南引用 | JSON | 输出 `total_score`、`dimension_scores`、`score_details`、`errors`、`improvement_plan` 等 | +| 五分制评分 | `app/prompts/scoring/default_five_point.md` | 训练评价五分制评分模板 | 当前 `ScoringAgent` 支持五分制转换,文件作为模板资产 | `POST /api/v1/sessions/{session_id}/evaluation` | 同百分制 | JSON | `score_type=five_point` 时将百分制结构转换为五分制 | +| 儿科肺炎评分 | `app/prompts/scoring/pediatrics_pneumonia.md` | 儿科支气管肺炎病例评分参考 | 当前评分依赖 `scoring_rule` 和病例数据,文件作为病例专科提示资产 | `POST /api/v1/sessions/{session_id}/evaluation` | 儿科病例、检查、诊断、治疗、评分规则 | JSON | 用于病例专科评分要求维护 | +| 教学互动评分 | `app/prompts/scoring/teaching_interaction_evaluation.md` | 教学互动答题评价 | `app/agents/scoring_agent.py::ScoringAgent.score_teaching` | `POST /api/v1/teaching/evaluation` | 病例、教学题、标准答案、解析、学生作答、评分规则 | JSON | 评价答题正确率、错误原因、学习建议和教学维度表现 | +| 报告整理 | `app/prompts/report/evaluation_report.md` | 评价报告整理模板资产 | `app/agents/report_agent.py::ReportAgent.build_report` 使用代码内校验整理 | 评价生成、PDF 下载 | Scoring Agent 输出 | JSON | Report Agent 不重新评分,只补齐报告字段 | +| 学习助手命中知识库 | `app/prompts/learning_assistant/rag_answer.md` | RAG 命中时的回答模板资产 | 当前 `LearningAssistantAgent._messages` 在代码内拼接同等规则 | `POST /api/v1/learning-assistant/chat/stream` | 用户问题、知识库片段、PDF 名称、页码、chunk_uid | 流式纯文本 | 回答中必须引用来源编号,不得编造 PDF 和页码 | +| 学习助手未命中知识库 | `app/prompts/learning_assistant/no_reference_answer.md` | 未命中知识库时的回答模板资产 | 当前 `LearningAssistantAgent._messages` 在代码内拼接同等规则 | `POST /api/v1/learning-assistant/chat/stream` | 用户问题 | 流式纯文本 | 开头必须说明“未检索到本机构知识库参考” | + +## 3. 训练问诊提示词调用链 + +```text +POST /api/v1/sessions/{session_id}/chat/stream + -> app/api/sessions.py::chat_stream + -> app/services/session_service.py::stream_chat + -> app/agents/patient_agent.py::PatientAgent.stream_reply + -> PatientAgent._build_messages + -> LLM stream_chat +``` + +输入包括: + +- `case_base.chief_complaint` +- `case_base.ai_patient_profile` +- `case_base.hidden_patient_info` +- `patient_config` +- Redis 短期 memory +- 医学生当前问题 + +输出要求: + +- 纯文本 +- 1 到 3 句话 +- 患者或家属口吻 +- 不输出 JSON / Markdown / 思考过程 +- 不主动泄露隐藏信息 +- 不编造检查结果 + +## 4. 王主任练习提示调用链 + +```text +POST /api/v1/sessions/{session_id}/hints/stream + -> app/api/sessions.py::stream_hints + -> app/services/session_service.py::stream_hints + -> HintAgent 或业务兜底逻辑 +``` + +结构化提示接口: + +```text +POST /api/v1/sessions/{session_id}/hints + -> app/api/sessions.py::generate_hints + -> app/services/session_service.py::generate_hints + -> app/agents/hint_agent.py::HintAgent.generate + -> app/prompts/hint/novice_case_hint.md +``` + +输出字段: + +```json +{ + "hints": [], + "missing_dimensions": [], + "next_questions": [], + "recommended_orders": [] +} +``` + +LLM 输出非法 JSON 或调用失败时,`HintAgent._fallback_output` 会根据病例关键点和已申请检查生成稳定提示。 + +## 5. 训练评价提示词调用链 + +```text +POST /api/v1/sessions/{session_id}/evaluation + -> app/api/sessions.py::create_evaluation + -> app/services/evaluation_service.py::create_evaluation + -> app/agents/scoring_agent.py::ScoringAgent.score + -> app/agents/report_agent.py::ReportAgent.build_report +``` + +输入包括病例、Redis 问诊 memory、`training_order` 检查结果、`training_submission` 诊断治疗提交、`scoring_rule` 评分规则和预留指南引用。 + +输出必须包含 `score_type`、`total_score`、`dimension_scores`、`score_details`、`errors`、`improvement_plan`、`evidence_summary`、`guideline_refs`、`overall_comment`。 + +异常处理: + +- LLM 异常、JSON 解析失败、字段缺失时,`ScoringAgent._fallback_score` 返回稳定结构。 +- `ReportAgent` 只整理字段,不重新评分。 + +## 6. 教学互动评价提示词调用链 + +```text +POST /api/v1/teaching/evaluation + -> app/api/teaching.py::create_teaching_evaluation + -> app/services/teaching_service.py::create_evaluation + -> app/agents/scoring_agent.py::ScoringAgent.score_teaching +``` + +输入包括病例信息、教学题列表、标准答案、解析文本、学生作答和评分规则。输出复用训练评价结构,写入 `training_record` 和 `training_score_detail`。 + +## 7. AI 学习助手提示词调用链 + +```text +POST /api/v1/learning-assistant/chat/stream + -> app/api/learning_assistant.py::learning_assistant_stream_chat + -> app/services/learning_assistant_service.py::stream_chat + -> app/agents/learning_assistant_agent.py::LearningAssistantAgent.stream_answer +``` + +命中知识库时: + +- 输入用户问题和检索到的知识片段。 +- 每个来源包含 PDF 名称、页码、chunk_uid、引用文本。 +- 回答必须标注来源编号,例如 `【来源1】`。 + +未命中知识库时: + +- 开头必须说明未检索到机构知识库参考。 +- 不得伪造 PDF、页码或指南名称。 +- 回答仅作为医学学习参考。 + +## 8. 后续维护规则 + +新增提示词时必须同时确认: + +- 是否有明确调用接口。 +- 是否有明确 Agent。 +- 输入字段是否来自可信数据源。 +- 输出格式是否可被前端或服务稳定解析。 +- 是否有 fallback。 +- 是否需要同步 `prompt_templates` 元数据。 +- 是否需要新增 API 测试或 Agent 单元测试。 diff --git a/tests/test_api_contract.py b/tests/test_api_contract.py index c770b8d..fa987cf 100644 --- a/tests/test_api_contract.py +++ b/tests/test_api_contract.py @@ -362,6 +362,25 @@ def run_api_contract_tests() -> None: assert detail.status_code == 200 assert detail.json()["data"]["evaluation_id"] == evaluation_id + history_page_one = client.get("/api/v1/evaluations?page=1&page_size=1", headers=headers) + assert history_page_one.status_code == 200 + history_data = history_page_one.json()["data"] + assert len(history_data["items"]) == 1 + assert history_data["pagination"]["page"] == 1 + assert history_data["pagination"]["page_size"] == 1 + assert history_data["pagination"]["total"] >= 2 + assert history_data["pagination"]["total_pages"] >= 2 + assert history_data["pagination"]["has_next"] is True + assert history_data["pagination"]["has_prev"] is False + + empty_cross_user_history = client.get( + "/api/v1/evaluations?page=1&page_size=10", + headers={"Authorization": "Bearer api_user_002_token", "X-Entry-Scene": "api_test"}, + ) + assert empty_cross_user_history.status_code == 200 + assert empty_cross_user_history.json()["data"]["items"] == [] + assert empty_cross_user_history.json()["data"]["pagination"]["total"] == 0 + cross_user_detail = client.get( f"/api/v1/evaluations/{evaluation_id}", headers={"Authorization": "Bearer api_user_002_token", "X-Entry-Scene": "api_test"},