MemOS MemReader 实战指南:用示例代码把聊天、文档与图片解析为结构化记忆

原创2026-09-24 02:21:2439 阅读
文章标签:人工智能大模型Agent 记忆AI AgentRAG知识图谱dsh-plugin

MemOS MemReader 实战指南:用示例代码把聊天、文档与图片解析为结构化记忆

本文以 MemOS 仓库中的 MemReader 示例文档 为主线,结合 examples/mem_reader 目录下的完整示例代码、src/memos/mem_reader 核心实现与 docker/.env.example-full 环境变量配置,系统讲解 MemReader 模块如何把聊天记录、纯文本、文件与图片等异构输入解析成结构化的 TextualMemoryItem 记忆条目。读完本文,你将掌握 SimpleStructMemReaderMultiModalStructMemReader 两大读取器的配置方法、运行命令、Fast/Fine 双模式与 FAST→FINE 迁移流程,并能结合实际源码理解底层解析链路,直接上手把 MemReader 接入自己的 Agent 记忆管线。

一、MemReader 在 MemOS 中的定位

MemOS 是一套面向 LLM 与 AI Agent 的“自进化记忆操作系统”,其记忆体系分为明文记忆(explicit memory)、激活记忆(activation memory)与参数化记忆(parametric memory)等层次。MemReader 位于明文记忆的输入端:它的职责是把各种类型的原始输入数据——文本、聊天历史、文件、图片——解析为统一的结构化记忆格式(TextualMemoryItem),供下游的嵌入、检索与召回使用。

官方文档对 MemReader 的定义是:

MemReader is responsible for parsing various types of input data (text, chat history, files, images) into structured memory formats.

这一模块对外暴露两类读取器:

读取器 适用场景 特点
SimpleStructMemReader 标准文本型聊天应用 轻量、高效,专注纯文本聊天与文档
MultiModalStructMemReader 处理图片、文件附件与复杂工具交互的进阶 Agent 继承前者,并扩展多模态解析能力

两者对应配置类分别为 SimpleStructMemReaderConfigMultiModalStructMemReaderConfig,定义在 src/memos/configs/mem_reader.py 中,并通过 MemReaderConfigFactorybackendsimple_struct / multimodal_struct / strategy_struct)统一装配。

二、示例代码目录结构

MemReader 的全部示例位于 examples/mem_reader 目录,结构如下:

examples/mem_reader/
├── builders.py          # 工厂函数:初始化 LLM、Embedder、Parser 与两种 Reader
├── parser_demos/        # 单个解析器的独立演示
│   ├── demo_user.py     # UserParser 演示(解析用户消息)
│   ├── demo_assistant.py# AssistantParser 演示(解析助手消息)
│   ├── demo_system.py   # SystemParser 演示(解析系统消息)
│   ├── demo_tool.py     # ToolParser 演示(解析工具调用结果)
│   ├── demo_string.py   # StringParser 演示(解析纯字符串)
│   ├── demo_image.py    # ImageParser 演示(解析图片内容)
│   ├── demo_file_content.py  # FileContentParser 演示(解析文件内容)
│   ├── demo_text_content.py  # TextContentParser 演示(解析文本片段)
│   ├── demo_multi_modal.py   # MultiModalParser 演示(混合输入)
│   └── _base.py         # 所有 demo 的公共基类(初始化与通用步骤)
├── runners/             # 主执行脚本
│   ├── run_simple.py    # SimpleStructMemReader 的运行入口
│   └── run_multimodal.py# MultiModalStructMemReader 的运行入口
├── samples.py           # 全部样例数据(聊天日志、多模态用例、测试用例)
├── settings.py          # 配置管理(从 .env 加载环境变量)
├── utils.py             # 工具函数(格式化打印记忆条目等)
├── text1.txt            # 文档解析用样例文件
└── text2.txt            # 文档解析用样例文件

与示例对应的核心实现位于 src/memos/mem_reader,其中 simple_struct.py 实现了 SimpleStructMemReadermulti_modal_struct.py 实现了 MultiModalStructMemReaderread_multi_modal 包内则包含 String、System、User、Assistant、Tool、TextContent、FileContent、Image 八类消息解析器。

三、快速开始:配置与运行

3.1 环境变量配置

运行示例前,需要配置环境变量。将项目根目录的 .env.example 复制为 .env 并填入 API Key;示例中的 settings.py 会在导入时通过 load_dotenv() 自动加载。官方文档列出的关键变量包括:

  • OPENAI_API_KEY:用于 LLM 与 Embeddings。
  • MOS_CHAT_MODEL:默认对话模型(如 gpt-4o)。
  • MOS_EMBEDDER_MODEL:嵌入模型。

结合 settings.py 源码,实际生效的变量远比这三个更丰富。LLM 配置读取逻辑为:

  • MEMRADER_MODEL:MemReader 专用模型,未设置时回退到 MOS_CHAT_MODEL,最终默认 gpt-4o-mini
  • MEMRADER_API_KEY:未设置时回退到 OPENAI_API_KEY
  • MEMRADER_API_BASE:未设置时回退到 OPENAI_API_BASE,默认 https://api.openai.com/v1
  • MEMRADER_LLM_BACKEND:后端类型,openai(默认)或 ollama
  • MEMRADER_TEMPERATURE:采样温度,openai 后端默认 0.5,ollama 后端默认 0.0
  • MEMRADER_REMOVE_THINK_PREFIX:是否移除思考前缀,默认 true
  • MEMRADER_MAX_TOKENS:最大生成 token 数,默认 8192
  • OLLAMA_API_BASE:ollama 后端地址,默认 http://localhost:11434

Embedder 配置读取逻辑为:

  • MOS_EMBEDDER_BACKENDuniversal_apiollama,默认 ollama
  • MOS_EMBEDDER_PROVIDER:universal_api 后端下的提供方,默认 openai
  • MOS_EMBEDDER_API_KEY:未设置时回退到 OPENAI_API_KEY
  • MOS_EMBEDDER_MODEL:嵌入模型,默认 text-embedding-3-large;ollama 后端默认 nomic-embed-text:latest
  • MOS_EMBEDDER_API_BASE:默认回退到 OPENAI_API_BASE

仓库还提供了完整的参考配置 docker/.env.example-full,其中与 MemReader 直接相关的片段如下:

## MemReader / retrieval LLM
MEMRADER_MODEL=gpt-4o-mini
MEMRADER_API_KEY=sk-xxx                    # 可复用 OPENAI_API_KEY
MEMRADER_API_BASE=http://localhost:3000/v1
MEMRADER_MAX_TOKENS=5000

## Embedding & rerank
MOS_EMBEDDER_BACKEND=universal_api        # universal_api | ollama
MOS_EMBEDDER_PROVIDER=openai              # 当 universal_api 时必填
MOS_EMBEDDER_MODEL=bge-m3
MOS_EMBEDDER_API_BASE=http://localhost:8000/v1
MOS_EMBEDDER_API_KEY=EMPTY
OLLAMA_API_BASE=http://localhost:11434

## Reader chunking
MEM_READER_BACKEND=simple_struct           # simple_struct | strategy_struct
MEM_READER_CHAT_CHUNK_TYPE=default         # default | content_length
MEM_READER_CHAT_CHUNK_TOKEN_SIZE=1600      # 每个 chunk 的 token 数(default 模式)
MEM_READER_CHAT_CHUNK_SESS_SIZE=10         # 每个 chunk 的会话数(default 模式)
MEM_READER_CHAT_CHUNK_OVERLAP=2            # chunk 间重叠

其中 MEM_READER_CHAT_CHUNK_* 系列变量用于控制聊天数据的分块策略,与 SimpleStructMemReader 源码中的滑动窗口逻辑(chat_window_max_tokens,默认 1024 token)互相呼应。需要注意:.env.example-full 面向整体部署场景,示例脚本 settings.py 只消费其中的 LLM / Embedder / Chunker 三组配置。

3.2 运行 Simple Reader

run_simple.py 演示 SimpleStructMemReader,它针对基于文本的聊天历史与文档做了优化,运行命令:

python -m examples.mem_reader.runners.run_simple

该脚本覆盖官方文档列出的四项核心能力:

  1. Fine Mode(精细模式):调用 LLM 对对话进行深度理解,抽取结构化的记忆条目(key、value、tags、memory_type 等)。
  2. Fast Mode(快速模式):不调用 LLM,基于规则/正则直接生成记忆,速度快、成本低。
  3. Transfer(迁移):把 Fast 模式产出的扁平记忆列表喂给 fine_transfer_simple_mem(),升级为 Fine 模式的精细记忆。
  4. Document Parsing(文档解析):读取 text1.txttext2.txt 等文本文件并抽取记忆(仅支持 Fine 模式)。

从源码看,脚本通过 SimpleStructMemReaderConfig(**get_reader_config()) 构造读取器,随后以 {"user_id": "simple_user", "session_id": "simple_session"} 作为 info,调用 reader.get_memory(SIMPLE_CHAT_SCENE, type="chat", info=info, mode="fine")。每次调用结束后,会打印 📊 Total memory items 统计与每个窗口/会话(Window/Conversation)内的记忆条目。

3.3 运行 Multimodal Reader

run_multimodal.py 演示 MultiModalStructMemReader,它支持 StringMultimodalRaw 三种输入类型,并可通过命令行参数控制运行范围、模式与输出格式:

# 以 fine 模式运行全部示例
python -m examples.mem_reader.runners.run_multimodal --example all --mode fine

# 仅运行多模态输入示例
python -m examples.mem_reader.runners.run_multimodal --example multimodal

# 查看帮助
python -m examples.mem_reader.runners.run_multimodal --help

命令行参数解析自 argparse

参数 可选值 默认值 说明
--example all / string_message / multimodal / raw_input all 选择要运行的测试用例集合
--mode fast / fine fine 处理模式
--format text / json text 输出格式,json 会将记忆条目序列化为 JSON 数组

三种 --example 取值分别对应 samples.py 中的 STRING_MESSAGE_CASESMULTIMODAL_MESSAGE_CASESRAW_INPUT_CASESall 则顺序拼接三组用例。脚本会根据用例来源自动判定 input_typestring / chat / raw),再调用 reader.get_memory(scene_data, type=input_type, mode=args.mode, info=info),并在 --format json 时输出包含每个用例耗时与记忆数量的 JSON 报告。

3.4 运行单解析器 Demo

如果想理解某个具体解析器的工作方式(例如系统如何区分解析 User 消息与 Assistant 消息),可以直接运行 parser_demos/ 下的独立脚本:

python -m examples.mem_reader.parser_demos.demo_user
python -m examples.mem_reader.parser_demos.demo_image

所有 demo 继承 parser_demos/_base.py 中的 BaseParserDemo,初始化流程一致:调用 build_llm_and_embedder() 构建 LLM 与 Embedder,再 create_parser() 创建目标解析器。以 demo_image.py 为例,它依次演示:

  1. 从图片消息创建 SourceMessagecreate_source);
  2. SourceMessage 重建原始消息(rebuild_from_source);
  3. Fast 解析(图片场景下预期返回空列表,因为图片必须走 Fine 模式调用视觉模型);
  4. Fine 解析(依赖支持视觉的 LLM,若模型不支持视觉或图片 URL 不可达会提示失败)。

这正好印证了官方文档的说明:Fast 模式是纯规则/正则的快速通道,而图片等需要“看懂”的内容只能走 LLM 的 Fine 模式。

四、核心组件深度解析

4.1 SimpleStructMemReader:轻量文本记忆抽取器

SimpleStructMemReadersrc/memos/mem_reader/simple_struct.py 中实现,官方文档将其定位为“standard text-based chat applications,轻量高效”。其核心工作流包含:

输入校验。 get_memory() 会强制校验 info 必须为字典且包含字符串类型的 user_idsession_id,否则抛出 ValueError。这与示例中 info = {"user_id": "simple_user", "session_id": "simple_session"} 的写法严格对应。

聊天窗口切分。 _iter_chat_windows() 使用 token 计数实现滑动窗口:默认 chat_window_max_tokens=1024,每条消息拼接为 role: [chat_time]: content 形式的行,超过窗口上限即产出当前窗口,并通过 overlap=200 token 的重叠保留上下文连续性。get_scene_data_info() 还会把单条消息按每 10 条一个窗口、末 2 条作为重叠进行预分块。

Fast 模式。 _process_chat_data(mode="fast") 不调用 LLM,直接把每个窗口的文本作为一条记忆,依据窗口中出现的角色集合决定记忆类型:若全是 user 则标记为 UserMemory,否则为 LongTermMemory,并打上 tags=["mode:fast"]。Fast 模式使用 ContextThreadPoolExecutor(max_workers=8) 并行构建节点。

Fine 模式。 _process_chat_data(mode="fine") 调用 _get_llm_response(),按文本语言(detect_lang)选择中/英文提示词模板(见 src/memos/templates/mem_reader_prompts.py),把对话窗口塞入模板后交给 LLM,解析返回的 "memory list" JSON,逐条构造 TextualMemoryItemvalue 为记忆正文,tagskeysummary(写入 background)均来自 LLM 输出;memory_type 会把中文的“长期记忆 / 用户记忆”归一化为 LongTermMemory / UserMemory。若 LLM 返回解析失败,代码会把整段文本作为 UserMemory 兜底保存(源码注释特别提醒下游通过 resp.get("memory list", []) 读取该键)。

FAST→FINE 迁移。 fine_transfer_simple_mem() 接收 Fast 模式产出的扁平 list[TextualMemoryItem],逐条重新走一遍 LLM 精细抽取,_process_transfer_chat_data() 会保留原节点的 user_idsession_idsources 等元信息,这正是 run_simple.py 中第三步“Transfer”的实现基础。

文档解析。 _process_doc_data() 仅支持 Fine 模式(Fast 模式会抛 NotImplementedError)。它把文件/内联文本合并为全文,交给 chunker 切块,再对每个 chunk 构造 doc 提示词模板,用 ContextThreadPoolExecutor(max_workers=50) 并行调用 LLM 生成记忆节点,并保留 SourceMessage(type="doc", doc_path=filename) 作为来源信息。

4.2 MultiModalStructMemReader:多模态记忆抽取器

MultiModalStructMemReadersrc/memos/mem_reader/multi_modal_struct.py 中实现,直接继承 SimpleStructMemReader,官方文档将其定位为“advanced agents that handle images, file attachments, and complex tool interactions”。它在前者的基础上增加了:

多模态解析器路由。 构造时初始化 MultiModalParser,传入 embedder、主 llm、专用 image_parser_llm(视觉模型,未配置时回退 general_llm)与 document_parser_llm(文档内容抽取专用 LLM)。消息会按类型路由到 String / System / User / Assistant / Tool / TextContent / FileContent / Image 八个解析器(见 src/memos/mem_reader/read_multi_modal/init.py)。

Fast→Fine 两段式。 _process_multi_modal_data() 先把多模态消息展开并并行 parse 生成 Fast 记忆条目(不嵌入),随后 _process_string_fine() 逐条调用 LLM 升级为 Fine 条目。_get_llm_response() 覆写了父类方法:优先从 sources 中读取 Fast 阶段标注的 lang,回退到 detect_lang(mem_str);并通过 _determine_prompt_type() 根据来源角色选择 chat / doc / general_string 提示词模板。

记忆聚合与长文拆分。 _concat_multi_modal_memories() 复用了与 _iter_chat_windows 类似的滑动窗口逻辑:对超过 max_tokens 的单条记忆先用 chunker 拆分为带 ingest_batch_id / chunk_index / chunk_total 内部信息的分片,再把窗口内条目合并为一条聚合记忆,最后批量计算 embedding(失败时逐条回退)。_build_window_from_items() 依据窗口内角色集合决定 UserMemory / LongTermMemory,与 Simple 版保持一致。

工具轨迹记忆。 _process_tool_trajectory_fine() 识别包含 tool:[tool_calls]:<tool_schema> 的文本,用 TOOL_TRAJECTORY_PROMPT 模板调用 general_llm 生成 ToolTrajectoryMemory 类型的记忆(含 correctnessexperiencetool_used_status 字段),用于沉淀工具调用的成败经验。

可选扩展开关。 配置类还支持 memory_version_switch(记忆版本化,默认 off,开启后通过 MEMORY_VERSION_PREPARE_UPDATES / MEMORY_VERSION_APPLY_UPDATES 钩子走版本更新管线)、direct_markdown_hostnames(命中白名单的主机直接返回 markdown 而不走文件解析,未配置时读取 FILE_PARSER_DIRECT_MARKDOWN_HOSTNAMES 环境变量)、oss_configskills_dir_config

五、样例数据与测试用例速览

examples/mem_reader/samples.pydataclass TestCase(字段:namedescriptionscene_dataexpected_count)组织了大量覆盖真实场景的样例,官方文档强调的 String / Multimodal / Raw 三类输入在其中都有对应实现:

  • 简单聊天(SIMPLE_CHAT_SCENE):一段 12 轮、带 rolechat_time 的中英文混合心理疏导对话,用于 run_simple.py 的 Fast/Fine/Transfer 演示。
  • 字符串用例(STRING_MESSAGE_CASES):纯字符串数组,由 StringParser 转成 SourceMessage 处理,覆盖英文与中文多消息场景。
  • 聊天用例(CHAT_MESSAGE_CASES):普通对话、含 system 消息的对话,以及 text + file + image 混合的复杂多模态对话(content 为 part 数组,含 {"type":"text"}{"type":"file","file":{...}}{"type":"image_url",...})。
  • 工具用例(TOOL_MESSAGE_CASES):用户请求 + role:"tool" 返回 JSON/文本结果的对话,覆盖天气查询、数据 API、数据库查询三类。
  • 文件内容(FILE_CONTENT_PARTS):PDF / DOCX / CSV 的 file_datapath 载体,另有真实本地文件 examples/mem_reader/text1.txt 的引用样例。
  • 角色专项用例SYSTEM_MESSAGE_CASESUSER_MESSAGE_CASESASSISTANT_MESSAGE_CASES 分别覆盖纯文本、多 part 与结构化指令等变体,供对应 parser demo 使用。
  • 图片用例(IMAGE_MESSAGE_CASES):真实 MemOS Banner 图片 URL 与一个模拟图片 URL(负例),demo_image.py 会额外追加本地 Base64 图片用例。
  • 多模态用例(MULTIMODAL_MESSAGE_CASES):text+image、text+file、OSS URL 文件、纯文本 file_data、本地路径 file_data、互联网 URL 文件、text+file+image 混合以及纯音频(input_audio)等边界场景。
  • Raw 输入用例(RAW_INPUT_CASES):无对话上下文的纯文本 part、仅 file_id、仅 filename、Base64 编码 file_data、URL file_data、纯文本 file_data 以及 file_data 与 file_id/filename 组合的各种变体,专门测试 FileContentParserfile 字段缺省组合的鲁棒性。

这些样例不仅可以直接运行,也是理解各解析器输入协议(尤其是 {"type": "file", "file": {...}}{"type": "image_url", ...} 的结构约定)最直接的参考。

六、定制与扩展

官方文档指出,可以通过修改 settings.pybuilders.py 更换底层 LLM 后端(例如从 OpenAI 切到 Ollama)或调整分块策略。builders.py 提供了四组可复用工厂函数:

  • build_llm_and_embedder():通过 LLMConfigFactory.model_validate() / EmbedderConfigFactory.model_validate() 校验配置字典,再用 LLMFactory / EmbedderFactory 实例化组件;
  • build_file_parser():构建 MarkItDown 文档解析器(初始化失败时打印警告并返回 None,不阻塞流程);
  • build_simple_reader():基于 get_reader_config() 构造 SimpleStructMemReader
  • build_multimodal_reader():构造 MultiModalStructMemReader

切换后端最省事的方式是环境变量:

# 切到 Ollama 本地推理
MEMRADER_LLM_BACKEND=ollama
MEMRADER_MODEL=qwen2.5:7b
OLLAMA_API_BASE=http://localhost:11434
MOS_EMBEDDER_BACKEND=ollama
MOS_EMBEDDER_MODEL=nomic-embed-text:latest

分块策略则通过 settings.pyget_chunker_config() 控制:默认使用 sentence 后端,token 计数器为 gpt2chunk_size=512chunk_overlap=128min_sentences_per_chunk=1。如需调整,直接修改该函数返回值即可,无需改动读取器核心代码。

七、常见问题与注意事项

  • user_id / session_id 必填get_memory() 要求 info 是包含字符串 user_idsession_id 的字典,缺失或类型不符会直接抛 ValueError
  • Fast 模式不调用 LLM:适合低成本的粗抽取;若需要语义化记忆(带 key/tags/摘要),应使用 Fine 模式或 Fast→Fine Transfer。文档解析(type="doc")在 Simple 读取器中仅支持 Fine 模式
  • 图片必须走 Fine 模式ImageParser.parse_fast 预期返回空列表,视觉理解依赖配置了视觉能力的 LLM(image_parser_llm),模型不支持视觉或图片 URL 不可达时不会生成记忆。
  • 滑动窗口参数:聊天抽取的窗口大小与重叠由 chat_window_max_tokens(默认 1024)与 overlap=200 控制,超长对话会被自动切窗,保证送入 LLM 的上下文长度可控。
  • 环境变量优先级:MemReader 专用变量(MEMRADER_*)优先于通用变量(OPENAI_*MOS_*),具体回退链路以 settings.py 为准。

通过 examples/mem_reader 提供的这组可直接运行的脚本与用例,再对照 simple_struct.pymulti_modal_struct.pyread_multi_modal 的源码,你即可完整掌握 MemOS 记忆输入端的解析机制,并把它平滑接入自己的 Agent 记忆与检索管线。

登录后查看全文
MemOS