"""五维自检题目生成 API 提供基于用户画路径的智能题目生成服务,包含: - AI生成 20 道情境化问卷题目 - 支持自定义维度分配 - 支持历史记题目去重 - 与现有 15 题固定题库并存""" import logging from fastapi import APIRouter from pydantic import BaseModel from typing import Any, Dict, List, Optional from app.graphs.question_generation_graph import run_question_gen logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/v1", tags=["question-generate"]) class QuestionGenRequest(BaseModel): """题目生成请求""" user_id: int count: int = 20 # 题目总数,默认20(满足系统≥10题门槛) difficulty: str = "balanced" # balanced/easy/hard/contextual dimensions: Optional[List[str]] = None # 可选指定维度,如 ["body", "wisdom"] exclude_ids: Optional[List[int]] = None # 历史题目ID列表,用于避免重复 class QuestionGenResponse(BaseModel): """题目生成响应""" questions: List[Dict[str, Any]] total_generated: int generation_time: float rationale: str dimension_distribution: Dict[str, int] fallback_used: bool @router.post("/question-generate", response_model=QuestionGenResponse) async def question_generate(req: QuestionGenRequest): """AI生成五维自检题目 根据用户画像生成 20 道情境化问卷题目,满足系统最低门槛: - 总题数≥10 - 每个维度至少 1 题 请求体: - user_id: 必填,用户 ID - count: 可选,题目总数,默认 20 - difficulty: 可选,难度提示,默认 "balanced" - dimensions: 可选,指定维度列表,如 ["body", "wisdom"] - exclude_ids: 可选,历史题目 ID 列表,用于避免重复提问 响应体: - questions: 生成的题目列表,每题包含 id, dimension, text, options - total_generated: 实际生成数量 - generation_time: 生成耗时(秒) - rationale: 生成 rationale,解释维度选择逻辑 - dimension_distribution: 各维度题目数量分布 - fallback_used: 是否使用了降级方案(LLM失败回退到本地题库) """ try: result = await run_question_gen( user_id=user_id, count=req.count, difficulty=req.difficulty, dimensions=req.dimensions, exclude_ids=req.exclude_ids, ) return QuestionGenResponse( questions=result.get("questions", []), total_generated=result.get("total_generated", 0), generation_time=result.get("generation_time", 0.0), rationale=result.get("rationale", ""), dimension_distribution=result.get("dimension_distribution", {}), fallback_used=result.get("fallback_used", False), ) except Exception as e: logger.error(f"AI 生成题目失败: {e}", exc_info=True) # 降级:返回空结果,前端可据此提示用户或使用固定题库 return QuestionGenResponse( questions=[], total_generated=0, generation_time=0.0, rationale=f"生成失败: {str(e)},将使用固定题库", dimension_distribution={}, fallback_used=True, )