跳到主要内容

Mem0 可插拔后端 — 工厂与配置

这章讲 Mem0 怎么做到「换个向量库/LLM 只改一行 config」。核心是四个工厂类 + 一套 Pydantic config。这是项目能支持 20+ 向量库的工程底座。

1. 它要解决的小问题

用户的技术栈千差万别:有人用 Qdrant,有人用 pgvector,有人只想本地 FAISS;LLM 有人 OpenAI 有人 Anthropic 有人 Ollama。Mem0 的核心逻辑(抽取、融合、实体链接)不该和具体后端耦合。

答案是经典的工厂模式 + 抽象基类:核心代码只面向 VectorStoreBase / BaseLlmConfig 等接口编程,具体实现由工厂按配置实例化。

2. 思路/直觉:一张注册表 + 动态 import

每个工厂里有一张 provider_to_class 映射:provider 名 → ("模块.类路径字符串", Config类)create() 时按名查表,importlib 动态导入那个类再实例化。

动态 import 是关键:这样 qdrant-clientweaviate-client 这些重依赖只在你真用到时才被 import——没装 Weaviate 也能用 Qdrant,不会一启动就 import 全家桶报错。

# mem0/utils/factory.py:27 —— 把 "mem0.llms.openai.OpenAILLM" 拆开动态加载
def load_class(class_type):
module_path, class_name = class_type.rsplit(".", 1)
module = importlib.import_module(module_path)
return getattr(module, class_name)

3. 全景:四个工厂

Memory.__init__ 一上来就用四个工厂把后端全建好(mem0/memory/main.py:447-467):

MemoryConfig
├─ embedder.provider ──▶ EmbedderFactory.create ──▶ self.embedding_model
├─ vector_store.provider ▶ VectorStoreFactory.create ─▶ self.vector_store
├─ llm.provider ──▶ LlmFactory.create ──▶ self.llm
└─ reranker.provider ──▶ RerankerFactory.create ──▶ self.reranker (可选)

四个工厂都在 mem0/utils/factory.py,长得一模一样:一张注册表 + 一个 create()

工厂注册表符号覆盖
LlmFactoryprovider_to_class (:40)openai/anthropic/gemini/ollama/groq/bedrock/… 15+
VectorStoreFactory(同文件)qdrant/pgvector/weaviate/faiss/milvus/… 20+
EmbedderFactory(同文件)openai/azure/gemini/ollama/huggingface/…
RerankerFactory(同文件)cohere/huggingface/llm/sentence_transformer/…

4. config 怎么传

create() 兼容三种形式(factory.py:83-120):

  • config=None → 用 kwargs 建默认 config。
  • config=dict → merge kwargs 后塞进 provider 专属 Config 类。
  • config=BaseLlmConfig 实例 → 必要时转成 provider 专属类(逐字段搬,见 factory.py:91-118)。

LLM 这里有个细节:reasoning 字段(reasoning_effort / is_reasoning_model)只在目标 config 类确实接受时才转发(用 inspect.signature 探测,factory.py:108-113),否则有些 provider 会因为「意外 kwarg」报错。

顶层 config 是 MemoryConfig(mem0/configs/base.py:29),嵌套 vector_store / llm / embedder / reranker 四块子 config,加 history_db_pathversion(默认 v1.1)、custom_instructions。全是 Pydantic 模型,带默认值,所以 Memory() 不传任何东西也能跑(默认 OpenAI + Qdrant)。

# mem0/configs/base.py:29 —— 子配置各有 default_factory,所以零参可用
class MemoryConfig(BaseModel):
vector_store: VectorStoreConfig = Field(default_factory=VectorStoreConfig)
llm: LlmConfig = Field(default_factory=LlmConfig)
embedder: EmbedderConfig = Field(default_factory=EmbedderConfig)
# history_db_path / reranker / version / custom_instructions ...

Memory.from_config(config_dict)(main.py:687)是从纯 dict 建实例的便捷入口,常见于「从 YAML/JSON 配置启动」。

5. 向量库抽象契约

所有向量库实现 VectorStoreBase(mem0/vector_stores/base.py:4)。核心抽象方法:insert / search / update / delete / get / list / reset / create_col。两个非抽象、带默认的方法是后端能力差异的体现:

  • keyword_search(base.py:68):默认返回 None。支持 BM25 的库(qdrant/elasticsearch/pgvector)才 override。这直接决定 02 章的 BM25 路是否生效。
  • search_batch(base.py:85):默认顺序调 search;有原生批量的库(如 Qdrant query_batch_points)才 override 加速。

这种「能力用可选 override 表达」的设计,让核心代码可以统一调用、各库按能力自然降级——Memory.__init__ 里那段「检测 keyword_search 是否被 override,没 override 就 warning」(main.py:499)就是在读这个契约。

6. 巧妙之处

  • 动态 import 让重依赖按需加载:装哪个后端用哪个,不强制全装。
  • 能力差异用「可选 override + 默认实现」表达:核心代码无 if-else 分支,各库自然降级。
  • 三态 config 入参:None/dict/实例都接,既好用又向后兼容老的 BaseLlmConfig
  • reasoning 字段按签名探测转发:避免给不认识该参数的 provider 传入而报错。

7. 边界与局限

  • provider 名打错直接 ValueError: Unsupported ... provider(factory.py:77)——没有模糊匹配。
  • 各向量库对「高级过滤算子」(eq/gt/in/$or/$not)的支持不一,Mem0 只把它们翻译成通用格式下发(main.py:1471 _process_metadata_filters),最终能不能用看具体库。
  • 工厂只负责实例化;后端的连接/认证错误要到首次真正调用才暴露。

8. 代码地图

主题文件符号
动态加载mem0/utils/factory.pyload_class (:27)
LLM 工厂mem0/utils/factory.pyLlmFactory (:33), provider_to_class (:40)
其余三工厂mem0/utils/factory.pyVectorStoreFactory / EmbedderFactory / RerankerFactory
顶层配置mem0/configs/base.pyMemoryConfig (:29)
从 dict 建实例mem0/memory/main.pyMemory.from_config (:687)
向量库契约mem0/vector_stores/base.pyVectorStoreBase (:4)
后端实例化mem0/memory/main.pyMemory.__init__ (:444)