"""STEP 02 — Simulation Panel CRUD + multi-source question intake. Question bank for the AI quality audit. Sources: manual 手工添加 imported 批量导入的真实用户问题 resolved 从"已答好"的 question_traces 采纳的回归用例 ai_gen AI 生成的覆盖性问题 """ from fastapi import APIRouter, HTTPException from app.auth import CurrentUser from app.config import settings from app.contracts import SimulationQuestion, SimulationQuestionUpdate from app.db import ( list_simulation_questions, create_simulation_question, update_simulation_question, delete_simulation_question, bulk_create_simulation_questions, adopt_hit_question_traces, ) router = APIRouter() _T = settings.default_tenant _P = settings.default_project @router.get("/simulation-panel") async def _list(_user: CurrentUser = None): return await list_simulation_questions(_T, _P) @router.post("/simulation-panel") async def _create(body: SimulationQuestion, _user: CurrentUser): data = body.model_dump() data["tenant_id"] = _T data["project_id"] = _P data["source"] = "manual" return await create_simulation_question(data) @router.post("/simulation-panel/import") async def _import(body: dict, _user: CurrentUser): """Bulk import real user questions — one per line, or a list.""" raw = body.get("text") or body.get("questions") or "" texts = raw if isinstance(raw, list) else str(raw).splitlines() added = await bulk_create_simulation_questions(_T, _P, texts, "imported") return {"added": added} @router.post("/simulation-panel/adopt-hits") async def _adopt_hits(_user: CurrentUser): """Adopt well-answered questions as regression cases (source=resolved).""" added = await adopt_hit_question_traces(_T, _P) return {"added": added} @router.post("/simulation-panel/ai-generate") async def _ai_generate(body: dict, _user: CurrentUser): """Let the LLM propose representative coverage questions.""" from app.llm_client import LlmClient llm = LlmClient.from_settings() if settings.llm_api_key else None if not llm or not llm.available(): raise HTTPException(400, "未配置 LLM(Agent 设置里填好 API Key 后可用)") count = max(1, min(int(body.get("count", 10)), 30)) topic = (body.get("topic") or "城市本地生活知识图谱(地点、区域、路线、夜间体验、标签)").strip() try: out = llm.chat_json( system=( "你是知识图谱质量稽查助手。请基于给定主题,生成有代表性、" "覆盖不同场景的真实用户中文问题,用于检验知识图谱数据的完善度。" '严格返回 JSON:{"questions": ["...", "..."]}' ), user=f"主题:{topic}。生成 {count} 个不同角度、不重复的问题。", ) qs = out.get("questions") or [] qs = [str(q).strip() for q in qs if str(q).strip()][:count] except Exception as e: raise HTTPException(400, f"AI 生成失败:{str(e)[:160]}") added = await bulk_create_simulation_questions(_T, _P, qs, "ai_gen") return {"added": added, "generated": len(qs)} @router.patch("/simulation-panel/{q_id}") async def _update(q_id: int, body: SimulationQuestionUpdate, _user: CurrentUser): row = await update_simulation_question(q_id, body.model_dump(exclude_none=True)) if not row: raise HTTPException(404, "Simulation question not found") return row @router.delete("/simulation-panel/{q_id}") async def _delete(q_id: int, _user: CurrentUser): await delete_simulation_question(q_id) return {"ok": True}