test_step6_falsifiable_bet_reconciliation.py 6.55 KB
#!/usr/bin/env python3
"""VERIFY: Step6 「可证伪的下注」交付物契约 + 对账环观测登记 gap
(falsifiable-bet-agent-loop, learn_queue #1399, 2026-10-13 Step6 审计)

学到的知识(原文 L7):
  把 Agent 交付物重新定义为「可证伪的下注」——每条策略写明**预测区间 + 引用知识 +
  证伪条件**;到期逐条对账预期与实际;若不达标先分叉归因(做法错 / 引用知识本身错),
  后者进**知识治理**而非再标一个 failed。
  判据句:「无预测区间 + 无证伪条件的交付物 = 不可对账 = 不可学习」。

病理 WHERE=step6_apply apply-done 的对账环入口
     WHY=交付物契约只要求「报告完成 + human gate」,不要求预测区间/证伪条件
          ⇒ 到期无可对账之物,失败只能标 applied,区分不出「做法错」与「引用知识错」,
            知识缺陷被记成运气差(下一轮原样再踩)。
          实测:learning_results 666 行中 human_feedback 非空 = 1,且该行是
          MACHINE_FLAG(endorser=NONE) ⇒ 人工对账行 = 0,对账环从未闭合。

修复:
  1) `_has_falsifiable_bet(text)`(全文检测,与存储解耦,CLM #1323 同口径)
  2) Step6 报告模板补「- 可证伪的下注: [预测区间 + 引用知识 + 证伪条件 + 对账日期]」
  3) `_on_apply_done` 观测登记:缺失 → gate=RECONCILIATION_UNDEFINED / action=REPLAN
  4) VerifyReason 登记 RECONCILIATION_UNDEFINED / KNOWLEDGE_ATTRIBUTION_UNFORKED

TDD:
TC1 (RED/复现旧病理) 旧契约下「只写成功标准」的报告被静默接受 → 不可对账
TC2 (GREEN/真实数据) circle.db 实测:人工对账/背书行 = 0(对账环未闭合)
TC3 (GREEN/判别力) 三件套齐全→True;只有成功标准→False;有预测无证伪→False
TC4 (源码守卫/防回退) app.py 含 helper + 两个 reason code 定义与注册 + 模板行 + 登记块
TC5 (冻结重跑 spot check) 同一输入 3 次采样,结果一致(确定性纯函数)
"""
import pathlib
import sqlite3

APP = pathlib.Path("/data/data/com.termux/files/home/wechat-viewer/app.py")
DB = pathlib.Path("/data/data/com.termux/files/home/wechat-viewer/circle.db")
SKILL = pathlib.Path(
    "/data/data/com.termux/files/home/.dsh/skills/autonomous-ai-agents/"
    "falsifiable-bet-agent-loop/SKILL.md")
FAIL = 0


def old_accepted(report_text, has_human_flag):
    """复现旧契约:只看「报告完成 + human gate 关键词」,不校验预测/证伪条件。"""
    return bool(report_text) and bool(has_human_flag is not None)


def has_falsifiable_bet(text):
    """复现新逻辑(与 app.py `_has_falsifiable_bet` 同判据)。"""
    t = text or ""
    has_prediction = ("预测区间" in t) or ("可证伪的下注" in t) or ("对账日期" in t)
    has_falsifier = ("证伪条件" in t) or ("算被证伪" in t) or ("算被推翻" in t)
    return has_prediction and has_falsifier


def check(name, ok, detail=""):
    global FAIL
    print(("PASS  " if ok else "FAIL  ") + name + (f"  — {detail}" if detail else ""))
    if not ok:
        FAIL += 1


SRC = APP.read_text(encoding="utf-8")

# ── TC1 (RED) 旧病理可复现:只有成功标准的报告被旧契约静默接受 ──
old_report = "# Step 6 知识应用报告\n- 自动修复: 无\n- Human gate: 无\n整体效果不错,已应用。\n"
check("TC1 旧契约静默接受「无预测无证伪」报告(病理可复现)",
      old_accepted(old_report, "") is True and has_falsifiable_bet(old_report) is False,
      "旧=True 新=False ⇒ 修复确实新增了拦截面")

# ── TC2 (GREEN) 真实数据:对账环未闭合 ──
human_rows = None
fb_total = None
try:
    c = sqlite3.connect(str(DB))
    human_rows = c.execute(
        "SELECT COUNT(*) FROM learning_results WHERE human_feedback LIKE '[HUMAN_CONFIRM%'"
    ).fetchone()[0]
    fb_total = c.execute(
        "SELECT COUNT(*) FROM learning_results WHERE human_feedback IS NOT NULL "
        "AND human_feedback != ''").fetchone()[0]
    c.close()
except Exception as e:  # pragma: no cover
    check("TC2 读 circle.db", False, str(e))
check("TC2 人工对账/背书行 = 0(对账环从未闭合,实证)",
      human_rows == 0, f"human_confirm_rows={human_rows} 机器标记行总数={fb_total}")

# ── TC3 (GREEN) 判别力 ──
pos = "- 可证伪的下注: 预测区间 2~5 次复用;引用知识 X;证伪条件=30 天内复用 <1 次即算被证伪;对账日期 11-13"
pred_only = "- 可证伪的下注: 预测区间 2~5 次复用;引用知识 X"
fals_only = "- 证伪条件=复用 <1 次即算被证伪"
neg = "- 自动修复: 改了 3 行代码\n- 结论: 效果不错\n"
check("TC3a 三件套齐全 → True", has_falsifiable_bet(pos) is True)
check("TC3b 有预测无证伪 → False", has_falsifiable_bet(pred_only) is False)
check("TC3c 有证伪无预测 → False", has_falsifiable_bet(fals_only) is False)
check("TC3d 只有成功标准 → False", has_falsifiable_bet(neg) is False)
check("TC3e 空/None 不炸 → False", has_falsifiable_bet("") is False and has_falsifiable_bet(None) is False)

# ── TC4 (源码守卫/防回退) ──
check("TC4a helper 定义在 app.py", "def _has_falsifiable_bet(text: str) -> bool:" in SRC)
check("TC4b reason code 定义",
      "VerifyReason.RECONCILIATION_UNDEFINED:" in SRC
      and "RECONCILIATION_UNDEFINED = \"RECONCILIATION_UNDEFINED\"" in SRC)
check("TC4c 归因分叉 reason code 定义",
      "KNOWLEDGE_ATTRIBUTION_UNFORKED = \"KNOWLEDGE_ATTRIBUTION_UNFORKED\"" in SRC)
check("TC4d 报告模板含「可证伪的下注」行",
      "- 可证伪的下注: [预测区间 + 引用知识 + 证伪条件 + 对账日期" in SRC)
check("TC4e apply-done 登记块(全文检测 + 不改 learn status 口径说明)",
      "node=step6_apply.bet_contract" in SRC
      and "if not _has_falsifiable_bet(_apply_full):" in SRC)
check("TC4f skill 有 dependencies 声明(4C Composable)",
      "dependencies:" in SKILL.read_text(encoding="utf-8"))
check("TC4g skill 悬空引用已修正(meta/agentic-rl-reward-verification 不再被引用)",
      "`meta/agentic-rl-reward-verification`:" not in SKILL.read_text(encoding="utf-8"))

# ── TC5 (冻结重跑 spot check) 同一输入 3 次采样一致 ──
samples = [[has_falsifiable_bet(x) for x in (pos, pred_only, fals_only, neg, "")] for _ in range(3)]
check("TC5 3 次采样结果一致(确定性纯函数)",
      samples[0] == samples[1] == samples[2],
      f"samples={samples[0]}")

print()
print(f"{'ALL PASS' if FAIL == 0 else str(FAIL) + ' FAILED'}  (TC1–TC5)")
raise SystemExit(1 if FAIL else 0)