question_generate.py 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. """五维自检题目生成 API
  2. 提供基于用户画路径的智能题目生成服务,包含:
  3. - AI生成 20 道情境化问卷题目
  4. - 支持自定义维度分配
  5. - 支持历史记题目去重
  6. - 与现有 15 题固定题库并存"""
  7. import logging
  8. from fastapi import APIRouter
  9. from pydantic import BaseModel
  10. from typing import Any, Dict, List, Optional
  11. from app.graphs.question_generation_graph import run_question_gen
  12. logger = logging.getLogger(__name__)
  13. router = APIRouter(prefix="/api/v1", tags=["question-generate"])
  14. class QuestionGenRequest(BaseModel):
  15. """题目生成请求"""
  16. user_id: int
  17. count: int = 20 # 题目总数,默认20(满足系统≥10题门槛)
  18. difficulty: str = "balanced" # balanced/easy/hard/contextual
  19. dimensions: Optional[List[str]] = None # 可选指定维度,如 ["body", "wisdom"]
  20. exclude_ids: Optional[List[int]] = None # 历史题目ID列表,用于避免重复
  21. class QuestionGenResponse(BaseModel):
  22. """题目生成响应"""
  23. questions: List[Dict[str, Any]]
  24. total_generated: int
  25. generation_time: float
  26. rationale: str
  27. dimension_distribution: Dict[str, int]
  28. fallback_used: bool
  29. @router.post("/question-generate", response_model=QuestionGenResponse)
  30. async def question_generate(req: QuestionGenRequest):
  31. """AI生成五维自检题目
  32. 根据用户画像生成 20 道情境化问卷题目,满足系统最低门槛:
  33. - 总题数≥10
  34. - 每个维度至少 1 题
  35. 请求体:
  36. - user_id: 必填,用户 ID
  37. - count: 可选,题目总数,默认 20
  38. - difficulty: 可选,难度提示,默认 "balanced"
  39. - dimensions: 可选,指定维度列表,如 ["body", "wisdom"]
  40. - exclude_ids: 可选,历史题目 ID 列表,用于避免重复提问
  41. 响应体:
  42. - questions: 生成的题目列表,每题包含 id, dimension, text, options
  43. - total_generated: 实际生成数量
  44. - generation_time: 生成耗时(秒)
  45. - rationale: 生成 rationale,解释维度选择逻辑
  46. - dimension_distribution: 各维度题目数量分布
  47. - fallback_used: 是否使用了降级方案(LLM失败回退到本地题库)
  48. """
  49. try:
  50. result = await run_question_gen(
  51. user_id=user_id,
  52. count=req.count,
  53. difficulty=req.difficulty,
  54. dimensions=req.dimensions,
  55. exclude_ids=req.exclude_ids,
  56. )
  57. return QuestionGenResponse(
  58. questions=result.get("questions", []),
  59. total_generated=result.get("total_generated", 0),
  60. generation_time=result.get("generation_time", 0.0),
  61. rationale=result.get("rationale", ""),
  62. dimension_distribution=result.get("dimension_distribution", {}),
  63. fallback_used=result.get("fallback_used", False),
  64. )
  65. except Exception as e:
  66. logger.error(f"AI 生成题目失败: {e}", exc_info=True)
  67. # 降级:返回空结果,前端可据此提示用户或使用固定题库
  68. return QuestionGenResponse(
  69. questions=[],
  70. total_generated=0,
  71. generation_time=0.0,
  72. rationale=f"生成失败: {str(e)},将使用固定题库",
  73. dimension_distribution={},
  74. fallback_used=True,
  75. )