Agent 中途换掉 Fable 5.2,怎样保留进度并验证结果

通过已验证检查点和工具结果账本,将任务交给另一模型继续。包含真实 Ofox 交接测试、合成库存数据、下载脚本与独立验证器。

一根完整的橙色接力棒位于两位接棒者之间,表示模型间传递经过验证的工作。

Agent 从 Fable 5.2 切到其他模型时,应将经过验证的任务状态与会话历史分开保存。新模型需要输入、已完成工具的结果、剩余工作和验收条件。把上个模型“在想什么”整理成可读摘要,不能可靠地替代这些信息。

我们于 2026 年 11 月 10 日通过 Ofox 实测了这个方法:Fable 5.2 请求只读库存查询,接收三行合成数据,并完成原生工具往返。随后,将应用生成的检查点交给一个新的 Opus 5.5 会话。最终 JSON 中每个 SKU 和数量都通过了独立检查。这只验证了一个小型状态交接流程,不是持续数小时的 Agent 基准测试,也不是保留隐藏推理。

为什么备用模型请求成功,仍可能丢状态

官方保留思考文档区分了模型兼容性和应用进度。在文档所述 Claude API 与 Google 托管路径上,旧模型无法读取 Fable 5.2 的 thinking 块;不支持的块可能被丢弃,但请求仍然成功。换账户续接时,账户绑定又构成一个边界。

这些规则并不表示每次切模型都会报错,而是说明 HTTP 成功不足以证明推理得到了延续。Ofox 目录列出了路由能力,不能证明跨供应商或账户的每一种签名历史迁移都有效。我们没有测试跨账户重放,本次交接刻意不依赖它。

这里有两种策略。如果需要保留原历史,就在兼容且经过验证的路由上继续未修改的会话。如果有意改变模型、工具或任务配置,就用应用持有的状态开启新会话。本文实现第二种,不修改签名,也不重建私有思考过程。

下载可复现的小示例

下载 checkpoint_demo.py。它需要 Python 3.9 或更新版本,只用标准库,默认检查不会调用 API。请放进空文件夹,避免生成文件覆盖已有任务。

若要运行完整的“Fable 工具请求 → 工具结果续接 → 新 Opus 会话”,再把 run_handoff.py下载到同一文件夹。python3 run_handoff.py 只做本地检查。设置 OFOX_API_KEY 后,明确执行 python3 run_handoff.py --live 才会发出最多三个付费请求,在新建的 handoff-run 文件夹保存请求/响应,并验证最终结果。脚本不会自动重试,也拒绝复用该输出文件夹。创建下一次运行前,先检查失败或结果不确定的尝试。

每个请求的输出上限为 4,096 tokens,请求体仅包含少量合成数据。这些约束用于限制本示例,不是通用美元费用上限,也不能保证超时请求免费。公开运行器沿用实测请求顺序,其本地检查和请求序列化另行做过验证。

python3 checkpoint_demo.py self-test
python3 checkpoint_demo.py prepare

第一条命令运行正向和反向检查。第二条生成 checkpoint.json 与 handoff.txt,已有同名文件时拒绝覆盖。数据为合成库存测试集,不是客户库存或商业建议:

SKU可用数量目标数量预期缺货量
DEMO-0017103
DEMO-00215120
DEMO-003044

任务要求逐行计算 max(target − available, 0),并明确禁止下单。零缺货行也必须保留:这里只列需要补货的商品仍是不完整答案。

交给新模型前,先保存证据

检查点记录目标、源数据行的哈希、一次已完成的读取操作、其真实结果和哈希、待办事项,以及禁止外部写入的要求。该记录由应用根据工具结果创建,不让模型为自己的操作作证。

{
  "schema_version": 1,
  "task_id": "synthetic-stock-20261110",
  "objective": "Report shortage=max(target-available,0) for every SKU. Do not place orders.",
  "completed": [{
    "operation_id": "read-stock-1",
    "operation": "read_stock",
    "status": "verified"
  }],
  "pending": ["Produce all three SKU shortage rows", "Pass independent validator"],
  "side_effects": []
}

为了便于阅读,此摘录省略了源哈希与结果行,生成的完整文件包含这些字段。执行时保留完整文件。哈希能发现与可信来源不一致的内容,但不能证明来源是谁写的、库存是否最新,或不可信检查点是否安全。生产环境还要保留权威输入版本与访问控制。

真实 Agent 应在工具执行完成的边界持久化检查点。同时保存用于会话配对的工具调用 ID,以及应用操作账本使用的独立、稳定操作 ID。二者用途不同。新模型可能为同一个逻辑操作生成另一个工具调用 ID,因此服务商生成的 ID 本身不是通用的重复操作防护。

实测的 Fable 工具往返

第一个请求使用 anthropic/claude-fable-5.2、Ofox 原生 Messages 端点、output_config.effort: "low"、max_tokens: 4096,只声明一个输入 schema 为空对象的 read_stock 工具。工具选择为 auto。

响应以 tool_use 结束,要求用 {} 调用 read_stock。运行器检查工具名和参数后,在匹配的 tool_use_id 下返回三行测试数据,并保留全部 assistant 内容。第二个 Fable 请求以 end_turn 结束,报告缺货量 3、0、4。

这一步原生续接很重要:检查点不能代替对损坏工具循环的修复。同模型请求若因 ID 不匹配失败,先修复工具结果配对。若错误涉及签名历史,再参考 thinking 签名诊断和 5.2 升级检查。

我们有意为这组极小算术测试使用 low effort,不能据此推断复杂长任务需要什么思考力度。此外,目录中供应商类型为 aws_bedrock,不能证明某条响应使用了哪个具体上游账户;本次记录的是 Ofox 原生端点和返回的模型 ID。

用明确的交接说明启动接收模型

生成的 handoff.txt 内容如下:

Resume this read-only task from verified application state.
Do not claim access to prior reasoning.
Do not call tools or place orders.
Return only a JSON object with key rows, containing every SKU
and integer shortage. No Markdown.
Checkpoint: [the complete generated checkpoint JSON]

从该文件创建接收请求。运行这个例子会消耗 API 额度,上面的本地准备命令则不会。

import json
from pathlib import Path

request = {
    "model": "anthropic/claude-opus-5.5",
    "max_tokens": 4096,
    "output_config": {"effort": "low"},
    "messages": [{"role": "user", "content": Path("handoff.txt").read_text()}],
}
Path("handoff-request.json").write_text(json.dumps(request))
curl --fail-with-body --max-time 180 \
  https://api.ofox.ai/anthropic/v1/messages \
  -H "x-api-key: $OFOX_API_KEY" \
  -H 'anthropic-version: 2023-06-01' \
  -H 'content-type: application/json' \
  --data-binary @handoff-request.json > handoff-response.json

此请求不含先前的 assistant 消息或带签名的 thinking 块。它恢复的是任务,不是同一个服务商会话,因此无需修改旧前缀就能切模型。更大的任务需要更丰富的证据包,包含来源位置和未解决问题,而不是篇幅更长却未经验证的叙述。

独立验证交付结果

只提取已完成的文本输出,将其解析为 JSON,并运行提供的验证器:

import json
from pathlib import Path

r = json.loads(Path("handoff-response.json").read_text())
if r.get("stop_reason") != "end_turn":
    raise SystemExit("Incomplete response; inspect before accepting.")
text = "\n".join(b["text"] for b in r.get("content", []) if b.get("type") == "text")
result = json.loads(text)  # Reject prose and Markdown instead of guessing.
Path("result.json").write_text(json.dumps(result, indent=2))
python3 checkpoint_demo.py validate result.json

真实 Opus 响应解析后为:

{"rows":[{"sku":"DEMO-001","shortage":3},{"sku":"DEMO-002","shortage":0},{"sku":"DEMO-003","shortage":4}]}

验证器通过了三行结果,缺货总量为 7。它检查 SKU 是否恰好覆盖全部输入、有无重复 ID、类型是否为整数,以及逐项重算的数量。字符串 "3"、布尔值、缺少或多出的 SKU、错误数量都会被拒绝。检查点验证器还会单独拒绝源/结果哈希变化、未经验证的读取操作或意外副作用。

对这个任务,确定性检查比询问模型“你确定答案完整吗”更有证明力。但范围也刻意保持很窄:脚本不能验证任意商业工作流,不读取实时库存,也不是生产事务系统。

本次运行证明了什么,又没证明什么

阶段实际结果输入/输出 tokens
Fable 5.2 请求读取工具HTTP 200、tool_use,工具正确且在允许列表中467 / 27
Fable 5.2 接收工具结果HTTP 200、end_turn,三项缺货量正确578 / 235
新 Opus 5.5 会话接收检查点HTTP 200、end_turn,最终 JSON 通过验证457 / 67

三个响应报告的缓存读取和缓存创建 tokens 都为零。按已核查的标准牌价计算,三次请求共 $0.022008,分别为 $0.004816、$0.014024、$0.003168。这是根据用量计算的费用,没有另外与账单对账。模型比较文章解释了费率,以及为什么不能凭小测试给模型排高低。

本次没有触发真实服务故障、测试跨账户思考重放、比较大量任务,也没有证明自动备用切换的可靠性。源数据是合成的,只读工具在本地执行,没有提交订单;模型响应本身来自真实 API。

11 月 10 日合成任务的真实 API 响应与交接验证

英文截图来自本地渲染的测试记录,数据取自真实 API 响应;不是 Ofox 产品界面截图。

写入操作中断,需要另一套处理

只读操作在明确数据时效与成本后,重复查询可能可以接受。写操作超时则留下更危险的问题:客户端失去响应前,外部系统是否已经完成动作?

生产操作账本应区分计划中、执行中、已确认、失败和不确定状态。超时后,在重试前使用稳定操作键向目标系统查询。如果目标支持幂等键,就使用它并保存响应;如果不支持,不能仅靠保存一个本地 JSON 文件实现“恰好执行一次”。

不要因为模型摘要说“已完成”就将不确定写入标为完成,也不要让接收模型为了“保险”再执行一次。应从已验证结果恢复,未解决操作进入对账流程。同样的方法可用于表单提交、工单创建、外发邮件和付款,但每个目标系统都需要自己的控制措施。

检查点应围绕交付物设计

研究报告应携带来源 URL、取得的证据、论断状态和未回答问题。表格任务应携带输入版本、行标识、转换规则和输出检查。代码改动应携带 commit 或 diff、真正运行过的测试、失败项与剩余工作。保存已完成工作的证据,而不只是一个进度百分比。

首次迁移从小范围开始:一个只读任务、一次模型切换、一组独立验收条件。理解失败情况后再扩大。API 指南覆盖初始连接,Ofox 原生 Messages和供应商路由说明接入方式。即使下一个请求成功,状态连续性仍是应用的责任。