feat: requirements analysis, submission checks (stability stress test), AI inspection pipeline
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
import json
|
||||
import logging
|
||||
|
||||
import requests
|
||||
from django.conf import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
ANALYZE_PROMPT = """\
|
||||
你是 OJ 检测平台的分析引擎。请根据下面的作业要求,分析出需要对该成员提交的项目做哪些检测。
|
||||
|
||||
必须始终包含 ai_inspection(AI 检测)。根据作业要求酌情包含以下维度:
|
||||
- stability: 稳定度检测(压测)。requirements 用中文给出具体指标,例如:
|
||||
基准并发 {stability_concurrency} 下错误率 ≤ 1%, 基准持续 {base_duration}s 后加压 50% 持续 {rampup_duration}s 崩溃率 ≤ 0.5%
|
||||
- functional: 功能点完善度检测。points 为需要核实的中文功能点列表(必须覆盖作业要求中的全部功能点)
|
||||
- performance: 性能检测。requirements 为中文指标,例如: 基准并发下 P95 延迟 ≤ 500ms
|
||||
- security: 安全检测(漏洞扫描),requirements 为中文说明
|
||||
|
||||
输出格式: 只输出一个 JSON 对象,不要输出任何其他文字:
|
||||
{{"ai_inspection": true, "stability": {{"enabled": true, "requirements": "中文要求"}}, "functional": {{"enabled": true, "points": ["功能点1", "功能点2"]}}, "performance": {{"enabled": false, "requirements": ""}}, "security": {{"enabled": false, "requirements": ""}}}}
|
||||
|
||||
作业要求如下:
|
||||
{content}
|
||||
"""
|
||||
|
||||
|
||||
def _validate_spec(spec: dict) -> dict:
|
||||
if not isinstance(spec, dict):
|
||||
raise ValueError("LLM 未返回 JSON 对象")
|
||||
result: dict = {"ai_inspection": bool(spec.get("ai_inspection", True))}
|
||||
for key in ("stability", "performance", "security"):
|
||||
node = spec.get(key)
|
||||
if isinstance(node, dict):
|
||||
result[key] = {
|
||||
"enabled": bool(node.get("enabled")),
|
||||
"requirements": str(node.get("requirements", "")),
|
||||
}
|
||||
else:
|
||||
result[key] = {"enabled": False, "requirements": ""}
|
||||
func = spec.get("functional")
|
||||
if isinstance(func, dict):
|
||||
points = func.get("points")
|
||||
result["functional"] = {
|
||||
"enabled": bool(func.get("enabled")),
|
||||
"points": [str(p) for p in points] if isinstance(points, list) else [],
|
||||
}
|
||||
else:
|
||||
result["functional"] = {"enabled": True, "points": []}
|
||||
return result
|
||||
|
||||
|
||||
def _extract_json(text: str) -> dict:
|
||||
start, end = text.find("{"), text.rfind("}")
|
||||
if start == -1 or end <= start:
|
||||
raise ValueError("LLM 响应中未找到 JSON")
|
||||
return json.loads(text[start:end + 1])
|
||||
|
||||
|
||||
def analyze_requirements(content: str) -> dict:
|
||||
"""调用 LLM 分析作业要求,返回检测维度计划。未配置 LLM 时返回默认计划。"""
|
||||
key = settings.LLM_API_KEY
|
||||
if not key or key.startswith("sk-placeholder") or not settings.LLM_MODEL:
|
||||
logger.warning("LLM 未配置,使用默认检测计划")
|
||||
return _default_spec()
|
||||
url = settings.LLM_BASE_URL.rstrip("/") + "/chat/completions"
|
||||
prompt = ANALYZE_PROMPT.format(
|
||||
content=content[:8000],
|
||||
stability_concurrency=settings.STABILITY_CONCURRENCY,
|
||||
base_duration=settings.STABILITY_BASE_DURATION,
|
||||
rampup_duration=settings.STABILITY_RAMPUP_DURATION,
|
||||
)
|
||||
resp = requests.post(
|
||||
url,
|
||||
headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"},
|
||||
json={
|
||||
"model": settings.LLM_MODEL,
|
||||
"messages": [
|
||||
{"role": "system", "content": "你是严格遵循 JSON 输出格式的 OJ 检测分析引擎。"},
|
||||
{"role": "user", "content": prompt},
|
||||
],
|
||||
"temperature": 0.1,
|
||||
},
|
||||
timeout=120,
|
||||
)
|
||||
resp.raise_for_status()
|
||||
text = resp.json()["choices"][0]["message"]["content"]
|
||||
spec = _extract_json(text)
|
||||
return _validate_spec(spec)
|
||||
|
||||
|
||||
def _default_spec() -> dict:
|
||||
return {
|
||||
"ai_inspection": True,
|
||||
"stability": {"enabled": True, "requirements": "默认稳定度要求: 基准并发下错误率≤1%, 加压后崩溃率≤0.5%"},
|
||||
"functional": {"enabled": True, "points": []},
|
||||
"performance": {"enabled": True, "requirements": "默认性能要求: 基准并发下 P95 延迟 ≤ 500ms"},
|
||||
"security": {"enabled": False, "requirements": ""},
|
||||
}
|
||||
|
||||
|
||||
def ai_summarize(project_dir: str, spec: dict) -> dict:
|
||||
"""AI 检测: 阅读项目关键文件,对照检测计划输出中文报告片段。"""
|
||||
key = settings.LLM_API_KEY
|
||||
if not key or key.startswith("sk-placeholder") or not settings.LLM_MODEL:
|
||||
return {"summary": "LLM 未配置,跳过 AI 检测。请在 .env 中配置 LLM_API_KEY。", "issues": []}
|
||||
from .code_reader import read_key_files
|
||||
excerpt = read_key_files(project_dir, max_chars=12000)
|
||||
prompt = (
|
||||
"你是 OJ 平台的代码审查专家。下面是成员提交的项目关键代码摘录和检测计划(JSON):\n"
|
||||
f"{json.dumps(spec, ensure_ascii=False)}\n\n"
|
||||
f"代码摘录:\n{excerpt}\n\n"
|
||||
"请输出一个 JSON 对象: {\"summary\": \"中文总体评价(200字内)\", "
|
||||
"\"issues\": [{\"point\": \"检查点\", \"status\": \"pass|fail|warn\", \"detail\": \"中文说明\"}]}\n"
|
||||
"对照检测计划逐项核查,重点关注: 功能点是否完善、稳定度是否可能达标、潜在风险。只输出 JSON。"
|
||||
)
|
||||
url = settings.LLM_BASE_URL.rstrip("/") + "/chat/completions"
|
||||
resp = requests.post(
|
||||
url,
|
||||
headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"},
|
||||
json={
|
||||
"model": settings.LLM_MODEL,
|
||||
"messages": [{"role": "user", "content": prompt}],
|
||||
"temperature": 0.2,
|
||||
},
|
||||
timeout=180,
|
||||
)
|
||||
resp.raise_for_status()
|
||||
text = resp.json()["choices"][0]["message"]["content"]
|
||||
data = _extract_json(text)
|
||||
return {
|
||||
"summary": str(data.get("summary", "")),
|
||||
"issues": data.get("issues", []) if isinstance(data.get("issues"), list) else [],
|
||||
}
|
||||
Reference in New Issue
Block a user