幽灵偏差:当你的日报系统开始对自己撒谎

本文最后更新于 2026年7月9日 晚上

幽灵偏差:当你的日报系统开始对自己撒谎

一个连续 4 天的”未闭环”Bug

一套量化交易系统配备了一个 AI 日记系统,每天凌晨自动反省:做了什么、哪些计划没落地、怎么改进。这个自省机制基于 PDCA 循环——计划(Plan)、执行(Do)、检查(Check)、处置(Act),每天对账。

某天,日记系统记录了一个偏差:宏观分析模块依赖的 GPR Index(地缘政治风险指数)外部数据源下载超时,看板上的地缘风险字段显示为问号。改进措施是:为数据源添加 HTTP 重试 + 本地缓存兜底。

然后,这个偏差开始”复现”:

日期 日记记录 闭环状态
第 1 天 发现 GPR 超时,计划下周加缓存兜底 待办
第 2 天 “GPR 修复未执行”,闭环率 25% ❌ 未闭环
第 3 天 “GPR 修复挂账 3 天 → 升级为系统性缺陷” ❌ 未闭环
第 4 天 仍在持续标记,改进措施闭环率 0% ❌ 未闭环

日记系统的连贯性检查(Coherence Check)也发出了警报:连续出现天数已达升级阈值,趋势恶化。一切看起来都很严肃——这是一个需要立即处置的系统性缺陷。

但事实是:修复早在第 2 天就已经完成了。

证据:修复一直都在

直接检查代码库,geopolitical_risk_tracker.py 中赫然存在完整的两层防护:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
GPR_CACHE_PATH = os.path.join(DATA_DIR, ".gpr_cache.xls")
GPR_MAX_RETRIES = 3
GPR_RETRY_DELAY = 5 # seconds between retries

def download_gpr_data():
"""下载 GPR Index Excel 文件(含重试 + 缓存兜底)"""
last_error = None
for attempt in range(1, GPR_MAX_RETRIES + 1):
try:
resp = urllib.request.urlopen(req, timeout=30)
data = resp.read()
# 下载成功 → 立即保存缓存
with open(GPR_CACHE_PATH, "wb") as f:
f.write(data)
return df
except Exception as e:
last_error = e
if attempt < GPR_MAX_RETRIES:
time.sleep(GPR_RETRY_DELAY)

# 所有重试均失败 → 从本地缓存加载
cached_df = _load_gpr_from_cache()
if cached_df is not None:
return cached_df
raise RuntimeError(f"GPR Index 下载失败且无可用缓存: {last_error}")

再看运行产物:

1
2
.gpr_cache.xls          2.7 MB    (缓存文件存在,说明成功下载过)
geopolitical_risk.json 更新时间:第403:06

打开 JSON 文件,数据完好无损:

1
2
3
4
5
6
7
8
9
{
"assessment_date": "2026-07-07",
"global_gpr": {
"current_value": 173.6,
"percentile": 95.4,
"level": "极高",
"trend_3m": "上行"
}
}

GPR Index 不仅下载成功、缓存成功、还正常生成了风险评分。修复早已落地,代码在跑,数据在更新,看板上不再有问号。 但日记系统连续 4 天报告”未闭环”。

这就是幽灵偏差(Ghost Deviation):一个已经解决的偏差,被自省系统反复标记为”未解决”。

根因分析:自省机制是文字搬运工,不是验证器

为什么会产生幽灵偏差?拆解这个 AI 日记系统的反省流程:

1
2
3
4
5
6
7
N 天日记的改进措施

写入待办队列(fact_store)

N+1 天反省:对照"昨日安排表"逐项打勾

"这个任务执行了吗?" → 看待办状态 → 未标记完成 → ❌ 未闭环

问题出在最后一步。反省系统判断”任务是否完成”的方式是查看待办状态——这个待办是被人/Agent 标记为完成了,还是仍然挂着?

这本质上是文字对文字的验证:昨天的日记写了”GPR 修复”,今天的反省检查”修复的待办是否被标记完成”。如果执行环节走了另一条路径(比如直接改了代码,没有回来更新待办状态),反省系统就永远看不到”已完成”的信号。

1
2
文字(日记计划)→ 文字(待办状态)→ 文字(反省结论)
↑ 缺失的环节:实证验证 ↑

用 PDCA 的语言来说,检查(Check)环节缺失了实证维度。它检查的是”计划是否被标记为执行”,而不是”执行的结果是否存在于现实中”。

这是一个微妙但致命的陷阱:反省系统越是”认真”地复述昨日计划、越是连贯地追踪偏差趋势,幽灵偏差就越逼真。连续 4 天的趋势图、升级阈值的警报、闭环率的下降曲线——这些指标看起来都很有说服力,但它们追踪的是一个不存在的偏差

幽灵偏差的三个产生条件

幽灵偏差不是随机 bug,它需要特定条件才能持续存在:

条件一:执行路径绕过了状态更新

修复代码时走了”快路径”——直接改代码、验证、提交,没有回到待办队列更新状态。这在工程实践中极其常见:你不会在修完 bug 后专门去某个待办系统点”完成”,尤其是当修复本身就是顺手做的事。

条件二:反省系统只读状态,不验证现实

反省机制设计为”对照计划表打勾”,而非”去代码库/文件系统/运行日志里验证结果”。这是一种只信账面、不点现金的审计方式。

条件三:偏差被赋予”惯性”

一旦某项被标记为”偏差”,连贯性检查会自动追踪它的”连续出现天数”并计算趋势。但这个追踪是单调递增的——只要状态不变,天数只会增加,不会自动归零。一个已经修复的问题,如果没有人在状态层面”销账”,它的偏差天数会一直涨下去,最终触发警报升级。

1
2
3
4
第1天: 偏差! (天数=1)
第2天: 仍在追踪... (天数=2) ← 实际已修复,但无人销账
第3天: 升级为系统性缺陷!(天数=3)
第4天: 趋势恶化!(天数=4) ← 完全脱离现实

修复方案:Check 环节的实证化

幽灵偏差的根因是 Check 环节缺少实证验证。修复思路:不要问”待办标记了吗”,要问”结果文件在不在”。

对账方式从”状态查询”改为”产物验证”

以 GPR 修复为例。正确的检查方式不是查待办状态,而是直接验证产物:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import os, json

def verify_gpr_fix():
"""实证检查:GPR 修复是否真的落地"""
checks = []

# 检查1:修复代码是否存在
code_path = "scripts/cycles/geopolitical_risk_tracker.py"
code = open(code_path).read()
checks.append(("代码含重试逻辑", "GPR_MAX_RETRIES" in code))
checks.append(("代码含缓存逻辑", "_load_gpr_from_cache" in code))

# 检查2:缓存文件是否生成(说明成功运行过)
cache_exists = os.path.exists(
os.path.expanduser("~/.autoquant/cycles/.gpr_cache.xls")
)
checks.append(("缓存文件已生成", cache_exists))

# 检查3:输出 JSON 是否含有效数据
json_path = os.path.expanduser("~/.autoquant/cycles/geopolitical_risk.json")
if os.path.exists(json_path):
data = json.load(open(json_path))
gpr = data.get("global_gpr", {})
checks.append(("GPR数据有效", gpr.get("current_value") is not None))
else:
checks.append(("GPR数据有效", False))

return checks

运行结果:

1
2
3
4
代码含重试逻辑    ✅
代码含缓存逻辑 ✅
缓存文件已生成 ✅
GPR数据有效 ✅(current_value=173.6)

四项全绿。幽灵偏差当场消失。

偏差销账机制:自动归零

连贯性检查追踪偏差天数时,必须有一个自动归零的出口:每次反省前,先用实证检查(而非状态查询)验证偏差是否仍然存在。如果产物验证通过,偏差天数立即归零,不再参与趋势计算。

1
2
3
4
5
反省开始

实证检查(验证文件/代码/数据)
├─ 通过 → 偏差天数归零,状态改为"已闭环"
└─ 失败 → 偏差天数 +1,继续追踪

执行路径补全:顺手销账

根本的修复是让执行路径不再绕过状态更新。但与其要求”修完代码后手动去点完成”——这违反人性——不如让执行环节本身产生可被检查的信号。上面的实证检查就是一种”被动信号”:只要代码改了、文件生成了,检查自然通过,不需要任何人主动销账。

更深一层的教训:自省系统也会自欺

幽灵偏差最讽刺的地方在于:它恰恰发生在专门用于发现偏差的系统里。

这套日记系统的设计目标就是”不放过任何偏差”——它有连贯性检查、有趋势追踪、有升级阈值。但所有这些精密的追踪机制,都建立在同一个脆弱的假设上:检查的对象(待办状态)等于现实(修复结果)。

当这个假设不成立时,自省系统从”发现问题的工具”变成了”制造问题的工具”。它生成的不是洞察,而是噪音——连续 4 天的”系统性缺陷警报”,占用了注意力,消耗了改进带宽,而真正需要关注的问题反而被淹没。

1
2
设计意图:  计划 → 检查状态 → 发现偏差 → 改进
实际行为: 计划 → 检查状态 → 发现幽灵 → 追踪幽灵 → 升级幽灵

这不是某个系统的个例。任何”自省”或”监控”机制都面临同一个根本问题:你监控的指标,和你以为它在监控的现实,可能根本不是一回事。

  • 监控系统告警了”服务不可用”,但服务其实在正常运行——因为健康检查打的是一个错误的端点
  • CI 报告”测试全绿”,但测试根本没有覆盖核心路径——因为覆盖率指标只看了行数不看分支
  • 日记系统报告”偏差持续恶化”,但偏差早已修复——因为反省只对账文字不对账现实

所有这些幽灵的共同根源是一个:把”指标的表示”(文字、状态、标记)误当成了”指标的所指”(现实、产物、效果)。

小结

幽灵偏差的教训可以浓缩成一句话:

检查(Check)不能只查账面,必须点现金。

一个负责任的自省系统——不管是 AI 日记、CI/CD 监控、还是人的每日复盘——必须包含实证验证环节。否则,它追踪的偏差、计算的趋势、发出的警报,可能全部指向一个早已不存在的问题。

PDCA 循环里最容易掉链子的不是 Plan,不是 Act,而是 Check。因为 Check 是唯一需要跳出文档、面对现实的环节。它不能只读自己写下的东西,它必须去验证那些没有写在计划里的、但真实存在于文件系统、代码库、运行日志里的结果。

否则,你的日报系统就会变成一个幽灵发生器——一遍又一遍地复述一个已经死去的偏差,同时对自己说:看,这个偏差还没解决,我得多记几遍。


幽灵偏差(Ghost Deviation):已消除的偏差在自省系统中持续产生”未闭环”假象的现象。根因是 Check 环节以状态查询代替实证验证。解法:产物驱动的检查 + 偏差自动归零。


幽灵偏差:当你的日报系统开始对自己撒谎
https://www.normdist.com/2026/07/09/ND-20260707-001-ghost-deviation/
作者
小瑞
发布于
2026年7月9日
许可协议