Files
fastapi/docs/03_api_design.md
T
2026-06-01 14:28:43 +08:00

22 KiB
Raw Blame History

前端 API 对接文档

本文档面向 Vue 前端与后续正式前端开发,描述当前第一版 Demo 已实现的后端接口、请求头、状态流转、字段结构和错误处理规则。

1. 通用约定

1.1 Base URL

本地开发默认地址:

http://127.0.0.1:8000/api/v1

前端默认配置:

Vite dev server: http://127.0.0.1:5173
API base: http://127.0.0.1:8000/api/v1

1.2 必传 Header

正式联调时,前端必须携带宿主系统登录态,推荐使用 Authorization

Header 类型 说明
Authorization string 宿主系统登录 token,例如 Bearer <token>。医疗问诊 Agent 会转发到 Django /api/user/users/me/
Cookie string 如果宿主系统使用 Cookie 登录,浏览器可通过 withCredentials 携带 Cookie。

本地 Demo 兼容以下 Header

Header 类型 说明
X-User-Id string Demo 兼容用户标识。正式联调启用外部鉴权后,以 Django /me/ 返回的用户为准。
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 流式接口外,后端统一返回:

{
  "code": "OK",
  "message": "success",
  "data": {}
}

前端处理规则:

  • code === "OK":读取 data
  • code !== "OK":展示 message,不要直接展示底层异常。
  • HTTP 401 通常表示缺少 X-User-Id
  • HTTP 404 通常表示资源不存在或不属于当前 user_id
  • HTTP 400 通常表示当前状态不允许操作或入参不合法。

1.4 常见错误码

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,不允许确认导入。
AUTH_CREDENTIAL_REQUIRED 启用外部鉴权后未携带 Authorization/Cookie 引导用户回宿主系统登录。
AUTH_USER_CENTER_UNAVAILABLE Django 用户中心不可访问或超时 提示稍后重试,检查用户中心服务。
AUTH_USER_INVALID Django 返回用户无效或登录态过期 引导用户重新登录。
AUTH_USER_PARSE_FAILED Django 返回结构无法提取 user_id 联系后端确认用户接口字段。

2. 前端主流程

入口页
  -> GET /auth/me
  -> 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

训练会话状态流转:

inquiry -> diagnosis -> treatment -> evaluating -> evaluated

前端按钮启用规则:

阶段 允许操作
inquiry 问诊、查看提示、申请检查、完成问诊
diagnosis 提交诊断
treatment 提交治疗方案
evaluating 生成评价报告
evaluated 查看报告、导出 PDF、查看历史

3. 认证接口

GET /auth/me

用途:进入医疗问诊 Agent 前校验当前登录用户,并返回标准化用户信息。

调用链路:

Vue 前端
  -> GET /api/v1/auth/me
FastAPI 医疗问诊 Agent
  -> GET http://192.168.2.76:8000/api/user/users/me/
Django 用户中心
  -> 返回当前登录用户
FastAPI
  -> 提取 user_id 并返回标准结构

正式联调请求头:

Authorization: Bearer <宿主系统token>
X-Entry-Scene: mac_vue_dev

Cookie 登录时:

Cookie: sessionid=...
X-Entry-Scene: mac_vue_dev

Response data

{
  "user_id": "123",
  "source": "django_user_center",
  "username": "zhangsan",
  "display_name": "张三",
  "tenant_id": null,
  "role": null
}

本地 Demo 兼容模式中,如果未开启外部鉴权或允许 X-User-Id 兜底,返回:

{
  "user_id": "demo_user_001",
  "source": "demo_header",
  "username": null,
  "display_name": null,
  "tenant_id": null,
  "role": null
}

前端处理规则:

  • 进入 Agent 时先调用 /auth/me
  • 成功后使用返回的 user_id 展示当前用户。
  • 后续业务接口继续携带相同 Authorization 或 Cookie。
  • 正式联调不要让用户手动输入 X-User-Id

4. Agent Hello

GET /agent/hello

用途:检查后端连接,返回当前用户上下文和 Demo 能力开关。

Response data

{
  "user": {
    "user_id": "demo_user_001",
    "tenant_id": null,
    "role": null
  },
  "features": {
    "stream_chat": true,
    "score_types": ["percentage", "five_point"],
    "pdf_export": true,
    "knowledge_search": true,
    "llm_mock_enabled": 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"
  }
}

前端展示说明:

  • 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:短期记忆后端,通常为 redismemory

5. 病例接口

GET /cases

用途:获取病例列表。不会返回标准答案、隐藏病史、评分细则。

Query

参数 类型 必填 说明
department_id number 科室筛选。
training_type string 训练类别筛选:case_analysisdiagnosis_treatmentconsultation
mode string 模式筛选:practiceteaching

Response data

{
  "items": [
    {
      "id": 2,
      "case_code": "SRC_2",
      "department_id": 1,
      "title": "支气管肺炎 - 6岁男性患儿",
      "difficulty": "medium",
      "chief_complaint": "发热、咳嗽4天,喘息1天。",
      "supported_training_type": "diagnosis_treatment",
      "supported_mode": "free_chat",
      "has_teaching_video": false,
      "has_knowledge_points": true,
      "has_quiz": false
    }
  ]
}

GET /cases/{case_id}

用途:获取病例训练入口详情。不会返回标准诊断、标准治疗、隐藏病史。

Response data

{
  "id": 2,
  "case_code": "SRC_2",
  "title": "支气管肺炎 - 6岁男性患儿",
  "department": "儿科",
  "difficulty": "medium",
  "patient": {
    "name": null,
    "age": 6,
    "gender": "male",
    "occupation": null
  },
  "chief_complaint": "发热、咳嗽4天,喘息1天。",
  "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

用途:删除病例前统计影响范围。前端删除弹窗先调用该接口。

Response data

{
  "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

{
  "confirm": true,
  "delete_training_data": true
}

Response data

{
  "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

6. 会话与问诊接口

POST /sessions

用途:创建训练会话,初始化短期 memory。

Request

{
  "case_id": 2,
  "training_type": "diagnosis_treatment",
  "mode": "practice",
  "score_type": "percentage"
}

字段说明:

字段 允许值 说明
training_type case_analysisdiagnosis_treatmentconsultation 训练类别。
mode practiceteaching 训练模式。后端兼容旧值 novice,会归一为 practice
score_type percentagefive_point 评分输出类型。

Response data

{
  "session_id": 10,
  "session_code": "sess_20260528100000_abcd1234",
  "status": "inquiry",
  "patient_opening": "家长:医生,孩子发热咳嗽好几天了,昨天开始喘得厉害,精神也不太好。"
}

POST /sessions/{session_id}/chat

用途:普通非流式问诊。

Request

{
  "message": "孩子发热几天了?最高体温多少?"
}

Response data

{
  "reply": "发热有4天了,最高烧到39度多,吃了退烧药能降下来,但过几个小时又会烧。",
  "latency_ms": 2500,
  "model": "deepseek-v4-pro",
  "fallback_used": false
}

校验:

  • 会话必须属于当前 X-User-Id
  • 当前状态必须为 inquiry
  • message 长度 1 到 2000。

POST /sessions/{session_id}/chat/stream

用途:SSE 流式问诊。当前 Chat 页面优先使用该接口。

Request 同普通问诊。

SSE 事件:

event: message_delta
data: {"delta":"发热有4天了,"}

event: message_done
data: {"latency_ms":3200,"first_token_ms":800,"model":"deepseek-v4-pro","fallback_used":false}

event: error
data: {"code":"LLM_STREAM_TIMEOUT","message":"AI 病人回复超时,请重试"}

前端处理规则:

  • 收到 message_delta:追加到当前 AI 病人气泡。
  • 收到 message_done:结束 pending,启用发送按钮。
  • 收到 error:结束 pending,展示错误。
  • fetch abort 或 reader done 但未收到 message_done:结束 pending,提示“AI 病人回复超时或失败,请重试”。

POST /sessions/{session_id}/hints

用途:练习模式下手动生成提示。提示基于病例、当前对话和已申请检查动态生成。

Request

{
  "last_user_message": "孩子发热几天了?最高体温多少?",
  "scope": "current_conversation"
}

Response data

{
  "hints": [
    "可以继续追问最高体温、热型和退热药反应。"
  ],
  "missing_dimensions": [
    "既往史",
    "严重程度评估"
  ],
  "next_questions": [
    "孩子以前有没有喘息、哮喘或过敏史?",
    "现在血氧是多少?有没有呼吸困难?"
  ],
  "recommended_orders": [
    {
      "item_code": "oxygen_saturation",
      "reason": "用于判断低氧和病情严重程度"
    }
  ]
}

前端处理规则:

  • 提示不自动弹出。
  • 只在练习模式中显示“查看提示”按钮。
  • 练习模式中是否点击提示不影响评分链路。
  • 教学互动模式当前不显示提示入口。

7. 检查/检验接口

GET /sessions/{session_id}/order-items

用途:获取当前病例可申请的检查项目。只返回名称和类型,不返回检查结果。

Response data

{
  "items": [
    {
      "item_code": "complete_blood_count",
      "item_name": "血常规",
      "item_type": "lab"
    }
  ]
}

POST /sessions/{session_id}/orders

用途:申请检查/检验,结果必须来自数据库 case_exam_item,不允许 LLM 编造。

Request

{
  "item_code": "complete_blood_count"
}

Response data

{
  "item_code": "complete_blood_count",
  "item_name": "血常规",
  "item_type": "lab",
  "result_text": "WBC 12.4×10^9/L,中性粒细胞72%。",
  "result_structured": {},
  "is_key": true,
  "is_abnormal": true,
  "context_written": true,
  "already_ordered": false
}

幂等规则:

  • 同一 session_id + item_code 只写入一次。
  • 重复申请返回已有结果,already_ordered=true
  • 重复申请不重复写入 runtime memory。
  • 前端按 item_code 去重展示。

8. 阶段提交接口

POST /sessions/{session_id}/complete-inquiry

用途:完成问诊,进入诊断阶段。

Response data

{
  "session_id": 10,
  "status": "diagnosis"
}

校验:至少存在一轮医生提问。

POST /sessions/{session_id}/diagnosis

用途:提交诊断。

Request

{
  "primary_diagnosis": "支气管肺炎",
  "differential_diagnoses": ["毛细支气管炎", "支气管哮喘急性发作", "上呼吸道感染"],
  "diagnosis_basis": "结合发热、咳嗽、喘息、肺部体征、炎症指标升高、胸片异常和血氧情况,符合儿童支气管肺炎表现。"
}

Response data

{
  "status": "treatment"
}

POST /sessions/{session_id}/treatment

用途:提交治疗方案。

Request

{
  "treatment_principle": "抗感染、止咳平喘、改善氧合、严密观察病情变化。",
  "treatment_measures": "根据病情选择抗感染治疗,必要时雾化吸入缓解喘息,监测体温、呼吸、血氧和精神反应。",
  "risk_plan": "关注低氧、呼吸困难加重、持续高热、精神反应差、脱水等情况。",
  "communication": "向家属说明肺炎病情、用药注意事项、观察指标和复诊/住院指征。",
  "follow_up": "治疗后复查体温、呼吸、血氧和必要炎症指标,症状加重时及时就诊。"
}

Response data

{
  "status": "evaluating"
}

9. 评价与报告接口

POST /sessions/{session_id}/evaluation

用途:生成 AI 评价报告。后端读取评分规则、知识检索结果、检查申请、问诊过程和提交内容后调用 Scoring Agent。

Request

{
  "score_type": "percentage"
}

Response data

{
  "evaluation_id": 1,
  "score_type": "percentage",
  "total_score": 82,
  "dimension_scores": [
    {
      "dimension": "信息采集",
      "score": 18,
      "max_score": 25,
      "comment": "已覆盖发热和咳嗽,但既往喘息史和家族过敏史追问不足。",
      "evidence": ["询问发热天数和最高体温", "申请血常规和CRP"],
      "deductions": ["未充分询问既往喘息史"],
      "improvement": "补充既往史、过敏史、严重程度评估相关问题。"
    }
  ],
  "errors": [],
  "improvement_plan": ["加强儿童肺炎严重程度评估训练。"],
  "evidence_summary": ["检查结果已写入评分依据。"],
  "guideline_refs": [],
  "overall_comment": "诊断方向正确,检查利用和沟通细节仍需加强。"
}

评价完成后:

  • 写入 training_record
  • 释放当前会话短期 memory。
  • 历史记录只保存评价报告,不长期保存完整聊天记录。

GET /evaluations

用途:按当前 X-User-Id 查询历史评价。

Response data

{
  "items": [
    {
      "evaluation_id": 1,
      "case_title": "支气管肺炎 - 6岁男性患儿",
      "score_type": "percentage",
      "total_score": 82,
      "created_at": "2026-05-29T10:00:00",
      "pdf_exported": true
    }
  ]
}

GET /evaluations/{evaluation_id}

用途:获取评价详情。只能读取当前用户自己的评价。

Response data:继承评价报告字段,并额外包含:

{
  "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"
}

POST /evaluations/{evaluation_id}/export-pdf

用途:生成本地 PDF 报告并保存文件路径。

Response data

{
  "export_id": 1,
  "file_path": "storage/reports/training_record_1_percentage_xxx.pdf"
}

10. 病例 SQL 导入接口

POST /imports/case-sql/preview

用途:上传接口解析后的 SQL 文件,只解析和校验,不写入数据库。

Request

Content-Type: multipart/form-data
file: case.sql

Response data

{
  "file_name": "case.sql",
  "encoding": "utf-8",
  "tables": {
    "case_base": 1,
    "traditional_case": 1,
    "teaching_case": 0,
    "scoring_rule": 1
  },
  "can_import": true,
  "warnings": [
    "源 SQL 未包含 case_exam_item,导入器会按当前业务规则处理。"
  ],
  "errors": [],
  "preview_cases": [
    {
      "id": 2,
      "title": "支气管肺炎 - 6岁男性患儿",
      "case_type": "traditional",
      "difficulty": "medium"
    }
  ]
}

规则:

  • 只识别 case_basetraditional_caseteaching_casescoring_rule
  • 不执行源 SQL 中的 DROP TABLECREATE TABLEALTER TABLELOCK TABLES
  • 字段数量不匹配、JSON 损坏、字符串未闭合时返回 can_import=false
  • preview 不写库。

POST /imports/case-sql/apply

用途:确认导入,将 SQL 中的病例源表数据映射写入当前数据库。

Request 同预检接口。

Response data

{
  "imported": true,
  "file_name": "case.sql",
  "encoding": "utf-8",
  "inserted_or_updated_cases": 1,
  "imported_traditional_cases": 1,
  "imported_teaching_cases": 0,
  "imported_scoring_rules": 1,
  "generated_exam_items": 6,
  "warnings": []
}

前端处理:

  • 先调用 preview,只有 can_import=true 才允许点击“确认导入”。
  • 导入成功后刷新病例列表。
  • 如果源 SQL 缺少 case_exam_item,后端会生成基础检查项,保证新病例可训练。

11. 知识检索接口

GET /knowledge/search

用途:按科室、训练类别和关键词检索评分参考指南。第一版主要供评价链路和调试使用。

Query

参数 类型 必填 说明
department_id number 科室 ID。
training_type string 训练类别。
q string 关键词,多个关键词用英文逗号分隔。

Response data

{
  "matched_chunks": [],
  "source_refs": [],
  "no_match": true
}

12. LLM 测试接口

POST /llm/test/deepseek-fast

用途:测试快速模型耗时。

Request

{
  "message": "请用一句话说明医疗问诊训练 Demo 的用途。"
}

Response data

{
  "model": "deepseek-v4-pro",
  "first_token_ms": null,
  "total_latency_ms": 3000,
  "stream": false,
  "mock_mode": false,
  "fallback_used": false,
  "thinking_enabled": false,
  "reasoning_effort": null
}

POST /llm/test/deepseek-reason

用途:测试 reason 模型耗时。接口内部会优先按配置执行,流式不兼容时降级为非流式,不影响问诊主链路。

Request 同 Fast 测试。

Response 字段同 Fast 测试。

13. 前端字段枚举

字段 允许值
score_type percentagefive_point
mode practiceteaching
training_type case_analysisdiagnosis_treatmentconsultation
session.status inquirydiagnosistreatmentevaluatingevaluated
patient.gender malefemalenull

14. 前端联调注意事项

  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。