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
+77 -181
View File
@@ -1,188 +1,88 @@
<?php $show_title=$id." - $MSG_COMPILE_INFO - $OJ_NAME"; ?>
<?php include("template/$OJ_TEMPLATE/header.php");?>
<script src="template/<?php echo $OJ_TEMPLATE?>/js/textFit.min.js"></script>
<script src="include/vditor-adapter.js"></script>
<link href='<?php echo $OJ_CDN_URL?>highlight/styles/shCore.css' rel='stylesheet' type='text/css'/>
<link href='<?php echo $OJ_CDN_URL?>highlight/styles/shThemeDefault.css' rel='stylesheet' type='text/css'/>
<style>
#compile-location-section { margin: 0 0 18px; }
.compile-location-card { border-left: 4px solid #db2828 !important; margin-bottom: 12px !important; }
.compile-location-title { display: flex; align-items: center; gap: 8px; font-weight: 600; margin-bottom: 8px; }
.compile-source-excerpt {
border: 1px solid #e0e0e0;
border-radius: 5px;
overflow-x: auto;
background: #fafafa;
font-family: 'Fira Mono', 'Cascadia Code', Consolas, monospace;
font-size: 0.9em;
line-height: 1.55;
}
.compile-source-line { display: flex; min-width: max-content; white-space: pre; }
.compile-source-line.is-error { background: rgba(219, 40, 40, 0.12); }
.compile-source-lineno {
width: 50px;
padding: 1px 10px;
text-align: right;
color: #999;
background: #f1f1f1;
border-right: 1px solid #ddd;
user-select: none;
flex-shrink: 0;
}
.compile-source-line.is-error .compile-source-lineno { color: #db2828; font-weight: 700; }
.compile-source-text { padding: 1px 12px; }
</style>
<div class="padding">
<div style="margin-top: 0px; margin-bottom: 14px; padding-bottom: 0px; " >
<p class="transition visible">
<strong ><?php echo $MSG_SOURCE_CODE ?></strong>
</p>
<div style="margin:0 0 14px;">
<p class="transition visible"><strong><?php echo $MSG_SOURCE_CODE ?></strong></p>
<div class="ui existing segment">
<pre v-if="escape" style="margin-top: 0; margin-bottom: 0; "><code><div class="brush:c" id='source' name="source"></div></code></pre>
<pre style="margin:0;"><code><div class="brush:c" id="source" name="source"></div></code></pre>
</div>
</div>
<div style="margin-top: 0px; margin-bottom: 14px; " >
<p class="transition visible">
<strong ><?php echo $MSG_COMPILE_INFO ?></strong>
</p>
<div style="margin:0 0 14px;">
<p class="transition visible"><strong><?php echo $MSG_COMPILE_INFO ?></strong></p>
<div class="ui existing segment">
<pre v-if="escape" style="margin-top: 0; margin-bottom: 0; "><code><div id='errtxt'><?php echo $view_reinfo?></div></code></pre>
<pre style="margin:0; white-space:pre-wrap; word-break:break-word;"><code><div id="errtxt"><?php echo $view_reinfo?></div></code></pre>
</div>
</div>
<div style="margin-top: 0px; margin-bottom: 14px; " >
<p v-if="title" class="transition visible">
<strong v-html="title"><?php echo $MSG_ERROR_EXPLAIN;?></strong>
</p>
<div class="ui existing segment">
<pre v-if="escape" style="margin-top: 0; margin-bottom: 0; "><code><div id='errexp'></div></code></pre>
<?php if (!empty($compile_locations) && is_array($compile_locations)): ?>
<div id="compile-location-section">
<h3 class="ui header">
<i class="map marker alternate icon"></i>
<div class="content">
编译错误定位
<div class="sub header">只标出你的源码行号和列号,不提供修改答案</div>
</div>
</h3>
<?php foreach ($compile_locations as $location): ?>
<div class="ui segment compile-location-card">
<div class="compile-location-title">
<span class="ui red label">第 <?php echo intval($location['line']); ?> 行<?php
if (!empty($location['column'])) echo ',第 ' . intval($location['column']) . ' 列';
?></span>
<span>编译器在这里报告错误</span>
</div>
<div class="compile-source-excerpt">
<?php foreach ($location['excerpt'] as $source_line): ?>
<div class="compile-source-line<?php echo $source_line['is_error'] ? ' is-error' : ''; ?>">
<span class="compile-source-lineno"><?php echo intval($source_line['number']); ?></span>
<span class="compile-source-text"><?php echo htmlspecialchars($source_line['text'], ENT_QUOTES, 'UTF-8'); ?></span>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<script>
var i=0;
var pats=new Array();
var exps=new Array();
pats[i]=/System\.out\.print.*%.*/;
exps[i++]="<?php echo $MSG_SYSTEM_OUT_PRINT; ?>";
pats[i]=/.*没有那个文件或目录.*/;
exps[i++]="<?php echo $MSG_NO_SUCH_FILE_OR_DIRECTORY; ?>";
pats[i]=/not a statement/;
exps[i++]="<?php echo $MSG_NOT_A_STATEMENT; ?>";
pats[i]=/class, interface, or enum expected/;
exps[i++]="<?php echo $MSG_EXPECTED_CLASS_INTERFACE_ENUM; ?>";
pats[i]=/asm.*java/;
exps[i++]="<?php echo $MSG_SUBMIT_JAVA_AS_C_LANG; ?>";
pats[i]=/package .* does not exist/;
exps[i++]="<?php echo $MSG_DOES_NOT_EXIST_PACKAGE; ?>";
pats[i]=/possible loss of precision/;
exps[i++]="<?php echo $MSG_POSSIBLE_LOSS_OF_PRECISION; ?>";
pats[i]=/incompatible types/;
exps[i++]="<?php echo $MSG_INCOMPATIBLE_TYPES; ?>";
pats[i]=/illegal start of expression/;
exps[i++]="<?php echo $MSG_ILLEGAL_START_OF_EXPRESSION; ?>";
pats[i]=/cannot find symbol/;
exps[i++]="<?php echo $MSG_CANNOT_FIND_SYMBOL; ?>";
pats[i]=/';' expected/;
exps[i++]="<?php echo $MSG_EXPECTED_SEMICOLON; ?>";
pats[i]=/should be declared in a file named/;
exps[i++]="<?php echo $MSG_DECLARED_JAVA_FILE_NAMED; ?>";
pats[i]=/expected .* at end of input/;
exps[i++]="<?php echo $MSG_EXPECTED_WILDCARD_CHARACTER_AT_END_OF_INPUT; ?>";
pats[i]=/invalid conversion from .* to .*/;
exps[i++]="<?php echo $MSG_INVALID_CONVERSION; ?>";
pats[i]=/warning.*declaration of 'main' with no type/;
exps[i++]="<?php echo $MSG_NO_RETURN_TYPE_IN_MAIN; ?>";
pats[i]=/'.*' was not declared in this scope/;
exps[i++]="<?php echo $MSG_NOT_DECLARED_IN_SCOPE; ?>";
pats[i]=/main must return int/;
exps[i++]="<?php echo $MSG_MAIN_MUST_RETURN_INT; ?>";
pats[i]=/expected identifier or '\(' before numeric constant/
exps[i++]="<?php echo $MSG_EXPECTED_IDENTIFIER; ?>";
pats[i]=/printf.*was not declared in this scope/;
exps[i++]="<?php echo $MSG_PRINTF_NOT_DECLARED_IN_SCOPE; ?>";
pats[i]=/warning: ignoring return value of/;
exps[i++]="<?php echo $MSG_IGNOREING_RETURN_VALUE; ?>";
pats[i]=/:.*__int64 undeclared/;
exps[i++]="<?php echo $MSG_NOT_DECLARED_INT64; ?>";
pats[i]=/:.*expected ; before/;
exps[i++]="<?php echo $MSG_EXPECTED_SEMICOLON_BEFORE; ?>";
pats[i]=/ .* undeclared \(first use in this function\)/;
exps[i++]="<?php echo $MSG_UNDECLARED_NAME; ?>";
pats[i]=/scanf.*was not declared in this scope/;
exps[i++]="<?php echo $MSG_SCANF_NOT_DECLARED_IN_SCOPE; ?>";
pats[i]=/memset.*was not declared in this scope/;
exps[i++]="<?php echo $MSG_MEMSET_NOT_DECLARED_IN_SCOPE; ?>";
pats[i]=/malloc.*was not declared in this scope/;
exps[i++]="<?php echo $MSG_MALLOC_NOT_DECLARED_IN_SCOPE; ?>";
pats[i]=/puts.*was not declared in this scope/;
exps[i++]="<?php echo $MSG_PUTS_NOT_DECLARED_IN_SCOPE; ?>";
pats[i]=/gets.*was not declared in this scope/;
exps[i++]="<?php echo $MSG_GETS_NOT_DECLARED_IN_SCOPE; ?>";
pats[i]=/str.*was not declared in this scope/;
exps[i++]="<?php echo $MSG_STRING_NOT_DECLARED_IN_SCOPE; ?>";
pats[i]=/import does not name a type/;
exps[i++]="<?php echo $MSG_NO_TYPE_IMPORT_IN_C_CPP; ?>";
pats[i]=/asm undeclared/;
exps[i++]="<?php echo $MSG_ASM_UNDECLARED; ?>";
pats[i]=/redefinition of/;
exps[i++]="<?php echo $MSG_REDEFINITION_OF; ?>";
pats[i]=/expected declaration or statement at end of input/;
exps[i++]="<?php echo $MSG_EXPECTED_DECLARATION_OR_STATEMENT; ?>";
pats[i]=/warning: unused variable/;
exps[i++]="<?php echo $MSG_UNUSED_VARIABLE; ?>";
pats[i]=/implicit declaration of function/;
exps[i++]="<?php echo $MSG_IMPLICIT_DECLARTION_OF_FUNCTION; ?>";
pats[i]=/too .* arguments to function/;
exps[i++]="<?php echo $MSG_ARGUMENTS_ERROR_IN_FUNCTION; ?>";
pats[i]=/expected =, ,, ;, asm or __attribute__ before namespace/;
exps[i++]="<?php echo $MSG_EXPECTED_BEFORE_NAMESPACE; ?>";
pats[i]=/stray \\[0123456789]* in program/;
exps[i++]="<?php echo $MSG_STRAY_PROGRAM; ?>";
pats[i]=/division by zero/;
exps[i++]="<?php echo $MSG_DIVISION_BY_ZERO; ?>";
pats[i]=/cannot be used as a function/;
exps[i++]="<?php echo $MSG_CANNOT_BE_USED_AS_A_FUNCTION; ?>";
pats[i]=/format .* expects type .* but argument .* has type .*/;
exps[i++]="<?php echo $MSG_CANNOT_FIND_TYPE; ?>";
pats[i]=/类.*是公共的,应在名为 .*java 的文件中声明/;
exps[i++]="<?php echo $MSG_JAVA_CLASS_ERROR; ?>";
pats[i]=/expected \) before .* token/;
exps[i++]="<?php echo $MSG_EXPECTED_BRACKETS_TOKEN; ?>";
pats[i]=/找不到符号/;
exps[i++]="<?php echo $MSG_NOT_FOUND_SYMBOL; ?>";
pats[i]=/需要为 class、interface 或 enum/;
exps[i++]="<?php echo $MSG_NEED_CLASS_INTERFACE_ENUM; ?>";
pats[i]=/符号: 类 .*List/;
exps[i++]="<?php echo $MSG_CLASS_SYMBOL_ERROR; ?>";
pats[i]=/方法声明无效;需要返回类型/;
exps[i++]="<?php echo $MSG_INVALID_METHOD_DECLARATION; ?>";
pats[i]=/expected.*before.*&.*token/;
exps[i++]="<?php echo $MSG_EXPECTED_AMPERSAND_TOKEN; ?>";
pats[i]=/非法的表达式开始/;
exps[i++]="<?php echo $MSG_DECLARED_FUNCTION_ORDER; ?>";
pats[i]=/需要 ';'/;
exps[i++]="<?php echo $MSG_NEED_SEMICOLON; ?>";
pats[i]=/extra tokens at end of #include directive/;
exps[i++]="<?php echo $MSG_EXTRA_TOKEN_AT_END_OF_INCLUDE; ?>";
pats[i]=/int.*hasNext/;
exps[i++]="<?php echo $MSG_INT_HAS_NEXT; ?>";
pats[i]=/unterminated comment/;
exps[i++]="<?php echo $MSG_UNTERMINATED_COMMENT; ?>";
pats[i]=/expected '=, ,, ;, asm or __attribute__ before { token/;
exps[i++]="<?php echo $MSG_EXPECTED_BRACES_TOKEN; ?>";
pats[i]=/进行语法解析时已到达文件结尾/;
exps[i++]="<?php echo $MSG_REACHED_END_OF_FILE_1; ?>";
pats[i]=/subscripted value is neither array nor pointer/;
exps[i++]="<?php echo $MSG_SUBSCRIPT_ERROR; ?>";
pats[i]=/expected expression before % token/;
exps[i++]="<?php echo $MSG_EXPECTED_PERCENT_TOKEN; ?>";
pats[i]=/expected expression before .* token/;
exps[i++]="<?php echo $MSG_EXPECTED_EXPRESSION_TOKEN; ?>";
pats[i]=/expected but/;
exps[i++]="<?php echo $MSG_EXPECTED_BUT; ?>";
pats[i]=/redefinition of main/;
exps[i++]="<?php echo $MSG_REDEFINITION_MAIN; ?>";
pats[i]=/iostream: No such file or directory/;
exps[i++]="<?php echo $MSG_IOSTREAM_ERROR; ?>";
pats[i]=/expected unqualified-id before \[ token/;
exps[i++]="<?php echo $MSG_EXPECTED_UNQUALIFIED_ID_TOKEN; ?>";
pats[i]=/解析时已到达文件结尾/;
exps[i++]="<?php echo $MSG_REACHED_END_OF_FILE_2; ?>";
pats[i]=/非法字符/;
exps[i++]="<?php echo $MSG_INVALID_SYMBOL; ?>";
pats[i]=/应在名为.*的文件中声明/;
exps[i++]="<?php echo $MSG_DECLARED_FILE_NAMED; ?>";
pats[i]=/variably modified/;
exps[i++]="<?php echo $MSG_VARIABLY_MODIFIED; ?>";
<?php endif; ?>
<?php if (isset($OJ_LLM_ENABLED) && $OJ_LLM_ENABLED) {
require("template/$OJ_TEMPLATE/llm-guidance.php");
} ?>
</div>
function explain(){
//alert("asdf");
var errmsg=$("#errtxt").text();
var expmsg="";
for(var i=0;i<pats.length;i++){
var pat=pats[i];
var exp=exps[i];
var ret=pat.exec(errmsg);
if(ret){
expmsg+=ret+":"+exp+"<br>";
}
}
document.getElementById("errexp").innerHTML=expmsg;
//alert(expmsg);
}
</script>
<script src='<?php echo $OJ_CDN_URL?>highlight/scripts/shCore.js' type='text/javascript'></script>
<script src='<?php echo $OJ_CDN_URL?>highlight/scripts/shBrushCpp.js' type='text/javascript'></script>
<script src='<?php echo $OJ_CDN_URL?>highlight/scripts/shBrushCss.js' type='text/javascript'></script>
@@ -198,17 +98,13 @@ document.getElementById("errexp").innerHTML=expmsg;
<script>
$(document).ready(function(){
$("#source").load("showsource2.php?id=<?php echo $id?>",function(response,status,xhr){
if(status=="success"){
SyntaxHighlighter.config.bloggerMode = false;
SyntaxHighlighter.config.clipboardSwf = '<?php echo $OJ_CDN_URL?>highlight/scripts/clipboard.swf';
SyntaxHighlighter.highlight();
explain();
}
});
$('#source').load('showsource2.php?id=<?php echo intval($id)?>', function(response, status){
if (status === 'success') {
SyntaxHighlighter.config.bloggerMode = false;
SyntaxHighlighter.config.clipboardSwf = '<?php echo $OJ_CDN_URL?>highlight/scripts/clipboard.swf';
SyntaxHighlighter.highlight();
}
});
});
</script>