ASTER CODE / 构建终端智能体
10 · 记忆的数据范围
区分会话历史、文件记忆与持久记忆,建立准确命名空间并验证恢复绑定。
会话历史与持久记忆
会话历史用于继续这段对话;长期记忆用于让后续会话召回已经确认的项目知识。把所有历史永久放进系统提示会增加上下文负担,也无法表达候选、过期、冲突和撤销。
先想清楚这条记忆属于谁、用于哪个项目。单人练习可以使用固定的演示身份;真实应用需要由宿主提供租户、主体和范围,不能让模型选择它想读取的命名空间。
从文件记忆开始理解生命周期
with_file_memory(dir) 为会话提供文件记忆存储。把目录放到宿主管理的位置,并保留不同项目的数据隔离。这个入口与第六章的 SessionStore 是不同的存储:前者保存记忆,后者保存会话与检查点。
如果需要明确的候选激活、精确命名空间、版本和审计,使用下面的 V2 绑定。不要把给 Provider 起个名字当作已经接入持久记忆。
建立一个 V2 文件仓库
在练习包增加本地依赖 aster-memory = { path = "../../crates/memory" }。在 runtime.rs 增加:
use std::sync::Arc;
use aster_memory::repository::{FileMemoryRepository, MemoryNamespace};
use aster_core::durable_memory::{DurableMemorySession, DurableMemoryRecallPolicy};
async fn project_memory(state_dir: &std::path::Path)
-> anyhow::Result<DurableMemorySession>
{
let repository = Arc::new(FileMemoryRepository::open(state_dir).await?);
let namespace = MemoryNamespace::try_new(
"tutorial-tenant", "tutorial-user", "tutorial-demo",
)?;
let policy = DurableMemoryRecallPolicy::try_new(8, 1.0)?;
Ok(DurableMemorySession::active_recall(repository, namespace, policy))
}
在创建新会话前,把结果交给已有 options 的 with_durable_memory(binding)。这里最多召回 8 项,最低词法分数是示例设置,应根据自己的数据验证,不是通用质量阈值。
文件仓库目录同一时间由一个活实例持有。应用内共享同一个仓库对象;不要为两个并发会话分别打开同一目录。重启后重新打开仓库,并注入与快照相同的可见绑定。
候选不会自动成为事实
| 阶段 | 宿主需要做什么 | 能否参与召回 |
|---|---|---|
| Candidate | 核对来源、证据和准确修订 | 不能,仅有提取结果还不够 |
| 激活 | 提供 Manual 或 Verification 的明确决策证据 | 通过激活后才进入 Active 范围 |
| Active | 按命名空间和预算查询,记录准确修订进入上下文 | 可以,但还要经过上下文选择 |
| 过期、冲突、替代或删除标记 | 保留历史并更新状态 | 不能继续作为当前 Active 内容召回 |
模型的信心、重要程度和“我记住了”都不能授权激活。界面可以显示候选审核列表,但不应在用户发送下一条消息时顺手把全部候选激活。
召回、进入上下文、实际使用是三件事
preview_recall 可以预览检索结果,不会把它们自动加入提示,也不记录为已经使用。最终上下文组装后才记录 admission;实际被引用或用于决定时,再由宿主按接口记录 use。
想接语义召回,还需要嵌入执行器、向量索引、同一份可服务代际和刷新回执。先把词法召回、激活和命名空间验证正确,再增加这些后端。嵌入服务故障时也要验证回退行为,不能扩大读取范围来“提高召回率”。
验收
建立两个不同 scope 的练习会话,在 A 激活一条项目约定,B 不应召回。候选未激活时不能出现在后续模型输入中。重启后用原绑定恢复应成功;换成另一主体或范围应拒绝恢复,而不是悄悄改绑。