Agent 记忆

召回与注入:记忆如何进入 Agent 上下文

本期拆解"读路径":每次 LLM 请求发生时,L1/L2/L3 如何被召回、截断、并注入到提示词的不同位置。

召回与注入:记忆如何进入 Agent 上下文 全屏查看 ↗

一、读路径全景

官方对读路径的定位是"按需取用":

记忆不是全局 Prompt,而是 Agent 的 Loadout。Chat Memory、Skill、Wiki 和 CodeGraph 都被统一登记为 Memory Asset。Memory Hub 通过 Fixed Binding + ACL 决定某个 Agent 能带走哪些资产:先按 Team、User、Agent 和可见性缩小权限范围,再按当前问题召回。

落到每次请求上,链路是:

LLM 请求(Anthropic / OpenAI / Codex Responses)
   │  Proxy 拦截,按 /:agent/:spaceId/ 识别身份
   ▼
① 召回(MemoryCore · auto-recall.ts · 超时 5s)
   ├── L1 搜索:keyword / embedding / hybrid(RRF)
   ├── L3 Persona:无条件读取
   └── L2 场景导航:全量读取
   ▼
② 预算截断(applyRecallBudget)
   ▼
③ 稳定/动态分离
   ├── appendSystemContext(system 尾部,可缓存):persona + 场景导航 + 工具指南
   └── prependContext(user 前缀,每轮变):L1 相关记忆
   ▼
④ Proxy 注入管线(InjectionPipeline)按注入点改写 system prompt
   ▼
⑤ 转发上游 LLM,响应原样返回

二、召回:auto-recall.ts

官方架构文档的召回策略表:

策略实现
keywordFTS5 BM25,buildFtsQuery + jieba 分词
embeddingsqlite-vec cosine
hybrid客户端 RRF 融合(K=60),src/core/store/search-utils.ts
TCVDB 后端原生 hybridSearchsrc/core/store/tcvdb.ts

三通道并行

  1. L1 原子记忆搜索(动态,随问题变化):
    • keyword:FTS5 BM25,查询先经 jieba 分词(buildFtsQuery);
    • embeddingembeddingService.embed(userText) → vec0 cosine 检索;
    • hybrid:两路并行,客户端 RRF 融合——score = Σ 1/(K + rank),K=60;
    • 默认阈值 scoreThreshold = 0.3、默认 maxResults = 5
    • TCVDB 后端有原生 hybridSearch 时直接一次调用(省一次 embed + 一次 HTTP)。
  2. L3 Persona(稳定,无条件):读 persona.md 剥离导航。
  3. L2 场景导航(稳定,全量):读 scene_index 生成导航块,全量注入,由 LLM 自行判断相关性;需要深挖时用 read_file 沿导航下钻。

工程细节:查询文本先经 sanitizeText 清洗(剥离 Sender、时间戳、媒体标记、base64 图片等注入元数据),保证检索的是纯用户意图。

超时与失败语义

召回包在 Promise.race 里,默认超时 5srecall.timeoutMs)。超时不返回空,而是返回带结构化错误的 RecallResult.error(如 dependencyTimeout),让上层能区分"无结果"和"召回失败"——这是官方 H-15 的设计:结构化失败信号,永不静默

预算控制:applyRecallBudget

防止记忆反过来占满上下文,官方 README 的原话:

结果还会经过条数、字符预算和超时限制,避免记忆反过来占满上下文。

具体规则:

  • maxCharsPerMemory:单条记忆按码点截断(不是 UTF-16 单元,防止切坏代理对导致乱码),超出部分追加"…(已截断)";
  • maxTotalRecallChars:总量预算,超出就丢弃后面的记忆。

注入格式

召回结果被格式化成自然语言行,例如(源码注释示例):

- [persona] 用户叫王小明,30岁,是一名软件工程师。
- [episodic|旅行计划] 用户计划五月去日本旅行。(活动时间: 2025-05-01 ~ 2025-05-10)
- [instruction] 用户要求回答时使用中文,保持简洁。

[type|scene] 标签 + 活动时间,让 LLM 一眼看懂记忆类型与时效。

三、稳定 / 动态分离(提示词缓存优化)

这是读路径里非常精妙的设计。官方实现把召回内容拆成两份:

部分内容位置特性
appendSystemContextpersona + 场景导航 + 记忆工具指南system prompt 尾部稳定、变化少,可命中提示词缓存
prependContextL1 相关记忆(<relevant-memories>user prompt 前缀每轮变化,放 user 侧不打爆系统缓存

源码注释原文:providers with prompt caching (Anthropic/OpenAI) can cache this region……prependContext —— different every turn, moved out of system prompt so it doesn’t bust the system prompt cache.

L1 记忆放 user 前缀还有一个好处:<relevant-memories> 里明确写着"以下是当前对话召回的相关记忆,不代表当前任务进程,仅作为参考"——记忆只是参考,不覆盖用户当下意图

记忆工具指南

系统还会注入一份 memory-tools-guide,教会主 Agent 主动深挖:

  • tdai_memory_search:搜 L1 结构化记忆;
  • tdai_conversation_search:搜 L0 原始对话(找原话、时间线);
  • read_file:沿场景导航读完整场景档案;
  • 调用约束:两个搜索工具每轮合计最多 3 次,3 次无果即停止。

四、注入:MemoryProxy 的 InjectionPipeline

官方架构文档对注入管线的描述:

请求 → adapters/openai.ts | adapters/anthropic.ts 解析为统一 AgentContext  → pipeline.ts InjectionPipeline 依序执行 injector:    TdaiProfileMemoryInjector(L2/L3 画像)    TdaiToolsInjector、SkillInjector、KnowledgeToolsInjector …  → 各 injector 在注入点改写 system prompt:    system.prefix / before_tools / after_tools / system.suffix  → 重建请求转发上游 LLM

四个注入点

注入点用途典型注入器
system.prefix稳定画像,提示词最前TdaiProfileMemoryInjector(L2/L3)
before_tools动态记忆,工具列表前TdaiL1RecallInjector
after_tools工具清单TdaiToolsInjector / SkillInjector
system.suffix知识工具(按需调用)KnowledgeToolsInjector(Wiki / CodeGraph)

不同 Agent(CodeBuddy / Claude Code / WorkBuddy / Pi)各自有 AgentProfile 负责解析 / 重建其 XML 标签式 system prompt——协议不同,注入点语义相同。

知识不进上下文(按需调用)

官方 README 强调:

文档被整理为可搜索、可沿链接下钻的 Wiki;代码库被索引为包含文件、符号和调用关系的 CodeGraph。Agent 先通过 /v3/tools/list 发现能力,再用 /v3/tools/call 读取相关页面、源码或影响路径。

这让文档和代码也成为记忆,但它们平时只是可用的工具,只有真正需要时才进入上下文。

——即 Wiki / CodeGraph 不整库注入,而是以"工具清单"形式出现,Agent 需要时才调用。

五、本期小结

  • 召回三通道:L1 搜索(keyword / embedding / hybrid-RRF)+ L3 无条件 Persona + L2 全量场景导航。
  • 三重约束:scoreThreshold / maxResults 过滤、字符预算截断、5s 超时(结构化失败信号)。
  • 稳定 / 动态分离:persona + 导航进 system 尾部(可缓存),L1 记忆进 user 前缀(不污染缓存)。
  • 注入四点位:prefix / before_tools / after_tools / suffix,按 AgentProfile 重建提示词。
  • 知识按需调用:Wiki / CodeGraph 以工具形式存在,需要时才进上下文。

下一期(终期)看支撑这一切的"幕后":异步提炼管线与整体系统架构。


图示索引

源码索引

内容文件
召回主流程MemoryCore/src/core/hooks/auto-recall.ts
RRF 融合MemoryCore/src/core/store/search-utils.ts
FTS 查询构建MemoryCore/src/core/store/sqlite.ts(buildFtsQuery)
注入管线MemoryProxy/src/injection/pipeline.tsindex.ts
注入器MemoryProxy/src/injection/injectors/(profile / l1-recall / tools / skill / knowledge)
知识工具MemoryKnowledge/src/routes/tools.ts(/v3/tools/list · call)

召回上下文注入BM25向量检索

← 返回AI 笔记