Skip to content
1 概述:上下文窗口与 LLM 记忆
站在 API 调用者的角度,LLM 本身不具备传统意义上的“记忆”。模型处理某次请求时,只能看到当前消息中携带的文本。上一轮对话、用户偏好、早前任务的状态都不会自动出现在本次请求中。所谓记忆,是应用层把历史信息重新放入上下文窗口,使模型在生成时能够再次看到这些信息。
这种设计来自 Transformer 架构的一个基本约束:模型对输入的处理长度有限。这个长度称为上下文窗口(context window)。窗口以内的信息,模型可以感知;窗口以外的信息,模型无从知晓。因此,围绕窗口边界构建的管理机制,构成了 LLM 应用记忆系统的主体。
2 基本概念:短期记忆与长期记忆
LLM 应用的记忆可以分为两类:
- 短期记忆:当前上下文窗口内保留的信息。多轮对话中,应用把历史消息拼接进请求,这些消息暂时占据窗口空间。窗口容量有限,因此短期记忆需要裁剪、压缩、丢弃。
- 长期记忆:保存在上下文窗口之外的信息。它通常存放在数据库或文件中,需要时通过检索取回并重新注入窗口。
短期记忆是“工作台”,长期记忆是“仓库”。工作台决定当前生成的直接依据,仓库决定工作台能补充哪些材料。二者通过上下文窗口建立联系。
一项面向 LLM Agent 记忆机制的学术调研将记忆生命周期分为写入、管理与读取三个阶段[5]。写入负责把新信息存入存储介质;管理负责整合、更新或删除已有记忆;读取负责在生成前筛选并取出相关记忆。在短期记忆中,读取是隐式的——所有窗口内的消息都会参与生成。在长期记忆中,读取必须经过检索或调度,因为不可能把全部存储内容一次性放入窗口。
3 工作原理:上下文窗口与 token 计算
上下文窗口的单位是 token,不是字符。token 是文本被分词后的最小单位。英文中一个 token 大约覆盖 3 到 4 个字符;中文通常一个字对应一到两个 token。不同模型的分词规则不同,同一句话的 token 数在不同模型间可能不同。
窗口有上限,因此每个请求的 token 数必须满足:
输入 token + 最大输出 token ≤ 上下文窗口输入部分包含系统提示、对话历史、检索结果。一旦输入接近窗口上限,有两种选择:减少输入内容,或降低最大输出限制。前者影响模型可参考的信息量,后者影响回答长度。设计记忆系统时,需要对输入部分做预算分配。
token 计算是记忆管理的基本操作。编写代码时,使用模型提供方的 tokenizer 或第三方库(如 js-tiktoken)来统计 token 数。字符串长度不能替代 token 统计。
typescript
import { getEncoding } from "js-tiktoken";
const encoding = getEncoding("cl100k_base");
const text = "你好,世界";
const tokens = encoding.encode(text);
console.log(tokens.length); // 输出在该模型分词规则下的 token 数cl100k_base 是 OpenAI 部分模型使用的编码器,其他模型可能使用不同的编码器。编写通用的记忆管理代码时,应把 token 统计函数抽象为接口,按模型传入对应的编码实现。
4 基本用法:多轮对话拼接
最简单的多轮对话实现,是把历史消息按时间顺序累积在数组中。每次请求时将数组整体发送给模型。
typescript
interface Message {
role: "system" | "user" | "assistant";
content: string;
}
class ChatSession {
private messages: Message[] = [];
constructor(systemPrompt: string) {
this.messages.push({ role: "system", content: systemPrompt });
}
addMessage(message: Message) {
this.messages.push(message);
}
getMessages(): Message[] {
return this.messages;
}
}
const session = new ChatSession("你是一个乐于助人的助手。");
session.addMessage({ role: "user", content: "杭州的市花是什么?" });
session.addMessage({ role: "assistant", content: "桂花。" });
session.addMessage({ role: "user", content: "它一般在几月开放?" });
session.addMessage({ role: "assistant", content: "桂花通常在 9 月到 10 月开放。" });第二轮用户消息发送给模型时,messages 数组包含前四条消息(system、user、assistant、user)。当模型回复后,数组变为五条。模型能回答“它”指代什么,依靠的是第一轮对话信息仍然在上下文中。这就是短期记忆的基本机制。
拼接方式存在两个直接问题:
- 长度增长:随着对话轮次增加,消息累积会触及窗口上限。
- 历史噪声:早期无关信息持续占据空间,降低有效信息密度。
对话越长,这两点越突出。滑动窗口和摘要压缩是两种主要的缓解手段。
5 基本用法:滑动窗口与历史裁剪
滑动窗口的基本做法是:只保留最近 N 条消息,丢弃更早的消息。
typescript
function trimMessagesByCount(
messages: Message[],
maxMessages: number
): Message[] {
if (messages.length <= maxMessages) {
return messages;
}
// 始终保留第一条(通常为 system),然后从尾部截取最近的消息
const head = messages.slice(0, 1);
const tail = messages.slice(messages.length - maxMessages + 1);
return [...head, ...tail];
}这种实现的问题在于:消息数量不能反映真实的 token 消耗。一条长文档消息可能抵得上十轮简短问答。更精确的做法是按 token 数量裁剪。
typescript
function trimMessagesByTokens(
messages: Message[],
maxTokens: number,
countTokens: (text: string) => number
): Message[] {
const headTokens = countTokens(messages[0].content);
if (headTokens > maxTokens) {
throw new Error("system prompt 超过了 token 预算");
}
const kept = [messages[0]];
let totalTokens = headTokens;
// 从尾部消息开始向前遍历,直到达到预算
for (let i = messages.length - 1; i >= 1; i--) {
const cost = countTokens(messages[i].content);
if (totalTokens + cost > maxTokens) break;
kept.push(messages[i]);
totalTokens += cost;
}
kept.reverse();
return kept;
}上面的实现中,maxTokens 是分配给历史消息的 token 预算。顺序从尾部开始累积,直到预算耗尽。这里存在一个需要关注的取舍:是“尽量保留更多消息导致超出预算”,还是“严格限制在预算内导致丢失部分尾部内容”。上面代码选择后者,且一旦某条消息无法装入预算就停止,不会继续尝试保留更早的更短消息。
滑动窗口的实质是直接丢弃。被丢弃的消息无法参与后续生成。如果对话中存在必须长期保留的信息(如用户告知的称呼、项目约束),需要写入长期记忆,不能依赖短窗口保存。
6 基本用法:对话摘要与上下文压缩
当对话累积到一定规模时,直接截断会丢失细节。摘要压缩的思路是:用较短的文字概括历史对话的要点,替代原始消息放入上下文。
LlamaIndex 的 ChatSummaryMemoryBuffer 是这个模式的参考实现。它默认将 token 上限设为 2000,摘要触发比率设为 0.75;历史 token 数超过 1500(2000×0.75)时,模型会按提示词“The following is a conversation between the user and assistant. Write a concise summary about the contents of this conversation.”对已有对话进行摘要,摘要完成后保留摘要与最近的对话消息[3]。该类在较新版本中已标记为 Deprecated,官方建议使用新的 llama_index.core.memory.Memory[3]。
基本的摘要压缩流程如下:
typescript
async function summarizeHistory(
messages: Message[],
callLLM: (messages: Message[]) => Promise<string>
): Promise<Message> {
const summaryPrompt: Message = {
role: "system",
content: "请对以下对话生成简洁摘要,保留关键信息:",
};
const summary = await callLLM([summaryPrompt, ...messages]);
return { role: "system", content: `对话摘要:${summary}` };
}
async function compressHistory(
messages: Message[],
maxTokens: number,
countTokens: (text: string) => number,
callLLM: (messages: Message[]) => Promise<string>
): Promise<Message[]> {
if (countTokens(messages.map((m) => m.content).join("\n")) <= maxTokens) {
return messages;
}
const summary = await summarizeHistory(messages.slice(1), callLLM);
const recent = messages.slice(-4);
return [messages[0], summary, ...recent];
}执行压缩后,长期对话的结构变为:
system(原始提示词)
system(历史摘要)
最近几轮消息示例假设 messages[0] 是 system 消息,recent 取最近四轮以内的消息。实际实现中应显式检查数组结构,并确认所用 Chat API 允许多条 system 消息共存。
摘要适合事件型对话、阶段性任务汇报,但存在两点限制:
- 损耗:摘要不可能保留全部细节。用户如果追问“我三天前说的那句话里的准确数字”,摘要模式可能无法回答。
- 叠加:多次摘要后,摘要本身会越滚越长,仍需二次压缩。
工程实现注意点:
- 摘要提示词应明确要求保留用户偏好、实体、时间、任务状态等信息。
- 摘要多次累积后,需要把旧摘要作为源文本重新压缩,而不是对全部原始消息反复摘要。
- 摘要是对有损信息的压缩。对需要精确追溯的内容,应把原始事实同时写入可检索的长期记忆,避免仅依赖摘要。
7 基本概念:长期记忆的存储形式与向量检索
长期记忆存放在上下文窗口之外。常见存储介质与适用内容:
| 存储形式 | 适用信息 |
|---|---|
| 键值数据库(Redis、MongoDB) | 用户偏好、身份信息、会话状态 |
| 关系型数据库 | 结构化业务记录、事件日志 |
| 向量数据库 | 非结构化文本语义检索 |
| 文件系统 | 文档原文、长文本记录 |
向量检索是长期记忆中最常用的读取方式。流程是:将一段文本通过 embedding 模型转换为向量,存入向量数据库;查询时把用户当前输入转换为向量,通过余弦相似度等度量找出最相近的若干条记忆。
typescript
interface MemoryRecord {
id: string;
text: string;
metadata: {
createdAt: number;
updatedAt: number;
importance?: number;
};
}
interface ScoredMemoryRecord extends MemoryRecord {
score: number;
}
class VectorMemory {
async add(record: MemoryRecord): Promise<void> {
const vector = await embed(record.text);
await vectorStore.insert({ id: record.id, vector, payload: record });
}
async update(id: string, record: MemoryRecord): Promise<void> {
const vector = await embed(record.text);
await vectorStore.update(id, { vector, payload: record });
}
async recall(query: string, topK = 5): Promise<ScoredMemoryRecord[]> {
const queryVector = await embed(query);
const results = await vectorStore.search(queryVector, topK);
return results.map((r) => ({ ...r.payload, score: r.score }));
}
}embed 和 vectorStore 是接口占位,不是某个具体库的 API。实际项目中,embedding 通过 OpenAI Embeddings、本地模型或开源模型获得;向量存储可以选择 Redis、PGVector、Milvus、Chroma 等。score 是向量检索返回的相似度分数,具体含义取决于所选的向量数据库。
向量检索并非唯一读取方式。MemGPT 等系统还使用结构化内存块,按层次组织信息,通过调度策略决定读哪些块、写哪些块。向量检索适合“语义接近”的信息,结构化内存适合“固定角色设定”和“用户长期档案”。
8 工作原理:记忆的写入、更新与遗忘
设计长期记忆时,写入、更新与遗忘三个环节缺一不可。
写入:什么信息值得写入?并非所有对话内容都该永久保存。可以设定条件,比如用户显式声明偏好、消息中出现重复主题、信息带有事实属性(如“我住在北京”)。写入时保留原始时间戳和来源。
更新:记忆不是只增不改。用户说“我搬家到上海了”,旧地址应被覆盖。更新策略通常是:先按语义检索同主题旧记忆,比较时间戳,再决定是新增还是替换。
typescript
async function saveMemory(record: MemoryRecord) {
const existing = await vectorMemory.recall(record.text, 3);
const conflict = existing.find((r) => isSameTopic(r.text, record.text));
if (conflict) {
await vectorMemory.update(conflict.id, {
id: conflict.id,
text: record.text,
metadata: { ...conflict.metadata, updatedAt: Date.now() },
});
} else {
await vectorMemory.add(record);
}
}isSameTopic 通常也需要用 embedding 相似度判断:两条文本向量距离较近时认为是同一主题。
遗忘:遗忘包括主动删除与自动衰减。主动删除由用户发起或业务规则触发;自动衰减可以基于时间或重要性评分。调研中关注的核心问题是:长期记忆中哪些信息应该被压缩、合并、删除,以维持记忆库的信息密度和可用性[5]。在实现层面上,可以设定过期时间(TTL)或在检索后按重要度过滤。
MemGPT 的“pause/think”机制可以看作写入与遗忘的统一调度:当上下文快满时,系统比较分析和反思,将重要信息写入外部记忆,丢弃不重要内容[5]。
9 工作原理:记忆整合与反思机制
在上述生命周期中,管理阶段进一步体现为合并、反思与遗忘[5]。合并(Merging)把多条相关记忆整合成一条更高层的表述,减少存储冗余;反思(Reflection)从近期经历中抽象出新的规律或偏好;遗忘(Forgetting)删除不再成立或不再重要的信息。
合并与反思通常由应用层调用 LLM 完成。触发条件可以是固定时间步、上下文 token 阈值或对话阶段性结束。
typescript
async function mergeRelatedRecords(
records: MemoryRecord[],
callLLM: (messages: Message[]) => Promise<string>
): Promise<MemoryRecord> {
const mergedText = await callLLM([
{
role: "system",
content: "合并以下记忆条目,保留事实,去重并压缩表达。",
},
{ role: "user", content: records.map((r) => r.text).join("\n") },
]);
return {
id: crypto.randomUUID(),
text: mergedText,
metadata: {
createdAt: Math.min(...records.map((r) => r.metadata.createdAt)),
updatedAt: Date.now(),
importance: Math.max(...records.map((r) => r.metadata.importance ?? 0)),
},
};
}
async function reflectOnRecentEvents(
recentMessages: Message[],
callLLM: (messages: Message[]) => Promise<string>
): Promise<MemoryRecord> {
const observation = await callLLM([
{
role: "system",
content: "根据以下对话提取一条需要长期保存的观察,保留实体与时间信息。",
},
...recentMessages,
]);
return {
id: crypto.randomUUID(),
text: observation,
metadata: { createdAt: Date.now(), updatedAt: Date.now(), importance: 1 },
};
}合并与反思不能代替原始事实的留存。对于需要精确追溯的原始消息,应先写入可检索的原始存储,再在归档层进行抽象。
10 应用:基于 RAG 的记忆设计
RAG(检索增强生成)通常被理解为“从知识库检索文档辅助回答”。当 RAG 应用于记忆时,检索源从“通用知识库”变为“用户的历史交互记录”。这带来两个设计变化。
第一,分割单位不同。知识库 RAG 通常按段落或 chunk 切分文档;记忆 RAG 按“事件”或“事实”切分。一条记忆可以是一次用户陈述、一个决策、一段简短对话。分割粒度太小导致检索碎片化,太大导致信息混杂。
第二,相关性标准不同。知识库 RAG 追求语义相关;记忆 RAG 还要考虑时间衰减、用户特定性、信息冲突。用户三个月前说过“我不喜欢喝咖啡”和昨天说“我开始喝咖啡了”,两条信息语义都相关,但后者有更高的时效权重。可以在检索后的排序阶段纳入这些信号。
下面的例子在向量语义检索的结果上叠加时间衰减权重:
typescript
async function recallWithRecency(
query: string,
topK = 5,
recencyWeight = 0.3 // 示例权重,实际值需按业务调整
): Promise<MemoryRecord[]> {
const results = await vectorMemory.recall(query, topK * 2);
const now = Date.now();
return results
.map((r) => {
const days = (now - r.metadata.updatedAt) / 86400000;
const recencyScore = 1 / (1 + days);
const semanticScore = r.score;
const finalScore =
semanticScore * (1 - recencyWeight) + recencyScore * recencyWeight;
return { record: r, finalScore };
})
.sort((a, b) => b.finalScore - a.finalScore)
.slice(0, topK)
.map((entry) => entry.record);
}工程实现时需要注意:这种加权排序发生在向量相似度检索之后,如果向量检索的初选数量(这里是 topK * 2)太少,会漏掉语义不近但时间很新的记忆。
11 应用:Memory Bank 与 MemGPT 模式
Memory Bank 模式将记忆划分为若干有明确职责的“区块”,每一块占用独立上下文位置。典型的区块包括:
- 核心记忆(Core Memory):系统人设、用户档案、全局指令。始终在上下文中。
- 工作记忆(Working Memory):当前任务的临时状态,对话结束后可丢弃。
- 归档记忆(Archival Memory):长期存储,通过检索访问。
- 回想记忆(Recall Memory):历史对话的向量索引,用于查询过去的事件。
MemGPT(现在称为 Letta)将类操作系统概念引入记忆管理。关键思想是“虚拟上下文”:模型并不拥有无限上下文,但当上下文占满时,系统执行类似内存分页的操作——主动把当前不重要的信息移至外部存储,把需要的档案调入上下文。
MemGPT 中的 memory block 结构可以近似理解为一个带元数据的 JSON 对象。每次对话时,模型能够看到特定 block 中的内容,并可以通过函数调用请求更新 block:
typescript
interface MemoryBlock {
label: string;
value: string;
limit: number;
lastEdited: number;
}
interface AgentMemory {
core: MemoryBlock[]; // 始终在上下文中(示意字段名,对应 core memory 概念)
archival: MemoryBlock[]; // 长期存储,按需检索
recall: MemoryBlock[]; // 历史事件记录
}core 数组对应始终保留在上下文中的区块,archival 对应外部存储,recall 对应可检索的历史对话索引。模型本身不直接访问 archival 和 recall,而是通过工具调用间接检索,再把结果作为新消息加入上下文。
实际项目中,Memory Bank 与 MemGPT 的选择取决于对可控制性和实现成本的要求。Memory Bank 模式实现简单、结构清晰,适合固定业务逻辑;MemGPT 模式更灵活,但需要额外维护存储调度逻辑。两种模式并不互斥:可以在 Memory Bank 的基础上,增加“当上下文接近上限时归档”的调度触发条件。
12 注意点:Token 预算、Embedding 与 Prompt 缓存
Token 预算
每个请求的 token 预算需要按层次分配。下面示例中的数值只是演示值,不是固定定额。
typescript
interface TokenBudget {
systemPrompt: number;
coreMemory: number;
workingMemory: number;
retrievedMemory: number;
recentMessages: number;
maxOutputTokens: number;
}
const totalWindow = 128_000;
const budget: TokenBudget = {
systemPrompt: 1_000,
coreMemory: 1_500,
workingMemory: 2_000,
retrievedMemory: 2_000,
recentMessages: 4_000,
maxOutputTokens: 1_000,
};示例中预算之和为 11,500 token,远小于 128,000 的窗口上限。预算管理的目标不是“塞满窗口”,而是“留出可控余量”——响应速度、输出长度、解析时间都受输入规模影响。每项预算的大小应根据应用的实际需要调整。
Embedding 的更新代价
Embedding 模型通常不支持增量更新。当一条记忆发生变更,正确的做法是新文本重新生成 embedding,替换旧向量。因此,包含大量 embedding 的记忆库在更新时会消耗额外成本。设计上尽量将“频繁更新的键值信息”与“稳定不变的语义文本”分离存储:前者用 Redis 等键值库保存,后者用向量库保存。
Prompt 缓存
多轮对话中,请求之间常常存在重复前缀(system prompt、历史对话的稳定部分)。Prompt caching 机制允许应用复用先前计算过的前缀 token 结果,减少重复处理的开销[1][2]。
第三方文档整理了以下行为[1][2]:OpenAI 对至少 1024 个 token 的合格提示自动生效,缓存前缀在约 5 到 10 分钟不活跃后可能失效;Claude 在 Anthropic API 中支持显式配置缓存控制参数,可以指定缓存点的位置和 TTL;Gemini 2.5 及更新模型会自动启用缓存,无需显式设置。
从记忆系统设计的角度看,Prompt caching 带来的关键是结构调整:把不变的记忆内容(system prompt、核心记忆、固定摘要)放在提示词的前部,把变化的内容(最近几轮消息、当前任务指令)放在后部,这样长对话中稳定的前缀可以命中缓存,降低输入成本。缓存命中时,输入 token 的计费价格低于普通输入 token 价格[2];各家定价细节应以提供方的最新价格表为准。
Embedding 的维度与选择
不同 embedding 模型输出的向量维度不同。维度高可能带来更好的表现,但存储和检索开销也更高。选择时需考虑向量数据库对维度的支持,以及后续检索延迟。对于对话记忆这类短文本场景,中等维度的 embedding 模型通常已够用,不需要追求高维模型。
13 对比:主流框架的 Memory 模块
LangChain、LlamaIndex、Letta 是三个有代表性的框架,它们对 Memory 的抽象层次不同。
LangChain
LangChain 生态的社区资料将 Memory 描述为应用如何“记住”过去发生过什么——它关心的是多轮交互中的对话历史、用户偏好、会话状态,而不是让模型自身变得更聪明[4]。这也解释了为什么 Memory 与 RAG 关注点不同:Memory 回答“这次会话里发生过什么、这个用户之前说过什么”,RAG 回答“系统从外部知识库里查到了什么”[4]。
LangChain 的早期抽象包括 ConversationBufferMemory(完整存储)、ConversationBufferWindowMemory(按轮数裁剪)、ConversationSummaryMemory(摘要压缩)等。在此基础上,LangChain 支持将 Memory 与 Chain 结合。在最近的版本中,LangChain 开始转向通过 LangGraph 管理持久化状态,用显式的状态对象替代传统的隐式记忆类。
LlamaIndex
LlamaIndex 的 ChatSummaryMemoryBuffer 是一个基于摘要的对话记忆缓冲区,提供了 token 上限、摘要触发比率和自定义摘要提示词等参数[3]。该类的默认触发条件是历史 token 数超过上限的 75%[3]。在较新版本中,此实现被标记为 Deprecated,官方推荐使用 llama_index.core.memory.Memory——新的 Memory 类提供更清晰的接口,支持多种存储后端。这个更迭过程表明,LlamaIndex 正在将记忆从对话缓冲的单一实现抽象为可插拔模块。
Letta(原 MemGPT)
Letta 将记忆作为系统的核心抽象,提供了 memory block、archival memory、recall memory 等结构化概念。与 LangChain 和 LlamaIndex 不同,Letta 不把记忆视为对话的“辅助模块”,而是整个 Agent 状态管理的基础。模型通过定义好的工具操作 memory block,外部存储与上下文之间的调度由 Letta 服务负责。代价是框架本身较重,与现有 Node.js 应用的集成难度相比前两者更高。
框架选择的取舍
读 LangChain、LlamaIndex、Letta 的源码和文档,会发现它们各自的侧重点不同。用表格做简要对齐:
| 框架 | 记忆抽象 | 适用定位 | 注意点 |
|---|---|---|---|
| LangChain | Conversation Memory 类 / LangGraph 持久化 | 已有 LangChain 生态,快速接入对话历史管理 | 传统 Memory 类与新版 LangGraph 状态字段的概念迁移 |
| LlamaIndex | ChatSummaryMemoryBuffer / Memory | 以检索为核心的应用,需要对话与检索深度结合 | 部分旧接口已弃用,应直接用新 Memory API |
| Letta(MemGPT) | Memory Block / Archival / Recall | 需要操作系统级上下文调度的复杂 Agent | 定制性强,集成和维护成本较高 |
上述总结针对的是框架在“记忆管理”维度上的差异,不涉及框架整体优劣的评价。实际选型还要考虑语言生态、部署方式、社区活跃度等因素。
14 限制:长上下文模型与外部记忆方案
长上下文模型(上下文窗口达到 200k 或更高)确实减轻了短期记忆的压力。用户可以在不裁剪历史的情况下完成更长的对话或更长的文档分析。这带来两个变化:
- 短期记忆的“容器”变大了:原来必须截断的 20 轮对话,现在可以一次性放入窗口。
- 长期记忆的价值没有消失:即使窗口可以容纳大量 token,也容不下用户数月的全部交互记录。长期记忆仍然负责“在需要时把最相关的历史取回”。
窗口变长不等于记忆效果变好。模型对窗口中间部分的关注度可能低于首尾(即“lost in the middle”效应),长上下文中噪声信息也可能干扰推理。因此,长上下文模型并不能替代检索和调度,而是把记忆管理的重心从“物理裁剪”移向“选择性注入”——决定哪些信息放入窗口,比简单地把窗口用满更重要。
选择长上下文模型还是外部记忆方案,可以从三个方面比较:
- 成本:输入 token 随上下文长度线性增长。长上下文把大量历史放入一次请求中,适合偶发长对话;高频长对话的输入成本会更高。外部记忆每次只注入检索结果,输入量较少,但要为 embedding 检索和摘要更新支付额外费用。
- 延迟:长上下文会加重模型 prefill 阶段的计算量,首 token 延迟随输入规模增加。外部记忆在生成之前增加一次检索往返,但该延迟通常远小于长上下文带来的额外推理延迟;如果检索结果质量低,可能还要进行第二次检索。
- 准确度:长上下文中的信息分散可能降低模型对关键信息的关注度。外部记忆通过相关性筛选提高命中准确率,但筛选本身可能出现遗漏。
基本的设计思路是:当对话历史可以被压缩到可控 token 预算内时,直接使用上下文窗口即可;当历史规模持续超过预算,或大部分历史与当前请求无关时,使用摘要与检索,把窗口留给最相关的信息。
参考链接
- [1] https://docs.typingmind.com/prompts/automatic-prompt-caching
- [2] https://docs.nano-gpt.com/api-reference/miscellaneous/prompt-caching
- [3] https://github.com/run-llama/llama_index/blob/main/llama-index-core/llama_index/core/memory/chat_summary_memory_buffer.py
- [4] https://github.com/didilili/ai-agents-from-zero/blob/main/9-LangChain%E6%A6%82%E8%BF%B0%E4%B8%8E%E6%9E%B6%E6%9E%84.md
- [5] https://dl.acm.org/doi/10.1145/3748302
