上下文 · 占用 & 压缩

占用与压缩

写给实现占用圆环、/context 面板和压缩管道的人。圆环、面板、压缩报告读同一份「模型下一轮实际读到的视图」。压缩不移动 HEAD,所以「HEAD 没变」不能当成「图没变」。

读者:实现 斜杠 /compact 已能发出去 闲时自动压:产品未定,不做

范围

做

一根占用尺子;无效压缩不再装成功;圆环接入进行中;面板可以立刻压;四种系统行自动压和手动共用。

不做

不改 insert_summary_node 的不删旧轮设计。不做闲时自动压。不改 DAG 渲染管线。snip 仍只砍这一次要发出去的 history,不写回 DAG。

01 · 一根尺子

模型下一轮读到的是 rendered_history:当前有效摘要 + 没被盖住的尾巴。圆环、面板、压缩报告都按这张视图计。

计数入口

session_stats.compute_breakdown 只吃 rendered_history。Messages 按折叠后计,带着 thinking / tool 块。这就是圆环和面板的分项。

压缩报告

tokens_before / tokens_after 走同一入口的总量(estimate_total_used),不再用只算可见正文的 estimate_history_tokens。

服务端缓存

压缩不移动 HEAD。占用缓存不能只看 HEAD。图变了(压缩落地)必须重算,不能把压缩前的 measured / 分类快照钉住。

已压缩会话再 /compact 时,报告可以是 40.7k,面板却仍显示 Messages 125k——那是缓存把折叠前的实测钉住了,不是摘要没写入。

谁用哪张视图

路径视图规则
session_stats.compute_breakdown rendered_history 唯一占用尺子。圆环和面板都从这里来。
engine.compact 同一把尺子的总量 压缩前后各估一次,写入 compaction_finished。
refresh_context_stats 重算 breakdown + 总量 压缩成功或手动压结束都要走。basis 翻成 estimated。
GET /api/sessions/{id}/context 当前占用快照 只在「图没变」时复用快照。压缩算图变,即使 HEAD 没动。
session_context_stats 的 measured 复用 上一次真实请求 只在 HEAD 相同且占用代数未变时保留。压缩会加一代。
_get_messages / get_branch 未折叠枝 聊天记录缓存,不是占用。占用路径禁止走这里。

02 · 压缩什么时候算做成了

一次会话最多一条 active summary。再压是替换,不是叠。DAG 只追加,不删旧节点,HEAD 不动。

prepare
按 rendered 拼 prompt,算 budget_pct
→
≥ 80% 则 compact
LLM 写摘要,追加 summary 节点
→
snip 兜底
摘要仍超才砍这一次要发出去的 history
→
再 prepare
这次请求读 summary + 尾巴

四种结果

结果条件事件
开始 已经决定要打摘要 LLM compaction_started
成功 摘要写入,summarised_count > 0 compaction_finished(无 no_op)
无可压 rendered 已 ≤ keep 尾巴,或 cut_idx ≤ 0 / 盖住 0 条 / insert 返回 None compaction_finished 带 no_op: true。调 LLM 之前能看出来的,不发 started、不烧摘要。
失败 摘要调用或管道异常 compaction_failed
触发仍是三条:手动 /compact 或面板按钮;发请求前 budget ≥ 80%;413 / prompt_too_long 抢救。圆环按占用比例着色,不另发建议态。

03 · 圆环参与生命周期

平常

中性底环,进度弧按占用比例着色。点开面板。数字来自同一把尺子。

正在压

compaction_started 到 finished / failed:圆环忙碌态。面板按钮禁用。

面板底部有「Compact now / 立即压缩」。点击发 {action:"compact", session_id},和斜杠 /compact 同一条 WS。没有 session 或正在压时禁用。

04 · 四种系统行

自动压和手动压走 chat-handlers.ts 同一分支。文案双语。

事件英文中文
started 正在压缩上下文… 正在压缩上下文…
finished 上下文已压缩:覆盖 N 条较早消息,X → Y token 上下文已压缩:盖住 N 条旧消息,X → Y tokens
failed 上下文压缩失败[:错误] 上下文压缩失败[: error]
finished + no_op 上下文已压缩,没有可继续合并的较早消息。 当前上下文已是压缩后状态,没有可压缩的旧消息

验收

  1. 压缩成功后,圆环和面板总量明显回落,并与 compaction_finished.tokens_after 同口径。
  2. 已压缩会话再压:不烧摘要 LLM,系统行是「无可压」,不是「盖住 0 条,X → X」。
  3. 低占用和高占用都没有建议外框;进度弧仍按比例着色。点「立即压缩」能发出 compact。
  4. 压缩进行中圆环忙碌,按钮禁用;结束回到平常态。

文件

openprogram/context/ ├── session_stats.py 占用尺子(rendered_history) ├── engine.py compact / no_op / tokens_before|after ├── summarize.py find_cut_index;调 LLM 前可短路 └── persistence.py rendered_history;insert 不删旧轮 apps/server/openprogram_server/ ├── server.py 占用代数、refresh、measured 复用 └── _webui/ ├── ws_actions/chat.py 手动 compact 结束后 refresh ├── _execute/chat.py 自动压结束后 refresh └── routes/tree.py GET /context apps/web/ ├── components/chat/context-badge.tsx ├── components/chat/context-breakdown-panel.tsx └── lib/runtime-bridge/chat-handlers.ts

本页路径:docs/reference/design/context/occupancy-status.html

实现状态

正文是目标设计。落地进度: