Skip to main content

作用

让 agent 能“记住跨会话的事实”,同时避免对话上下文无限增长。harness 把记忆拆成两层:高频低质量的“流水账” + 低频高质量的“策划后长期记忆”,并补上一套 FTS5 检索 + 后台维护。

触发

关键逻辑

双层记忆模型

  • 第一层·流水账 memory/YYYY-MM-DD.mdMemoryFlushManager 专属,只追加,不去重;是“刚刚在说什么”的原始记录。
  • 第二层·策划后长期记忆 MEMORY.mdMemoryConsolidator 专属,整体重写;MemoryFlushManager 不会去动它。每次推理都走 WorkspaceContextHook 注入到 system prompt。
  • 索引 MemoryIndex:启动时 indexAllFromWorkspace 一次;每次 flush 写今日流水账后增量重建该文件索引;SQLite 文件位于 <workspace_parent>/memory_index.db

对话压缩(ConversationCompactor

默认值(全部可调):

TruncateArgsConfig—轻量预处理(可选)

在 LLM 摘要之前,可以先走一个不走 LLM 的干预:对老消息里不那么重要的 ToolUseBlock 参数做字符串截断(默认阈值 25 条 / 40k tokens,参数超 2000 字符被裁揉)。适合 write_file 这种入参体量大、后期不需要原貌的场景。

上下文溢出自动恢复

当模型返回 context_length_exceeded / maximum context 之类错误,HarnessAgent.recoverFromOverflowforceCompactAndRetry 会拼一个临时 triggerMessages=1CompactionConfig 走一轮压缩,清空 Memory 后重试;前提是配置了 compaction(...),否则直接抛错。

记忆提取(MemoryFlushManager

  • flushMemories(messages):拿当前 MEMORY.md 和今日流水账作为“去重参考”丢给 LLM,要求输出 仅新增的 bullet;“NO_REPLY” 表示什么都不写。
  • 写入位置固定是 memory/YYYY-MM-DD.md不会动 MEMORY.md(以防二层被一层覆写)。
  • 写完后立刻 indexFromString 重建该文件索引,并调 MemoryMaintenanceScheduler.requestConsolidation() 提示“能合并了合并下”。

二次合并(MemoryConsolidator

  • 读 mtime 超过 watermark 的日流水账 + 当前 MEMORY.md,调 LLM 合并、去重、裁剪。
  • 输出限制:默认 maxMemoryTokens=4000(约 16k 字符),prompt 会以字符预算的形式告诉 LLM。
  • 写后推进 watermark,存于 memory/.consolidation_state;下次只看 mtime 超 watermark 的日文件。
  • 合并仅在后台 executor 跳:定期 tick 或 requestConsolidation() 发起,永不阻塞推理循环。

后台维护(MemoryMaintenanceScheduler

HarnessAgent.build() 中自动创建并 start(),每个 tick 顺序跑:
  1. expireDailyFiles — 超过 dailyFileRetentionDays 的日文件归档到 memory/archive/默认 90 天
  2. consolidateMemory — 调 MemoryConsolidator.consolidate()
  3. pruneOldSessions — 删除 mtime 超 sessionRetentionDays 的会话文件(默认 180 天
  4. reindexMemoryIndex.indexAllFromWorkspace
默认间隔 Duration.ofHours(6);opportunistic 调用实际节流间隔 30 分钟,避免频繁 flush 打爆 LLM。

工具结果卸载(ToolResultEvictionConfig

与压缩独立。某次 tool_call 返回的文本超过阈值时,全文写到 evictionPath 下的文件,原位置只留一个“首尾预览 + 路径”的占位符,agent 需要完整内容时走 read_file

配置与代码示例

相关文档

  • 工具memory_search / memory_get 的参数与调用例
  • 工作区MEMORY.md / memory/*.md 在工作区的位置
  • 会话.log.jsonl / .jsonl 怎么反过来被记忆提取使用
  • 架构 — CompactionHook / MemoryFlushHook / ToolResultEvictionHook 在生命周期中的位置