Skip to content

Commit b4d7352

Browse files
SummerOneTwoclaude
andcommitted
fix: 修复 MCP 层面问题,增强测试数据生成
## 修复的问题 1. **终端卡住问题** - 添加 _force_terminate_process 函数,确保进程被强制终止 - 改进异常路径的进程清理逻辑 - 添加等待进程退出的超时保护 2. **测试数据不极限** - 新增 constraints 参数支持题目约束 - 动态生成极限数据配置(N=max, N=max-1, 接近极限) - 添加边界情况生成(N=1, T=max 等) - 支持 sum_n_max 约束 3. **跳步问题(MCP 层面)** - 所有工具描述中添加前置条件说明 - 所有工具描述中添加建议下一步 - 帮助 LLM 理解正确的工作流程 4. **新增复杂度分析工具** - solution_analyze 工具分析时间/空间复杂度 - 检测常见算法模式(二分、排序、DP 等) - 根据复杂度推荐测试参数 - 提供约束验证和警告 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent f690db9 commit b4d7352

8 files changed

Lines changed: 529 additions & 27 deletions

File tree

src/autocode_mcp/server.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from .tools.base import Tool as BaseTool
1616
from .tools.base import ToolResult
1717
from .tools.checker import CheckerBuildTool
18+
from .tools.complexity import SolutionAnalyzeTool
1819
from .tools.file_ops import FileReadTool, FileSaveTool
1920
from .tools.generator import GeneratorBuildTool, GeneratorRunTool
2021
from .tools.interactor import InteractorBuildTool
@@ -44,6 +45,7 @@ def register_all_tools() -> None:
4445
# Solution 工具组
4546
register_tool(SolutionBuildTool())
4647
register_tool(SolutionRunTool())
48+
register_tool(SolutionAnalyzeTool())
4749

4850
# Stress Test 工具组
4951
register_tool(StressTestRunTool())
Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
"""
2+
Complexity 分析工具 - 分析解法复杂度。
3+
4+
基于代码静态分析估算时间/空间复杂度,并推荐测试参数。
5+
"""
6+
7+
import re
8+
from typing import Literal
9+
10+
from .base import Tool, ToolResult
11+
12+
13+
class ComplexityLevel:
14+
"""复杂度等级。"""
15+
16+
CONSTANT = "O(1)"
17+
LOG_N = "O(log n)"
18+
LINEAR = "O(n)"
19+
N_LOG_N = "O(n log n)"
20+
QUADRATIC = "O(n^2)"
21+
CUBIC = "O(n^3)"
22+
EXPONENTIAL = "O(2^n)"
23+
FACTORIAL = "O(n!)"
24+
25+
26+
# 复杂度到推荐 n_max 的映射
27+
COMPLEXITY_TO_N_MAX = {
28+
ComplexityLevel.CONSTANT: 10**9,
29+
ComplexityLevel.LOG_N: 10**9,
30+
ComplexityLevel.LINEAR: 10**7,
31+
ComplexityLevel.N_LOG_N: 10**6,
32+
ComplexityLevel.QUADRATIC: 5000,
33+
ComplexityLevel.CUBIC: 500,
34+
ComplexityLevel.EXPONENTIAL: 20,
35+
ComplexityLevel.FACTORIAL: 12,
36+
}
37+
38+
# 复杂度到推荐时间限制的映射(毫秒)
39+
COMPLEXITY_TO_TIME_LIMIT = {
40+
ComplexityLevel.CONSTANT: 1000,
41+
ComplexityLevel.LOG_N: 1000,
42+
ComplexityLevel.LINEAR: 1000,
43+
ComplexityLevel.N_LOG_N: 2000,
44+
ComplexityLevel.QUADRATIC: 3000,
45+
ComplexityLevel.CUBIC: 5000,
46+
ComplexityLevel.EXPONENTIAL: 10000,
47+
ComplexityLevel.FACTORIAL: 10000,
48+
}
49+
50+
51+
def analyze_loop_complexity(code: str) -> str:
52+
"""分析循环复杂度。
53+
54+
Args:
55+
code: C++ 源代码
56+
57+
Returns:
58+
估算的复杂度字符串
59+
"""
60+
# 统计嵌套循环层数
61+
loop_patterns = [
62+
r"\bfor\s*\(",
63+
r"\bwhile\s*\(",
64+
r"\bfor\s+.*:\s*", # range-based for
65+
]
66+
67+
max_nesting = 0
68+
current_nesting = 0
69+
70+
lines = code.split("\n")
71+
for line in lines:
72+
# 计算当前行的循环数
73+
loop_count = 0
74+
for pattern in loop_patterns:
75+
loop_count += len(re.findall(pattern, line))
76+
77+
# 检测循环结束
78+
brace_change = line.count("{") - line.count("}")
79+
80+
# 更新嵌套深度
81+
current_nesting += loop_count
82+
max_nesting = max(max_nesting, current_nesting)
83+
current_nesting = max(0, current_nesting + brace_change)
84+
85+
# 根据嵌套层数估算复杂度
86+
if max_nesting == 0:
87+
return ComplexityLevel.LINEAR # 默认假设
88+
elif max_nesting == 1:
89+
return ComplexityLevel.LINEAR
90+
elif max_nesting == 2:
91+
return ComplexityLevel.QUADRATIC
92+
elif max_nesting == 3:
93+
return ComplexityLevel.CUBIC
94+
else:
95+
return ComplexityLevel.EXPONENTIAL
96+
97+
98+
def detect_algorithm_patterns(code: str) -> tuple[str, list[str]]:
99+
"""检测常见算法模式。
100+
101+
Args:
102+
code: C++ 源代码
103+
104+
Returns:
105+
(复杂度, 检测到的模式列表)
106+
"""
107+
patterns = []
108+
complexity = ComplexityLevel.LINEAR # 默认
109+
110+
# 二分查找
111+
if re.search(r"\bbinary_search\b|\blower_bound\b|\bupper_bound\b", code):
112+
patterns.append("binary_search")
113+
complexity = ComplexityLevel.N_LOG_N
114+
115+
# 排序
116+
if re.search(r"\bsort\b|\bstable_sort\b|\bpartial_sort\b", code):
117+
patterns.append("sorting")
118+
complexity = ComplexityLevel.N_LOG_N
119+
120+
# 图算法 - BFS/DFS
121+
if re.search(r"\bbfs\b|\bdfs\b|queue<|stack<", code):
122+
patterns.append("graph_traversal")
123+
complexity = ComplexityLevel.LINEAR
124+
125+
# 动态规划
126+
if re.search(r"dp\[|memo\[|memoization", code):
127+
patterns.append("dynamic_programming")
128+
# DP 复杂度取决于状态数和转移
129+
complexity = ComplexityLevel.QUADRATIC
130+
131+
# 哈希表
132+
if re.search(r"unordered_map|unordered_set|hash_map", code):
133+
patterns.append("hash_table")
134+
# 如果主要操作是哈希,可能更优
135+
136+
# 递归
137+
if re.search(r"\breturn\s+\w+\s*\([^)]*\)", code) and re.search(
138+
r"\b\w+\s*\([^)]*\)\s*{", code
139+
):
140+
# 简单的递归检测
141+
patterns.append("recursion")
142+
143+
# 位运算
144+
if re.search(r"1\s*<<\s*\d|bitmask|bitset", code):
145+
patterns.append("bitmask")
146+
complexity = ComplexityLevel.EXPONENTIAL
147+
148+
return complexity, patterns
149+
150+
151+
def estimate_memory_usage(code: str) -> tuple[str, int]:
152+
"""估算内存使用。
153+
154+
Args:
155+
code: C++ 源代码
156+
157+
Returns:
158+
(空间复杂度描述, 估算的内存 MB)
159+
"""
160+
# 检测大数组
161+
array_patterns = [
162+
r"(\w+)\s*\[(\d+)\]", # int arr[1000]
163+
r"vector<\w+>\s+(\w+)\s*\((\d+)\)", # vector<int> v(1000)
164+
r"array<\w+,\s*(\d+)>", # array<int, 1000>
165+
]
166+
167+
total_elements = 0
168+
for pattern in array_patterns:
169+
matches = re.findall(pattern, code)
170+
for match in matches:
171+
try:
172+
# 获取数字部分
173+
if isinstance(match, tuple):
174+
size = int(match[-1])
175+
else:
176+
size = int(match)
177+
total_elements += size
178+
except (ValueError, IndexError):
179+
pass
180+
181+
# 估算内存(假设每个元素 4 字节)
182+
memory_bytes = total_elements * 4
183+
memory_mb = max(1, memory_bytes // (1024 * 1024))
184+
185+
if total_elements == 0:
186+
return "O(1) - O(n)", 64
187+
elif total_elements < 10000:
188+
return "O(n)", memory_mb
189+
elif total_elements < 1000000:
190+
return "O(n)", memory_mb
191+
else:
192+
return "O(n) - large", memory_mb
193+
194+
195+
class SolutionAnalyzeTool(Tool):
196+
"""分析解法复杂度。"""
197+
198+
@property
199+
def name(self) -> str:
200+
return "solution_analyze"
201+
202+
@property
203+
def description(self) -> str:
204+
return """分析 C++ 解法代码的时间/空间复杂度。
205+
206+
基于静态分析估算:
207+
- 时间复杂度(循环嵌套、算法模式)
208+
- 空间复杂度(数组、容器大小)
209+
- 推荐的测试参数
210+
211+
前置条件:
212+
1. 已有解法代码(可以是未编译的源码)
213+
214+
建议下一步:
215+
- 根据推荐的 n_max 调整测试数据生成参数
216+
- 根据推荐的 time_limit 设置题目时间限制
217+
"""
218+
219+
@property
220+
def input_schema(self) -> dict:
221+
return {
222+
"type": "object",
223+
"properties": {
224+
"code": {
225+
"type": "string",
226+
"description": "C++ 源代码",
227+
},
228+
"constraints": {
229+
"type": "object",
230+
"description": "已知的题目约束(可选)",
231+
"properties": {
232+
"n_max": {"type": "integer"},
233+
"time_limit_ms": {"type": "integer"},
234+
},
235+
},
236+
},
237+
"required": ["code"],
238+
}
239+
240+
async def execute(
241+
self,
242+
code: str,
243+
constraints: dict | None = None,
244+
) -> ToolResult:
245+
"""执行复杂度分析。"""
246+
# 1. 分析循环复杂度
247+
loop_complexity = analyze_loop_complexity(code)
248+
249+
# 2. 检测算法模式
250+
pattern_complexity, patterns = detect_algorithm_patterns(code)
251+
252+
# 3. 选择更优的复杂度估计
253+
# 优先使用模式检测的结果
254+
complexity_order = [
255+
ComplexityLevel.CONSTANT,
256+
ComplexityLevel.LOG_N,
257+
ComplexityLevel.LINEAR,
258+
ComplexityLevel.N_LOG_N,
259+
ComplexityLevel.QUADRATIC,
260+
ComplexityLevel.CUBIC,
261+
ComplexityLevel.EXPONENTIAL,
262+
ComplexityLevel.FACTORIAL,
263+
]
264+
265+
loop_idx = complexity_order.index(loop_complexity)
266+
pattern_idx = complexity_order.index(pattern_complexity)
267+
268+
# 如果模式检测到更优的复杂度,使用它
269+
if pattern_idx < loop_idx:
270+
final_complexity = pattern_complexity
271+
else:
272+
final_complexity = loop_complexity
273+
274+
# 4. 估算内存
275+
space_complexity, memory_mb = estimate_memory_usage(code)
276+
277+
# 5. 生成推荐参数
278+
recommended_n_max = COMPLEXITY_TO_N_MAX.get(final_complexity, 10000)
279+
recommended_time_ms = COMPLEXITY_TO_TIME_LIMIT.get(final_complexity, 1000)
280+
281+
# 如果有题目约束,验证是否合理
282+
warnings = []
283+
if constraints:
284+
if constraints.get("n_max"):
285+
if constraints["n_max"] > recommended_n_max:
286+
warnings.append(
287+
f"Warning: n_max={constraints['n_max']} may cause TLE "
288+
f"for {final_complexity} algorithm. Recommended: {recommended_n_max}"
289+
)
290+
if constraints.get("time_limit_ms"):
291+
if constraints["time_limit_ms"] < recommended_time_ms:
292+
warnings.append(
293+
f"Warning: time_limit={constraints['time_limit_ms']}ms may be too tight "
294+
f"for {final_complexity} algorithm. Recommended: {recommended_time_ms}ms"
295+
)
296+
297+
return ToolResult.ok(
298+
time_complexity=final_complexity,
299+
space_complexity=space_complexity,
300+
estimated_memory_mb=memory_mb,
301+
detected_patterns=patterns,
302+
recommended_n_max=recommended_n_max,
303+
recommended_time_limit_ms=recommended_time_ms,
304+
warnings=warnings,
305+
suggested_test_configs=self._generate_test_configs(
306+
recommended_n_max, constraints
307+
),
308+
message=f"Analyzed complexity: {final_complexity}",
309+
)
310+
311+
def _generate_test_configs(
312+
self, n_max: int, constraints: dict | None
313+
) -> list[dict]:
314+
"""生成推荐的测试配置。
315+
316+
Args:
317+
n_max: 推荐的 n 最大值
318+
constraints: 题目约束
319+
320+
Returns:
321+
测试配置列表
322+
"""
323+
# 使用约束中的 n_max 或推荐值
324+
actual_n_max = constraints.get("n_max", n_max) if constraints else n_max
325+
326+
configs = [
327+
# 边界情况
328+
{"type": "1", "n_min": 1, "n_max": 1, "t_min": 1, "t_max": 1},
329+
{"type": "1", "n_min": 1, "n_max": 10, "t_min": 1, "t_max": 1},
330+
# 随机数据
331+
{"type": "2", "n_min": 10, "n_max": actual_n_max // 10, "t_min": 1, "t_max": 1},
332+
{"type": "2", "n_min": actual_n_max // 10, "n_max": actual_n_max // 2, "t_min": 1, "t_max": 1},
333+
# 极限数据
334+
{"type": "3", "n_min": actual_n_max // 2, "n_max": actual_n_max, "t_min": 1, "t_max": 1},
335+
{"type": "3", "n_min": actual_n_max, "n_max": actual_n_max, "t_min": 1, "t_max": 1},
336+
]
337+
338+
return configs

src/autocode_mcp/tools/generator.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ def description(self) -> str:
2626
2727
保存并编译 gen.cpp。
2828
29-
注意:此工具不生成代码,代码由 Client LLM 生成后传入。
29+
前置条件:
30+
1. 已运行 problem_create 创建题目目录
31+
32+
建议下一步:
33+
- 运行 validator_build 构建校验器
34+
- 运行 stress_test_run 进行对拍测试
3035
"""
3136

3237
@property
@@ -106,6 +111,13 @@ def description(self) -> str:
106111
107112
自动通过 Validator 过滤无效输入。
108113
支持去重、平衡、采样。
114+
115+
前置条件:
116+
1. 已运行 generator_build 构建生成器
117+
118+
建议下一步:
119+
- 运行 stress_test_run 验证解法正确性
120+
- 运行 problem_generate_tests 生成最终测试数据
109121
"""
110122

111123
@property

0 commit comments

Comments
 (0)