一个小 LLM 技巧:为 AI 助手提供长期记忆
Source: Dev.to
一个小技巧:让 AI 助手拥有长期记忆
在使用大型语言模型(LLM)时,我们经常会遇到一个问题:模型对之前的对话内容记忆有限。即使在同一个会话中,模型也只能“看到”最近的几千个 token,这导致它很快就会忘记之前的重要信息。下面介绍一种简单的办法,利用外部存储(如向量数据库)为 LLM 提供长期记忆。
思路概述
-
捕获关键信息
当用户提供重要信息(例如名字、偏好、项目细节)时,使用 LLM 生成一个结构化的摘要(JSON、YAML 等),并将其保存到向量数据库中。 -
检索相关记忆
在后续对话中,先用当前上下文(或用户的查询)在向量数据库中进行相似度搜索,取回最相关的记忆条目。 -
注入记忆
将检索到的记忆以提示(prompt)的形式注入到 LLM 的上下文中,使模型能够在生成回复时参考这些长期记忆。
示例实现(Python + LangChain)
下面的代码演示了如何使用 LangChain、OpenAI 和 Chroma 向量数据库实现上述流程。代码块保持原样,不进行翻译。
from langchain.llms import OpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
import json
# 初始化 LLM 和向量数据库
llm = OpenAI(temperature=0)
embeddings = OpenAIEmbeddings()
db = Chroma(collection_name="memory", embedding_function=embeddings)
# 1️⃣ 捕获关键信息的提示模板
capture_template = PromptTemplate(
input_variables=["user_input"],
template="""
You are an AI assistant. Summarize the important facts from the user's input in JSON format.
Only include facts that are likely to be useful later.
User input: {user_input}
JSON:"""
)
capture_chain = LLMChain(llm=llm, prompt=capture_template)
def capture_and_store(user_input: str):
# 让 LLM 生成结构化摘要
result = capture_chain.run(user_input=user_input)
try:
data = json.loads(result)
except json.JSONDecodeError:
# 如果 LLM 没有返回有效 JSON,直接返回
return
# 将摘要保存到向量库(使用原始文本作为检索依据)
text = json.dumps(data)
db.add_texts([text])
# 2️⃣ 检索记忆的提示模板
retrieve_template = PromptTemplate(
input_variables=["user_query", "memory"],
template="""
You are an AI assistant. Use the provided memory to answer the user's question.
Memory: {memory}
Question: {user_query}
Answer:"""
)
retrieve_chain = LLMChain(llm=llm, prompt=retrieve_template)
def answer_with_memory(user_query: str):
# 在向量库中检索最相似的记忆(取前 3 条)
results = db.similarity_search(user_query, k=3)
memory = "\n".join([r.page_content for r in results])
# 将记忆注入到 LLM 的提示中
answer = retrieve_chain.run(user_query=user_query, memory=memory)
return answer
# 示例对话
capture_and_store("My name is Alice and I love hiking in the mountains.")
capture_and_store("I'm working on a project about renewable energy.")
print(answer_with_memory("What should I pack for my next hike?"))
关键点解释
| 步骤 | 目的 | 实现细节 |
|---|---|---|
| 捕获关键信息 | 将用户的关键事实转化为结构化数据,便于后续检索 | 使用 LLM 生成 JSON;如果 LLM 未返回有效 JSON,直接跳过存储 |
| 向量化存储 | 为每条记忆创建向量表示,以实现语义相似度搜索 | OpenAIEmbeddings 将文本转为向量;Chroma 负责存储与检索 |
| 检索记忆 | 根据当前查询找到最相关的记忆条目 | similarity_search(query, k) 返回前 k 条最相似的记忆 |
| 注入记忆 | 将检索到的记忆作为上下文喂给 LLM,提升回答的连贯性和准确性 | 在提示中使用占位符 {memory},让 LLM 在生成答案时参考这些信息 |
常见问题
-
记忆会不会无限增长导致检索变慢?
可以定期对记忆进行归档或删除旧的、低相关性的条目。向量数据库通常支持删除和更新操作。 -
如何防止敏感信息泄露?
在存储前对敏感字段进行脱敏或加密;检索时仅返回必要的非敏感摘要。 -
LLM 生成的 JSON 可能不合法怎么办?
在代码中加入try/except捕获json.JSONDecodeError,并在必要时让模型重新生成或直接跳过该条记忆。
小结
通过将重要的对话信息以结构化形式保存到向量数据库,并在后续对话中检索并注入这些记忆,我们可以在不改变底层模型的情况下,为 AI 助手提供“长期记忆”。这种方法简单、可扩展,并且可以与任何支持文本嵌入的模型配合使用。
本文基于作者在 Dev.to 上的原文改编,保留了所有代码示例和技术细节。
我有一个忏悔
我经常忘记自己的项目是怎么运作的。
通常的情形是这样的:我花一个周末搭建一个概念验证(Proof of Concept),随后生活的琐事占据了三周的时间,当我终于回到项目时,看到的文件夹结构对 “Past Morten” 来说很有条理,但对 “Current Morten.” 来说却是彻底的谜团。
现在我通常会有一个 AI 助手帮忙。但即便是它们,也会在我所谓的 “session void”(会话空白)中挣扎。它们能看到代码,却看不到三周前所做更改背后的 意图。它们在一次长聊天中学到一些东西,但一旦开启新会话,那种 “啊哈!” 的瞬间就消失了。
我一直在尝试一种新技术来解决这个问题。虽然还处于早期阶段,但初步结果已经足够令人鼓舞,以至于我想与大家分享。我把它称为 AGENTS.md 技巧。
Beyond the System Prompt
许多使用 AI 进行编码的开发者已经熟悉项目级提示或说明文件的概念。通常,这些是静态的——你写一次,它们就告诉代理如何格式化代码或偏好使用哪个库。
我正在进行的实验是停止把 AGENTS.md 当作静态说明手册,而是把它视为 long‑term memory。
Source: …
策略:回忆的相似性
目标是给代理(agent)一套指令,使其在不同会话和不同代理之间持续构建并保持对项目的理解。
我已经开始在项目根目录的 AGENTS.md 文件中添加这些核心的“记忆”指令:
- 维护真相来源 – 每当代理进行重要的架构更改或学习到项目的“隐藏规则”时,必须更新
AGENTS.md。 - 外化发现 – 每当代理花时间“探索”复杂的逻辑流程以理解它时,应该在
./docs/中新建一个文件,写入该发现的简短摘要。 - 维护地图 – 必须保持
./docs/index.md与这些发现列表同步更新。 - 引用个人标准 – 我会指向一个全局目录(例如
~/prompts/),其中保存我对 JavaScript 风格、测试模式等的一般偏好。这些标准的优势在于它们是 跨项目 的。- (注:如果你的 AI 工具——如 Cursor 或 GitHub Copilot——允许引用或索引当前项目根目录之外的文件,这种方式效果最佳。)
- 如果你在团队中工作,可以将这些文件托管在共享的 Git 仓库,并指示代理在文件夹缺失时克隆它们。这样可以确保代理学习到“我们全局的构建方式”,而不仅仅是“这个项目的工作方式”。
为什么我喜欢这样(尤其是对已有项目)
LLMs 擅长读取眼前的内容,但对于发生在其他聊天线程或尚未打开的文件中的事,它们几乎没有“记忆”。
通过强迫代理记录它自己的“恍然大悟”时刻,我实际上在会话之间搭建了一座桥梁。
- 连续性 – 当我开启新会话时,代理读取
AGENTS.md,看到index.md中的映射,立刻拥有了像是已经在项目上工作了数周的上下文。 - 有机增长 – 我不必坐下来写《大手册》。文档会在复杂之处自然增长,因为正是代理需要花费精力去理解的地方。
- 遗留代码 – 这对旧项目来说是救星。我不需要事先完整记录所有内容。我只需告诉代理:“在你弄清楚的过程中,把它写下来”。
演化模式
这个设置的一个最酷的副作用是它处理不断演进的标准的方式。
如果我决定要把箭头函数换回普通的函数声明,我不仅仅是修改代码;我会告诉代理。由于代理被指示要维护记忆,它实际上可以建议更新我的全局标准。
我已经指示它,如果发现我持续偏离我的 javascript-writing-style.md,就应该询问:
“嘿,看来你正在远离箭头函数。我应该更新你的全局模式文件以反映这一点吗?”
这让我的偏好像代码本身一样保持活力。
初步结果
它完美吗?还没有。有时代理需要一点提醒才能记住他们的文档职责,我仍在寻找最佳平衡,以防 ./docs/ 文件夹变得杂乱。
但到目前为止,它已经大幅降低了我和 AI 的 “What was I thinking?” 因素。
隐藏的好处:文档园艺
超越仅仅在下次聊天会话中“记住”内容,这一模式为项目的健康创造了良性循环。
- 自动化园艺 – 定期地,你可以让代理人检查
./docs/中散落的笔记,并将它们重新格式化为真正的、结构化的项目文档。由于代理人已经捕获了有效工作所需的技术细节,这些文档往往比人类从头编写的更准确、更详尽。 - 审阅者的上下文 – 当你打开 Pull Request 时,文档的变更为人工审阅者提供了极好的上下文。如果你引入了足以记录的更改,看到代理人的“记忆”随代码一起更新,就能让变更背后的 原因 更加透明。
- 高级技巧:自动文档钩子 – 对于真正懒惰的人(比如我),你可以设置一个 Git 钩子,在提交后运行代理人。它会审查你的手动更改,并确保相应地更新
./docs/文件夹。这意味着即使你绕过 AI 进行快速修复,项目的“记忆”也能保持同步。
“Memory” 模板
如果你想尝试一下,这里是我一直在使用的行为模板。请注意:这仍在持续完善中。 我预计在接下来的几个月里会大幅改进这些指令。
# Agent
# (Insert the rest of your template here – e.g., system prompt, duties, examples, etc.)
欢迎根据自己的工作流程自行调整模板,祝记录愉快!
本仓库指南
重要:您负责维护本项目的长期记忆。
1. 您的行为规则
- 增量文档 – 当您发现系统的复杂部分或组件之间的非显而易见的关联时,请在
./docs/中创建一个文件进行说明。 - 自我纠正 – 如果您注意到
./docs/(或本文件)中的现有文档与当前代码不符,请立即更新。 - 索引维护 – 保持
./docs/index.md为最新状态,确保它始终链接到所有相关文档,提供简洁的概览。 - 个人标准 – 在开始重要工作之前,请查阅
~/prompts/index.md以了解我的偏好(例如javascript‑writing‑style.md、good‑testing‑patterns.md等)。 - 进化反馈 – 如果您反复遇到与这些标准相冲突的请求或代码,请询问是否需要更新
~/prompts/中的全局文件,以反映新的模式。
2. 项目背景
用户或代理人:
在此简要描述当前状态和技术栈,为代理人提供即时的起点。
UI 控件(可选)
Enter fullscreen mode
Exit fullscreen mode
让 AI 成为合作伙伴,而非访客
每天早上第一次看到你的代码的工具,与能够记住你之前架构决策的工具之间的差距是巨大的。
这是一项简单的实验,但任何人今天都可以尝试。它将代理从代码库中的临时访客转变为帮助你保持对实际构建内容的持续理解的合作伙伴。
这对你有效吗?我还不知道,但我很期待继续完善它。