feat(llm): 引入基于完成度的 AI 提交指导与限流

- 新增 llm_guidance 策略层,根据源码、判题结果与通过率评估完成度分级
- llm-review.php 改为服务端缓冲上游输出,仅下发白名单分类的固定模板
- 新增 llm_review 与 llm_review_rate_limit 表,实现结果缓存与账户/提交两级限流
- ceinfo 模板渲染编译器标出的源码行号/列号,reinfo 仅向高完成度提交展示结构化 diff
- download.php 按完成度授权测试点下载,封堵白卷提交套取隐藏用例
- judge_client 在 ACM 非比赛场景下记录已通过测试点数,前端可显示进度
- 附 llm_guidance_policy_test 单测覆盖空白卷、注释卷、隐藏 SPJ、OJ_FULL_DIFF 等关键路径
This commit is contained in:
2026-07-23 10:47:50 +08:00
parent 5a27c07bba
commit 12bea84d8b
12 changed files with 1721 additions and 768 deletions
+42 -7
View File
@@ -10,6 +10,7 @@ $OJ_CACHE_SHARE=false;
exit(0);
}
require_once("./include/const.inc.php");
require_once("./include/llm_guidance.inc.php");
if (!isset($_GET['sid'])){
$view_errors= "No such code!\n";
require("template/".$OJ_TEMPLATE."/error.php");
@@ -36,19 +37,53 @@ $ok=false;
$id=intval($_GET['sid']);
$sql="SELECT * FROM `solution` WHERE `solution_id`=?";
$result=pdo_query($sql,$id);
$row=$result[0];
if ($row && $row['user_id']==$_SESSION[$OJ_NAME.'_'.'user_id']) $ok=true;
if (empty($result) || $result === -1) {
$view_errors = "No such code!\n";
require("template/".$OJ_TEMPLATE."/error.php");
exit(0);
}
$solution_row=$result[0];
if ($solution_row && strval($solution_row['user_id'])===strval($_SESSION[$OJ_NAME.'_'.'user_id'])) $ok=true;
if (isset($_SESSION[$OJ_NAME.'_'.'source_browser'])) $ok=true;
$view_reinfo="";
$compile_error_raw="";
$student_source="";
$compile_locations=array();
if ($ok==true){
if($row['user_id']!=$_SESSION[$OJ_NAME.'_'.'user_id'])
$view_mail_link= "<a href='mail.php?to_user={$row['user_id']}&title=$MSG_SUBMIT $id'>Mail the auther</a>";
if (intval($solution_row['result']) !== 11) {
header("Location: reinfo.php?sid=" . intval($id));
exit(0);
}
if(strval($solution_row['user_id'])!==strval($_SESSION[$OJ_NAME.'_'.'user_id']))
$view_mail_link= "<a href='mail.php?to_user={$solution_row['user_id']}&title=$MSG_SUBMIT $id'>Mail the auther</a>";
$sql="SELECT `error` FROM `compileinfo` WHERE `solution_id`=?";
$result=pdo_query($sql,$id);
$row=$result[0];
if($row&&is_valid($row['error']))
$view_reinfo= htmlentities(str_replace("\n\r","\n",$row['error']),ENT_QUOTES,"UTF-8");
$compile_row=(!empty($result) && $result !== -1) ? $result[0] : null;
if($compile_row&&is_valid($compile_row['error'])) {
$compile_error_raw=str_replace(array("\r\n", "\r"),"\n",$compile_row['error']);
$view_reinfo= htmlentities($compile_error_raw,ENT_QUOTES,"UTF-8");
}
$source_result=pdo_query("SELECT `source` FROM `source_code_user` WHERE `solution_id`=?",$id);
if(!empty($source_result) && $source_result !== -1) {
$student_source=$source_result[0]['source'];
}
$llm_guidance=llm_guidance_assess(
$student_source,
intval($solution_row['result']),
floatval($solution_row['pass_rate'])
);
$llm_guidance_auto_fetch=true;
if($llm_guidance['mode']==='compile_location') {
$compile_locations=llm_guidance_extract_compile_locations(
$compile_error_raw,
$student_source,
2,
3
);
}