2026 年,大模型智能体正在进入办公、研发和个人助理场景。它们不再只完成一次问答,而是持续参与项目、处理文档并适应用户习惯。长期协作要求智能体记住一些信息,但“记得越多越好”是一个危险的设计方向:偏好和稳定配置能够提升效率,临时状态与敏感内容却可能变成隐私、合规和错误传播的来源。
真正需要设计的不是一个无限增长的聊天记录库,而是一套包含写入判断、生命周期、访问控制和删除能力的记忆系统。
记忆不是聊天记录的另一个名字
完整保存对话最容易实现,却会把不同性质的信息混在一起。工程上可以把智能体接触到的信息分为四类:
| 信息类型 | 示例 | 默认处理 |
|---|---|---|
| 稳定偏好 | 使用中文回答、代码示例优先 Python | 可长期保存,并允许用户修改 |
| 工作配置 | 仓库路径、测试命令、文档模板 | 保存到限定作用域,定期确认 |
| 临时上下文 | 本轮调试假设、今天的会议安排 | 设置较短 TTL,到期删除 |
| 敏感信息 | 密码、令牌、身份证号、私密原文 | 默认拒绝写入长期记忆 |
这套划分的关键不是名称,而是不同类别必须有不同的保留策略。一个“回答尽量简洁”的偏好可能几个月都有效;“服务现在运行在 8080 端口”也许十分钟后就失效。若两者以相同方式进入向量库,检索系统就会持续把过期事实注入模型上下文。
长期记忆还应区分作用域。团队代码规范不该变成用户在所有项目中的个人偏好,某个仓库的构建命令也不该泄漏到另一个客户项目。常见作用域可以设计为:
user:跨会话的个人偏好。workspace:只对当前工作区有效的路径与工具配置。task:仅服务于当前任务,完成后即可清理。session:只在本次会话中使用,不进入长期存储。
写入前要经过一道“记忆闸门”
记忆系统至少需要回答五个问题:信息是否稳定、未来是否有用、来自谁、保存多久、是否包含敏感内容。不要让模型仅凭一句“这似乎很重要”就直接写数据库。模型可以提出候选记忆,但最终写入应经过确定性的安全规则和权限检查。
一条可操作的写入流程如下:
- 从对话中提取候选事实,同时保留来源和时间。
- 检测凭据、身份信息等敏感内容,命中后默认拒绝。
- 判断作用域、类型和有效期;无法判断时使用短期存储。
- 对影响较大的长期记忆请求用户确认。
- 写入后保留审计字段,并提供查询、更正和删除入口。
“用户喜欢深色主题”适合成为候选偏好;“这是我的 API Key”即使用户明确说“记住”,系统也不应直接放进普通记忆库。确有凭据托管需求时,应交给专门的密钥管理服务,只在执行工具调用时按权限临时获取。
一个可运行的最小记忆闸门
下面是一个仅使用 Python 标准库的示例。它不是特定产品的官方实现,而是一种可以这样实践的最小方案:敏感文本拒绝持久化,工作区信息设置有效期,用户偏好可以长期保存,并支持列出和删除记录。
将代码保存为 memory_gate.py,使用 Python 3.10 或更高版本运行:
import argparse
import re
import sqlite3
import time
import uuid
DB_PATH = "agent_memory.db"
SENSITIVE_PATTERNS = [
re.compile(r"\b(?:sk|ghp)_[A-Za-z0-9_-]{16,}\b"),
re.compile(r"\bpassword\s*[:=]\s*\S+", re.I),
re.compile(r"\b\d{17}[0-9Xx]\b"),
]
TTL_BY_SCOPE = {
"session": 0,
"task": 24 * 3600,
"workspace": 30 * 24 * 3600,
"user": None,
}
def connect():
db = sqlite3.connect(DB_PATH)
db.execute("""
CREATE TABLE IF NOT EXISTS memories (
id TEXT PRIMARY KEY,
scope TEXT NOT NULL,
content TEXT NOT NULL,
source TEXT NOT NULL,
created_at INTEGER NOT NULL,
expires_at INTEGER
)
""")
return db
def contains_sensitive_data(text):
return any(pattern.search(text) for pattern in SENSITIVE_PATTERNS)
def remember(scope, content, source="user-confirmed"):
if scope not in TTL_BY_SCOPE:
raise ValueError(f"unsupported scope: {scope}")
if scope == "session":
return "Skipped: session data is not persisted."
if contains_sensitive_data(content):
return "Rejected: possible sensitive data."
now = int(time.time())
ttl = TTL_BY_SCOPE[scope]
expires_at = now + ttl if ttl is not None else None
memory_id = str(uuid.uuid4())
with connect() as db:
db.execute(
"INSERT INTO memories VALUES (?, ?, ?, ?, ?, ?)",
(memory_id, scope, content, source, now, expires_at),
)
return memory_id
def list_active(scope=None):
now = int(time.time())
with connect() as db:
db.execute("DELETE FROM memories WHERE expires_at <= ?", (now,))
sql = "SELECT id, scope, content, source, expires_at FROM memories"
params = ()
if scope:
sql += " WHERE scope = ?"
params = (scope,)
return db.execute(sql, params).fetchall()
def forget(memory_id):
with connect() as db:
result = db.execute("DELETE FROM memories WHERE id = ?", (memory_id,))
return result.rowcount > 0
def main():
parser = argparse.ArgumentParser()
sub = parser.add_subparsers(dest="command", required=True)
add = sub.add_parser("remember")
add.add_argument("scope", choices=TTL_BY_SCOPE)
add.add_argument("content")
show = sub.add_parser("list")
show.add_argument("--scope", choices=TTL_BY_SCOPE)
delete = sub.add_parser("forget")
delete.add_argument("id")
args = parser.parse_args()
if args.command == "remember":
print(remember(args.scope, args.content))
elif args.command == "list":
for row in list_active(args.scope):
print(row)
else:
print("Deleted" if forget(args.id) else "Not found")
if __name__ == "__main__":
main()
可以直接测试不同策略:
python memory_gate.py remember user "回答代码问题时优先使用 Python"
python memory_gate.py remember workspace "测试命令是 pytest -q"
python memory_gate.py remember session "当前正在排查登录超时"
python memory_gate.py remember user "password=correct-horse-battery-staple"
python memory_gate.py list
这个示例故意保持简单。进入生产环境后,敏感信息检测不能只依赖正则表达式,还应结合结构化字段校验、数据分类服务和人工确认;数据库也需要加密、租户隔离与严格授权。若引入向量检索,还应把 scope、expires_at 和权限主体作为强制过滤条件,而不是只按语义相似度取回内容。
遗忘能力和记忆能力同样重要
删除按钮只是遗忘机制的一部分。生产系统还需要处理更新、冲突和错误传播。例如,用户先说“默认使用 Java 17”,后来改为 Java 21,此时应更新同一条偏好,而不是让两个版本同时参与检索。对于模型推断出来、但未经用户确认的信息,应降低可信度并缩短有效期。
每条记忆至少应带有以下元数据:
- 来源:用户明确输入、工具返回,还是模型推断。
- 创建时间与最后确认时间。
- 作用域和数据所有者。
- 到期时间或复核时间。
- 可信度以及是否经过用户确认。
- 用途限制,例如只允许用于代码生成,不允许用于推荐。
检索阶段也要遵循最小必要原则。当前任务只需要代码风格偏好时,就不该把用户全部历史记忆塞入提示词。这样既减少隐私暴露,也能降低无关上下文对模型判断的干扰。
上线前的检查清单
评估一个智能体记忆功能时,可以从以下问题入手:
- 用户能否看到系统保存了什么,并逐条修改或删除?
- 默认策略是否拒绝保存密码、令牌和高风险身份信息?
- 临时事实是否有 TTL,而不是永久保留?
- 用户、工作区、任务和会话数据是否真正隔离?
- 记忆是否记录来源,模型推断是否与用户确认的信息区分开?
- 检索是否同时执行权限、作用域和有效期过滤?
- 删除是否覆盖主库、缓存、向量索引和合理范围内的备份流程?
- 用户是否可以关闭长期记忆,并获得可理解的行为变化?
好的记忆系统不是记住最多内容,而是在正确的时间取回最少但足够的信息。把“是否写入”“保存多久”和“谁能读取”设计成明确的工程策略,智能体才能在保持连续体验的同时,避免把便利变成长期风险。