把章节接成一个可检查交付
本章不再增加新概念,而把前七章变成读者能从新目录运行、修改、解释的项目。入门边界是能运行 Python、读 JSON 和字典;不要求购买模型服务或下载权重。完成能力是:给定一组带版本资料与任务,导入事件、恢复状态、构造日期视图、检索、压缩、实际分词预算、生成带引用结果、验证支持、保存回执,并通过更新/撤销后的同一流程复验。它是受控字段资料编译器,不是开放域聊天机器人。
下载包中 context.py 是机制实现,run.py 是统一入口,tests.py 是验收,vendor/tokenizer.py 与 data/tokenizer.json 是随包分词器实现和冻结表,data/docs、events.json、tasks.jsonl 是原创资料与固定任务。evidence/final 是最终实验,development 系列只是历史。学生执行不读取网页源码或任何兄弟课程,作者章节审计也不属于运行依赖。学习网页是讲解载体,项目 ZIP 是可独立执行的产物,两者用清单关联而不互相偷偷依赖。
调用顺序从明确契约开始。query 提供问题,fields 指明要提取的字段,limit 和 reserve 给容量,as_of 决定事件前缀,at 决定业务日期,scope 决定记忆范围。应用 validate_task 后回放事件,派生 temporal_view,再得到 units 与压缩检查;rank 自动算字面分数,pack 用完整 wire 计数;answer 只发可见且有效 claim,并经全库同字段冲突门与实际输出计数。最终保存输入、输出、token_ids 和所有中间身份。每一步的输出都是下一步可审计的输入。
完整算例一:新目录运行到具体引用
先使用一个没有 memory.sqlite 的输出目录执行 demo。它从真实 Markdown 文件读取,而不是直接导入一组人工答案;每次 import 计算源文件 SHA,逐事件写入 SQLite。十八任务分别回放指定前缀,主配置 time-current 使用前十二事件和固定14日日期,输出 schedule@2:3 的时间。这个引用可回到 schedule-v2.md 第三行复核。正文开销、JSON 结构、出处和特殊标记都已进入实际编码,不拿人工写的“该段30token”当成本。
实际结果中的时间题输入958、输出89,预留240、容量1500。先检查 ,说明准入按最大允许输出预留成立;再检查 ,得实际总 。这是一条精确容量推导,与是否引用正确分开。支持检查再按当前源版本、SHA、第三行逐字内容和可见集合验证。读者应在报告里分别看到容量真、支持真和覆盖一,不能只看“answered”这个状态。
python3 tests.py
python3 run.py demo --output learner-run
python3 run.py import --db learner-memory.sqlite
python3 run.py query --db learner-memory.sqlite --task-id time-current
python3 run.py query --db learner-memory.sqlite --task-id conflicting-time
python3 run.py replay --db learner-memory.sqlite --as-of 6读自己的任务,而不是只能点固定演示
query 命令同时支持 task-id 与 task-file,二者互斥。最小自定义文件需 query、fields、limit、reserve;可以明确给 as_of=12 来查看未冲突通知。query 必须非空、最多4096字符,fields 非空且字段名无重复,预算是真整数而非 True/False。日期要能解析为 ISO 年月日,未知字段被拒绝,防止用户以为某个拼错参数已经生效。任务字段和业务数据分开,外部 quote 不会被当成任务参数覆盖。
direct CLI 遇到 schema 或数据库错误会显示异常,不伪装成正常答复;evaluate 只把 BudgetError 转成对应任务状态。开发者若把它包装成网页 API,应将 ContractError 与内部错误分成稳定响应,并添加认证、并发与输入大小控制;当前课程没有提供这种服务器,也不让“本地命令行能运行”替代真实服务测试。读者首先应能解释本地结果,再决定是否引入外部模型或网络。
import json,pathlib,tempfile,subprocess,sys
from run import setup,ROOT
with tempfile.TemporaryDirectory() as d:
d=pathlib.Path(d);db=d/'m.sqlite';repo=setup(db);repo.db.close()
task={'query':'活动地点','fields':['活动地点'],'limit':1500,'reserve':240,'as_of':12}
f=d/'task.json';f.write_text(json.dumps(task,ensure_ascii=False),encoding='utf-8')
result=subprocess.run([sys.executable,str(ROOT/'run.py'),'query','--db',str(db),'--task-file',str(f)],capture_output=True,text=True,check=True)
row=json.loads(result.stdout)
assert row['compiled']['result']['coverage']==1
assert row['compiled']['result']['claims'][0]['id']=='location@2:3'
print('custom task CLI checked')完整算例二:一次批准、更新、撤销的闭环
初始完整库里表达偏好已撤销。读者在临时库追加 version=3、scope=club、approved=true 的记忆“先列证据,再解释预算”,关闭连接再重新打开。查询该 key 时 units 仅暴露当前范围内这条第三版记忆;它进入实际 wire、被抽取、通过 source 与 visible 检查,输出引用 memory/club/表达偏好@3。不是程序打印“记住了”就算持久化,必须重开数据库从日志恢复并查出。
随后追加 revoke_memory 指定相同 key、scope 与 version=3,再次查询应 abstain。旧v1与v2不会复活,历史事件仍可通过明确 as_of 查看。整个闭环包含批准意图、实际状态写入、重开回放、预算构建、来源验证与撤销后复测;只展示一个 Python 字典内容无法证明这些环节。这个程序也不把回答自动写成新记忆,避免模型从自己未经支持的文本制造“事实”再用它给自己引用。
import pathlib,tempfile
from context import Repository,compile_context
from run import setup,load_tokenizer
t={'query':'表达偏好','fields':['表达偏好'],'limit':1500,'reserve':240}
with tempfile.TemporaryDirectory() as d:
db=pathlib.Path(d)/'m.sqlite';r=setup(db)
r.append({'type':'memory','key':'表达偏好','value':'先列证据,再解释预算。','scope':'club','version':3,'approved':True});r.db.close()
r=Repository(db);a=compile_context(r.replay(),t,load_tokenizer())
assert a['result']['claims'][0]['id']=='memory/club/表达偏好@3'
r.append({'type':'revoke_memory','key':'表达偏好','scope':'club','version':3})
assert compile_context(r.replay(),t,load_tokenizer())['result']['status']=='abstain'
r.db.close()
print('persist, reopen, cite, revoke, abstain')对照一条失败的修复流程
先在 final/evaluation.json 找到 head、compressed=true、task=project 的记录。固定所有源文件,改选择策略为 rank,只重跑同一任务:若目标行进入 selected 并恢复 coverage=1,可以把这次差异归因到已声明的选择规则,而不是“换了更聪明的模型”。再恢复 head 保持失败,作为负对照;不要把失败任务从任务表删除后宣布所有配置满分。完整十八题仍应重跑,确认局部修复没有在过期、冲突和范围题上回归。
若想修改资料本身,应导入新版本而非在 SQLite 里原地改 text。事件摘要绑定旧原文,直接替换会破坏链或让来源摘要不匹配。原 Markdown 文件改变但没有重新导入,也不会自动修改已存资料:这是快照外部输入的明确边界。复现时可新建库并导入新清单,保存新代码/数据摘要与逐题报告;旧实验仍留在旧目录,不能把新资料和旧模型/选择结果拼成“同一冻结成绩”。
何时才算这门课完成
先检查二十二项测试,包含非单调 BPE 前缀、Unicode切片、源行与可见支持、篡改摘要、历史撤销、过期、未来生效、冲突、schema及输出预留。再运行 demo 得到十八主任务和一百零八对照,检查 head 的既知失败仍保留、删除证据反事实仍1到0、截断实验仍三分之一。最后从新解压包运行同样验收,不借作者工作区的模块。源码正确与打包独立性是两个不同检查,学生缺一个文件就无法复现。
交付说明还应列出未验证项:远程 LLM NOT RUN、语义向量检索 NOT IMPLEMENTED、通用语义压缩未实现、真实来源认证未实现、多进程并发和断电故障未测。全库冲突门是精确字段文本比较,不推理任意逻辑矛盾;日期元数据由作者提供,不从“本周五”自动推算。完整性不是把系统说成无所不能,而是读者能从输入一路重建结论,并准确指出每条结论依赖哪条假设。
这一最终项目把上下文工程从措辞建议变成可检查的对象:允许状态决定资料域,实际编码决定容量,来源与可见交集决定可发声明,事件日志决定历史,固定任务决定当前验收结论。课程完成的标准是能修改其中一个环节并预测哪个正负对照应变化,再用新的逐题证据检验预测;不是再增加十条口号或把一个演示回答写得更华丽。
文件清单与行为复现的不同证据
最后的project-receipt绑定当前项目源码、数据、冻结输出与章节,每个条目用实际文件SHA计算。清单能检验下载后文件是否与已审计构建一致,但不能单凭摘要证明程序正确。行为复现仍要从干净目录运行测试与demo。反过来,行为输出相同也不代表所有文件都没变:不同注释或重构可能产生同样结果。课程因此同时保存来源清单和运行结果,不把二者混成一个“完全一样”的含糊断言。
修改实验应只变化一个明确环节。比如将rank的k常数从60改为30,保留资料、tokenizer与容量,再预测名次差距和selected是否可能改变;若只改常数却任务结果无变化,也应报告这个负结果,而不是继续换很多项直到找到一个成功案例。将所有修改累积后回头说某一项提高效果,会失去因果定位。小项目的优势就是成本低,可以保留每个候选的真实报告。
读者最终应能独立回答三个验收问题:某条回答具体源于哪一份文件哪一行;这一行为何在该日期与版本前缀可用;完整输入与实际输出各占多少冻结词元。如果任何答案只能诉诸“模型应该知道”,说明这条受控管线的链路还未闭合。能回答它们并不使系统自动成为通用助手,却给后续引入更强模型提供一组稳定接口与可反驳假设,避免效果只停留在一次漂亮演示。
TRY IT YOURSELF
基础:两道容量检查
输入958、预留240、实际输出89、容量1500,写出准入与实际总量检查。
提示 1
提示 2
对照参考解答
TRY IT YOURSELF
进阶:持久化证据
打印“已记住”后程序结束,为什么不足?
提示 1
提示 2
对照参考解答
TRY IT YOURSELF
挑战:项目独立性
学生ZIP里tests导入工作区另一课程的tokenizer,作者机器通过,合格吗?