# AI 记忆工程：Java 21 离线示例

教学演示，仅使用标准库。源码与测试可从博客直接下载：

- https://blog.traceof.me/examples/ai-memory-java/MemoryDemo.java
- https://blog.traceof.me/examples/ai-memory-java/MemoryDemoTest.java
- https://blog.traceof.me/writing/ai-memory-engineering-guide

## 运行

确认 `java -version` 与 `javac -version` 都为 21。在源码所在目录运行：

```bash
javac -encoding UTF-8 -d out MemoryDemo.java MemoryDemoTest.java
java -cp out MemoryDemo
java -cp out MemoryDemoTest
```

macOS 可用 `export JAVA_HOME=$(/usr/libexec/java_home -v 21)` 和 `export PATH="$JAVA_HOME/bin:$PATH"` 选择已安装的 JDK 21。

测试使用显式检查，不需要 `-ea`。2026-09-23 使用 Java 21.0.1 实际运行 20/20 个具名契约测试通过。这不是模型准确率。

## 案例与约定

固定时钟：2026-09-23T08:00:00Z。Alice 的当前背景为 Kotlin、目标为 Python、进度为 Python 生成器；Bob 的背景为 Python。

规则只支持完整句式“我主要写 X”“我的主要语言改为 X”“我最近在学习 X”“我学到 X”。分别写入 background.primary、goal.current、progress.current。它不能理解任意否定、引用、多主体或假设语句。

- 幂等键为 `(user, eventId)`，相同事件不同内容或时间参数会被拒绝。
- 相同有效事实及到期时间合并来源证据，不增加版本；更新关闭旧版本的有效区间。
- 区间为 `[validFrom, validUntil)`；乱序及同时冲突事件被拒绝。
- 检索先限制用户和当前有效版本，再按值的词项交集、时间倒序、ID 字典序排序。
- 英文词项转小写，中文使用连续双字词项；不实现 BM25、向量检索或语义理解。
- 上下文预算按 Unicode 码点计算，包含标签和换行，放不下一整条就跳过；不是 Token 计数。
- `forget(user, slot)` 删除所有对应版本和原文证据；保留事件摘要收据、用户标识和版本计数以阻止相同事件重试。摘要不是匿名化。
- 新事件 ID 可以再次记忆；因此尚不阻止新 ID 携带旧内容的回写，也不是完整账户删除。
- 无匹配输入和查询返回空结果。无匹配输入不保存幂等收据。

## 边界

纯内存、单进程，重启清空。同步锁不代表分布式并发保证。user 参数不包含真实鉴权，服务化时必须从可信认证构造。历史接口同样需要鉴权。

不调用模型、Embedding、网络服务或数据库；不提供 HTTP 服务。本示例不处理消息队列、备份、外部索引、日志、已返回对象引用及历史响应中的副本。删除保证仅覆盖 Store 内部的后续读取。

若扩展持久化、模型提取、混合检索或异步写入，应分别建立新的接口契约、故障测试与真实数据评测。
