刷题 Agent 的失败模式分析:什么时候 AI 给的答案靠不住
一、Agent 不是永远都对
构建了刷题 Agent 之后,你会逐渐发现它有一些"固定失败模式"。不是随机出错,而是在特定类型的输入下系统性失败。
理解这些失败模式,不是为了否定 Agent 的价值,而是为了在它能做好的领域让它自动运作,在它容易出错的领域引入人工审查或其他兜底机制。这是工程化的思维方式——不追求完美,追求可控。
二、失败模式的分类
flowchart TD A[Agent 失败模式] --> B[模式理解失败] A --> C[约束遗漏失败] A --> D[复杂度误判失败] A --> E[边界遗漏失败] A --> F[过度自信失败] B --> B1["复杂条件判断: if嵌套深"] B --> B2["隐含条件: 题目没明确说但必须考虑"] B --> B3["多步骤推理: 需要3步以上的推导"] C --> C1["忽略时间约束: n=10^5 却给 O n² 解"] C --> C2["忽略空间约束: 要求O 1 却用 O n"] C --> C3["忽略特殊限制: 不能使用除法等"] D --> D1["低估嵌套循环: 3层循环以为是2层"] D --> D2["高估剪枝效果: 以为能剪掉大部分"] D --> D3["忽略递归开销: 栈空间和函数调用"] E --> E1["空输入/单元素输入"] E --> E2["极大值/极小值"] E --> E3["重复元素/相同值"] F --> F1["生成的代码有逻辑bug但声称完全正确"] F --> F2["复杂度分析错误但语气很确定"] F --> F3["生成了不存在的API"]三、失败模式检测系统
from dataclasses import dataclass, field from typing import Optional, Callable from enum import Enum import re class FailureCategory(Enum): """失败类别""" PATTERN_MISUNDERSTANDING = "pattern_understanding" CONSTRAINT_MISSING = "constraint_missing" COMPLEXITY_MISJUDGE = "complexity_misjudge" BOUNDARY_MISSING = "boundary_missing" OVERCONFIDENCE = "overconfidence" @dataclass class AgentOutput: """Agent 的一次输出""" code: str analysis: str problem_description: str constraints: dict language: str = "python" class FailureDetector: """失败模式检测器 在 Agent 输出后、用户看到前,自动检测常见失败模式。 检测到问题时可以:自动修正、降低置信度、或标记需人工审查。 """ def detect(self, output: AgentOutput) -> dict: """检测所有失败模式,返回检测报告""" detections = { "has_failure": False, "failures": [], "confidence": 1.0, "recommendation": "pass", # pass / review / reject } # 检测 1:约束遗漏 constraint_issues = self._check_constraints(output) if constraint_issues: detections["has_failure"] = True detections["failures"].extend(constraint_issues) detections["confidence"] -= 0.3 * len(constraint_issues) # 检测 2:复杂度误判 complexity_issues = self._check_complexity(output) if complexity_issues: detections["has_failure"] = True detections["failures"].extend(complexity_issues) detections["confidence"] -= 0.2 * len(complexity_issues) # 检测 3:边界遗漏 boundary_issues = self._check_boundaries(output) if boundary_issues: detections["has_failure"] = True detections["failures"].extend(boundary_issues) detections["confidence"] -= 0.15 * len(boundary_issues) # 检测 4:过度自信 overconfidence = self._check_overconfidence(output) if overconfidence: detections["failures"].append(overconfidence) # 根据置信度给出建议 detections["confidence"] = max(0.0, detections["confidence"]) if detections["confidence"] < 0.3: detections["recommendation"] = "reject" elif detections["confidence"] < 0.6: detections["recommendation"] = "review" else: detections["recommendation"] = "pass" return detections def _check_constraints(self, output: AgentOutput) -> list[dict]: """检查是否违反了题目约束 例如:题目要求 O(n) 但给了 O(n²) 解法 """ issues = [] constraints = output.constraints analysis = output.analysis # 检查时间约束 if "n" in constraints: n = constraints.get("n") if n and isinstance(n, int) and n >= 10**5: # n 很大,必须 O(n) 或 O(n log n) if "O(n²)" in analysis or "O(n^2)" in analysis: issues.append({ "category": FailureCategory.CONSTRAINT_MISSING, "detail": f"n={n} 但给出了 O(n²) 解法,会超时", "severity": "critical", }) # 检查空间约束 if constraints.get("space") == "O(1)": if "O(n)" in analysis: issues.append({ "category": FailureCategory.CONSTRAINT_MISSING, "detail": "要求 O(1) 空间但分析显示 O(n)", "severity": "high", }) return issues def _check_complexity(self, output: AgentOutput) -> list[dict]: """检查复杂度声明与实际代码是否一致""" issues = [] code = output.code analysis = output.analysis # 检测嵌套循环(简单启发式) lines = code.split("\n") nested_loops = 0 in_loop = 0 for line in lines: stripped = line.strip() if stripped.startswith("for ") or stripped.startswith("while "): in_loop += 1 if in_loop >= 3: nested_loops = 3 break # 简化:不检测循环结束(实际需要 AST 分析) if nested_loops >= 3 and "O(n" in analysis and "O(n^" not in analysis and "O(n²" not in analysis): issues.append({ "category": FailureCategory.COMPLEXITY_MISJUDGE, "detail": ( f"代码中有 {nested_loops} 层嵌套循环," f"但复杂度声明中未体现" ), "severity": "high", }) # 检查递归是否分析了栈空间 if "def " in code and "return" in code: # 检测递归调用 func_name_match = re.search(r"def (\w+)", code) if func_name_match: func_name = func_name_match.group(1) if func_name + "(" in code.split("def " + func_name)[-1]: if "栈" not in analysis and "stack" not in analysis.lower(): issues.append({ "category": FailureCategory.COMPLEXITY_MISJUDGE, "detail": "使用了递归但未分析栈空间复杂度", "severity": "medium", }) return issues def _check_boundaries(self, output: AgentOutput) -> list[dict]: """检查是否处理了关键边界情况""" issues = [] code = output.code # 检查空输入处理 if "def " in code and "nums" in code or "arr" in code or "list" in code: if "if not" not in code and "if len" not in code and "== 0" not in code: issues.append({ "category": FailureCategory.BOUNDARY_MISSING, "detail": "未检测到空数组的边界处理", "severity": "medium", }) # 检查除零保护 if "/" in code and "if" not in code.split("/")[0].rsplit("\n", 1)[-1]: if "!= 0" not in code and "== 0" not in code: pass # 不是所有除法都需要保护,降低误报 return issues def _check_overconfidence(self, output: AgentOutput) -> Optional[dict]: """检查过度自信的标志""" analysis = output.analysis # 过度自信的语言特征 overconfident_phrases = [ ("显然", "算法分析中使用'显然'可能掩盖了推导漏洞"), ("容易看出", "如果'容易看出'后面没有解释,可能是理解不到位的信号"), ("一定正确", "100% 确定的表述在算法验证中需要警惕"), ] for phrase, explanation in overconfident_phrases: if phrase in analysis: return { "category": FailureCategory.OVERCONFIDENCE, "detail": f"发现过度自信表述: '{phrase}'——{explanation}", "severity": "low", } return None四、如何利用失败模式分析
4.1 构建检测先验规则
将已知的失败模式编码为检测规则,在 Agent 输出后自动运行。检测到问题后:
- critical/high 级别:拒绝输出,触发重新生成
- medium 级别:标记为"需人工审查"
- low 级别:仅记录日志,不阻塞流程
4.2 反馈给 Agent 自身
将检测到的问题反馈给 Agent,让它根据反馈修正。这构成了"检测-反馈-修正"的闭环。
4.3 统计与趋势
记录各失败模式的出现频率,形成 Agent 的"能力画像"。如果"约束遗漏"占比 30%,说明需要加强约束感知能力——比如在 prompt 中显式强调约束条件。
五、总结
AI Agent 的失败不是随机的,而是有模式的。识别这些模式,建立自动检测机制,是为 Agent 构建安全护栏的关键步骤。不追求 Agent 永远不犯错(不现实),追求的是当它犯错时,能检测到、能补救、能避免把错误输出给用户。