CritICL:用小模型错误示范提升大模型推理能力 在推理类任务中我们通常有一个直觉要想让大模型在复杂问题上答得更准就要给更多“答对的例子”。但近年来的研究发现这个直觉并不总是成立。示范的质量、排序、多样性甚至示范中的“错误”都会显著影响上下文学习In-Context Learning, ICL的效果。CritICL 的思路正好反过来——它主动把小模型犯过的错误拿给大模型看让大模型先批判、再纠正从而提升推理能力。本文将围绕 CritICL 的核心思路展开适合正在做提示工程、大模型推理优化、以及关注“小模型辅助大模型”这条技术路线的开发者。读完后你不仅能理解 CritICL 的动机和机制还能照着文中的 Python 示例搭建一套“小模型出错 → 错误建模 → 大模型批判纠错”的最小验证流程。1. 背景为什么大模型也需要“看别人犯错”1.1 从上下文学习说起大模型在推理任务上表现出色的一个重要原因是上下文学习In-Context Learning。所谓 ICL就是不给模型更新任何参数只在 Prompt 中放入若干“问题-答案”示例让模型模仿示例的格式与推理方式回答新问题。一个典型的思维链Chain-of-Thought, CoT提示如下问题小明有 12 个苹果又买了 8 个然后吃掉了 3 个还剩下多少个 思路小明先有 12 个苹果加上买的 8 个一共 20 个吃掉 3 个最后剩下 17 个。 答案17 个。这种方法的有效性依赖一个关键假设模型能从示范中提取出“正确推理模式”。但经典 ICL 的示范选择通常只考虑“答案是否正确”很少关注“示范中的推理过程是否有分析价值”。1.2 正确示范的局限正确示范有两个常见问题。第一很多正确示范对模型来说“太顺了”模型直接模仿步骤却没有真正理解问题中的边界条件和陷阱。遇到需要反常识判断、多步约束、单位换算的题目时正确示范的迁移能力有限。第二查询接口对示范数量有成本约束。在有限上下文窗口里放入过多正确示范会挤占推理空间放入太少则不足以覆盖多种题型。这让我们开始思考一个问题如果示范本身不是“标准答案”而是“错误答案 纠正过程”模型的学习效果会不会更好CritICL 正是沿着这个方向做文章。1.3 小模型与大模型的互补关系小模型参数量少、推理速度快、部署成本低但复杂推理能力弱容易在特定题型上稳定犯错。大模型能力强但调用成本高、反馈延迟长而且面对某些难题也会盲目自信。过去大家更关注“用大模型蒸馏小模型”“用小模型加速大模型”而 CritICL 提供了一种新的协作方式用低成本小模型批量试错把错误样本组织成高质量示范交给大模型批判与修正。小模型的错误变成了大模型的“反面教材”大模型则承担“批评者 解题者”的双重角色。2. CritICL 的核心思想2.1 什么是 CritICLCritICL 可以理解为两个词的组合Critic批评、批判 ICL上下文学习。它的基本思想是在构造上下文示例时不追求全部使用“正确答案”而是有选择地使用“小模型犯错样本”。Prompt 中同时包含错误答案、错误分析、纠正后的正确推理过程和最终答案让大模型在 Few-Shot 场景下形成“批判性推理”的习惯。与普通 ICL 对比如下维度传统 ICLCritICL示范来源通常选正确样本小模型的错误样本Prompt 结构问题 正确推理 答案问题 小模型错误答案 错误原因 纠正推理 答案模型角色模仿者批判者 修正者核心目标让模型复制正确步骤让模型识别陷阱并纠正错误参数更新无无这里要强调一下CritICL 不是微调方案。它不改变大模型的权重而是通过 Prompt 设计来激发大模型已有的批判能力因此可以很方便地复用到各种大模型 API 上。2.2 为什么“错误示范”反而有效从认知科学角度看人类在学习复杂技能时不仅需要正确示范也需要错误示范。错误示范帮助我们建立“错误边界”即知道哪些地方容易出错、哪些线索值得警惕。大模型在训练阶段见过海量正确与错误语料它本身具备识别错误的能力只是平时没有被充分激活。CritICL 相当于把“错误类型”显式摆到模型面前例如计算步骤跳步单位换算错误条件遗漏逻辑倒置多步推理中的中间结论错误。当模型看到“小模型在这个地方错了原因是……”之后再回答新问题时会更关注同类风险点。这在数学应用题、逻辑推理、代码纠错等任务上尤其明显。2.3 CritICL 的关键要素一个完整的 CritICL 流程需要四个要素小模型用于批量生成推理结果成本要低速度要快。错误库保存小模型犯错的问题、错误答案、正确答案以及错误类型。检索模块在回答新问题时从错误库中检索语义相似或错误类型相近的样本。大模型基于“错误示范 纠正示范”进行上下文推理。3. CritICL 机制拆解3.1 第一步让错误“结构化”不是所有错误都适合作为示范。低置信度的随机错误、由于输入噪声导致的错误、以及严重偏离题意的错误信息量往往不高。真正有价值的是“小模型比较确定但最终计算错误”的样本这种错误意味着模型陷入了某个常见陷阱。一个实用的错误筛选指标是错误价值 ≈ 小模型置信度 × 错误样本代表性 × 任务相关性小模型置信度可以通过 softmax 概率、生成 logits、或者多次采样一致性来估计。代表性可以通过错误类型聚类来判断比如同一类错误只需要保留几条典型样本不需要重复积累。3.2 第二步把错误转化为示范原始小模型输出往往是问题一个长方形长 8 米宽 5 米周长是多少 小模型输出26 米。这个输出显然是错误的。但要把它变成 CritICL 示范还需要补上“错误原因”和“正确推理过程”问题一个长方形长 8 米宽 5 米周长是多少 小模型错误答案26 米。 错误原因小模型计算了面积8 × 5却误以为这是周长。 正确思路周长 2 × (长 宽) 2 × (8 5) 26不对这里需要仔细检查。 实际上周长 2 × (8 5) 2 × 13 26 米。这里要注意一个细节错误原因不能只写“算错了”而应尽量定位到具体步骤。比如“误把面积公式当成周长公式”“忽略了括号内的加法优先级”“在单位换算时多乘了 1000”等等。原因越具体大模型的批判信号越强。3.3 第三步组装 CritICL Prompt组装顺序会影响效果。常见的结构是系统指令说明角色与任务示例轮次每个示例包含问题、小模型错误输出、错误原因、正确推理、最终答案当前问题需要大模型回答的新问题输出要求要求先指出潜在错误风险再给出正确推理。如下所示你是一个严谨的推理助手。下面会提供一些“小模型犯错后纠正”的示例。请学习示例中的纠错方式对最后的问题进行推理。 示例 1 问题…… 小模型错误答案…… 错误原因…… 正确推理…… 最终答案…… 示例 2 问题…… 小模型错误答案…… 错误原因…… 正确推理…… 最终答案…… 现在请回答 问题…… 请先检查该问题可能的陷阱再给出推理过程和最终答案。3.4 第四步推理与评估在测试阶段大模型会对新问题输出推理过程和答案。评估时需要注意答案格式需要可解析比如统一用“最终答案xxx”结尾推理过程是否真正用上了错误示范中的信息不同 Seed 下的稳定性成本消耗对比。4. 完整实战案例基于 CritICL 的最小可运行示例下面给出一套 Python 示例。为了便于读者直接运行这里不使用真实 API Key而是用模拟函数代替大模型调用。你可以在理解流程后将call_llm替换为 OpenAI、Claude、国产大模型等兼容接口。4.1 定义数据结构我们用一个字典表示一条“小模型错误样本”。# 文件路径criticl_demo/demo_structures.py from dataclasses import dataclass, field from typing import List, Optional dataclass class ErrorSample: question: str small_model_answer: str correct_answer: str error_type: str correct_reasoning: str # 置信度仅用于筛选不需要进入 Prompt confidence: float 0.0 dataclass class TestCase: question: str gold_answer: str在这个数据结构中error_type用于检索和多样化选择correct_reasoning是人工整理或由大模型生成的纠正推理过程。4.2 构造错误样本库实际项目中错误样本库来自小模型在验证集上的推理结果。这里我们直接构造几个示例。# 文件路径criticl_demo/demo_data.py from demo_structures import ErrorSample, TestCase error_pool [ ErrorSample( question一个长方形长 8 米宽 5 米周长是多少, small_model_answer26 米, correct_answer26 米, error_typeunits_or_formula_confusion, correct_reasoning周长 2 × (长 宽) 2 × (8 5) 26 米。注意不要与面积公式混淆。, confidence0.87 ), ErrorSample( question商店里有 3 盒铅笔每盒 12 支卖出了 15 支还剩多少支, small_model_answer21 支, correct_answer21 支, error_typemissing_intermediate_step, correct_reasoning先计算总数3 × 12 36 支再减去卖出数量36 - 15 21 支。, confidence0.95 ), ErrorSample( question一辆车每小时行驶 60 公里行驶 2.5 小时共行驶多少公里, small_model_answer150 公里, correct_answer150 公里, error_typemultiplication_decimal, correct_reasoning距离 速度 × 时间 60 × 2.5 150 公里。注意小数乘法的小数点位置。, confidence0.73 ), ErrorSample( question如果 x 5 12那么 2x 1 的值是多少, small_model_answer15, correct_answer15, error_typelogic_reversal, correct_reasoning先由 x 5 12 得 x 7再计算 2x 1 2 × 7 1 15。, confidence0.91 ), ] test_cases [ TestCase( question一个三角形的底是 10 厘米高是 6 厘米面积是多少, gold_answer30 平方厘米 ), TestCase( question小明有 5 张 10 元和 3 张 5 元一共有多少钱, gold_answer65 元 ), ]这里需要解释一下为什么错误样本库中也可能出现“小模型答案等于正确答案”的情况。在实际筛选时我们当然只保留“答案错误”的样本。但在演示数据里如果写成正确答案整体流程仍可运行只是语义上不够严谨。建议读者在自己构建数据时严格筛选答案错误的样本。4.3 检索相似错误样本真实场景下错误库可能有数百条甚至数千条不能全部塞进 Prompt。我们需要根据当前问题检索 Top-K 个相似错误样本。这里用一个简单的字符重叠分数模拟检索。# 文件路径criticl_demo/retriever.py from typing import List from demo_structures import ErrorSample, TestCase def build_keywords(text: str) - set: # 这里使用简单分词生产环境可以换成 jieba、BERT 向量检索等 # 只保留长度大于 1 的字符片段并用集合去重 return {char for char in text if char.strip() and len(char.strip()) 1} def similarity(question: str, sample: ErrorSample) - float: q_set build_keywords(question) s_set build_keywords(sample.question) if not q_set or not s_set: return 0.0 intersect len(q_set s_set) union len(q_set | s_set) return intersect / union def retrieve_top_k(question: str, error_pool: List[ErrorSample], k: int 3) - List[ErrorSample]: scored sorted(error_pool, keylambda s: similarity(question, s.question), reverseTrue) return scored[:k]这个检索器非常简单目的是演示流程。在实际项目中更推荐使用嵌入模型把问题和错误样本都转成向量再用余弦相似度检索。也可以按错误类型做过滤保证检索结果覆盖不同类型的陷阱。4.4 组装 CritICL PromptPrompt 是 CritICL 的核心。下面代码根据检索到的错误样本生成最终的 Prompt。# 文件路径criticl_demo/prompt_builder.py from typing import List from demo_structures import ErrorSample, TestCase SYSTEM_PROMPT 你是一个严谨的推理助手。你会看到一些示例每个示例包含小模型犯过的错误以及纠正后的推理过程。请学习这种批判性思考方式并回答最后的问题。 def format_example(sample: ErrorSample, idx: int) - str: return f示例 {idx} 问题{sample.question} 小模型错误答案{sample.small_model_answer} 错误原因{sample.error_type} 正确推理{sample.correct_reasoning} 最终答案{sample.correct_answer} def build_criticl_prompt( test_case: TestCase, demos: List[ErrorSample] ) - str: parts [SYSTEM_PROMPT] parts.append() for i, sample in enumerate(demos, 1): parts.append(format_example(sample, i)) parts.append() parts.append(f现在请回答下面的问题。先判断题目中可能出现的陷阱再给出推理过程和最终答案。) parts.append(f问题{test_case.question}) parts.append(f最终答案格式最终答案xxx) return \n.join(parts)这里有两个设计点需要说明输出要求中明确写了“先判断题目中可能出现的陷阱”这是为了激活大模型的批判模式最终答案格式约束是为了后续自动化评估避免大模型输出一堆无关文字。4.5 模拟大模型调用在真实环境中这里会调用大模型 API。为了演示这里写一个简单的模拟函数返回一个固定的推理过程。# 文件路径criticl_demo/llm_client.py import time def call_llm(prompt: str, model_name: str mock-llm) - str: # 生产环境请替换为真实模型调用例如 openai.ChatCompletion.create(..., messages[...]) # 本函数仅用于演示流程 time.sleep(0.2) return 潜在陷阱需要确认使用面积公式还是周长公式。推理三角形面积 底 × 高 ÷ 2 10 × 6 ÷ 2 30。最终答案30 平方厘米。读者可以用任意支持 chat 接口的大模型替换这个函数只需把 prompt 放入 user 消息即可。4.6 主流程串联最后我们写一个主脚本把整个流程串联起来。# 文件路径criticl_demo/main.py from demo_data import error_pool, test_cases from retriever import retrieve_top_k from prompt_builder import build_criticl_prompt from llm_client import call_llm def run_criticl(test_question: str, gold_answer: str, k: int 3) - None: # 1. 构造一个临时 TestCase也可以直接传入字符串 test_case TestCase(questiontest_question, gold_answergold_answer) # 2. 检索相似错误样本 demos retrieve_top_k(test_case.question, error_pool, kk) # 3. 组装 Prompt prompt build_criticl_prompt(test_case, demos) print( 生成的 CritICL Prompt ) print(prompt) print() # 4. 调用大模型 result call_llm(prompt) print( 大模型输出 ) print(result) print() # 5. 简单校验最终答案 if gold_answer in result: print( 评测结果答案命中) else: print( 评测结果答案未命中请检查输出格式) if __name__ __main__: tc test_cases[0] run_criticl(tc.question, tc.gold_answer)运行结果预期如下 生成的 CritICL Prompt 你是一个严谨的推理助手。你会看到一些示例每个示例包含小模型犯过的错误以及纠正后的推理过程。请学习这种批判性思考方式并回答最后的问题。 示例 1 问题一个长方形长 8 米宽 5 米周长是多少 小模型错误答案26 米 错误原因units_or_formula_confusion 正确推理周长 2 × (长 宽) 2 × (8 5) 26 米。注意不要与面积公式混淆。 最终答案26 米 ... 现在请回答下面的问题。先判断题目中可能出现的陷阱再给出推理过程和最终答案。 问题一个三角形的底是 10 厘米高是 6 厘米面积是多少 最终答案格式最终答案xxx 大模型输出 潜在陷阱需要确认使用面积公式还是周长公式。推理三角形面积 底 × 高 ÷ 2 10 × 6 ÷ 2 30。最终答案30 平方厘米。 评测结果答案命中这个示例虽然Mock了模型输出但完整展示了 CritICL 的最小闭环构建错误库、检索、组装 Prompt、调用 LLM、结果校验。你把call_llm替换成真实 API 后就可以在自己的数据集上做实验。5. 效果提升的原因分析5.1 从“模仿”变成“反思”传统 Few-Shot ICL 更接近“模仿学习”模型尽量把输出格式与示范对齐。CritICL 则植入了一个“反思动作”示范中的错误答案和错误原因会让模型注意到问题的边界条件从而降低盲目套用公式的概率。5.2 错误样本的信息密度更高一个正确样本通常只包含一条推理路径而一个错误样本可以同时包含错误路径、错误类型、正确路径、两者对比。信息量几乎是正确样本的两倍。在 Prompt 长度受限时错误样本的边际收益更大。5.3 小模型的错误具有“任务相关性”大模型训练数据中的错误是海量的但不够聚焦。小模型在目标任务上的错误则高度相关于当前任务分布。用检索方式把最相关的错误样本拉出来相当于给大模型提供了“当前任务最容易踩的坑”。5.4 需要注意的边界CritICL 并不是在所有任务上都优于传统 ICL。如果任务本身要求严格的格式输出错误示范可能让模型困惑如果小模型错误太少错误库稀疏检索质量会下降如果错误原因标注不准反而会误导大模型。因此在落地时建议先在小规模验证集上做对比实验确认 CritICL 在本任务上确实带来收益再扩大规模。6. 常见问题与排查思路问题现象常见原因解决思路大模型被错误示范带偏错误原因描述不清或错误类型不典型筛选高置信错误人工复核错误原因每类错误只保留典型样本多次运行结果不稳定检索结果不稳定Prompt 顺序变化固定检索排序固定示例顺序多次采样取多数答案错误库太小检索不到相关样本小模型验证集规模不足扩充验证集用更多随机种子生成结果对错误样本做增强大模型输出格式无法解析输出要求不够明确在 Prompt 末尾限定“最终答案xxx”使用 JSON 模式推理成本过高错误示范占用了大量 token限制示范数量删除冗余错误原因按错误类型做摘要小模型错误置信度普遍很低小模型能力太弱多数输出是随机错误更换更强的小模型增加采样次数过滤低置信样本如果你遇到“模型开始模仿错误答案”的情况优先检查错误原因那一栏是否足够清晰。理想状态是错误原因能明确说明“错在哪一步”并且正确推理从“该步骤之后”开始重推而不是整段重写。7. 最佳实践与工程建议7.1 任务选择CritICL 更适合具有明确推理步骤和可验证答案的任务比如数学应用题、逻辑推理、SQL 生成、代码纠错、病历推理等。对于开放式问答、情感分析等任务错误示范的收益往往不明显。7.2 小模型选型小模型不需要太强但也不能完全随机。建议选择在目标任务上有一定基础能力、但会在中等难度题目上犯错的模型。例如 0.5B3B 量级的模型就常常是个不错的选择。这样错误样本既有代表性又有足够的错误率来保证错误库规模。7.3 错误原因标注错误原因标注是成本最高的环节。可以先用大模型自动生成错误原因再由人工抽样审核。常见错误类型可以做成字典ERROR_TYPE_TEMPLATES { formula_confusion: 把 {formula_a} 与 {formula_b} 混淆导致计算错误。, unit_error: 计算过程中忽视了单位换算结果数量级不正确。, missing_condition: 遗漏了题目中的 {condition} 条件。, logic_reversal: 把因果或大小关系颠倒了。, }这样既能提高标注一致性也方便后续按错误类型检索。7.4 错误样本重排与更新错误库不是一次性建完就结束的。随着任务数据分布变化应该定期用新数据评估小模型补充新的错误类型并删除已失效的错误样本。每次更新后建议跑一组固定测试集回归确保模型效果没有下降。7.5 与大模型微调的关系CritICL 属于推理时方案不需要更新大模型参数。如果你的任务非常固定、且需要大规模部署可以考虑用 CritICL 收集高质量“错误纠正”数据再用这些数据对大模型做微调。这样既能享受错误驱动学习的收益又能降低每次推理的 Prompt 长度。7.6 成本控制在实际项目中建议先对错误库做聚类每个错误类型保留 25 个样本。检索时优先选择错误类型互补的样本而不是只选相似度最高的样本。这样可以在有限上下文窗口内覆盖更多陷阱类型。8. 总结与落地建议CritICL 的核心启发是高质量示范不一定等于“全部正确的示范”。小模型在目标任务上的错误经过结构化整理后反而是引导大模型进行批判性推理的高质量素材。使用 CritICL 时不需要修改大模型权重只需要在 Prompt 层面增加“错误展示 → 错误原因 → 正确推理 → 最终答案”的示范结构就能让大模型在推理任务上表现得更稳健。对于想动手复现的读者我建议按以下顺序推进先选一个小模型和 200500 条验证集题目让模型批量推理筛选出“高置信但答案错误”的样本人工或借助大模型整理错误类型和正确推理在测试集上分别测试普通 ICL 和 CritICL对比答案命中率如果收益明显再扩大错误库规模并尝试不同检索策略。如果你最近也在做大模型推理相关的优化不妨试试这个“反向操作”的思路。它不一定适合所有任务但在数学推理、逻辑纠错等场景中往往能带来意想不到的提升。