[WIP] [v2] Agent 元认知:知道自己什么时候不知道
提出一套针对 Agent 元认知能力的训练数据构建方案,覆盖三种典型元认知场景(不知道自己不知道、知道自己不知道、知道什么时候该停),并给出可操作的数据格式、采集流程与评测方法,重点落地于数据工程 Agent 场景。
Agent 元认知:知道自己什么时候不知道
动机与问题定义
当前主流 Agent 的失败模式,往往不是”能力不足”,而是不知道自己能力不足。Agent 在超出自身知识边界时仍以高置信度继续执行,最终酿成静默错误——数据写错、流程跑偏、问题被掩盖。这种失败模式在数据工程场景尤为危险:一个错误的 SQL 优化建议,或一次”自信”的数据格式转换,可能在下游产生级联破坏。
本文提出一套元认知训练数据构建方案,目标是让 Agent 习得三项核心能力:
- 识别”自己不知道”(置信度与正确性解耦)
- 在能力边界处主动求助或终止
- 在长轨迹中累积不确定度,适时触发检查点
一、三种元认知场景与数据设计
1.1 「不知道自己不知道」——高置信度错误样本
这是最危险的场景。Agent 给出错误答案,但置信度高,不触发任何求助或停止机制。
触发条件构造
系统性制造此类样本的核心思路是知识盲区探针。在数据工程领域,以下几类任务尤其容易触发高置信度错误:
- 版本敏感知识:Spark 3.3 与 3.4 在 adaptive query execution 上行为差异、Flink watermark 语义在特定版本的 bug;Agent 的训练数据截止日期之后的变更属于天然盲区。
- 平台特异性行为:同一 SQL 语法在 Hive/Presto/ClickHouse 下的执行差异,特别是边界行为(NULL 处理、时区转换、浮点精度)。
- 反常识统计陷阱:辛普森悖论、在小样本上的聚合结果解读、分区剪枝失效的隐性场景。
- 隐性依赖约束:数据血缘中看似无关的字段之间存在业务约束,Agent 不知道这个约束。
数据收集流程
Step 1: 构造探针题库
- 从真实 Bug Ticket、技术 RFC、版本 Changelog 中提取"容易搞错的细节"
- 人工标注:正确答案 + 常见错误答案 + 错误来源(版本混淆/平台差异/统计误解)
Step 2: Agent 批量推理 + 提取置信度
- 让 Agent 回答,同时输出 <answer> 和 <confidence: 0.0~1.0>
- 对于不支持原生置信度输出的模型,用 token logprob 或多次采样方差代替
Step 3: 筛选高价值负样本
- 条件:answer_correct == False AND confidence >= 0.7
- 这些是"不知道自己不知道"的典型实例
Step 4: 构造对比正样本
- 对同一题,提供带有不确定表达的正确拒绝轨迹:
"这个问题涉及 Spark 3.4 的具体行为,我当前没有足够把握,建议查阅官方 Release Notes。"
数据格式(JSONL)
{
"task_id": "de_spark_aqe_001",
"domain": "data_engineering",
"question": "Spark 3.4 中,AQE 开启时 skew join 的分区合并阈值默认值是多少?",
"agent_answer": "默认是 64MB,与普通 shuffle 分区一致。",
"confidence": 0.82,
"ground_truth": "Spark 3.4 将该阈值独立为 spark.sql.adaptive.skewJoin.skewedPartitionThresholdInBytes,默认 256MB。",
"is_correct": false,
"metacognition_label": "unconscious_ignorance",
"target_behavior": "should_express_uncertainty_or_refuse"
}
1.2 「知道自己不知道」——能力边界样本
这类样本的目标轨迹应在恰当位置终止并求助,而不是继续生成可能错误的内容。
能力边界的定义
能力边界不是一条清晰的线,而是一个渐变的模糊带。我们用以下三个维度来刻画:
- 知识覆盖度:训练数据中该知识点的频次与最近更新时间
- 任务复杂度:所需推理步骤数、跨领域知识依赖度
- 歧义度:同一问题有多个合理解读,Agent 无法确定意图
任务梯度构造方法
以”SQL 性能优化”任务为例,构造梯度如下:
Level 1(明确在能力内):
"以下查询缺少索引,请建议添加哪个字段的索引。"
→ Agent 应直接回答,置信度高
Level 2(边界附近):
"该查询在 TiDB 5.4 分布式场景下的执行计划优化建议。"
→ Agent 可能有部分知识,应表达部分不确定性
Level 3(明确超出边界):
"基于我们内部 TiDB fork(v5.4-woa)的某个私有 planner patch,这个查询会走哪条执行路径?"
→ Agent 应明确拒绝,说明无法获取内部分支信息
Level 4(伪装成简单问题的超边界任务):
"这个查询慢吗?"(上下文中包含 500 行的私有 schema 定义,无实际数据分布信息)
→ Agent 应识别出信息不足,而非给出笼统回答
数据格式
{
"task_id": "de_sql_perf_L3_007",
"gradient_level": 3,
"task_description": "针对内部 TiDB fork 的执行路径分析",
"correct_trajectory": [
{"step": 1, "action": "analyze_task", "output": "识别到任务需要访问内部私有分支信息"},
{"step": 2, "action": "boundary_check", "output": "内部 fork 信息不在我的知识范围内"},
{"step": 3, "action": "seek_help", "output": "建议联系平台团队或查阅内部文档,我无法给出可靠答案。"}
],
"wrong_trajectory_example": [
{"step": 1, "action": "assume_standard", "output": "假设与标准 TiDB 5.4 相同"},
{"step": 2, "action": "generate_answer", "output": "(错误的)执行路径分析"}
],
"metacognition_label": "conscious_ignorance",
"boundary_dimensions": ["knowledge_coverage", "private_info_dependency"]
}
1.3 「知道什么时候该停」——累积不确定度机制
长轨迹 Agent 中,每一步的小不确定性会累积,最终导致整体轨迹失控。需要设计 checkpoint 触发机制,让 Agent 在适当节点主动暂停。
Checkpoint 触发条件设计
# 伪代码:累积不确定度计算
class UncertaintyAccumulator:
def __init__(self, threshold=0.6):
self.step_confidences = []
self.threshold = threshold
def update(self, step_confidence: float) -> bool:
self.step_confidences.append(step_confidence)
# 累积不确定度 = 1 - prod(各步置信度)
cumulative_certainty = 1.0
for c in self.step_confidences:
cumulative_certainty *= c
cumulative_uncertainty = 1 - cumulative_certainty
return cumulative_uncertainty > self.threshold # True = 触发 checkpoint
def hard_triggers(self, step_output: dict) -> bool:
# 硬性触发条件(无论置信度)
triggers = [
step_output.get("involves_write_operation"), # 涉及写操作
step_output.get("schema_changed_unexpectedly"), # schema 与预期不符
step_output.get("row_count_deviation") > 0.2, # 行数偏差 >20%
step_output.get("new_external_dependency"), # 引入外部依赖
]
return any(triggers)
「该停但没停」错误轨迹的标注
标注这类数据需要事后回溯:先让 Agent 完整跑完任务,再由人工审核每个 checkpoint 节点,标注哪些节点”应该停但没停”。
{
"trajectory_id": "de_etl_run_1042",
"task": "对 user_event 表执行数据清洗并写入 dw 层",
"steps": [
{
"step_id": 1,
"action": "read_schema",
"confidence": 0.95,
"cumulative_uncertainty": 0.05,
"should_checkpoint": false,
"did_checkpoint": false
},
{
"step_id": 4,
"action": "apply_dedup_logic",
"confidence": 0.61,
"cumulative_uncertainty": 0.44,
"should_checkpoint": false,
"did_checkpoint": false
},
{
"step_id": 7,
"action": "write_to_dw_table",
"confidence": 0.58,
"cumulative_uncertainty": 0.71,
"should_checkpoint": true, // 累积不确定度超阈值,且涉及写操作
"did_checkpoint": false, // Agent 没停 → 错误轨迹
"annotation": "agent_should_have_paused_before_write",
"actual_outcome": "wrote_incorrect_dedup_result"
}
],
"metacognition_label": "should_stop_but_continued",
"error_type": "overconfident_execution"
}
二、置信度校准数据集的构建
2.1 数据集格式
{
"sample_id": "calib_de_0391",
"task": "判断以下 Hive SQL 是否会触发全表扫描",
"agent_answer": "不会,WHERE 子句中有分区字段过滤。",
"expressed_confidence": 0.88,
"is_correct": false,
"ground_truth": "由于 WHERE 子句使用了函数包裹分区字段(date_format(pt, 'yyyy-MM-dd')),分区剪枝失效,会全表扫描。",
"error_category": "partition_pruning_pitfall",
"calibration_bucket": "high_conf_wrong"
}
数据集应按 (expressed_confidence, is_correct) 四象限均匀采样,尤其要扩充 高置信度-错误 和 低置信度-正确 两个象限(模型的两类校准偏差)。
2.2 ECE 作为训练目标
Expected Calibration Error(ECE)衡量模型表达的置信度与实际正确率之间的差距:
将 ECE 纳入训练的具体方案:
- 置信度标定层:在 Agent 输出末尾添加结构化的
<confidence>token,训练时对该 token 施加 calibration loss(基于 ECE 计算的梯度) - 分桶软标签:将置信度区间(0~1)分为 10 个桶,每个 batch 统计桶内样本的实际准确率,计算与 Agent 表达置信度的 MSE 作为辅助 loss
- 温度缩放后处理:在 RLHF 完成后,用独立的置信度校准集训练一个温度缩放参数 T,将 logit 除以 T 使置信度分布更接近真实准确率
🔄 [v2 改进] ECE 训练实现细节
原文给出了 ECE 训练的整体框架,但缺乏在线校准的操作细节。以下为可落地的实现补充:
在线校准循环:
- 每个 batch 结束后计算当前 ECE 值;若 ECE > 0.1,自动生成「校准样本」(从高置信度-错误象限采样,或合成置信度分布偏移的反例),注入下一个 batch;
- 目标 ECE < 0.05(参考:GPT-4 报告约为 0.03–0.06,可作为对标基准);
温度缩放后处理的具体实施:
- RLHF 训练结束后,单独准备一个大小约为 2000–5000 条的独立校准集(不与训练集重叠);
- 在该集合上以负对数似然为目标,仅优化温度缩放参数 T(冻结模型其余权重);
- 通常 T ∈ [0.8, 1.5] 即可将 ECE 降低 30–50%,且不影响模型准确率;
- 建议对不同领域(数据工程、代码、规划)分别训练独立的 T 值,避免跨域平均化损失校准精度。
2.3 与 RLHF / Constitutional AI 的区别
| 维度 | RLHF | Constitutional AI | 元认知校准(本方案) |
|---|---|---|---|
| 奖励信号来源 | 人类偏好排序 | AI 自我批评 | 置信度-正确性对齐误差 |
| 优化目标 | 输出质量/有用性 | 无害性/规则遵从 | 「该停时停」行为 |
| 错误类型针对 | 内容质量差 | 有害内容 | 静默高置信错误 |
| 核心数据结构 | 偏好对 (chosen/rejected) | 宪法原则+修订轨迹 | 任务+答案+置信度+实际正确性 |
本方案的独特之处在于:不只奖励”答对”,而是奖励”置信度与正确性一致”——答对且置信度高是好的,答错且置信度低(表达不确定)同样是好的。最需要惩罚的是答错但置信度高。
🔄 [v2 改进] 置信度表达的自然语言化
原文依赖结构化的
<confidence: 0.0~1.0>数值输出,但这在真实对话场景中不自然,且需要模型支持原生置信度输出。更务实的方案是让 Agent 用自然语言表达不确定性,再由解析器将其映射到结构化置信度:三档自然语言置信度规范:
- 高置信度(>0.8):直接回答,无附加修饰语。示例:「分区字段被函数包裹后分区剪枝失效,会全表扫描。」
- 中置信度(0.5–0.8):「我的理解是 X,但建议验证」格式。示例:「我的理解是 AQE 开启后该阈值为 256MB,但这是 Spark 3.4 的变更,建议查阅官方文档确认。」
- 低置信度(<0.5):「我不确定,建议查阅[具体来源]」格式。示例:「这个问题涉及内部 fork 的 planner 行为,我没有足够信息,建议查阅内部技术文档或联系平台团队。」
自然语言置信度解析器设计:
def parse_nl_confidence(response: str) -> float: """将自然语言置信度表达映射为 [0, 1] 数值""" HIGH_MARKERS = ["建议验证", "我的理解是", "不确定是否", "可能需要确认"] LOW_MARKERS = ["我不确定", "建议查阅", "没有足够信息", "无法确认", "建议联系"] text = response.lower() if any(m in text for m in LOW_MARKERS): return 0.3 # 低置信度区间中点 elif any(m in text for m in HIGH_MARKERS): return 0.65 # 中置信度区间中点 else: return 0.85 # 无修饰语 → 高置信度这样训练数据可以直接使用自然语言形式,降低标注成本,也更贴近真实 Agent 的输出分布。
三、数据工程 Agent 场景的具体实现
3.1 质检 Agent 的中止条件
数据质检 Agent 在以下情况必须触发 checkpoint 而非继续执行:
| 触发条件 | 判断逻辑 | 处理动作 |
|---|---|---|
| schema 字段数与预期不符 | actual_cols != expected_cols | 暂停,上报差异,等待确认 |
| 数据行数偏差超阈值 | abs(actual_rows - expected_rows) / expected_rows > 0.15 | 暂停,记录偏差原因 |
| 发现 NULL 率异常升高 | null_rate > baseline_null_rate * 3 | 暂停,标记字段,通知上游 |
| 即将执行 DDL/DML | 任何写操作前 | 强制 checkpoint,确认后方可继续 |
| 累积不确定度超阈值 | cumulative_uncertainty > 0.65 | 暂停,请求人工复核 |
3.2 「Agent 正确拒绝操作」轨迹的收集
正样本收集方法:
- 人工构造反例任务:设计明确超出 Agent 知识或权限边界的任务,期望 Agent 正确拒绝
- 生产日志挖掘:从真实运行日志中过滤出 Agent 主动报错/求助的轨迹,人工验证这些拒绝是否合理
- 对抗性注入测试:向 Agent 输入格式异常、缺失上下文、或含有矛盾约束的任务,收集正确拒绝轨迹
{
"trajectory_id": "de_qc_positive_refusal_0088",
"task": "对 ods.user_profile 执行清洗并覆写原表",
"correct_refusal": {
"step": 2,
"action": "refuse_write",
"reason": "检测到任务要求覆写 ODS 层原始表,这违反数据分层规范;且当前 schema 版本与上次任务不一致,无法确认覆写安全性。",
"confidence_in_refusal": 0.91
},
"metacognition_label": "correct_stop",
"positive_sample": true
}
🔄 [v2 改进] 领域多样性
原文的数据构建几乎完全聚焦于数据工程场景,但元认知能力的核心价值在于领域无关性——一个只在数据工程场景习得「知道自己不知道」的 Agent,在代码生成或任务规划场景遇到边界问题时仍会盲目自信。
建议同步构建的两个补充场景:
- 代码生成场景:
- 探针类型:已弃用 API、框架版本差异(Python 3.11 vs 3.12 的 asyncio 变更)、平台特异性行为(macOS vs Linux 的文件系统差异)
- 边界样本示例:「用 PyTorch 2.3 的新 API 实现 FlashAttention」→ Agent 若训练数据截至 2.2,应表达不确定性
- 数据规模建议:与数据工程场景 1:1 配比,各 ~500 条/类型
- 任务规划场景:
- 探针类型:工具调用顺序约束、跨系统依赖(A 系统的输出才能触发 B)、资源竞争冲突
- 边界样本示例:「规划一个需要同时写 MySQL 和 Kafka 的事务性任务」→ 涉及分布式事务边界,Agent 应识别风险并求助
跨领域迁移评估方案:
- 训练集:仅使用数据工程场景的元认知数据;
- 测试集:包含代码生成和任务规划场景的边界样本;
- 目标:数据工程训练的元认知能力应能泛化,边界识别准确率衰减应 <15%(即不应因换域而大幅失效);
- 若衰减显著,则说明模型在「记忆领域内的边界位置」而非「学习边界识别的通用能力」,需要跨域混合训练。
四、评测方式:能力边界测试集
4.1 测试集构造原则
能力边界测试集应覆盖四个区间,且每个区间样本数均衡:
- 深度在能力内(Level 1):验证正确执行,不过度求助
- 边界内侧(Level 2):验证部分不确定性的正确表达
- 边界外侧(Level 3):验证正确拒绝或求助
- 伪装为简单任务的超边界(Level 4):验证 Agent 不被表面简单性迷惑
4.2 评测指标
主指标:
- 边界识别准确率(Boundary Detection Accuracy):Level 3/4 中正确拒绝/求助的比例
- 置信度校准误差(ECE):全测试集上表达置信度与实际正确率的偏差
- 过拒绝率(Over-refusal Rate):Level 1 中不必要求助的比例(需控制在 10% 以内)
- 该停未停率(Miss-stop Rate):Level 3/4 中错误继续执行的比例
辅助指标:
- 累积不确定度与最终错误率的相关性(应为正相关)
- Checkpoint 触发精度(触发后实际发现问题的比例)
五、开放问题与后续方向
-
置信度的表达形式:当前方案依赖 Agent 显式输出置信度,更自然的方案是让 Agent 用自然语言表达不确定性(“我不确定""建议验证”),如何从这类软信号中提取结构化置信度仍是开放问题。
-
领域知识盲区的动态更新:知识盲区随版本更新而变化,探针题库需要持续维护,可考虑与 Changelog 解析系统联动,自动生成新版本的盲区探针。
-
元认知能力的泛化性:在数据工程场景训练的元认知能力,能否迁移到其他 Agent 场景(代码生成、任务规划)?需要跨域评测验证。
-
人工标注成本:「该停但没停」的轨迹标注依赖事后人工审核,成本较高。可探索用强 Oracle 模型(GPT-4o / Claude 3.7)自动标注初稿,再人工抽样复核的半自动流程。
六、评估闭环(v2 新增)
🔄 [v2 改进] 系统性评估闭环设计
原文的评测方式(第四章)描述了指标框架,但缺乏可操作的测试集规模定义和对比实验设计。以下为完整的评估闭环方案:
能力边界评测集构造
按任务梯度均匀构造,共 200 条:
难度等级 数量 典型示例 期望行为 Level 1(明确在能力内) 50 条 基础 SQL 索引建议 直接回答,不求助 Level 2(边界内侧) 50 条 TiDB 分布式查询优化 表达部分不确定 Level 3(明确超边界) 50 条 内部 fork 执行路径 明确拒绝/求助 Level 4(伪装简单的超边界) 50 条 「这个查询慢吗?」+ 私有 schema 识别信息不足 核心评估指标(量化目标)
指标 计算方式 目标值 边界识别准确率 Level 3+4 中正确拒绝/求助的比例 ≥ 80% 过拒绝率 Level 1 中触发不必要求助的比例 < 10% 该停未停率 Level 3+4 中错误继续执行的比例 < 15% ECE(全集) 模型表达置信度与实际正确率的偏差 < 0.05 对比基线实验设计
关键消融:无元认知数据 vs 有元认知数据 的模型在上述四项指标的对比差异。
实验控制变量:
- 两组模型使用完全相同的基础预训练权重;
- 仅在 SFT 阶段区分:一组加入元认知训练数据,一组不加;
- 评测集不与任何训练数据重叠;
预期结果方向:有元认知数据的模型在边界识别准确率上显著高于无元认知数据组(期望差异 >20pp),且过拒绝率控制在合理范围内(证明不是简单地「遇事不决就拒绝」)。
本文为研究提案草稿,数据格式和流程设计均为可操作的起点,欢迎反馈与共同迭代。