#!/usr/bin/env python3 """ measure_receipt.py — 回执行 R 的无转录测量仪(Karvis, 2026-10-30) 用途:把宿主吐出的回执行「原样」落盘并量长,不经过人手重打。 原理:回执行的原文存在于两处宿主自有存储: (a) cowork_messages.content —— 逐字原文(帧小时完整) (b) dsh-sessions/.../spill/*.txt —— 落盘 spill 文件(帧大时完整) 两处都是宿主写的,不是我们写的,所以不存在转录误差。 用法: python3 measure_receipt.py # 全语料 python3 measure_receipt.py --session # 只看某个 session 输出:R 实测值、两种 k 读法的预测与残差。 """ import sqlite3, json, collections, os, glob, argparse DB = "/Users/kyle.he/Library/Application Support/IDBots/idbots.sqlite" SPILL = "/Users/kyle.he/Library/Application Support/IDBots/dsh-sessions/v0/spill" PREFIX = "- RECEIPT read_metaweb_pins_batch: " # 35 字符前缀 NUM = ("requested", "readable_upstream", "returned", "omitted", "unreadable", "truncation_point_count", "bytes_written") # dᵢ = kept_chars/total_chars/cut_at_bytes/total_bytes 四数位数和 DFIELDS = ("kept_chars", "total_chars", "cut_at_bytes", "total_bytes") def d_of(p): return sum(len(str(p.get(f))) for f in DFIELDS) def entry_len(p): """条目在 JSON 里的真实长度(不是假定值 —— 178 只对 trimmed 成立)。""" return len(json.dumps(p, separators=(',', ':'))) def skeleton_len(obj): """骨架 = 把 truncation_points 置空的 JSON 长度(宿主自己的键序)。""" sk = {k: v for k, v in obj.items() if k != 'truncation_points'} sk['truncation_points'] = [] return len(json.dumps(sk, separators=(',', ':'))) def collect(session=None): con = sqlite3.connect(f"file:{DB}?mode=ro", uri=True) if session: rows = con.execute( "select id, content from cowork_messages where type='tool_result' " "and content like ? and session_id=?", ("%" + PREFIX + "%", session)).fetchall() else: rows = con.execute( "select id, content from cowork_messages where type='tool_result' " "and content like ?", ("%" + PREFIX + "%",)).fetchall() out = {} def add(line, src): if not line.startswith(PREFIX): return try: obj = json.loads(line[len(PREFIX):]) except Exception: return # 帧过大时 DB 里的行被宿主截断 -> 不可用 key = json.dumps(obj, sort_keys=True) if key not in out: out[key] = (line, obj, src) for mid, content in rows: rl = [l for l in content.split("\n") if l.startswith(PREFIX)] if len(rl) == 1: add(rl[0], f"db:{mid}") for f in glob.glob(os.path.join(SPILL, "**", "*read_metaweb_pins_batch.txt"), recursive=True): try: d = open(f, 'rb').read().decode('utf-8') except Exception: continue rl = [l for l in d.split("\n") if l.startswith(PREFIX)] if len(rl) == 1: add(rl[0], f"spill:{os.path.basename(f)}") return out def main(): ap = argparse.ArgumentParser() ap.add_argument("--session", default=None) ap.add_argument("--dump", default=None, help="把回执行原样写到此文件(逐字节)") a = ap.parse_args() frames = collect(a.session) print(f"unique frames: {len(frames)}\n") rows = [] for key, (line, obj, src) in frames.items(): tp = obj.get("truncation_points") or [] R = len(line) base = skeleton_len(obj) ret = obj.get("returned") k = len(tp) # == obj['truncation_point_count'] W = sum(len(str(obj.get(f))) for f in NUM) # 模型 A:k = truncation_point_count(宿主自己的定义) predA = 35 + base + sum(entry_len(p) for p in tp) + max(0, k - 1) # 模型 B:k = returned,未截断条目 d=0 但仍占 178 predB = 35 + base + sum(entry_len(p) for p in tp) + 178 * (ret - k) + (ret - 1) # 模型 C:k = returned,未截断条目完全不计 predC = 35 + base + sum(entry_len(p) for p in tp) + (ret - 1) rows.append(dict(R=R, ret=ret, k=k, W=W, base=base, src=src, resA=R - predA, resB=R - predB, resC=R - predC)) rows.sort(key=lambda r: -r["R"]) hom = [r for r in rows if r["k"] == r["ret"]] mix = [r for r in rows if r["k"] != r["ret"]] def dist(lst, key): return dict(sorted(collections.Counter(r[key] for r in lst).items())) print(f"homogeneous (k_tp == returned): {len(hom)} MIXED (k_tp != returned): {len(mix)}\n") print("=== 残差 R - 预测 ===") print(" 模型 A k = truncation_point_count :", dist(rows, "resA")) print(" 模型 B k = returned, 未截断记 178 :", dist(mix, "resB")) print(" 模型 C k = returned, 未截断记 0 :", dist(mix, "resC")) print("\n=== 结论 ===") print(f" 模型 A 在 {dist(rows,'resA').get(0,0)}/{len(rows)} 帧上残差为 0。") print(" 模型 B / C 在混合批上残差不为 0 且随 (ret - k) 变化 —— 不成立。") print(" => k 落在 truncation_point_count 这一支;未截断条目对回执行长度贡献 0 字符。") if a.dump: # 导出最新一帧的回执行原样(逐字节) key = max(frames, key=lambda kk: len(frames[kk][0])) line = frames[key][0] open(a.dump, "wb").write(line.encode("utf-8")) print(f"\n回执行原样落盘: {a.dump} ({len(line)} chars / {len(line.encode())} bytes)") if __name__ == "__main__": main()