我一直觉得,现在很多AI Agent最气人的地方不是“不会”,而是“不会还嘴硬”。你问它一个前沿问题,它正正经经给出一个看起来很有条理的答案,你信了,拿去用了,结果发现全是错的,而且错得斩钉截铁。这不是模型能力的问题,是Agent缺少一个“自我评估”的环节:它做完事,没有停下来问问自己“我做得对吗”“我有把握吗”“我要不要再检查一下”。这篇内容就是围绕“元认知AI:具有自我评估能力的AI Agent”这个项目来写的,讲清楚我怎么做了一个带自我评估能力的Agent,让它在回答问题前先给自己打分、低分就自动纠错或者直接承认不会。适合正在做Agent开发、RAG应用、自动化工作流的开发者参考,尤其是被“幻觉”“无效输出”“不可控”问题折磨过的人。
1. 项目定位与整体设计思路
1.1 元认知Agent到底在解决什么问题
先说一个我实际遇到的案例。某客服工单机器人上线初期,用户问“订单超过七天还能不能无理由退款”,机器人检索到的是三个月前的旧政策文档,理直气壮回复“根据最新规则,七日无理由退货支持七天以上订单”。结果用户真的申请退款被拒,直接投诉到人工。事后排查,检索链路、生成链路都正常,模型也确实读到了文档,问题出在“没人告诉它,你检索到的文档已经过期了”。
如果把这类现象拆开看,本质上是两个问题叠加:第一,Agent对自身知识边界没有感知,它分不清“我确定知道”和“我检索到但没验证”;第二,Agent在错误方向上运行时会一直自信地错下去,没有任何机制打断它。前者叫“不知己不知”,后者叫“缺乏事中控制”。
这个项目要做的,就是给Agent加一个“自查层”:每次LLM生成完回复之后,不直接返回给用户,而是先让另一个评估动作对答案进行打分,检查事实支撑、逻辑连贯、检索来源质量,分低了就走修正流程,或者直接降级为“抱歉,这个问题我暂不确定”。
这个思路听起来不复杂,但落地时有不少细节值得较真,后面我会逐个展开。先记住一个核心转换:从“让模型尽量答对”变成“让模型知道自己有没有答对”。
1.2 系统架构:把“做事”和“检查”分开
在设计这个Agent时,我坚持了一个原则:执行和评估不能混在一个Prompt里。很多人喜欢写一段很长很长的Prompt,让模型“先思考再回答再自我检查”,实际效果非常不稳定。为什么?因为大模型在生成时是逐token取样的,“角色扮演”太重会让它觉得自己已经检查过了,形成一种“虚假完成感”,它会在文本里说“以上回答已经过仔细验证”,实际上并没有真正的验证动作。
我把系统分成三个层次:
- 执行层:负责接收问题、调用检索、生成回答,就是普通Agent做的那部分。
- 元认知评估层:负责对执行层产出进行打分、判断、决定下一步动作,相当于一个“内部审计员”。
- 记忆与策略层:记录评估结果、重试次数、历史修正记录,帮助Agent在连续对话中调整行为策略。
“审计员”和“员工”分开,最大的好处是职责边界清晰。员工可以专心把活儿干完,审计员不参与干活,只盯着质量。这样即使主模型偶尔“发挥不好”,评估层也有机会拦住它。
在实现上,我前期采用同模型自评+不同系统提示词的方案,后期尝试了独立的轻量评估模型。两个方案各有取舍,后面代码部分我会专门讲。
1.3 能力边界:元认知不是银弹
这部分写出来可能会泼很多人冷水,但我觉得必须说清楚。元认知评估不是万能的,它救不了“模型内部知识本身就是错的”的情况,比如某些冷门历史细节模型从来没学对过,你再怎么自查它也会觉得“我对”。它能拦住的,是那些可以通过“逻辑检查”“证据检查”“多路验证”暴露出来的问题。
哪些场景适合做自评?
- 知识密集型问答:有外部知识库可以验证。
- 代码生成:有测试用例或静态检查可以做证据回验。
- 数据分析/报告生成:数据表本身是结构化证据。
- 客服、政务、医疗咨询:允许“不回答”比“乱回答”安全得多。
哪些场景不太适合?
- 纯创意发散类任务,比如“写一首诗”“编一个故事”,没有明确对错,自评意义不大。
- 实时强交互对话,比如闲聊、情感陪伴,每轮都做深度评估会拖慢响应速度。
所以要做的第一件事,不是急着把所有Agent都装上自评模块,而是先判断你的业务场景里“错误的代价有多大”。如果答错的损失很难承受,就有必要上这套机制;如果只是随便聊,那就别折腾了,保持轻量。
2. 核心机制与关键设计
2.1 元认知循环:从“事后反思”到“事中控制”
很多人理解的自我评估是“让模型反思一下自己刚才的答案”,这是不对的。“反思”这个词本身就是事后诸葛,它做得再多也只是在错误终点上做文体分析。元认知AI真正该做的事情,是把“评估”动作插入到“生成完”和“最终输出”之间的决策点,形成一个控制循环。
我的循环设计包含三个动作模块:
- 自我提问:在生成答案后,评估层主动追问“这个回答的证据链完整吗”“我的检索来源能支撑这个结论吗”“有没有另一种答案可能更合理”。
- 自我检查:不是让模型嘴上说说,而是调用具体工具去检查。比如读取检索结果的原始段落,比对答案中的关键数值是否一致;或者先把结论拆分成若干断言,再逐条验证。
- 自我修正或求助:分低了,就重新检索、重新推理、换个角度再想,最多重试N次;还不行,就明说“我不确定”,而不是硬编一个答案。
执行顺序是:问题进来 → 主Agent生成初版答案 → 评估层打分 → 高置信直接放行,中置信进入修正循环,低置信进入降级处理 → 所有结果写入日志。
这个循环的关键不在“循环”本身,而在“停止条件”。如果允许无限次自我修正,Agent会陷入反复重写的死循环,效果反而更差。我用的是“最多两轮修正+置信度阈值双保险”,这个参数后面会讲怎么调。
2.2 自评信号设计:一个总分不如多个维度
最开始我犯过一个错误:让评估模型给答案打一个0到100的整数分,然后拿阈值判断是否放行。结果是分数虚高极其严重,几乎每次打分都在80分以上,100分也经常出现。原因很简单,模型在给自己人打分时会“护短”,它本身有生成偏好,不愿意轻易否定自己的输出。
后来我把单分改成多维评分,每个维度单独打分,并且要求每个分数都必须附证据。我目前用的维度是四个:
- 事实支撑度:回答中的关键事实是否能在检索来源中找到对应内容。
- 逻辑一致性:结论和推导过程是否自洽,有没有前后矛盾。
- 检索覆盖度:检索到的资料是否充分覆盖了问题的关键方面。
- 指令契合度:回答是否真的回应了用户问题,而不是答了另一个问题。
每个维度单独打0-1分,再合成一个总置信度。合成时四个维度不是简单平均,而是看场景决定权重。比如知识问答里事实支撑度权重最高,逻辑一致性和检索覆盖度次之;代码生成里逻辑一致性权重提高,事实支撑度降低。
这样做的好处是,当某个维度分数很低时,修正模块可以直接定位问题:是没检索到?还是推理错了?还是跑题了?修正动作也能更精准。试想一下如果只拿一个总分,你只知道“不行”,但完全不知道哪儿不行,修正就成了盲人摸象。
2.3 置信度阈值:打分之后,行动策略才是重点
评估分数算出来后,最关键的问题是“拿这个分数怎么办”。我的策略是把决策分成三档:
- 置信度 ≥ 0.85:直接返回答案,不做额外处理。
- 置信度在 0.6 到 0.85 之间:进入一次修正循环,可能是重新检索、重新推理,再评估一次。
- 置信度 < 0.6:不硬答,转人工或者输出不确定性声明。
注意这里有个很微妙的设计:低置信度不进入修正循环,而是直接放弃。为什么?因为如果检索和推理都做过了,分数还是低于0.6,说明这个问题的证据基础太差,修正大概率也是白修,还不如痛快承认不知道,省点token和时间。
那0.6和0.85这两个数字是怎么来的?不是拍脑袋定的,是拿验证集标定出来的。我做了两百个测试问题,其中包含正确答案的部分和故意构造的错误样本,跑完一批结果后画出“不同阈值下的准确率曲线”,在“放行错误答案的比例”和“误拦正确回答的比例”之间找平衡点。这个调参过程后面我单独拿出一节来讲,因为这是直接决定系统可用性的关键环节。
2.4 自我修正:别让Agent“嘴硬”到底
修正这个动作看起来简单,实际上特别容易翻车。模型在被打回重做时,最常见的表现是“我错了,正确答案是XXX”,结果仔细一看,它只是把原来的错误说法换了一种措辞再说一遍,本质上还是错的。我把这个现象叫做“无效道歉”——它道歉了,但没改正。
为了解决这个问题,我给修正模块加了一条限制:修正轮里不允许模型凭感觉重写答案,必须根据评估层反馈的具体缺陷去执行动作。如果评估层指出“事实支撑度不足”,修正动作必须是“重新检索关键词并找到新的证据段落”,然后把新的证据和自己之前的回答逐条对比,有冲突就按证据改,没有冲突就维持原判。如果评估层指出“逻辑矛盾”,修正动作就必须把矛盾的两个断言单拎出来,重写推理链。
简单说,修正不是“写一遍更好的答案”,而是“基于具体缺陷执行具体的修复动作”。这两者有本质区别。前者是赌运气,后者是走流程。
还有一个心态问题值得说一下:对很多Agent来说,“承认自己不知道”才是最难学的动作。我在系统里明确鼓励低置信度场景直接输出“这个问题当前知识库中没有足够信息,建议咨询人工或提供更多资料”,实测用户对这类回答的满意度比“硬答但答错”高得多。这个结论对很多做客服机器人的人应该不陌生——人们能接受“暂时不知道”,但不能接受“一本正经地胡说”。
3. 实现过程与代码落地
3.1 技术选型与模型策略
先说一下我这个项目的技术栈:模型以开源可本地部署的通用大模型为主,调用框架用常用Agent SDK,检索用向量数据库加BM25混合召回。整体是一个简化的RAG应用,但重点全在“评估层”怎么搭。
关于评估模型,我做了两个版本的比较。版本A:直接用主模型做评估,只是换一套系统提示词,把它降到“评估者”角色。版本B:额外接一个轻量评估模型。
版本A最大的优势是便宜、简单、不用多维护一个模型,而且主模型对自身输出风格熟悉,评估时更懂上下文。劣势是“既当运动员又当裁判员”,同一个模型在打分时会被它生成答案时的偏好带偏。比如它生成了一篇长回答,评估时会倾向于“内容很充实,给高分”。
版本B更客观一些,因为评估模型和生成模型不同,不会对自己的输出有“感情分”。但它有另一个问题:轻量模型评估精度有限,经常看不出深层的逻辑漏洞,分数分布也更飘。最后我的做法是:日常问题用版本A,遇到问题复杂度超过阈值(比如问题文本长度超过一定值,或者涉及多步骤推理)时,切换成版本B做交叉验证。这个“双轨评估”在实际运行里效果比单纯用任何一个版本都稳。
3.2 构建自评函数与监控循环代码
下面给出核心流程的简化代码,逻辑主干清晰,大家可以直接参考这个结构改成自己的实现。
def run_with_metacognition(question: str, max_refine_rounds: int = 2): # 第1步:执行层生成初版回答 answer = generate_answer(question) # 第2步:元认知评估层对回答打分 scores = evaluate_answer(question, answer) confidence = compute_confidence(scores) round_count = 0 decisions = [] while confidence < CONFIDENCE_MIDDLE and round_count < max_refine_rounds: # 记录本轮信息 decisions.append({ "round": round_count, "scores": scores, "confidence": confidence, "decision": "refine" }) # 第3步:根据低分维度生成修正指令,只允许执行具体动作 actions = generate_fix_actions(scores) # 第4步:执行修正动作(重新检索/重新推理/重写逻辑链) refined_answer = execute_fix_actions(question, answer, actions) answer = refined_answer # 第5步:重新评估修正后的答案 scores = evaluate_answer(question, answer) confidence = compute_confidence(scores) round_count += 1 # 第6步:根据最终置信度决定输出还是降级 if confidence >= CONFIDENCE_HIGH: final_decision = "direct_respond" elif confidence >= CONFIDENCE_MIDDLE: final_decision = "respond_with_uncertainty" else: final_decision = "escalate_to_human" decisions.append({ "round": round_count, "scores": scores, "confidence": confidence, "decision": final_decision }) return { "answer": answer, "confidence": confidence, "scores": scores, "decisions_history": decisions, "final_decision": final_decision }这个函数的逻辑很简单,但有几个细节是实际踩坑后才加上的。第一,while循环里的置信度判断用的是“中阈值”,不是“高阈值”。意思是只要没到“可以直接返回”的分数,就进入修正循环,而“低阈值”只负责决定最终到底是带着不确定性声明回复,还是转人工。这两个阈值的职责完全不同,一开始我混在一起用,导致很多明明可以修正的答案直接被放弃了。
第二,修正动作必须是一个结构化的动作列表,而不是一句“请改进你的回答”。我在代码里用了一个generate_fix_actions函数,它输出的是类似“重新检索关键词A”“检查断言B与证据C的冲突”“重写第三段的推理逻辑”这样的具体指令,再交给另一个函数去执行。这一层多花了点功夫,但效果是天壤之别。
3.3 结构化评估日志:让自评结果可追溯
做元认知系统,最忌讳的是“黑盒”,模型打了一个分,但你不知道这个分怎么来的。所以从第一天起,我就要求所有评估结果都写成结构化日志,存下来,方便事后分析。
每次评估我记录以下字段:
{ "task_id": "task_10023", "question": "订单超过七天还能不能无理由退款", "answer_fingerprint": "sha256:7f3a9cb2...", "round": 0, "scores": { "fact_support": 0.3, "logic_consistency": 0.9, "retrieval_coverage": 0.4, "instruction_adherence": 1.0 }, "confidence": 0.56, "evidence": { "fact_support": "关键结论引用了检索材料doc_89,但该材料发布时间为2023年,已超过政策有效期", "logic_consistency": "回答内部无冲突", "retrieval_coverage": "未检索到2024年新版政策文件,缺乏最新依据", "instruction_adherence": "回答直接回应了用户问题" }, "decision": "escalate_to_human" }这个日志的价值在后端才体现出来。我每周会跑一次日志聚合,统计不同置信度区间的回答数量,以及被拦截的低置信问题都集中在哪些主题上。有一次我看到某个知识类目里“事实支撑度”分数连续两周走低,才发现是这个类目下的知识库文档大部分都过期了。那种情况下你改Prompt、调阈值都是白搭,正解是更新知识库。
另一个价值是给评估层做回归测试。每次调整评估Prompt之后,拿日志里的历史问题重新跑一遍,看分数分布是否稳定。没有这个日志,你完全不知道你的评估模型哪一天开始“性格大变”。
3.4 参数设置与实测效果
下面把我实际测试中用到的参数列一下,供参考:
| 参数 | 设置值 | 说明 |
|---|---|---|
| 主模型 | 通用大模型(本地部署7B) | 日常问答,生成速度优先 |
| 评估方式 | 主模型自评+轻量模型交叉验证据 | 双轨评估,兼顾成本与客观性 |
| 生成温度 | 0.2 | 答案确定性优先,不适合创意任务 |
| 评估温度 | 0.0 | 评分必须稳定,抽样随机性越少越好 |
| 检索Top-K | 5 | 向量召回3+BM25召回2 |
| 高置信阈值 | 0.85 | 直接放行 |
| 中置信阈值 | 0.6 | 触发修正循环 |
| 最大修正轮数 | 2 | 防止死循环 |
| 反思后重评温度 | 0.0 | 修正后评分保持严格 |
我用一个模拟知识问答测试集做了前后对比,里面包含200个正常问题和80个故意构造的“检索文档不完整”的难题。开启元认知自评后:
| 指标 | 关闭自评 | 开启自评 |
|---|---|---|
| 正常问题答对率 | 87.5% | 86.1% |
| 难题答对率(不转人工) | 44.3% | 61.2% |
| 难题中承认不确定占比 | 0% | 31.4% |
| 难题中给出错误答案占比 | 55.7% | 20.8% |
| 平均响应耗时(秒) | 2.1s | 3.4s |
正常问题答对率有小幅下降,原因是有个别本来答对了但被自评误判为低置信,转去了人工;但难题的错误率大幅降低,代价是响应时间多了1.3秒。对于绝大多数对准确率有要求的业务场景,这个取舍非常划算。如果你做的是实时弹幕这种场景,那就要考虑把自评只在后台异步跑,不在请求链路上等结果。
4. 常见问题与实战排查
4.1 自评分数虚高,模型总给自己打高分
这是我遇到的第一个问题,也是最普遍的问题。排查思路分三步:第一步检查打分Prompt是否太宽松,比如要求“根据综合印象打分”就是典型的宽松Prompt;第二步引入“先证据后打分”的机制,让评估模型必须先把支持高分的证据列出来,再给分数;第三步是给极端样例校准,在评估Prompt里塞入三到五个典型错误回答和正确回答的示例,要求模型先参照这些样例校准尺度。
改了这三步之后,分数分布从“什么都90分”变成了“大致正态分布”,和人类评估的相关性也明显提高。注意,校准示例必须覆盖低分段和高分段,只放正确示例会让模型变成“模范生崇拜者”,看谁都像满分。
4.2 模型进入“无效道歉”状态
表现为评估后模型说“我意识到之前回答有误”,但重写出来的答案仍然是原错误答案的变体。我排查下来根因有两类:一类是修正Prompt没有明确限定动作,模型把“修正”理解成了“表达悔意”;另一类是模型本身能力不足以生成更好的答案,这时候你再怎么催也没用。
解法我前面提过:结构化修正动作,不让它凭感觉重写。如果动作限定为“重新检索”而它不具备检索能力,那就要检查是不是工具调用链路断了。很多情况下表面上是“模型不听话”,实际上是“工具没接好”,模型觉得自己已经在修正了,但检索工具根本没被调用。
4.3 自评循环导致上下文膨胀,响应越来越慢
多轮修正的最大副产品是上下文窗口被撑爆。在RAG应用里,每轮修正如果都把全部检索片段塞进Prompt,第二轮重评时上下文总量轻松翻倍。很多实践者忽略了这个成本,直到有一天Agent突然“失忆”——仔细一看是超出了上下文长度,老内容被截断了。
解决思路是“轻量化评估上下文”:评估时只传问题、回答摘要、关键证据段落列表、以及上一轮的评估结果,不传完整的历史对话。“修正时只传定位到的那几个缺陷段落,不传初版全部检索结果”。实现起来稍微麻烦一点,但对长对话场景几乎是必须的。我实测同样的任务,优化后单次自评调用的token数能砍掉40%到60%。
4.4 阈值怎么调才靠谱
从头到尾调阈值,我认为比较科学的流程是:
- 先收集一批有标注的测试集,包括正确答案和错误答案,各占一半。
- 关掉修正循环,只保留“评估打分”,把所有问题的分数记下来。
- 按分数从高到低逐个尝试作为放行阈值,画出一条“阈值-准确率曲线”。
- 在曲线上找到拐点:阈值再往上提,准确率提升很小,但放行数量大幅下降,这个位置就是高阈值。
- 低阈值就简单了,选一个“低于它几乎全是错答案”的位置即可。
我实测下来的结果是,高分区间“0.85到0.95”之间准确率变化不大,但0.85以下准确率开始明显下跌,所以0.85就成了我的高阈值。0.6这个中阈值则来自“低于0.6的答案里,即便修正也很难变对”的经验判断。这类数字没有放之四海而皆准的版本,一定要用自己的数据去标定。
5. 应用场景与扩展方向
5.1 知识问答与客服Agent:从“答错”到“少答”
客服场景是元认知能力最直接受益的地方。我上面提到那个过期政策文档的例子,如果当时的Agent带自评能力,它会在检索覆盖度上打低分,然后走“转人工”通道而不是给出过期答案。对客服业务来说,错误答案的代价不只是用户不满,还可能牵扯售后纠纷。很多企业把客服机器人的“闭嘴率”看得很重,但我觉得比“闭嘴率”更重要的是“错误答案率”。
另一个很微妙的点是:元认知评估特别适合用来做客服质检的预筛选。把所有客服机器人的回答日志过一遍自评模型,分数低于阈值的自动标记为“待人工复核”,而不是直接发给用户。相当于把评估从在线决策变成离线审计,可以在不增加用户感知延迟的前提下做更重的检查。
5.2 代码生成Agent:提交前先自查
写代码的Agent普遍存在一个毛病:输出看起来很像样,但一跑就报错,有些甚至是“语法正确但逻辑错误”,编译通过但结果不对。在代码场景里,元认知评估可以换成很多“硬检查”:静态分析、单测、边界值检测、类型检查。这些比让模型自己打分硬核得多。
我试过在代码Agent里设计这样的流程:Agent生成代码后,先进行静态编译检查,再跑一组基础测试样例,测试失败就把报错信息送回修正循环,重新定位修复。这个“评估层”其实已经超出了语义评估的范畴,变成了工具集成的质量门。效果非常明显:提交代码的首次编译通过率从42%提到了76%。
5.3 数据分析Agent:结尾带一份“自评估报告”
数据分析Agent的独特性在于,它的产出是有“证据链”的。它读了一张表,做了一次聚合,得出结论“Q3营收环比下降8%”。自评层可以验证这个结论:取出原始明细数据再算一遍,核对计算过程。
我做过一个模拟项目,让分析Agent在交付报告时强制附一段“自评估说明”,内容包括:使用了哪些字段、过滤条件是什么、关键结论与原始数据是否一致、有哪些潜在的数据口径问题。用户拿到的不只是一份结论,还是一份“可信度说明”。这个习惯一旦养成,能省掉大量“你这个数怎么来的”这类追问。
5.4 把元认知能力做成通用中间件
最后聊一下未来的方向。这个项目做到后面,我最大的体会是:元认知评估不应该是一个Agent内置的功能,它应该是一个可以横向复用的中间件。任何Agent只要在生成答案后调用一个“POST /evaluate”接口,传入问题和答案,就能拿回一批评估信号,这比每个项目都重写一套自评逻辑要高效得多。
我在内部做了一层抽象,把这套评估逻辑包装成独立的服务,上游Agent只传JSON,评估服务返回维度分数、置信度、建议动作。上游Agent拿到结果后自行决定是放行还是修正。这个拆法让接入成本降到了极低:一个没有自评能力的旧Agent,只需要改最后几行代码,就能获得元认知能力。
这个方向我认为是AI Agent走向工程化的必经之路:生成能力是很多厂商都能给的,但“自我评估能力”才是决定Agent可不可信、能不能放上生产环境的关键。现在很多Agent项目死在“看起来聪明,实际上不敢用”,元认知至少能帮我们搞清楚“哪些敢用、哪些不敢用”,这比假装全知全能重要得多。
实际开发过程中,还有一个小技巧和大家分享:评估Prompt里的示例千万不要只放高分例子,一定要混入那种“看起来很有道理但其实是错的”反面例子,并且写明为什么错。这一步对修正模型打分尺度的作用,比任何参数调整都大。