reasoning-shift-and-supervision.md 18 KB

推理偏移、监督机制与上下文预算管理

背景: Yandex 研究者 Rodionov (2026.04) 发现 LLM 在长上下文中不是"找不到信息",而是"主动减少思考量"——推理 token 缩水高达 53%,且推理能力越强的模型偷懒越严重。 影响: 所有 Agent 框架(包括 lambdagentpaas)的 ReAct 循环和多步管道都受此影响。 应对: lambdagentpaas 的三层防御——形式化保证(不依赖模型行为)、架构抗偏移(短上下文设计)、运行时监督(CEK Yield 注入)。


一、推理偏移:模型在偷懒,不是在犯错

核心发现

Rodionov 的《Reasoning Shift》论文揭示了一个反直觉的现象:

实验: 同一道奥数题,在不同上下文条件下让 LLM 做

  基线 (干净):      准确率 74.5%,  推理 token 28,771
  + 64K 莎士比亚:   准确率 67.8%,  推理 token 16,415  ← 缩水 43%!

  模型完全知道莎士比亚无关 ("这跟几何题无关,专注几何")
  但还是少想了 43%

关键数据

指标 发现 含义
推理 token 缩水 最高 53% (Qwen 深度思考) 模型不是想错了,是想少了
自我检查率 43% → 32% 模型跳过了验证步骤
128 token 触发 仅 2-3 句话就砍掉 18% 推理 极其敏感的偷懒机制
能力越强越偷懒 深度思考模式缩短 53% vs 普通模式 19% 训练推理能力加剧了偷懒
"wait/but/maybe" 频率 下降 50-75% 犹豫、自我质疑被系统性砍掉

两种"失败"的区分

致命失败 (结构性错误):
  API 返回 500 / 解析异常 / 工具崩溃
  → 模型无法继续
  → lambdagentpaas 的 lint/type_check 可在编译时拦截

推理偏移 (认知节省):
  模型看到长上下文 → 主动减少思考量
  → 跳过自我验证
  → 该深入的地方草草带过
  → 不报错、不坦白、只是极其自信地给出敷衍的答案
  → 这是最危险的——你无法从输出中看出它偷懒了

对 Agent 系统的影响

所有依赖 LLM 多步推理的系统都受影响:

  ReAct 循环:
    Step 1-3: 上下文短 → 推理充分
    Step 8-10: 上下文长 → 推理缩水 → 选错工具/跳过验证
    Step 15-20: 推理严重衰减 → 回答质量不可预测

  多智能体协作:
    第 1 轮 GroupChat: 每个 Agent 推理充分
    第 5 轮: 对话历史累积 → 后面的 Agent 开始敷衍
    第 10 轮: 综合质量远不如单 Agent 执行

  LLM Wiki Ingest:
    前 100 个文件: 实体提取全面
    第 500 个文件: 提取开始变粗糙 (如果上下文累积)

二、为什么 lambdagentpaas 的架构天然抗偏移

2.1 形式化保证不依赖模型行为

模型偷不偷懒,以下保证都成立:

  类型安全 (T-Compose):
    output(f) <: input(g) → 不管模型输出什么质量的内容
    类型不兼容在编译时就拦截,不进入运行时

  成本上界 (分级类型):
    跑 20 步 Opus = $0.27 → 不管模型推理多深或多浅
    上界是数学计算,不是模型行为的函数

  终止性 (Theorem 5.4):
    fix_n 在 n 步后终止 → 不管模型是否找到答案
    即使模型每步都在偷懒,也不会无限运行

  并行安全 (Proposition 30):
    writes(f) ∩ writes(g) = ∅ → 不管模型输出什么
    Store independence 是静态分析,不受推理质量影响

这就是形式化方法的价值:模型行为不可靠时,数学保证仍然可靠。

2.2 LLM Wiki 模式:短上下文设计

论文发现推理偏移的触发条件是上下文长度。LLM Wiki 的设计恰好规避了这个条件:

传统 RAG:
  Query 时: 塞 5 个文档块 (几千 token) → 上下文长 → 触发偏移
  
  用户问 "有哪些安全风险?"
  → 检索 5 个原始文档块 (每个 500 token = 2500 token)
  → 塞进 prompt → 总上下文 ~4000 token
  → 论文数据: 即使 128 token 就能砍掉 18% 推理深度

LLM Wiki 模式:
  Ingest 时: LLM 在短上下文中阅读单个文件 → 写 wiki 页面
    每次 Ingest 只看一个文件 → 上下文短 → 不触发偏移
    知识被"编译"成结构化 wiki → 以后不需要重新处理
    
  Query 时: 读 index.md → 只读 2-3 个 wiki 页面
    wiki 页面是已编译的精华 (比原始文档短 5-10 倍)
    → 上下文更短 → 推理偏移更小
    → 而且 wiki 的交叉引用已经建好,不需要 LLM 自己推理关联

本质: 把"一次长上下文推理"拆成了"多次短上下文编译 + 一次短上下文查阅"

2.3 三层编程模型限制上下文增长

Layer 1 — Skill (原子能力):
  每个 Skill 是独立的短任务
  上下文 = Skill 自己的 prompt + 当次输入
  不累积历史 → 不触发偏移
  
  例: answer-generator Skill 每次只看 "检索到的 wiki 页面 + 问题"
      不看之前所有的对话历史

Layer 2 — Pattern (协作模式):
  fan_out_merge: 每个并行分支独立执行,上下文不共享
    → 3 路并行中每路只看自己的上下文
    → 不会因为分支 A 的输出让分支 B 的上下文变长

  review: 每轮审查是独立的 LLM 调用
    → 审查者只看"当前回答 + 参考原文"
    → 不累积前几轮的审查历史

  escalation: 每级只看上一级的最终结果
    → L2 不看 L1 的完整推理过程
    → 上下文不膨胀

Layer 3 — Orchestration (YAML 编排):
  声明式编排把大任务拆成小步骤
  每步的上下文是 YAML 定义的,受控的
  不会出现"整个任务历史塞进一个 prompt"

2.4 CEK Machine 的结构化执行

Python 递归调用栈:
  整个 ReAct 循环共享一个上下文
  Step 1 的输出 + Step 2 的输出 + ... 全部累积
  → 上下文线性增长 → 推理偏移线性加剧

CEK Machine:
  每个 Yield 点是一个状态快照 ⟨C, E, K, σ, c⟩
  上下文 E 可以在 Yield 点被压缩/重置
  → 上下文增长可控
  → 在偏移信号出现时主动干预

三、三级监督机制

lambdagentpaas 的 CEK Yield 机制天然支持在 Agent 执行过程中注入监督。

Level 1: 规则监督(已实现)

编译时和运行时的自动化检查,不需要 LLM 参与:

编译时:
  lint (26 规则):     "没有 terminate 工具,会死循环"
  type_check:         "Stage 2 输出 Json, Stage 3 期望 Str"
  cost_predict:       "这个管道最多花 $0.42,成功率 63%"
  store_independence:  "两个并行 Agent 写同一个 key"

运行时 (CEK Yield 点):
  成本熔断:     machine.state.cost.money > budget → 暂停
  循环检测:     连续 3 步 state hash 相同 → 停止
  步数上限:     step_count > max_steps → 终止

特点: 零 LLM 成本,毫秒级,确定性
局限: 不理解内容,只检查结构和数值

Level 2: 模型监督(可实现)

用一个轻量 LLM 作为"监督者",每 N 步检查 Agent 的输出质量:

实现方式:

  class SupervisedCEKEngine(CEKEngine):
      def __init__(self, supervisor_llm, supervise_every=3, **kwargs):
          self.supervisor = supervisor_llm
          self.supervise_every = supervise_every

      def execute(self, term, input_val, ctx, **opts):
          machine = AgentCEKMachine(...)
          machine.load(term, input_val)
          step = 0

          while not machine.state.is_terminal():
              transition = machine.step()
              step += 1

              # Level 1 检查 (已有)
              self._check_cost_budget(machine)
              self._check_loop(machine)

              # Level 2 检查 (新增): 每 N 步用监督 LLM 检查
              if step % self.supervise_every == 0:
                  verdict = self._supervise(machine.state, transition)
                  if verdict.action == "STOP":
                      break
                  elif verdict.action == "CORRECT":
                      machine.state.control = verdict.corrected_value
                  elif verdict.action == "COMPRESS":
                      machine.state.env = self._compress_context(machine.state.env)

监督者的检查内容:
  1. 推理偏移检测:
     "当前输出 (500 token) 比第 1 步 (2000 token) 短 75%,可能存在推理缩水"
     → 建议: COMPRESS (压缩上下文后重试)

  2. 方向偏离检测:
     "Agent 连续 3 步在搜索无关关键词,偏离了原始目标"
     → 建议: CORRECT (注入纠正信息)

  3. 质量衰减检测:
     "回答中缺少来源引用,且置信度异常高 (偷懒的典型特征)"
     → 建议: 要求 Agent 重新检查并补充引用

成本:
  主 Agent (Opus): $0.015/次
  监督者 (Haiku): $0.00025/次 (便宜 60 倍)
  每 3 步监督一次: 额外成本 ≈ 主 Agent 的 2%

Level 3: 人类监督(可实现)

CEK Yield 暂停 → 推送给人类审批 → 等待确认后继续:

实现方式:

  class HumanSupervisedEngine(CEKEngine):
      def __init__(self, approval_callback, require_approval_for=None, **kwargs):
          self.approval_callback = approval_callback
          self.require_approval = require_approval_for or [
              "WriteFile", "EditFile", "Bash", "DeleteFile"
          ]

      # 在 Yield(tool) 点检查是否需要人类审批
      if transition.label.kind == LabelKind.TOOL:
          if transition.label.name in self.require_approval:
              approved = await self.approval_callback({
                  "tool": tool_name,
                  "input": transition.label.input,
                  "cost_so_far": machine.state.cost,
              })
              if not approved:
                  break  # 人类拒绝 → 终止

适用场景:
  - Agent 要删除生产数据库 → 暂停,推送给人类确认
  - Agent 要修改敏感配置文件 → 暂停,人类审阅 diff
  - WikiIngest 发现矛盾 → 暂停,人类判断哪个版本正确

成本:
  最贵但最安全
  适用于高风险操作(不是每步都需要)

三级监督的成本效益对比

场景: Agent 跑 20 步,第 5 步走错方向

不监督:
  20 步全跑完 → 第 5-20 步全浪费 → $0.06 → 结果错误

Level 1 (规则):
  第 10 步检测到循环 → 停止
  $0.03 → 结果仍然错误,但省了钱

Level 2 (模型):
  第 6 步监督者发现方向错 → 纠正
  Agent 从第 7 步走对 → 第 12 步完成
  $0.04 + $0.002 (监督成本) → 结果正确

Level 3 (人类):
  第 5 步人类发现走错 → 手动纠正
  Agent 从第 6 步走对 → 第 10 步完成
  $0.03 + 5 秒人类注意力 → 结果正确且最优

四、推理偏移检测器

设计原理

基于 Rodionov 论文的发现,推理偏移有可量化的症状:

症状 1: 输出长度递减
  正常: Step 1 → 2000 token, Step 5 → 1800 token, Step 10 → 1600 token (缓慢下降)
  偏移: Step 1 → 2000 token, Step 5 → 1200 token, Step 10 → 600 token (加速下降)

症状 2: 自检词消失
  正常: "wait" 11%, "but" 46%, "maybe" 23%
  偏移: "wait" 5%, "but" 20%, "maybe" 9%

症状 3: 首答到终结的距离缩短
  正常: 找到答案后继续验证 (43% 概率)
  偏移: 找到答案后直接输出 (68% 概率)

症状 4: 置信度异常升高
  正常: "我认为...""可能是...""根据文档..."
  偏移: "答案是...""显然...""毫无疑问..."
  (偷懒的模型反而更自信)

检测指标

推理密度 (Reasoning Density):
  RD(step) = output_tokens(step) / max(output_tokens(step_1), 1)
  
  RD ≈ 1.0: 推理深度保持
  RD < 0.5: 推理深度衰减 50%+,高度疑似偏移

推理密度衰减率 (Decay Rate):
  DR = (RD(step_1) - RD(step_n)) / n
  
  DR < 0.02/步: 正常衰减
  DR > 0.05/步: 快速衰减,偏移确认

自检词频率 (Self-Check Frequency):
  SCF(step) = count("wait"|"but"|"maybe"|"however"|"等一下"|"不过"|"也许") / total_words
  
  SCF 基线: 从 Step 1 采样
  SCF < 基线 × 0.5: 自检行为被砍掉一半,偏移确认

综合偏移分数 (Shift Score):
  SS = 0.4 × (1 - RD) + 0.3 × DR_normalized + 0.3 × (1 - SCF/SCF_baseline)
  
  SS < 0.2: 正常
  SS 0.2-0.5: 轻度偏移 (告警)
  SS > 0.5: 重度偏移 (干预)

MCP 工具接口

工具名: monitor_reasoning_shift

输入:
  {
    "trace": [
      {"step": 1, "input_tokens": 500, "output_tokens": 2000, "output_text": "..."},
      {"step": 2, "input_tokens": 2500, "output_tokens": 1800, "output_text": "..."},
      ...
    ]
  }

输出:
  {
    "shift_score": 0.62,
    "severity": "HIGH",
    "reasoning_density": [1.0, 0.9, 0.75, 0.5, 0.3],
    "decay_rate": 0.175,
    "self_check_frequency": [0.11, 0.09, 0.06, 0.04, 0.02],
    "onset_step": 3,
    "recommendation": "Step 3 后推理密度下降 65%。建议: (1) 在 Step 3 压缩上下文 (2) 拆分为两个独立子任务 (3) 对 Step 4+ 的输出启用 Guard 验证"
  }

五、上下文预算管理

扩展成本分级

在原有的 CostGrade(p, p_fatal, t, l, m) 基础上,新增上下文负载维度:

扩展后:
  CostGrade(p, p_fatal, t, l, m, context_tokens, context_load)

  context_tokens: 累积输入 token 数(所有步骤的 input 之和)
  context_load:   = context_tokens / model_context_window

  context_load 是推理偏移的先导指标:
    < 30%:  安全区 → 推理偏移风险低
    30-60%: 警戒区 → 开始出现轻度偏移
    60-80%: 危险区 → 推理密度可能衰减 30-50%
    > 80%:  红线区 → 推理偏移几乎必然发生

上下文预算规则

每个 Agent 编译时计算上下文预算:

  ReAct 循环 (maxSteps=10, Sonnet 200K window):
    每步估算: system_prompt (2000) + history (step × 1500) + tools (500)
    
    Step 1:  2000 + 0 + 500 = 2,500 (1.25% load) → 安全
    Step 5:  2000 + 7500 + 500 = 10,000 (5% load) → 安全
    Step 10: 2000 + 15000 + 500 = 17,500 (8.75% load) → 安全
    
    结论: 10 步 Sonnet 循环的上下文负载始终 < 10%,偏移风险低

  GroupChat (5 agents, maxRounds=10):
    每轮: 5 agents × 1500 token/轮 = 7,500
    10 轮: 75,000 token → 37.5% load → 进入警戒区!
    
    建议: 第 6 轮后启用上下文压缩 (smart windowing)

  Pipeline 5 级 (每级 Opus 调用):
    每级输入: 上一级输出 (约 3000 token) + system_prompt (2000)
    → 上下文不累积 (每级是独立调用) → 始终安全
    
    这是 Pipeline 相比 ReAct 的天然优势: 上下文不膨胀

上下文压缩策略

当 context_load 超过阈值时的自动干预:

策略 1: Smart Windowing (已有)
  保留: system_prompt + 最近 N 步 + 关键中间结果
  丢弃: 早期步骤的完整输出
  → context_load 回落到 30% 以下

策略 2: Summary Compaction
  对早期历史做摘要: "Step 1-5 完成了文件读取和初步分析,发现 3 个关键问题: ..."
  替换原始历史 → 信息保留但 token 大幅减少
  → 需要额外 1 次 LLM 调用 (用便宜模型)

策略 3: Context Reset (最激进)
  在 CEK Yield 点完全重置上下文
  只保留: 当前任务目标 + 到目前为止的最终结果
  → context_load 回到接近 0%
  → 最有效但可能丢失细节

自动选择:
  context_load < 30%: 不压缩
  30-60%: Smart Windowing
  60-80%: Summary Compaction
  > 80%: Context Reset

六、与 Harness Engineering 的关系

文章的论点

Rodionov 和 Anthropic 的研究暗示:如果能从模型内部解决推理偏移(通过情绪向量注入等手段),外部的 Harness(Todo list/Checkpoint/子代理验证)就不再需要。

lambdagentpaas 的定位

会过时的 (外部约束):
  ✗ Todo list 强制模型按步骤做
  ✗ Checkpoint 定期验证中间结果
  ✗ 子代理交叉验证
  ✗ 上下文压缩/窗口管理
  → 模型足够可靠后,这些都是多余的脚手架

不会过时的 (lambdagentpaas 核心):
  ✓ 类型安全 → 数学定理,和模型行为无关
  ✓ 成本上界 → 算术计算,和推理深度无关
  ✓ 终止性保证 → Y 组合子性质,和模型质量无关
  ✓ 并行安全 → 集合论性质,和上下文长度无关
  ✓ Skill/Pattern 架构 → 短上下文设计,天然限制偏移触发条件
  ✓ LLM Wiki → 知识编译模式,根本不依赖长上下文推理

关键区别:
  Harness = "管住一个不可靠的模型" → 模型可靠后不需要
  λA 类型系统 = "数学保证组合的正确性" → 永远需要

两层护城河

第一层 (短期): 模型还在偷懒
  → 推理偏移检测 + 三级监督 + 上下文预算管理
  → 这是当前有价值的能力
  → 随着模型进化,这层价值会递减

第二层 (永久): 不管模型多完美
  → 类型检查: output(f) <: input(g) 是数学事实
  → 成本预测: 20 步 × $0.015 = $0.30 是算术事实
  → 终止性: fix_20 在 20 步后终止是定理
  → 这些和模型是否偷懒完全无关
  → 这层价值永不递减

七、总结

推理偏移的本质:
  不是 bug,不是能力不足
  是模型学到的"认知节省策略"——省力但不报错
  越聪明的模型越会偷懒(因为它知道怎么走捷径)

lambdagentpaas 的三层防御:

  Layer A — 架构抗偏移 (天然):
    LLM Wiki 短上下文编译
    Skill 独立执行
    Pattern 分支隔离
    → 从结构上限制上下文增长,不给偏移可乘之机

  Layer B — 运行时监督 (CEK Yield):
    Level 1 规则: 成本熔断 + 循环检测 (零成本)
    Level 2 模型: 监督 LLM 每 N 步检查 (低成本)
    Level 3 人类: 高风险操作暂停审批 (高安全)
    → 在偏移发生时实时检测和干预

  Layer C — 形式化保证 (永久):
    类型安全 + 成本上界 + 终止性 + 并行安全
    → 不依赖模型行为,数学保证永远成立
    → 即使模型偷懒到极致,这些保证仍然有效

这就是为什么 lambdagentpaas 不怕"Harness 被模型进化吞没":
  Harness 是外部脚手架 → 会被淘汰
  λA 类型系统是数学定理 → 不会被淘汰
  推理偏移检测是过渡期工具 → 有当前价值
  LLM Wiki 是架构选择 → 天然抗偏移