MemT 1.0 的长记忆该用在哪,先看会话里哪些信息要跨轮保留

文章导读
接上 MemT 1.0 后,长记忆开不开、开多大,通常不该按“这个会话像不像多轮任务”来拍,而该看字段:某一轮写下的信息,在后面几轮是否还会被指代、续用或用来校验。判断顺序建议是先把会话记录按轮次拆成字段,再看哪些字段在后续轮次被真正引用,最后才决定写入范围和丢弃条件。这样做的边界是可验证的:配置改完后,用同一段会话回放,逐轮检查引用对象是否命中;至于具体能省多少上下文,需要结合部署环境和会话长度
📋 目录
  1. A 把现有会话记录按轮次拆成字段
  2. B 标出跨轮必须保留的最小信息集
  3. C 给无需跨轮的内容设定丢弃条件
  4. D 用一段多轮回放验证保留结果
A A

接上 MemT 1.0 后,长记忆开不开、开多大,通常不该按“这个会话像不像多轮任务”来拍,而该看字段:某一轮写下的信息,在后面几轮是否还会被指代、续用或用来校验。判断顺序建议是先把会话记录按轮次拆成字段,再看哪些字段在后续轮次被真正引用,最后才决定写入范围和丢弃条件。这样做的边界是可验证的:配置改完后,用同一段会话回放,逐轮检查引用对象是否命中;至于具体能省多少上下文,需要结合部署环境和会话长度确认。

建议先把会话按轮次拆成角色、轮次、引用对象、任务状态四类字段,只把后续轮次还会再次引用的路径、文件名、已确认约束和未闭环状态写入长记忆;寒暄、已闭环的单轮问答、未被选中的候选对象可以留在会话内或直接丢弃。设置是否生效,用一段多轮回放逐轮检查指代解析结果;配置项名称需按实际部署确认。

把现有会话记录按轮次拆成字段

拆字段的目的不是做归档,而是让“后面还会不会用到”变成可检索的判断。建议先固定下列列,再从日志或会话表里导出一小段记录试拆;如果某一列在后续轮次从未被引用,它通常不该进入长记忆。

字段含义是否跨轮候选怎么判定
session_id会话标识否仅用于回放和关联,不写入记忆正文
turn轮次序号否用于排序和标注引用发生的位置
roleuser / assistant / tool否影响引用解析方向,不作为记忆内容
text该轮原文部分只保留被后续轮次指代的那一句或那个短语
refs路径、文件名、实体名、时间范围等引用对象是后续轮次出现“刚才那个、前一个、它”时能否命中
resolved_to指代解析后的具体对象是解析结果是否唯一、是否被用户确认
task_statecreated / awaiting_user / in_progress / closed是状态未闭环才需要跨轮续用
tool_result工具或查询返回的关键结论部分下一轮是否需要基于该结果继续操作

下面是一段脱敏后的两轮样例,按字段展开而不是整段保存。重点看第 2 轮的“刚才那个路径”是否还能落到第 1 轮的 /data/orders,以及 report_0421.csv 是否仍在任务状态里。

session_id: s-001
turn: 1
role: user
text: 把 /data/orders 里上周的订单按状态汇总,结果写到 report_0421.csv
refs: [path=/data/orders, file=report_0421.csv, time_range=上周]
task_state: created

turn: 1
role: assistant
text: 确认按状态分组输出 CSV,是否排除已取消订单?
refs: [file=report_0421.csv]
task_state: awaiting_user

turn: 2
role: user
text: 不排除,就按刚才那个路径跑
refs: [pronoun=刚才那个路径, resolved_to=/data/orders]
task_state: in_progress

标出跨轮必须保留的最小信息集

最小信息集的边界可以用一句话判断:删掉这条信息后,下一轮是否还能正确执行或正确指代。下面三个样本按这个标准逐条标注,保留项和丢弃项都写明依据,便于直接对照自己的会话日志。

样本轮次内容摘要必须保留可丢弃判断依据
A:数据汇总任务第 1 轮给出目录和输出文件,第 2 轮说“按刚才那个路径跑”path=/data/orders、file=report_0421.csv、task_state=in_progress、已确认的“不排除取消订单”“你好”“麻烦快一点”等语气内容第 2 轮出现指代,路径和输出文件必须能命中;约束条件影响执行结果,需要续用
B:单轮天气问答用户问某城市天气,助手回答后会话结束无,或只保留 session 级标记,不写入长记忆城市名、日期、天气结论、寒暄任务已闭环,后续轮次没有指代和续用动作,写入只会占上下文
C:两个候选对象消歧先讨论 A 服务日志目录,后提到 B 服务目录,用户说“还是看前一个”最终选定的对象标识、解析结果“前一个=A”、task_state被放弃候选 B 的详细内容,可只留标识指代对象已确定,保留被放弃候选全文没有续用价值

三个样本的共同点是:保留项都对应后续轮次的一个动作或一次指代,丢弃项都不影响下一轮执行。需要结合环境确认的是任务状态字段的取值集合,不同接入方式的枚举可能不同,先按实际日志对齐再写入配置。

MemT 1.0 的长记忆该用在哪,先看会话里哪些信息要跨轮保留

给无需跨轮的内容设定丢弃条件

丢弃条件建议写成可判定的布尔项,而不是靠“感觉不重要”。可以先从三类判定项入手:任务已闭环,即 task_state 已到 closed 且没有待确认问题;指代对象消失,即后续轮次不再出现指向该对象的代词或省略表达;仅寒暄,即该轮不包含路径、实体、约束和任务动作。任一项命中时,对应字段不进入长记忆写入队列。

  • 任务已闭环:检查 task_state 和待确认问题列表,二者都为空才允许丢弃任务过程。
  • 指代对象消失:回看后续若干轮是否还有“它、这个、前一个”等表达,没有则释放该引用对象。
  • 仅寒暄:按字段判断,text 中无 refs、无 task_state 变化、无约束确认,可直接丢弃。

落到配置时,下面是一段占位骨架,键名需要替换成实际部署支持的配置项;如果产品侧没有同名项,可以用等价的写入过滤规则实现。改完后先在小流量会话上观察日志中“跳过写入”的记录,确认丢弃条件真的被触发。

memory:
  write:
    keep_fields: [task_state, resolved_refs, confirmed_constraints, output_file]
    drop_when:
      task_closed: true
      no_pending_reference: true
      small_talk_only: true
    ref_ttl_turns: 3   # 占位值,按会话长度调整
    log_skipped: true  # 便于回放时核对丢弃是否发生

用一段多轮回放验证保留结果

配置改完后不要只看“记忆里有内容”,而要看每一轮的指代是否命中正确对象。回放脚本的骨架可以不变,只替换写入和读取的适配函数;期望值按轮次写成表,逐轮比对保留键和解析结果。

def replay(turns, adapter, expectations):
    adapter.reset('s-replay')
    for t in turns:
        adapter.append(t)                    # 写入一轮会话
        snap = adapter.snapshot('s-replay')  # 读取当前记忆快照
        exp = expectations.get(t['turn'], {})
        for key, want in exp.get('must_contain', {}).items():
            got = snap.get(key)
            status = 'OK' if got == want else f'FAIL got={got!r} want={want!r}'
            print('turn', t['turn'], key, status)
        for key in exp.get('must_not_contain', []):
            status = 'OK' if key not in snap else 'FAIL 仍保留'
            print('turn', t['turn'], key, status)

expectations = {
    1: {'must_contain': {'task_state': 'awaiting_user'}},
    2: {'must_contain': {'resolved_refs.刚才那个路径': '/data/orders'},
        'must_not_contain': ['寒暄']}
}

验证方式建议跑两遍:第一遍确认第 2 轮的“刚才那个路径”解析到 /data/orders,第二遍把期望值改成错误对象,确认脚本能报 FAIL,避免脚本本身永远通过。同时检查日志里的跳过写入记录,确认寒暄和已闭环内容没有进入快照。回放样本宜短,覆盖一至两个指代即可;更长的会话可以按同样结构追加轮次,但每轮都要有明确的期望对象。