芥末
发布于 2026-01-20 / 0 阅读
0
0

MemOS 入门:把 AI 长期记忆和知识库分开管理

大语言模型(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 + 长期记忆 + 状态管理”。

对比点传统 RAGMemOS
核心对象文档片段记忆、知识库、消息、工具轨迹
状态通常无状态面向跨会话状态
数据更新多依赖人工重新上传或重建索引对话过程中可持续写入和修正
适合内容稳定资料、产品文档、知识手册用户偏好、历史决策、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 中怎么流动

对话时,应用层通常只需要围绕三个动作组织逻辑:

  1. 搜索记忆:根据用户 ID、问题、知识库 ID 找到相关上下文;
  2. 添加消息:把用户输入和模型回复写入 MemOS;
  3. 获取消息:读取历史消息,用于会话连续性或构造上下文。
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 等文件,或填写 URLMemOS 接收原始资料
自动处理存储、解析、分段、生成可检索内容文档进入处理队列
等待可用文档状态变成可用可在对话中检索
复制 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 提供的是一套围绕“记忆生命周期”的工程框架,而不只是一次检索调用。


评论