开始前,会这些就够了
- 先读引用怎样支持答案
- 能读懂 Python 字典和简单条件判断
下午你问茶铺今天几点关门,助手刚成功查到“18 点”,便说这是最新信息。仔细一看,工具读的是上周生成的目录,今天的临时安排在另一份通知里。查询发生在刚才,只证明这次取回动作很新,没有证明被取回的事实也很新。
已有商户问答评测给出旧资料与今日工具结果冲突的题设;本篇继续追问,工具中的“今日”究竟凭什么成立。练习产物是一份证据准入清单,把时间条件写进程序。它不做用户记忆合并,也不实现 Redis 缓存一致性。
把例子带到自己的电脑上
下载完整练习包
需要 Python 3.10 或更新版本;制作时实测 3.14.0。使用标准库,不需要安装第三方包。下载 ZIP 后选择“全部提取”,进入能看到 README.md 的文件夹。
下载 ZIP · 5 个文件包里有什么
- evidence_freshness.py:完整示例
- README.md:环境、范围与独立练习
- observed.stdout.txt:制作时的观察记录
- verification.json:对应源码的验证范围
- SHA256SUMS.txt:文件内容摘要
在这个文件夹打开 PowerShell,逐行运行
python --version
python -B evidence_freshness.py解压后在含示例文件的文件夹打开 PowerShell,按命令运行。先预测结果,再与当次输出对照。 离线固定数据与应用规则,不代表真实模型、完整协议或服务联调。
找不到命令或文件时,先按 README 检查环境和所在目录。文中标出的阅读时间只估算文章篇幅,安装环境和动手练习可按自己的节奏进行。
一份资料至少可能带着三种时间
获取时间是应用什么时候拿到材料;来源核验时间是何时按可信流程确认过所用来源;事实适用时间是这句话描述哪一天或哪段时期。三个时间可以不同:今天下载的年度报告可以准确描述去年,却不能凭下载日期回答“此刻还有多少库存”。
还要区分资料本身更新与目录介绍更新。一本旧手册被重新登记进资料库,不代表正文已修订。W3C DCAT 3 分开描述资源修改、目录记录修改和数据覆盖时期,能帮助我们给字段起准确名字;本篇的字段名与准入算法是教学设计,并非 DCAT 的强制执行规则。
因此,先把用户问题中的时间写清。问“上周六几点关门”与“今天几点关门”,需要的事实区间不同。不能简单给所有旧资料扣分,否则历史问题最合适的原始记录反而会被清掉。
HTTP 说“新鲜”,回答就一定正确吗?
HTTP 缓存中的 freshness 处理的是存储响应何时可按缓存规则复用。RFC 9111 使用响应年龄与新鲜期比较;年龄会考虑生成或成功重新验证等信息,不只是你刚把数据收到本机的时刻。因此,“工具本次返回 200”不能被应用擅自翻译成“所有上游事实都实时”。
这层规则也不替应用判断资料内容是否准确。服务可能按约定缓存一份有效的旧年度报告,也可能返回尚未更新的业务表。缓存协议合规与回答所需的事实时效,应分别观察。本文不手写 HTTP 年龄计算器,避免拿简单减法冒充包含传输延迟的规范算法。
实务上,工具响应可以暴露业务允许的来源版本、数据日期和缓存提示,使调用方判断用途。没有这些字段时,应记录“来源时效未知”,而不是把缺失值补成当前时间。若系统没有可靠的核验动作,也就不应该自动产生“已核验”时间。
重新核验,也要说清核验了什么
HTTP ETag 是服务端给某个表示附上的不透明校验标识,不是能直接按大小排序的业务版本号。带条件的请求可能得到 304,让客户端复用已有表示。它能帮助判断该表示在协议条件下是否需要重新传输,不等于来源对现实世界的每条陈述都重新调查了一遍。
例如缓存中的“研究报告 2024 版”经重新验证仍未变化,你可以更新这份表示的核验记录,却不能顺手把报告事实日期改成今天。同样,业务数据版本 r9 与文档版本 r9 若来自不同来源,名称相同也不能证明内容一致。
遇到相互矛盾的证据,应保留来源、适用范围和版本,再按明确的业务优先规则处理。选择“最后收到的那条”只依赖网络顺序;选择“数字最大的版本”则假设了可能根本不存在的全局排序。本文示例不自动裁决多个来源之间的冲突。
先写本题规则,再写判断函数
本题约定:查询结果必须成功;获取时间和最近核验时间不能晚于当前时间;有效期终点可以在未来;材料给出的适用区间包含问题目标时刻;最近核验距当前时间必须小于五个时间单位。区间采用含起点、不含终点的写法,恰好到复查期限就需要重查。五只是方便观察的教学参数,不是推荐的生产刷新周期。
当期限缺失、已经超出区间或该重查时,程序输出不同原因。它不把这些情况统统叫“假消息”,也不自动把来源旧值替换为新值。能通过的结果叫 candidate,表示仅通过时间准入,还需要身份权限、内容支持和来源可靠性检查。
这组规则故意保守:缺失适用区间不等于永远有效,而是交给明确的补证流程。有些资料确实可以约定无期限,但应先为那类来源增加清晰规则,再扩展代码,不能偷偷把 None 当作无穷远。
同样刚取回,让六条资料走不同出口
运行前先预测:适用期已经结束、很久没核验、当前可用、用于历史问题、缺适用期和查询失败,应该分别去哪。例子把时钟固定为整数 100;历史问题的目标时刻为 70。没有网络、真实商户、模型调用,也没有声称测试过任何缓存服务。
读输出时注意两组对照。前两条都刚获取,却因为不同原因不能直接当当前依据;第四条材料覆盖过去,反而能通过历史问题的时间准入。末尾断言锁定到期边界、未知核验时间、未来时钟和错误区间,让规则不会随着运行日期变化。
展开收起代码 · 小实验
长行可在代码区域内左右滚动,按 Tab 可把焦点移入代码区。
"""Offline application policy, NOT an HTTP cache or a truth verifier."""
def decide(record, now, target_time, recheck_after):
# All times are integers in the same fictional time scale.
# target_time = when the question's fact should apply; now = evaluation time.
if record["status"] != "ok":
return "unavailable"
fetched = record["fetched_at"]
checked = record["checked_at"]
if fetched > now or (checked is not None and checked > now):
return "clock_error"
start, end = record["valid_from"], record["valid_until"]
if start is None or end is None:
return "unknown_period"
if start >= end:
return "invalid_period"
if not start <= target_time < end: # Our explicit half-open policy.
return "outside_period"
if checked is None:
return "needs_recheck"
if now - checked >= recheck_after: # Boundary itself requires rechecking.
return "needs_recheck"
return "candidate" # Still needs source, scope and claim-support checks.
base = {"status": "ok", "fetched_at": 100, "checked_at": 99,
"valid_from": 90, "valid_until": 110, "revision": "r1"}
cases = [
("fresh_fetch_old_period", {**base, "valid_until": 95}, 100, "outside_period"),
("fresh_fetch_old_check", {**base, "checked_at": 80}, 100, "needs_recheck"),
("current_candidate", base, 100, "candidate"),
("historical_question", {**base, "valid_from": 0, "valid_until": 90}, 70, "candidate"),
("missing_period", {**base, "valid_until": None}, 100, "unknown_period"),
("failed_tool", {**base, "status": "error"}, 100, "unavailable"),
]
for label, record, target, expected in cases:
actual = decide(record, now=100, target_time=target, recheck_after=5)
assert actual == expected, label
print(f"{label}: {actual}")
assert decide(base, 100, 110, 5) == "outside_period"
assert decide({**base, "checked_at": 95}, 100, 100, 5) == "needs_recheck"
assert decide({**base, "checked_at": None}, 100, 100, 5) == "needs_recheck"
assert decide({**base, "checked_at": 101}, 100, 100, 5) == "clock_error"
assert decide({**base, "valid_from": 110}, 100, 100, 5) == "invalid_period"
assert decide(base, 100, 90, 5) == "candidate"
print("PASS: 6 scenarios + 6 boundary assertions")
怎样运行:保存为 evidence_freshness.py,用 Python 3.10 或更新版本运行:python evidence_freshness.py。仅用标准库,不联网、不安装依赖。 2026-10-13 已用 3.14.0 执行这份示例并通过自带断言;下方为当次输出。离线固定数据与应用规则,不代表真实模型、完整协议或服务联调。
运行后对照结果
fresh_fetch_old_period: outside_period
fresh_fetch_old_check: needs_recheck
current_candidate: candidate
historical_question: candidate
missing_period: unknown_period
failed_tool: unavailable
PASS: 6 scenarios + 6 boundary assertions最终回答也应保留这层边界
通过时间准入后,回答仍应把具体结论挂到具体材料。例如可以说某份安排适用于哪个日期;核验失败时,只能说明目前无法确认。若用户需要立刻行动,应用应选择可用的核验渠道或明确停止,不能用流畅措辞把 unknown_period 包装成肯定答案。
证据在处理过程中也可能过期。检索开始时还在期限内,排队、长推理或等待人工确认之后已经超时,提交结论前就要按规则再检查。能否复用以及是否重新查来源,应由事实变化速度和业务容忍度决定,并把决定记录下来。
保存证据时,只记最终进入本次回答的来源标识、版本、目标日期、检查结果和必要的受控内容。检索候选列表不能替代实际上下文。资料后来更新后,旧回答记录仍应如实反映当时看到什么;需要修订时新增更正记录,别回写历史让旧回答看起来早就正确。
从规范走回自己的项目
先读 RFC 9111 的 Freshness 与 Calculating Age,标出哪些是缓存层证据;再读 RFC 9110 的 ETag 与 304,圈出它们验证的对象;最后看 DCAT 3 的修改日期和时间覆盖字段。给自己的工具响应设计字段时,每个时间都写明由谁生成、检查的是什么。
这份程序没有验证跨机器时钟同步、真正的 HTTP 缓存、来源数据更新或模型引用质量。跨系统时间还要处理时区与时钟误差,等待时长可参考墙上时钟与秒表;不要把本题统一整数时钟直接搬去比较不同机器的单调计时值。
记住这三点
- 刚取回的材料可能描述过去,获取时间不能替代事实适用时间。
- 缓存可复用、来源表示未变化、事实适合回答当前问题,是不同判断。
- 缺失时间应保持未知;通过时间准入还不能证明结论真实。
别把例子用过头
- 五个时间单位和半开区间是本题政策,不能说成 HTTP 或 DCAT 的通用要求。
- ETag 是不透明校验标识,不是可跨来源排序的版本号。
- 实验没有运行真实 Agent、HTTP 缓存、Redis 或外部数据源。
自己试一小步
一个活动助手要同时回答“明天安排”和“去年记录”,你会怎样给两类问题设计证据规则?
- 各写三份虚构材料,分别记录获取、来源核验和适用时间。
- 自行决定允许使用未知结束日期的条件,并说明依据与限制。
- 增加一个等待确认期间超过复查期限的场景,记录最终应补做的检查。
做到这些,就算完成
- 是否针对问题目标日期判断,而不是只看文件新旧?
- 是否保留查不到、核验失败和证据冲突各自的状态?
- 是否保存旧回答实际使用的版本,同时能给出后续更正?
查原始资料
想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。
- RFC 9111:HTTP 缓存新鲜度 (新标签页)↗
阅读 4.2 Freshness、4.2.1 Calculating Freshness Lifetime、4.2.3 Calculating Age;支持缓存年龄与新鲜期的语义。本文五单位复查规则不来自该规范。
- RFC 9110:ETag 与 304 (新标签页)↗
阅读 8.8.3 ETag、8.8.3.2 Comparison、15.4.5 304 Not Modified;支持不透明校验标识及条件请求复用表示的边界。
- DCAT 3:资料修改、目录修改与时间覆盖 (新标签页)↗
阅读 6.4.8、6.5.4、6.6.6,分别核对资源更新、目录记录更新和数据覆盖时期;不把这些元数据自动等同于事实正确性。