大语言模型(Large Language Model,LLM)本身是无状态的。一次请求发过去,模型只知道当前 prompt 里写了什么;之前聊过什么、用户偏好是什么、某个 Agent 曾经调用工具失败过几次,模型并不会天然记住。
常见做法是把历史对话、用户资料、业务文档全都拼进上下文窗口。这个办法能用,但问题也很明显:
- 上下文越塞越长,费用和延迟都会上升;
- 历史内容没有结构化,相关信息容易被噪声淹没;
- 文档知识和用户记忆混在一起,更新、删除、纠错都很麻烦;
- 多轮对话、跨会话、跨项目复用状态时,应用层要写大量胶水代码。
MemOS 的思路不是单纯把上下文窗口拉长,而是把“记忆”当成系统资源管理。它为 AI Agent 提供长期记忆、知识库接入、上下文检索、消息写入、工具轨迹记录和调度能力,让应用不必每次都把全部历史重新塞给模型。
项目地址:
https://github.com/MemTensor/MemOS
记忆和知识库不是同一件事
很多 AI 应用会把“知识库”和“记忆”混在一起讲,但它们解决的问题不一样。
| 类型 | 主要内容 | 变化频率 | 典型来源 | 适合回答的问题 |
|---|---|---|---|---|
| 知识库 | PDF、Word、网页、产品文档、技术手册、业务规则 | 相对低 | 文件上传、URL 解析、人工维护 | “某份文档里怎么规定?”“接口参数是什么?” |
| 记忆 | 用户偏好、历史决策、对话摘要、长期目标、Agent 工具调用经验 | 相对高 | 对话过程、用户行为、Agent 执行轨迹 | “这个用户通常喜欢什么格式?”“上次为什么没用这个工具?” |
| 上下文 | 当前问题需要临时带入模型的片段 | 每次请求变化 | 知识库检索结果、记忆检索结果、当前消息 | “这次回答需要参考哪些材料?” |
知识库更像稳定的外部资料库,记忆更像用户和 Agent 在运行过程中积累下来的状态。MemOS 的关键价值在于把这两类信息拆开管理,再在每次请求时按需组合成上下文。
flowchart LR
U[用户问题] --> A[AI 应用]
A --> M[MemOS]
subgraph MEM[MemOS 管理层]
M --> R1[检索长期记忆]
M --> R2[检索知识库]
M --> R3[读取历史消息]
M --> W1[写入新消息]
M --> W2[更新记忆]
end
R1 --> C[构造上下文]
R2 --> C
R3 --> C
C --> L[大语言模型]
L --> A
A --> U
A --> W1
W1 --> W2
这条链路的重点是:模型仍然只接收一次请求,但这次请求里的上下文不再靠应用层硬拼,而是由记忆系统筛选、组织和更新。
MemOS 主要解决什么问题
MemOS 可以理解成 AI Agent 的记忆操作层。它不是一个简单的向量库封装,也不只是文件检索工具,而是围绕“记忆生命周期”做了一层系统化管理。
它覆盖几类典型能力:
| 能力 | 作用 |
|---|---|
| 长期互动记忆 | 保存跨会话的用户偏好、历史行为、关键事实,不让对话每次从零开始 |
| 动态更新 | 新对话发生后,可以写入、修正或补充记忆 |
| 知识库接入 | 支持把文件、URL 等内容接入知识库,形成可检索的长期资料 |
| 多项目共享 | 一个知识库可以被多个项目复用,避免重复上传和重复解析 |
| 多模态记忆 | 不只局限于纯文本,也可面向图片、文档、图表等内容做理解和记忆 |
| 工具轨迹记忆 | 记录 Agent 调用工具的过程、结果和失败经验 |
| 调度机制 | 通过队列和调度处理高并发下的记忆读写、更新任务 |
如果一个应用只是临时问答,普通 RAG(Retrieval-Augmented Generation,检索增强生成)已经够用;如果应用需要长期服务同一个用户,或者 Agent 会持续执行任务、调用工具、修正策略,那么单纯 RAG 就会显得无状态。
MemOS 和传统 RAG 的区别
RAG 的核心流程是“把文档切片、向量化、检索、塞进 prompt”。它擅长解决外部知识接入,但不擅长解决长期状态。
flowchart LR
D[业务文档] --> C[切分 Chunk]
C --> E[向量化 Embedding]
E --> V[(向量库)]
Q[用户问题] --> S[相似度检索]
V --> S
S --> P[拼接 Prompt]
P --> L[大语言模型]
L --> A[回答]
传统 RAG 的一次问答结束后,系统通常不会自动记住新的用户偏好,也不会自然沉淀 Agent 的操作经验。下一次请求仍然只靠当前问题去检索文档。
MemOS 的工作方式更接近“RAG + 长期记忆 + 状态管理”。
| 对比点 | 传统 RAG | MemOS |
|---|---|---|
| 核心对象 | 文档片段 | 记忆、知识库、消息、工具轨迹 |
| 状态 | 通常无状态 | 面向跨会话状态 |
| 数据更新 | 多依赖人工重新上传或重建索引 | 对话过程中可持续写入和修正 |
| 适合内容 | 稳定资料、产品文档、知识手册 | 用户偏好、历史决策、Agent 经验、长期目标 |
| 检索目标 | 找到相关文档片段 | 找到相关记忆、知识片段、历史消息 |
| 工程侧重点 | 文档检索准确率 | 记忆生命周期、调度、上下文连续性 |
RAG 仍然有价值。它解决的是“模型不知道某份资料”的问题;MemOS 进一步解决“模型不知道自己和用户之前发生过什么”的问题。
MemOS 的核心架构
MemOS 把记忆拆成多种类型,并通过调度层管理读写、更新和复用。可以把它看成 AI 应用和底层存储之间的一层记忆操作系统。
flowchart TB
APP[AI 应用 / Agent] --> API[MemOS API]
API --> SEARCH[搜索记忆]
API --> ADD[添加消息]
API --> GET[获取消息]
API --> KBAPI[知识库接口]
KBAPI --> INGEST[文件 / URL 解析]
INGEST --> CHUNK[分段]
CHUNK --> INDEX[索引与存储]
SEARCH --> SCHED[调度层]
ADD --> SCHED
GET --> SCHED
subgraph MEMORY[记忆层]
TM[Textual Memory<br/>文本记忆]
AM[Activation Memory<br/>激活记忆]
PM[Parametric Memory<br/>参数记忆]
TOOL[Tool Memory<br/>工具记忆]
end
SCHED --> TM
SCHED --> AM
SCHED --> PM
SCHED --> TOOL
INDEX --> TM
subgraph STORE[底层存储]
DB[(结构化存储)]
VDB[(向量索引)]
QUEUE[(Redis Streams 队列)]
end
TM --> DB
TM --> VDB
AM --> DB
PM --> DB
TOOL --> DB
SCHED --> QUEUE
几个记忆类型需要分开理解。
Textual Memory:文本记忆
Textual Memory 保存可以直接用自然语言表达的事实和经验,例如:
- 用户偏好:“回答时喜欢先给结论,再给代码。”
- 历史事实:“用户之前上传过某项目的接口文档。”
- 对话摘要:“上一次讨论决定使用 Redis Streams 做任务队列。”
- 业务上下文:“当前项目的知识库 ID 是某个固定值。”
这是最容易理解的一类记忆,也是对话型 AI 应用最常用的部分。
Activation Memory:激活记忆
Activation Memory 用来保存推理过程中可复用的中间状态或激活信息,目标是减少重复计算和上下文 token 消耗。
它不像文本记忆那样面向人类阅读,而更偏向模型执行层面的加速。适合在同类任务反复出现、上下文结构相似时复用。
Parametric Memory:参数记忆
Parametric Memory 面向模型参数层面的记忆,例如保存 LoRA(Low-Rank Adaptation,低秩适配)权重。
LoRA 常用于让模型在较小代价下适配某类任务或领域。把这类参数变化纳入记忆系统后,Agent 不只是保存文本经验,也可以复用任务相关的参数适配结果。
Tool Memory:工具记忆
Tool Memory 记录 Agent 调用工具的轨迹,包括调用了什么工具、传了什么参数、返回了什么结果、失败原因是什么。
这类记忆很适合 Agent 场景。比如某个工具要求参数必须是 ISO 时间格式,如果 Agent 曾经因为格式错误调用失败,那么后续再遇到类似任务时,可以优先参考这条工具记忆,而不是重复犯错。
sequenceDiagram
participant A as Agent
participant M as MemOS
participant T as 外部工具
A->>M: 查询工具相关记忆
M-->>A: 返回历史调用经验
A->>T: 使用修正后的参数调用工具
T-->>A: 返回结果
A->>M: 写入本次工具调用轨迹
M->>M: 更新 Tool Memory
Redis Streams 调度器
记忆读写并不总是适合同步完成。文件解析、分段、生成记忆、更新索引、写入工具轨迹,都可能带来额外耗时。
MemOS 引入调度机制,并使用 Redis Streams 做多级队列,可以把高并发下的任务拆开处理:
- 用户请求链路优先保证响应;
- 文档解析和记忆生成可以异步处理;
- 大量消息写入可以排队消费;
- 不同类型记忆可以走不同优先级。
这种设计让 MemOS 不只是“存一下数据”,而是把记忆当成持续变化的系统资源来调度。
一次对话在 MemOS 中怎么流动
对话时,应用层通常只需要围绕三个动作组织逻辑:
- 搜索记忆:根据用户 ID、问题、知识库 ID 找到相关上下文;
- 添加消息:把用户输入和模型回复写入 MemOS;
- 获取消息:读取历史消息,用于会话连续性或构造上下文。
sequenceDiagram
participant U as 用户
participant APP as AI 应用
participant M as MemOS
participant KB as 知识库 / 记忆索引
participant LLM as 大语言模型
U->>APP: 输入问题
APP->>M: search_memory(user_id, query, knowledge_base_ids)
M->>KB: 检索相关记忆和知识片段
KB-->>M: 返回候选上下文
M-->>APP: 返回可放入 prompt 的内容
APP->>M: get_messages(user_id)
M-->>APP: 返回近期消息
APP->>LLM: 当前问题 + 相关记忆 + 知识片段 + 近期消息
LLM-->>APP: 生成回答
APP->>M: add_message(user_id, question, answer)
M->>M: 更新长期记忆
APP-->>U: 返回回答
这个流程有一个好处:上下文不再靠“全部历史全塞进去”,而是通过检索和调度挑出当前最相关的部分。
创建知识库
MemOS 支持通过控制台创建知识库,并上传文件或填入 URL。常见流程如下:
| 步骤 | 操作 | 结果 |
|---|---|---|
| 创建知识库 | 在 MemOS 控制台新建 Knowledge Base | 得到一个知识库容器 |
| 上传资料 | 上传 PDF、Word 等文件,或填写 URL | MemOS 接收原始资料 |
| 自动处理 | 存储、解析、分段、生成可检索内容 | 文档进入处理队列 |
| 等待可用 | 文档状态变成可用 | 可在对话中检索 |
| 复制 ID | 复制知识库 ID | 代码里作为 KNOWLEDGE_BASE_IDS 使用 |
知识库 ID 很重要。应用调用 MemOS 时需要告诉它检索哪些知识库,否则系统无法判断当前问题要参考哪批资料。
准备运行环境
Python 环境建议使用虚拟环境隔离依赖:
python -m venv .venv
# macOS / Linux
source .venv/bin/activate
# Windows PowerShell
.venv\Scripts\Activate.ps1
安装常用依赖:
pip install openai requests python-dotenv
datetime 是 Python 标准库,不需要通过 pip install datetime 安装。
准备两个 Key:
| Key | 用途 |
|---|---|
MEMOS_API_KEY | 调用 MemOS API |
OPENAI_API_KEY | 调用大模型服务商 API |
可以把配置放进 .env,避免把密钥写死在代码里:
MEMOS_API_KEY=你的_MemOS_Key
OPENAI_API_KEY=你的_OpenAI_Key
MEMOS_BASE_URL=https://memos.memtensor.cn/api/openmem/v1
KNOWLEDGE_BASE_IDS=["你的_知识库ID"]
对话助手的代码结构
应用层不需要直接关心 MemOS 内部怎样存储和调度,只要把它封装成一个客户端即可。核心接口保持三个动作:搜索记忆、添加消息、获取消息。
import json
import os
from dataclasses import dataclass
from typing import Any, Dict, List
import requests
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
@dataclass
class MemOSConfig:
api_key: str
base_url: str
knowledge_base_ids: List[str]
class MemOSClient:
"""
应用层只依赖这三个动作:
- search_memory:搜索相关记忆和知识库内容
- add_message:写入用户消息和模型回复
- get_messages:读取近期会话消息
具体 HTTP 路径以 MemOS 控制台或仓库 README 的 API 说明为准。
"""
def __init__(self, config: MemOSConfig):
self.config = config
self.headers = {
"Authorization": f"Bearer {config.api_key}",
"Content-Type": "application/json",
}
def _post(self, path: str, payload: Dict[str, Any]) -> Dict[str, Any]:
url = f"{self.config.base_url.rstrip('/')}/{path.lstrip('/')}"
response = requests.post(url, headers=self.headers, json=payload, timeout=30)
response.raise_for_status()
return response.json()
def search_memory(self, user_id: str, query: str) -> List[Dict[str, Any]]:
payload = {
"user_id": user_id,
"query": query,
"knowledge_base_ids": self.config.knowledge_base_ids,
}
# 按 MemOS API 文档替换为搜索记忆接口路径
data = self._post("REPLACE_WITH_SEARCH_MEMORY_PATH", payload)
return data.get("items", [])
def add_message(self, user_id: str, role: str, content: str) -> None:
payload = {
"user_id": user_id,
"role": role,
"content": content,
}
# 按 MemOS API 文档替换为添加消息接口路径
self._post("REPLACE_WITH_ADD_MESSAGE_PATH", payload)
def get_messages(self, user_id: str, limit: int = 20) -> List[Dict[str, Any]]:
payload = {
"user_id": user_id,
"limit": limit,
}
# 按 MemOS API 文档替换为获取消息接口路径
data = self._post("REPLACE_WITH_GET_MESSAGES_PATH", payload)
return data.get("messages", [])
有了 MemOSClient,对话编排就比较清晰了:先检索记忆,再读取近期消息,然后调用大模型,最终把新消息写回 MemOS。
class KnowledgeBaseAssistant:
def __init__(self, memos: MemOSClient):
self.memos = memos
self.llm = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
def chat(self, user_id: str, user_input: str) -> str:
memories = self.memos.search_memory(user_id=user_id, query=user_input)
history = self.memos.get_messages(user_id=user_id, limit=10)
memory_context = self._format_memories(memories)
history_context = self._format_history(history)
messages = [
{
"role": "system",
"content": (
"你是一个有长期记忆能力的助手。"
"回答时优先参考给定的知识库内容、用户记忆和近期对话。"
"如果上下文不足,不要编造事实。"
),
},
{
"role": "system",
"content": f"相关记忆和知识库内容:\n{memory_context}",
},
{
"role": "system",
"content": f"近期对话:\n{history_context}",
},
{
"role": "user",
"content": user_input,
},
]
response = self.llm.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
)
answer = response.choices[0].message.content
self.memos.add_message(user_id=user_id, role="user", content=user_input)
self.memos.add_message(user_id=user_id, role="assistant", content=answer)
return answer
@staticmethod
def _format_memories(memories: List[Dict[str, Any]]) -> str:
if not memories:
return "暂无相关记忆。"
lines = []
for index, item in enumerate(memories, start=1):
content = item.get("content") or item.get("text") or str(item)
lines.append(f"{index}. {content}")
return "\n".join(lines)
@staticmethod
def _format_history(messages: List[Dict[str, Any]]) -> str:
if not messages:
return "暂无近期对话。"
lines = []
for item in messages:
role = item.get("role", "unknown")
content = item.get("content", "")
lines.append(f"{role}: {content}")
return "\n".join(lines)
启动一个最小对话循环:
def load_config() -> MemOSConfig:
return MemOSConfig(
api_key=os.environ["MEMOS_API_KEY"],
base_url=os.environ["MEMOS_BASE_URL"],
knowledge_base_ids=json.loads(os.environ["KNOWLEDGE_BASE_IDS"]),
)
if __name__ == "__main__":
config = load_config()
memos = MemOSClient(config)
assistant = KnowledgeBaseAssistant(memos)
user_id = "test_user_001"
while True:
user_input = input("你:").strip()
if user_input.lower() in {"exit", "quit"}:
break
answer = assistant.chat(user_id=user_id, user_input=user_input)
print(f"AI:{answer}")
这段代码的关键不在 HTTP 路径,而在应用编排方式:
flowchart TD
A[用户输入] --> B[search_memory]
B --> C[get_messages]
C --> D[组装 Prompt]
D --> E[调用大模型]
E --> F[得到回答]
F --> G[add_message: 用户输入]
G --> H[add_message: 模型回复]
H --> I[MemOS 更新长期记忆]
把 MemOS 的 API 路径补齐后,这个结构就能变成一个带知识库和长期记忆的对话助手。
适合和不适合的场景
MemOS 更适合“长期运行的 AI 应用”,而不是一次性问答脚本。
| 场景 | 是否适合 | 原因 |
|---|---|---|
| 个人长期助手 | 适合 | 需要记住用户偏好、历史目标、常用资料 |
| 企业知识库问答 | 适合 | 文件和 URL 可进入知识库,多项目复用 |
| AI Agent 自动执行任务 | 适合 | 工具调用轨迹可以沉淀成 Tool Memory |
| 客服系统 | 适合 | 用户历史、问题偏好、处理过程需要跨会话复用 |
| 单次文档问答 Demo | 不一定需要 | 普通 RAG 已经能满足 |
| 强实时、极低延迟链路 | 需要谨慎 | 记忆检索和写入会带来额外链路 |
| 不允许保存用户数据的业务 | 需要谨慎 | 长期记忆涉及隐私、权限、删除策略 |
| 完全依赖模型内部知识的聊天 | 不适合 | 没有外部知识和长期状态,记忆系统价值不大 |
接入时容易踩的坑
1. 不要把所有历史都当记忆
每句话都写成长期记忆,会让系统越来越吵。更合理的做法是区分消息和记忆:
- 消息用于短期会话回看;
- 记忆用于保存长期有价值的事实、偏好和经验;
- 无意义寒暄、临时错误输入、重复信息不应该长期保留。
2. 知识库要有版本意识
文件更新后,旧内容可能仍然存在索引里。知识库需要配合版本、更新时间、来源信息管理,否则模型可能引用过期资料。
建议给每个知识片段保留这些元信息:
| 字段 | 作用 |
|---|---|
source | 来源文件或 URL |
version | 文档版本 |
updated_at | 更新时间 |
project_id | 所属项目 |
permission_scope | 权限范围 |
3. 用户 ID 和项目 ID 要设计清楚
长期记忆必须绑定身份边界。个人记忆不能串到另一个用户,项目知识库也不能误用于无权限项目。
常见隔离维度:
tenant_id / organization_id
project_id
user_id
agent_id
knowledge_base_id
4. 记忆检索结果要控制长度
MemOS 能找到相关内容,但应用仍然要控制 prompt 的大小。可以给检索结果设置:
- 最大条数;
- 最大 token 数;
- 相似度阈值;
- 时间范围;
- 记忆类型过滤。
否则上下文窗口仍然可能被占满。
5. 记忆不是事实校验系统
用户说过的话不一定正确,Agent 以前做过的决策也不一定永远正确。长期记忆需要有“可修正”的机制,而不是把所有历史当真理。
适合给记忆增加可信度字段:
{
"content": "用户偏好回答中包含示例代码。",
"type": "preference",
"confidence": 0.86,
"updated_at": "2026-06-07T10:00:00Z"
}
一个更工程化的接入分层
在生产代码里,可以把 MemOS 放在独立的 Memory Service 中,业务应用不直接操作底层 API。
flowchart LR
WEB[Web / App] --> BFF[业务后端]
BFF --> MS[Memory Service]
BFF --> LLM[LLM Gateway]
MS --> MEMOS[MemOS]
MEMOS --> KB[(知识库)]
MEMOS --> MEM[(长期记忆)]
MEMOS --> MSG[(消息历史)]
BFF --> AUTH[权限系统]
MS --> AUTH
这样做有几个具体好处:
- 统一处理用户权限和知识库权限;
- 统一控制记忆写入策略;
- 统一做脱敏、审计和删除;
- 替换底层记忆系统时,业务代码不用大改;
- 多个 Agent 可以共享同一套记忆服务。
MemOS 的核心价值
MemOS 把 AI 应用中的三类东西拆开了:
- 文档资料放进知识库;
- 用户和 Agent 的长期状态放进记忆;
- 每次请求需要的内容再动态组装进上下文。
这种拆分让 AI Agent 不必依赖越来越长的 prompt 维持连续性,也不用把所有状态逻辑堆在业务代码里。对于长期助手、企业知识库、多轮任务 Agent、工具型 Agent 来说,MemOS 提供的是一套围绕“记忆生命周期”的工程框架,而不只是一次检索调用。