MemOS MemReader 实战指南:用示例代码把聊天、文档与图片解析为结构化记忆
MemOS MemReader 实战指南:用示例代码把聊天、文档与图片解析为结构化记忆
本文以 MemOS 仓库中的 MemReader 示例文档 为主线,结合 examples/mem_reader 目录下的完整示例代码、src/memos/mem_reader 核心实现与 docker/.env.example-full 环境变量配置,系统讲解 MemReader 模块如何把聊天记录、纯文本、文件与图片等异构输入解析成结构化的 TextualMemoryItem 记忆条目。读完本文,你将掌握 SimpleStructMemReader 与 MultiModalStructMemReader 两大读取器的配置方法、运行命令、Fast/Fine 双模式与 FAST→FINE 迁移流程,并能结合实际源码理解底层解析链路,直接上手把 MemReader 接入自己的 Agent 记忆管线。
一、MemReader 在 MemOS 中的定位
MemOS 是一套面向 LLM 与 AI Agent 的“自进化记忆操作系统”,其记忆体系分为明文记忆(explicit memory)、激活记忆(activation memory)与参数化记忆(parametric memory)等层次。MemReader 位于明文记忆的输入端:它的职责是把各种类型的原始输入数据——文本、聊天历史、文件、图片——解析为统一的结构化记忆格式(TextualMemoryItem),供下游的嵌入、检索与召回使用。
官方文档对 MemReader 的定义是:
MemReaderis responsible for parsing various types of input data (text, chat history, files, images) into structured memory formats.
这一模块对外暴露两类读取器:
| 读取器 | 适用场景 | 特点 |
|---|---|---|
SimpleStructMemReader |
标准文本型聊天应用 | 轻量、高效,专注纯文本聊天与文档 |
MultiModalStructMemReader |
处理图片、文件附件与复杂工具交互的进阶 Agent | 继承前者,并扩展多模态解析能力 |
两者对应配置类分别为 SimpleStructMemReaderConfig 与 MultiModalStructMemReaderConfig,定义在 src/memos/configs/mem_reader.py 中,并通过 MemReaderConfigFactory 按 backend(simple_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 实现了 SimpleStructMemReader,multi_modal_struct.py 实现了 MultiModalStructMemReader,read_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_BACKEND:universal_api或ollama,默认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
该脚本覆盖官方文档列出的四项核心能力:
- Fine Mode(精细模式):调用 LLM 对对话进行深度理解,抽取结构化的记忆条目(key、value、tags、memory_type 等)。
- Fast Mode(快速模式):不调用 LLM,基于规则/正则直接生成记忆,速度快、成本低。
- Transfer(迁移):把 Fast 模式产出的扁平记忆列表喂给
fine_transfer_simple_mem(),升级为 Fine 模式的精细记忆。 - Document Parsing(文档解析):读取
text1.txt、text2.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,它支持 String、Multimodal 与 Raw 三种输入类型,并可通过命令行参数控制运行范围、模式与输出格式:
# 以 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_CASES、MULTIMODAL_MESSAGE_CASES 与 RAW_INPUT_CASES;all 则顺序拼接三组用例。脚本会根据用例来源自动判定 input_type(string / 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 为例,它依次演示:
- 从图片消息创建
SourceMessage(create_source); - 从
SourceMessage重建原始消息(rebuild_from_source); - Fast 解析(图片场景下预期返回空列表,因为图片必须走 Fine 模式调用视觉模型);
- Fine 解析(依赖支持视觉的 LLM,若模型不支持视觉或图片 URL 不可达会提示失败)。
这正好印证了官方文档的说明:Fast 模式是纯规则/正则的快速通道,而图片等需要“看懂”的内容只能走 LLM 的 Fine 模式。
四、核心组件深度解析
4.1 SimpleStructMemReader:轻量文本记忆抽取器
SimpleStructMemReader 在 src/memos/mem_reader/simple_struct.py 中实现,官方文档将其定位为“standard text-based chat applications,轻量高效”。其核心工作流包含:
输入校验。 get_memory() 会强制校验 info 必须为字典且包含字符串类型的 user_id 与 session_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,逐条构造 TextualMemoryItem:value 为记忆正文,tags、key、summary(写入 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_id、session_id、sources 等元信息,这正是 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:多模态记忆抽取器
MultiModalStructMemReader 在 src/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 类型的记忆(含 correctness、experience、tool_used_status 字段),用于沉淀工具调用的成败经验。
可选扩展开关。 配置类还支持 memory_version_switch(记忆版本化,默认 off,开启后通过 MEMORY_VERSION_PREPARE_UPDATES / MEMORY_VERSION_APPLY_UPDATES 钩子走版本更新管线)、direct_markdown_hostnames(命中白名单的主机直接返回 markdown 而不走文件解析,未配置时读取 FILE_PARSER_DIRECT_MARKDOWN_HOSTNAMES 环境变量)、oss_config 与 skills_dir_config。
五、样例数据与测试用例速览
examples/mem_reader/samples.py 用 dataclass TestCase(字段:name、description、scene_data、expected_count)组织了大量覆盖真实场景的样例,官方文档强调的 String / Multimodal / Raw 三类输入在其中都有对应实现:
- 简单聊天(SIMPLE_CHAT_SCENE):一段 12 轮、带
role与chat_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_data或path载体,另有真实本地文件examples/mem_reader/text1.txt的引用样例。 - 角色专项用例:
SYSTEM_MESSAGE_CASES、USER_MESSAGE_CASES、ASSISTANT_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 组合的各种变体,专门测试
FileContentParser对file字段缺省组合的鲁棒性。
这些样例不仅可以直接运行,也是理解各解析器输入协议(尤其是 {"type": "file", "file": {...}} 与 {"type": "image_url", ...} 的结构约定)最直接的参考。
六、定制与扩展
官方文档指出,可以通过修改 settings.py 或 builders.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.py 的 get_chunker_config() 控制:默认使用 sentence 后端,token 计数器为 gpt2,chunk_size=512、chunk_overlap=128、min_sentences_per_chunk=1。如需调整,直接修改该函数返回值即可,无需改动读取器核心代码。
七、常见问题与注意事项
user_id/session_id必填:get_memory()要求info是包含字符串user_id与session_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.py、multi_modal_struct.py 与 read_multi_modal 的源码,你即可完整掌握 MemOS 记忆输入端的解析机制,并把它平滑接入自己的 Agent 记忆与检索管线。