学习 / 实践

Java 会话历史与请求窗口:裁掉的旧事实还能回来吗?

AI 应用 / 构建 · 工程案例

通过裁剪、提交、重启和扩窗的连续实验,分离可回看的完整历史与一次模型请求的上下文。

进阶 · 9 分钟 · 更新于 2026-10-07 · 步骤 11 / 17

这篇之后,已经有了哪些结果?

保留本文发表时的结论与下一步设想;以下是已发表的后续内容,不代表所有局限都已解决。

继续探索

继续探索

  1. 工程案例

    Java 对话历史落盘:程序崩溃后,半轮记录怎么办?

    校验追加日志、142种截断前缀和三个真实异常退出子进程,明确本地提交与远端幂等的边界。

    9 分钟已发布

浏览统计暂不可用

本文目录 7
跳到正文 ↓

固定版本代码 · 原始证据 · 实验档案

事务式会话历史已经实现完整轮裁剪、UTF-8请求预算和失败回滚。这篇不重新发明这些规则,而是追问一个产品问题:当裁剪后的候选历史被提交后,用户还能回看被裁掉的旧轮吗?

旧方案把有界候选作为下一轮历史,适合只需要短期会话窗口的应用。本篇选择另一种语义:追加日志保存完整已确认问答,ContextBudget每次只生成请求投影。“本次不发送”与“从记录中删除”不再是同一个动作。 这不是后篇修复前篇,而是可回看需求改变了存储边界。

本篇实现一个可以直接阅读的 ContextBudget:保存完整历史,只为本次请求选取连续的最近若干整轮。 它约束的是序列化后 UTF-8 字节,不宣称知道 Agnes 的 Token 数。本轮没有调用模型;模型字段预留 agnes-3.0-flash。

从一个失败的裁剪方式开始

保存:  U1 A1 | U2 A2 | U3 A3
当前:  U4
逐消息:      A2 | U3 A3 | U4  ← A2失去所属问题
整轮:           U3 A3 | U4
请求:  SYSTEM + 选中的完整轮 + 当前问题

历史是一份可回看的记录,请求上下文则是针对当前任务的投影。两者不能共用一个会被 remove 操作破坏的列表。当前问题与系统约束是必需输入:连它们都放不下时应明确拒绝,而不是悄悄切掉用户的问题或安全约束。

预算必须量最终发出的东西

配套实现把 model 和 messages 一起构造为 Gson JSON,再编码为 UTF-8。中文常常占多个字节,换行和双引号还涉及 JSON 转义,Java String.length() 计数的是 UTF-16 单元,不能代替传输体积。

核心选择器只有一个方向:从完整历史开始,依次丢弃最早一轮,找到第一个可放下的连续后缀。这样保留顺序,且不会为了塞入较短旧轮而跳过较新的大轮。

for (int start = 0; start <= history.size(); start++) {
    byte[] payload = request(system,
        history.subList(start, history.size()), current);
    if (payload.length <= maxBytes) {
        return new Selection(payload, history.size() - start, start);
    }
}
throw new IllegalArgumentException("mandatory input exceeds budget");

这里仍是教学规模算法:多次序列化会重复工作。长历史可以维护每轮的编码贡献或二分搜索,但必须包含 JSON 分隔符及框架字段,不能拿“每条文字的字节数之和”冒充完整请求长度。本轮优先把边界写清楚,没有测量这些优化。

实际得到什么

固定材料包含英文、中文、引号、换行和 Emoji;原始用例在 Suite.java,结果在 results.json。

请求预算(字节) 保留历史轮 丢弃历史轮 最终请求字节
140 0 2 140
235 1 1 235
308 2 0 308
408 2 0 308

139 字节的预算被拒绝,因为必需输入已经需要140字节。原历史仍是两轮。这些数字属于固定序列化样本,不是平台上下文容量,也不是建议给所有应用设置308字节。

连续实验:裁剪后提交,再重启扩窗

ProjectionScenario.java把两个组件接起来。首先持久保存两轮:第一轮给出虚构项目代号ORCHID-17,第二轮说明Java 8。随后把请求预算缩到只够最近一轮,并用固定本地回答提交第三轮。关闭日志、重新打开,再扩大请求预算。

阶段 实际请求 / 保存结果 能说明什么
缩小窗口 264字节,只带最近1轮,没有ORCHID-17 旧事实确实没有被发送
提交固定回答 磁盘保留完整3轮 不用裁剪结果覆盖完整记录
重开并扩窗 480字节,带3轮,ORCHID-17重新出现 早期事实能从保存记录重新构造
必需输入超限 拒绝请求,日志字节前后相同 失败的投影不污染已确认历史

原始projection.json与conversation.log见集成实验记录。这是本地固定材料,只证明请求数据恢复,不证明模型在缺少旧事实的那轮仍能回答项目代号。

完整日志 U1 A1 | U2 A2
          ↓ 投影(不改日志)
本次请求 SYSTEM | U2 A2 | U3
          ↓ 完整回答提交
完整日志 U1 A1 | U2 A2 | U3 A3
          ↓ 重开 + 扩窗
下次请求 SYSTEM | U1 A1 | U2 A2 | U3 A3 | U4

这个拆分也有代价:完整日志占磁盘,且“用户删除历史”必须删除保存记录,不能仅把它藏出请求。示例限4MiB并拒绝超限;压缩、保留期限、用户删除与跨设备同步都尚未实现,不称为完整助手产品。

首次集成验证曾在Windows持有独占文件锁时另开读取句柄,核对程序因此失败。修正为关闭拥有者后核对文件字节,再重新打开继续验证,未通过绕开锁改变日志实现。

产品取舍:少传不等于少保存

最近轮优先适合普通聊天,但未必保住用户早期给出的重要事实。固定系统约束、长期记忆和检索证据应有独立来源与预算;本例没有自动摘要,也没有实验支持“裁剪后模型仍记得关键事实”。

如果服务提供 Tokenizer 或明确的 Token 计数接口,应另做模型级检查,并为输出预留空间。传输预算解决请求大小,Token 预算解决上下文窗口,不能互相代替。

动手检验

把第二轮改成长段中文,并把预算设成“必需输入加第一轮刚好能放下”的大小。预期:最新一轮放不下时,选择器会最终只保留必需输入,不回头拼接第一轮。然后把当前问题改长,观察必需输入超限的显式失败。

排错时先打印 payload.length 和保留/丢弃轮数,别在正式日志输出完整聊天或 API Key。若顺序出错,检查是否把答案作为独立消息裁剪;若字节不符,检查是否测量了实际 JSON 编码。

本篇是请求构造组件,尚无登录、跨设备记忆或线上质量结论。下一篇讨论预算之外的另一个边界:一次请求最多能等多久。

从干净目录复现

需要 Java 8、Python 3、Gson 2.10.1。请将两个路径替换为自己的 JDK 和 jar;既有工程可复用 Maven 缓存。

git clone https://github.com/VirtualSelect/hohoo-ai-lab.git hohoo-ai-lab-study
cd hohoo-ai-lab-study
git checkout 02a2660e22c969e70e3a6f095003614a7dfa448e
python demos/11-bounded-assistant/run.py --java-home /path/to/jdk8 --gson /path/to/gson-2.10.1.jar --out outputs/my-run
python demos/11-bounded-assistant/audit.py outputs/my-run
python demos/11-bounded-assistant/projection.py --java-home /path/to/jdk8 --gson /path/to/gson-2.10.1.jar --out outputs/projection
python demos/11-bounded-assistant/projection.py --audit --out outputs/projection

输出目录必须不存在。本机验证环境为 Windows,Linux/macOS尚未复测。不需要模型密钥或付费服务。manifest中的代码冻结提交早于上方含证据的归档提交,源码哈希对应实际执行文件。

写给未来的自己合上文章前,留下一点自己的理解。

你的私人学习便签,只存在当前浏览器,不会上传或公开。清理浏览器数据会丢失,请导出留存。三种语言共用这篇文章的便签。

下一篇已发布内容:Java 请求重试:三次超时,不等于一个总期限 →

当前学习位置

构建 / AI 应用开发

  1. ○ AI Radar 工程拆解:从 RSS 到网站,中间发生了什么?
  2. ○ Java 会话历史与请求窗口:裁掉的旧事实还能回来吗?
  3. ○ Java 请求重试:三次超时,不等于一个总期限

继续探索

继续探索

  1. 实验 / 030

    Java 会话历史与请求窗口:裁掉的旧事实还能回来吗?

    通过裁剪、提交、重启和扩窗的连续实验,分离可回看的完整历史与一次模型请求的上下文。

    已完成
  2. 项目 / 002

    Java LLM 实践集

    Java 会话、请求重试与持久化边界,以及注意力、缓存成本和量化误差的可复现实验。

    构建中
  3. 工程案例

    Java LLM 实践(三):超时之后,别让对话历史悄悄变了

    用候选副本、整轮提交与UTF-8请求预算重写多轮会话边界:41项离线检查,失败不污染历史,裁剪失败也能保留原状态。

    10 分钟已发布

接下来,走哪条路?

依据文章关联与同主题已发布内容整理,不使用随机推荐。

← 全部文章