提出Agent补丁应被block直到第二个进程重放seed-locked property campaign并匹配witness文件;通过独立测量trials spent、fixture digest和replay结果来防止同一上下文中测试通过带来的虚假信心。
一个 Agent Patch 会被阻断,直到第二个进程重放一次种子锁定的属性测试并匹配一个 Witness 文件。本地通过的运行日志只是一个候选观察结果,不是合并凭证。
Witness 有意保持精简。它记录的是 Fixture 摘要(digest)、属性 ID、种子(seed)、试验预算、已消耗的试验次数,以及重放期间是否调用了模型。审阅者可以重新生成这些字段,但无法重新生成对话记录。
Agent Diff 通常会携带一次通过运行,而这次运行与写出变更的上下文相同。共享的上下文会推高置信度。
在 Diff 辩论开始前,先分离三项测量结果:比较已消耗试验次数与预算;比较 Fixture 摘要与预生成时固定的针点;比较重放结果与 Witness。如果某项测量缺失,Patch 就继续处于阻断状态。
这是一种测试策略,不是评分。预算内零失败意味着测试活动(campaign)完成了,不代表属性集合本身是充分的。
使用固定的 Schema,使审阅不依赖日志格式。以下示例是一个提案,不是生产关卡的日志。
{
"schema": "agent-patch-witness/v1",
"fixture_digest": "sha256:<64 hex chars from step 1>",
"properties": ["balance_non_negative", "transfer_conserves_sum"],
"seed": 170924,
"trial_budget": 200,
"trials_spent": 200,
"failures": 0,
"counterexample": null,
"model_called_during_replay": false
}
摘要的是字节,不是文件名。字节完全相同的重命名 Fixture 是同一个 Fixture。一个字节的编辑就会启动新的测试活动,即使属性名保持不变。
不要把生成的用例放进 Fixture 树。用例来自种子(seed)。Fixture 是那些用例可能触碰的固定输入。把两者混在一起会让摘要追逐生成器,从而隐藏漂移。
以下命令假设 fixtures/、properties/campaign.py 和 witness/latest.json 存在。它们仅作说明用,不声称任何通过率。
在任何模型调用之前、在 Patch 应用之前,对 Fixture 树做哈希。如果 Diff 也编辑了 fixtures/,将该编辑拆分为独立的变更。Patch 应该消费这个针点,而不是替换它。
python3 - <<'PY'
import hashlib, pathlib
root = pathlib.Path("fixtures")
h = hashlib.sha256()
for p in sorted(x for x in root.rglob("*") if x.is_file()):
h.update(p.relative_to(root).as_posix().encode())
h.update(b"\0")
h.update(p.read_bytes())
print("sha256:" + h.hexdigest())
PY
将打印出的行存入审阅备注。之后的不匹配意味着测试活动测量了一棵与你固定的不同的树。丢弃那次运行,不要将其与更新的摘要取平均。
属性只有在审阅者将其重新陈述为输入与输出之间的一个关系之后,才能进入测试活动。这种重新陈述不能依赖于 Patch 引入的私有辅助函数。草案可以来自人或模型。在那句话存在之前,两者都只是提案。
丢弃那些不约束行为的谓词。assert True、恒等比较、仅确认返回值的检查都会消耗预算而不产生证据。它们可以干净地重放,却仍然什么也没教。在账本打开之前删除它们。
预算是输入。已消耗试验次数是输出。运行器在达到预算时停止,即使每次试验都通过了。只有在记录反例时允许提前停止。
# Proposal sketch. Not an executed measurement.
import hashlib, random
def run_campaign(props, seed, budget, fixture_bytes):
rng = random.Random(seed)
spent = 0
failures = []
digest = "sha256:" + hashlib.sha256(fixture_bytes).hexdigest()
for _ in range(budget):
spent += 1
case = {"n": rng.randrange(0, 50), "delta": rng.randrange(-10, 10)}
for name, fn in props:
ok, detail = fn(case)
if not ok:
failures.append({"property": name, "case": case, "detail": detail})
return {
"seed": seed,
"trial_budget": budget,
"trials_spent": spent,
"failures": failures,
"fixture_digest": digest,
}
return {
"seed": seed,
"trial_budget": budget,
"trials_spent": spent,
"failures": failures,
"fixture_digest": digest,
}
你仍然需要提供 props。这个草图不会发明一个领域 Oracle。它的职责是停止规则:一次完整的通过会消耗全部预算,而一次失败会保留打破该关系的用例。
在提交之前收缩反例。重放失败的用例,然后在同一属性仍然失败的前提下逐步减小 n 和 delta。存储最小的用例。大幅转储更难审阅,作为证据也没有更强。
只从账本字段写入 witness/latest.json。把提示词、Token 和聊天文本留在文件外。这些字符串不是种子的函数,所以第二个进程无法复现它们。
python3 properties/campaign.py --seed 170924 --budget 200 \
--fixtures fixtures/ --out witness/latest.json
python3 -m json.tool witness/latest.json > /tmp/witness.pretty.json
如果 failures 非空,附加收缩后的用例然后停止。把失败保留为属性结果。不要重写 Witness 以让同一个用例消失。
在干净的进程中运行同一测试活动。先取消设置 prompt 和凭证变量。然后比较摘要、种子、预算、消耗和失败字段与 Witness。
env -u OPENAI_API_KEY -u MODEL_PROMPT \
python3 properties/campaign.py --replay witness/latest.json --fixtures fixtures/
# Proposal check. Equality is exact, not approximate.
def replay_matches(witness, actual):
keys = ("fixture_digest", "seed", "trial_budget", "trials_spent")
if any(witness[k] != actual[k] for k in keys):
return False
if actual.get("model_called_during_replay"):
return False
return witness.get("failures", []) == actual.get("failures", [])
四项相等性决定重放结果。
Fixture 摘要等于预生成时的针点和 Witness 字段。
种子(seed)和 trial_budget 等于记录值。
trials_spent 等于预算,除非有记录的反例解释提前停止。
重放期间没有导入模型客户端。
托管运行器是可选容量,不是真相来源。超时或不打印匹配结果的传输成功都是缺失的重放。缺失的重放保持阻断状态。
证据完整不等于批准。审阅者仍然需要阅读 Diff 以查找被删除的检查、被放宽的边界以及超出声称修复范围之外的编辑。决策表只回答测试证据是否可以被重新生成。
披露:本文作为 MonkeyCode 产品推广的一部分准备。MonkeyCode 的免费模型访问适用于第 2 步——作为候选谓词的草案辅助,两者仍然是提案,直到审阅者重新陈述它们。免费服务器选项适用于第 5 步——当已有可用的干净重放主机时,作为可选的重放托管。
两个选项都不指定模型、配额、机器规格或时长,本文也不添加这些数字。如果任一选项宕机,在本地解释器上运行相同命令。可用性不是证据。
不要上传包含密钥的 Fixture。摘要不会删除你复制到主机上的字节。先剥离密钥,或者在已经持有密钥的机器上重放。
在审阅评论中放入四项观察。它们是计数,不是评分。
trials_spent 对比 trial_budget。在 200 中仅跑到第 12 次就停止且失败列表为空,意味着测试活动没有完成。
摘要与第 1 步针点相等。sha256: 后的十六进制是 64 个字符。更短的字符串是截断,不是匹配。
failures 长度。零是完整测试活动的必要条件。一个反例足以阻断。
重放是否导入了模型客户端。这里唯一可接受的值是无。
在一个队列中,统计有多少 Patch 生成了 Witness、多少在重放时不匹配、多少只提供了创作会话日志。这些统计描述的是你的审阅,不是产品基准,本文也不报告任何基准。
Witness 证明的是可重新生成性,不是完备性。一个在每个输入上都返回 true 的谓词可以在重放时匹配却仍然遗漏缺陷。在信任这次消耗之前,先读一下那个谓词。
random.Random 很方便。它不是可移植性承诺。同一个种子在不同的解释器上可能产生分歧。固定用于重放的解释器,或者用你能控制整数递推的生成器替换它。
对 Fixture 树做哈希会忽略时钟、网络调用和进程全局变量。读取这些的属性会过度声明隔离性。将这些输入作为 Fixture 字段注入,或者拒绝该属性。
免费托管选项可以被撤回、限流或不可用。策略必须能够承受这种情况。如果唯一的通过路径是一条你无法重复的远程运行器,你就没有 Witness。你只有一个会话。
跳过没有行为声明的纯文档编辑。跳过你无法陈述一个在 Patch 回滚后仍然有意义的不变量的工作。跳过验证依赖于已见证构建的紧急回滚,而不是新的 Agent Diff。
不要对包含密钥的 Fixture 使用托管重放。不要接受来自同一个未中断模型会话的 Patch 的草案谓词作为 Oracle,除非第 2 步的重新陈述已在审阅中。便利不是独立性。
固定 Fixture 摘要。消耗一个可见的试验预算。从账本写入 Witness,然后在模型无法帮助的地方重放它。创作日志可以保留附件,但它不携带 Patch。
如果你的设置中已经有免费的 MonkeyCode 服务器,将第 5 步指向它,并在审阅中附加 witness/latest.json。服务器是重新生成 Witness 的地方,不是合并的理由。