Grok Build 是如何工作的(三):compact 与会话减负
阅读顺序:四份历史数据 → 四类减负机制 → 摘要 compact 的 two-pass → compaction mode → 时间线与速查。
术语:
| 词 | 含义 |
|---|---|
| soft-trim | 超长 tool 正文保留文首 + 分隔标记 + 文尾,中间删除 |
| hard-clear | tool 正文整段换成固定占位句(如 [Tool result omitted — too old]) |
| 请求体 | 序列化后发往模型服务的 HTTP 载荷(含对话、tools、参数等)及其字节体积 |
| 请求副本 | 组装当次请求时,从内存对话历史 clone 出的条目列表;可只改这份 clone,不改内存真值 |
1. 四份与会话历史相关的数据
| 名称 | 是什么 | 用途与更新方式 |
|---|---|---|
| 内存对话历史 | ChatState 中的 conversation |
运行时真值。L2/L3 写入与读取。组装模型请求时从这里复制。可被摘要替换为更短的当前历史 |
chat_history |
磁盘上的对话列表文件 | 内存历史的镜像:追加则 persist;整表变化则 replace。无独立裁剪逻辑 |
updates.jsonl |
按时间追加的过程日志 | 与内存历史、chat_history 独立维护。供 replay、搜索索引等。内存历史变短或变摘要时,不要求本文件改写成同一列表 |
| 请求副本 | 当次调用用的条目 clone | 从内存历史复制;随本次请求结束丢弃。模型在当次调用中只看见经 backend 映射后的这份列表 |
chat_history 与内存历史:逻辑上同一份当前对话列表(磁盘 / 内存)。落盘可异步,崩溃瞬间磁盘可能略落后于内存。
updates.jsonl 与前两者:前两者是当前对话列表;updates.jsonl 是过程中按时间记过的更新。
本节只定义数据位置。对它们做 soft-trim、hard-clear、去图、摘要替换的规则见下一节。
2. 四类减负机制
| 机制 | 是否称 compact | 是否调摘要模型 | 改谁 |
|---|---|---|---|
| 请求副本 tool 裁剪 | 否 | 否 | 仅请求副本 |
| 内存 hard-clear | 否 | 否 | 内存历史,并同步 chat_history |
| 请求体体积控制(旧图) | 否 | 否 | 仅请求副本 |
| 摘要 compact | 是(/compact、auto-compact、超窗等) |
通常是 | 内存历史,并同步 chat_history |
请求副本 tool 裁剪
- 何时:每次组装请求时,若
total_tokens > context_window / 2。 - 做什么:在请求副本上按 tool 所属 turn 龄期(从尾向前数 User)处理 tool result。默认:最近 3 个 turn 不动;中等龄且正文超过约 4000 字 → soft-trim(文首/文尾各约 1500);龄期 ≥ 约 10 → hard-clear。
- 不做什么:不改内存历史、
chat_history、updates.jsonl。不调用摘要模型。
内存 hard-clear
- 何时:每次用户消息写入内存历史之后。与是否过半窗、是否 auto-compact 无关。
- 做什么:对龄期 ≥ 约 10 个真实 user turn 的 tool result,在内存历史上 hard-clear,并同步
chat_history。只 hard-clear,不做 soft-trim。合成 user(如 system-reminder)对龄期的影响由实现校正。 - 目的:限制长 session 进程内存。
- 不做什么:不改
updates.jsonl已写原文。不把整段对话换成摘要。
请求体体积控制(旧图)
- 何时:组装请求时,对话序列化体积接近代理/服务上限(实现上约 50MB 硬顶下留余量触发)。
- 做什么:在请求副本上移除较早 inline 图,换成说明文字。
- 不做什么:不改内存历史。与“token 过半窗才做 tool 裁剪”的条件相互独立。模型调用层若仍收到 413,还可能再剥图重试(发送路径,不改内存历史策略)。
摘要 compact
- 目的:用摘要替换内存历史中的当前对话列表,降低后续上下文占用。
- 触发(并列入口,共用内核):
- 用户
/compact - auto-compact:估算用量 ≥ 配置百分比(代码侧常见约 80%–85%,可配置、可按模型与托管覆盖)
- 超窗 / 溢出预检 / 部分换模型导致窗口变小等恢复路径
- 用户
- 成功后:内存历史变为摘要后结构,
chat_history同步。可写 checkpoint,供 rewind 跨压缩点。updates.jsonl不因此被改写成与内存历史同一列表。 - 与请求副本 tool 裁剪的阈值:tool 裁剪在 token 过半窗时作用于请求副本;auto-compact 使用单独的百分比阈值(常见约 80%–85%)。两套条件,分别触发。
3. 摘要 compact 内核:single-pass 与 two-pass
触发: /compact 或 auto-compact / 超窗等
\ /
\ /
compact 内核
│
two-pass 开启且 NOTE₁ 有效 → Pass 2
否则 → single-pass(一轮摘要)
│
写回内存历史(同步 chat_history)
│
按 compaction mode 附加回捞说明(可选)
切片与摘要的输入来自内存历史快照。请求副本与 updates.jsonl 全量不是该 split 的输入。
single-pass
一次调用摘要模型(或等价路径),把当前需压缩的历史压成摘要,写回内存历史。无前置缓存时走这条;two-pass 失败或关闭时也回退到这里。
two-pass
摘要 compact 内核内部的可选执行方式:用两轮摘要代替一轮。开关关闭则始终 single-pass。触发入口仍只有 /compact、auto-compact、超窗等。
Pass 1 在后台摘要历史前缀;Pass 2 在正式 compact 时用该前缀摘要加上较短尾部再摘要,从而缩短主路径上单次摘要的输入长度与等待时间。
| 步骤 | 名称 | 何时 | 输入 | 输出 | 是否改内存历史 |
|---|---|---|---|---|---|
| Pass 1 | prefire | 用量 ≥ auto-compact 阈值 − 提前量(默认提前约 10 个百分点);后台 | 内存历史快照按 token 重量切开后的前缀(默认约前 95%;不拆开 tool_call 与对应 tool_result) | NOTE₁:前缀摘要文本 + 前缀指纹 | 否 |
| Pass 2 | 正式 compact | 已进入 compact 内核,且 NOTE₁ 仍有效 | NOTE₁ + 仍较新的尾部(默认约后 5%) | 最终摘要,写回内存历史 | 是 |
NOTE₁:Pass 1 缓存的前缀摘要文本,附带前缀指纹。前缀因 edit / rewind / 分支等变化导致指纹不匹配时,NOTE₁ 作废,改走 single-pass。
Pass 1 其它退出:对话过短、切开结果空、摘要调用失败、空 NOTE₁、开关关闭 → 不产生可用缓存。
何时跑
- Pass 1:two-pass 开启,且用量 ≥ auto-compact 阈值 − 提前量。后台写 NOTE₁,不改内存历史。
- Pass 2:已进入 compact 内核,且 NOTE₁ 仍有效;否则 single-pass。
/compact与 auto-compact 都进该内核,故都能用 Pass 2。只有自动用量路径会启动 Pass 1。
4. compaction mode(摘要 compact 成功之后)
仅在摘要 compact 已成功写回内存历史与 chat_history 之后生效。决定摘要正文外是否附带“compact 前细节可从某路径读取”。不改变请求副本 tool 裁剪、内存 hard-clear、请求体去图的行为;也不取消“当前历史已被摘要替换”的结果。
| mode | 摘要外附加 | 回捞对象 |
|---|---|---|
summary |
无路径 | 仅摘要本身 |
transcript |
指向 updates.jsonl |
读过程日志拿 compact 前原文 |
segments |
指向 session 下 compaction/ |
读该次 compact 时另写的 markdown 分段(见下) |
segments 模式落盘内容
路径在 session 目录下的 compaction/(与 chat_history、updates.jsonl 并列)。
| 文件 | 内容 |
|---|---|
INDEX.md |
目录表。每成功 compact 一次追加一行:段号、文件名、turn 数、约略字节、关键词。供先扫目录再打开具体段 |
segment_NNN.md(如 segment_007.md) |
这一次 compact 从内存历史切出去、即将被摘要替换掉的那一段对话,渲染成可读 markdown |
单个 segment_NNN.md 大致结构:
- Segment metadata:段号、条目数、时间戳
- Turn statistics:角色计数、用过的 tool 名与次数、工具参数里出现过的路径、tool 错误数、末条 assistant 摘录等
- Summary:本次 compact 生成的摘要正文(与写回内存历史的摘要同次产物)
- 对话正文区(详略由
compaction_detail配置,默认 verbose):verbose:按条列出 Human / Assistant / Function 等,含正文、tool 名与参数、tool 返回全文(单文件有约 512KB 截断上限)balanced/minimal/none:参数与返回逐渐缩短,直至只有统计+摘要、无逐条正文
segments 回捞的是 compact 时专门导出的段落快照(统计 + 可选全文)。读的不是 updates.jsonl 事件流,也不是已经变成摘要后的 chat_history。
配置解析顺序通常为:环境变量 → 用户配置 → 托管 managed_config 的 [features].compaction_mode → 代码枚举缺省。
- 代码枚举缺省:
summary。 - 托管 features 可设为
segments或transcript。未设用户覆盖时,以托管与解析结果为准。
组装当次请求时仍只发送请求副本。updates.jsonl 或 segment_*.md 全文不会自动并进请求;需模型在后续 tool 轮次按摘要中的路径自行读取。
5. 同一次 turn 内的相对顺序
下列步骤不必每步都发生:
- L2 将用户消息写入内存历史 → 内存 hard-clear(可能)→ 同步
chat_history;事件可追加updates.jsonl。 - 主模型调用前:若 two-pass 开启且用量达到 prefire 水位 → 可后台 Pass 1。若达到 auto-compact 阈值(或 force 等)→ 摘要 compact(Pass 2 或 single-pass)→ 按 compaction mode 附加说明。
- 组装请求:内存历史 → 请求副本;体积过大 → 请求体体积控制去旧图;token 过半窗 → 请求副本 tool 裁剪;可选仅本请求的 memory reminder。
- 发出请求体;413 等可在发送路径再剥图。
- 调用失败且判定上下文超限,或工具输出使估算 token 顶破整窗(preflight)→ 可再做摘要 compact,然后重新组装请求副本再调用。
- 成功路径:结果写回内存历史(同步
chat_history)。
6. 机制对照(速查)
| 条件 | 行为 |
|---|---|
| token > 窗 / 2 | 请求副本 tool 裁剪 |
| 每次 push user,tool 够老 | 内存 hard-clear(同步 chat_history) |
| 请求体字节接近上限 | 请求副本上去掉较早 inline 图 |
用量 ≥ auto 阈值 / 超窗 / /compact |
摘要 compact 替换内存历史 |
| two-pass 开且近阈值 | 后台 Pass 1 → NOTE₁ |
| 已进 compact 内核且 NOTE₁ 有效 | Pass 2;否则 single-pass |
| 摘要 compact 成功后的 mode | 是否写回捞路径 / 是否写 compaction/ |
| 对象 | 请求副本 tool 裁剪 | 内存 hard-clear | 请求体去图 | 摘要 compact |
|---|---|---|---|---|
| 内存历史 | 不改 | hard-clear tool | 不改 | 换成摘要 |
chat_history |
不改 | 随内存历史 | 不改 | 随内存历史 |
updates.jsonl |
不改 | 不改已写正文 | 不改 | 不整表替换;transcript 可指向它 |
| 请求副本 | soft-trim / hard-clear | 起点可能已是 hard-clear 后内容 | 去旧图 | 不单独跑;clone 自 compact 后的内存历史 |

作者的邮箱:tokamak9000@163.com。如有问题,欢迎讨论

浙公网安备 33010602011771号