跳到正文

动手:把需要的历史选进本轮输入 ​

返回学习路线 · 上下文与记忆的机制解释 · 第一条本地练习

保存的 5 条聊天记录,在输入预算变小时就装不下了。这一课比较两种处理方法:扩大输入预算,或在相同预算下优先选入检索命中的记录。对照每次实际装入的内容,看旧约束怎样影响当前判断。

记录采用虚构的草稿讨论:最早一条说“本轮只允许生成预览稿;没有用户批准,不得对外发布”,后来又讨论目录、来源、排版和学习路线。程序读取这些材料后,判断当前是否有发布许可。

这是可选的机制小实验。运行前请确认 Python 3.10+;只用标准库,无需 API Key、不联网、不发布内容。judge_visible() 是识别固定短语的规则程序,作用是让你直接看见输入选择的效果。

先预测,再跑三轮 ​

从仓库根目录执行,使用 Python 3.10 或更新版本,无需安装第三方包:

bash
python3 examples/context-budget/demo.py --mode full
python3 examples/context-budget/demo.py --mode recent
python3 examples/context-budget/demo.py --mode retrieve
python3 -m unittest discover -s examples/context-budget -p 'test_*.py'

运行前先写下你预计各模式会选中的记录 ID;运行后对照 stored_records 和 visible_ids。前者是保存的材料,后者是本轮装入的材料。

本例按字符数设置预算,把规则、问题、记录 ID、正文、分隔符和换行一起计入。它用来演示长度限制,实际模型的 token 计数另有规则;计数细节见末节。

装配器先保留必要规则和问题,再逐条尝试完整记录;默认从最新的 r5 开始。某条装不下就跳过,继续尝试下一条,不把一条约束截成半句。检索模式把命中的旧约束 r1 提到候选顺序最前面,再用同一个预算重新装配。

模式总预算/实际占用本轮 visible_ids规则判读器结果
full1200/248 字符r5, r4, r3, r2, r1do_not_publish:看见明确的不得发布约束。
recent220/212 字符r5, r4, r3, r2insufficient_evidence:没有看见发布许可依据。
retrieve220/217 字符r1, r5, r4, r3do_not_publish:旧约束重新变得可见,r2 被挤出。

先看 full:预算足够,5 条记录都装入,程序读到最早的约束。recent 保留较新的 4 条,发布依据不足,因而返回 insufficient_evidence。retrieve 在相同的 220 字符预算内优先装入旧约束,恢复了对它的读取;代价是挤出 r2。这就是输入选择的取舍。

看清四份信息各归谁 ​

保存来源经选择或检索后进入本轮输入

查看原尺寸 SVG(可缩放)

概念图左边是保存或可查询的材料,中间负责选择,右边是一次请求的实际输入。本实验走其中的固定历史 → 选择/字面检索 → 本轮上下文路径。图的完整文字说明可单独阅读。

输出按四个位置组织:

  • 保存来源 stored_records:本次进程中保存的完整原记录,供读者对照。
  • 本轮输入 context 与 visible_ids:实际交给判读器的字符串和入选记录 ID。
  • 判读结果 answer:规则程序依据这份输入形成的判断。
  • 事后检查 audit:回答之后再对照完整历史,检查旧约束的保存和装入情况。

完整 JSON 是给读者的对照材料。判读器只收到 context;stored_records 和 audit 属于其他位置。顺着这三个字段查看,就能追踪一条信息从保存到使用的过程。

例如 recent 的最后一个选择事件为:

json
{
  "event": "select_record",
  "id": "r1",
  "characters": 36,
  "remaining_before": 8,
  "selected": false,
  "reason": "whole_record_does_not_fit"
}

这条记录表示一次选择:剩余 8 字符,完整的 r1 要 36 字符,所以本轮跳过它。它仍保存在 stored_records。audit 也分别写出 critical_record_stored=true 和 critical_record_visible=false,清楚显示保存与装入的位置。

沿代码找到四个函数(可选) ​

打开 demo.py,按四个函数阅读即可:

  1. retrieve() 在保存来源里查找字面字符串“用户批准”,返回命中 ID。查询词在 run() 中固定;单独改 QUESTION 时查询词保持原样。这是字面检索,改词或改来源措辞会影响命中。
  2. assemble() 先检查必要输入能否放下,再按候选顺序装完整记录。每次写下候选成本、剩余额度和取舍;预算紧张时,重新检索会改变谁被选中,而不会扩大窗口。
  3. judge_visible(context) 只收到装配后的字符串,没有历史、检索 ID 或 audit。它只识别固定的禁止发布短语;空输入也不会暗中去全局历史找答案。
  4. run() 得到回答后,再用完整记录生成 audit。检查端使用相同固定短语,并与判读器的输入分开;它用于对照记录的保存和装入情况。

来源仍在时,宿主可以重新查询、调整顺序,再生成下一份输入。这条思路也能帮助你阅读 Pi 的上下文装配、Mem0 的检索接入和 Letta 的可编辑记忆;具体实现见各项目章节。

改一个参数,用轨迹验收 ​

先预测把新近选择的预算从 220 调为 248 会发生什么,再只改命令参数:

bash
python3 examples/context-budget/demo.py --mode recent --budget 248

在输出里标出四项:5 条记录仍保存、r1 已装入、总占用为 248、回答为 do_not_publish。这四项连起来,就说明本模拟器怎样通过扩大预算恢复约束。

再保持检索开启,把预算缩为 91:

bash
python3 examples/context-budget/demo.py --mode retrieve --budget 91

这次查中了 r1,但必要输入先占 56 字符,剩余 35 字符放不下 36 字符的记录。装配器继续尝试,最后选中恰好 35 字符的 r3。因此 visible_ids=["r3"]、总占用 91,critical_record_visible=false。检索找到候选后,装配器仍要决定它能否进入输入。

最后试一个连必要规则与问题也装不下的预算:

bash
python3 examples/context-budget/demo.py --mode recent --budget 10

输出 budget_too_small,required_characters=56,context=null、answer=null;CLI 退出码为 2,表示预期的输入拒绝。程序不会静默截断问题,也不调用判读器。已有测试同时覆盖正常选择、必要输入不足、旧约束遗漏、同预算重新检索、检索命中仍放不下、整条记录的边界,以及判读器不能偷读历史。

比较三次修改中的预算、命中记录、装入记录和判读结果。留意 r1:有时因预算不足被跳过,有时检索命中了也放不下,有时通过扩大预算重新进入输入。

计数与模拟范围 ​

字符数使用 Python len(str) 的 Unicode 码点数:例如 e 加组合重音可能显示成一个字形,却占两个码点。它区别于 token 数和 UTF-8 字节数,也没有计入真实模型的输出、图像或工具 schema。中文和英文的 tokenizer 成本不同,本例数字只能用于本实验。

full 只因默认预算够大而装入全部记录,缩小预算仍会遗漏。recent 在依据不足时保持保守,这是规则程序的选定行为。本例的来源保存在进程内,检索是字面匹配,没有压缩摘要、长期记忆产品或磁盘持久化。

接真实模型时,可继续检查这轮请求输入、模型回答与任务结果。输入包含一条材料以后,是否理解并正确使用仍需单独检查。tokenizer、真实推理、记忆过期、冲突、权限、持久恢复和生产检索质量均不在本实验的已测范围内。

图的文字说明 · context-vs-memory ​

读书会的报名说明保存在文件中。助手回答报名要求时,先找到这份说明,读出相关内容,再交给模型。图把这段过程画成三部分:左边放着可用资料,中间挑选和读取,右边是模型这次实际收到的输入。

左边的四种来源各有用途:

  • transcript(会话记录)保存聊天和操作历史。
  • summary(压缩摘要)用较短的说明接续过去的工作。
  • memory(长期记忆)保留跨任务使用的事实、偏好或状态。
  • 外部资料和知识库提供文件、数据库或网页中的内容。需要时通过检索(retrieval)找到相关材料。

图中的“整理上下文”,就是从这些来源选取所需内容,再加上指令和新的输入,整理成一次模型请求。挑哪些历史消息、用哪份摘要、检索哪条记忆或文件,都由具体系统安排,同时要留意这次输入能容纳多少内容。

右边的“本轮模型输入”是整理后的结果。比如工具只返回了时间和地点,报名说明虽然仍保存在文件里,模型这次还缺这份材料,需要继续读取。

虚线列出可能使用的读取路径;具体系统可能只采用其中几种。Pi 的例子及固定源码位置见专题正文。


在线预览稿:书稿仍在校稿,系统篇以文内固定源码版本为准;静态阅读不等于运行验收。发现错误或有改进建议?按本章填写邮件,或查看反馈说明。

书稿提交:33f7dcdf7ac3。