首页
/ Vercel AI SDK 集成持久记忆完整指南:@mem0/vercel-ai-provider 六大使用模式与源码级原理

Vercel AI SDK 集成持久记忆完整指南:@mem0/vercel-ai-provider 六大使用模式与源码级原理

2026-09-07 20:54:54作者:吴年前Myrtle

Mem0 在 Vercel AI SDK 之上提供了一层"记忆即服务"的 Provider 封装(@mem0/verci-ai-provider,仓库内实现位于 integrations/vercel-ai-sdk),让 generateTextstreamTextgenerateObject 等调用在发出前自动检索相关记忆、在返回后自动沉淀新记忆。本文以仓库技能文档 usage-patterns.md 为核心骨架,结合 integrations/vercel-ai-sdk/src 下的真实源码,从包装模型、独立工具函数、多 Provider 配置、Next.js 实战到内部处理流程与配置优先级,给出可直接复制运行、且有源码依据可查的完整方案。

本文代码示例中的模型名(如 gpt-5-miniclaude-sonnet-4-20250514)与默认参数来自仓库文档,实际运行请以你账号可用的模型与当版本 SDK 为准。

环境准备与文档脉络

@mem0/vercel-ai-provider 的所有用法都建立在两个前提之上:安装依赖、准备环境变量。

npm install @mem0/vercel-ai-provider ai

示例默认通过环境变量读取密钥:

export MEM0_API_KEY="m0-xxx"
export OPENAI_API_KEY="sk-xxx"   # 或 ANTHROPIC_API_KEY / GOOGLE_GENERATIVE_AI_API_KEY 等

与本文配套的还有两份参考文档,建议对照阅读:provider-api.md 讲解 createMem0Mem0Provider 与全部类型定义;memory-utilities.md 逐一拆解四个记忆工具函数的签名、行为与参数表;SKILL.md 则给出安装、模式概览与边界场景速览。

从源码结构看,该集成对外只暴露一组精简的 API,见 index.tscreateMem0(Provider 工厂)、四个记忆工具函数 addMemories / retrieveMemories / searchMemories / getMemories,以及 mem0 单例导出。后续所有使用模式都是围绕"包装模型(Wrapped Model)"与"独立工具函数(Standalone Utilities)"两条主线展开。

模式一:包装模型 + generateText(最简接入)

让任何一次 LLM 调用自动带上记忆,最简单的方式是用 createMem0() 创建一个包装 Provider:

import { generateText } from "ai";
import { createMem0 } from "@mem0/vercel-ai-provider";

const mem0 = createMem0();

const { text } = await generateText({
  model: mem0("gpt-5-mini", { user_id: "alice" }),
  prompt: "Recommend a restaurant based on my preferences",
});

console.log(text);

只需做两件事:

  1. createMem0() 返回一个可调用的 Provider(默认 provider 为 "openai",见 mem0-provider.ts);
  2. mem0("模型ID", { user_id: "alice" }) 创建带记忆能力的语言模型。

mem0("gpt-5-mini", { user_id: "alice" }) 在内部创建的是 Mem0GenericLanguageModelmem0-generic-language-model.ts)。调用 generateText 时,"在调用前自动检索记忆、在调用后存储新对话" 分别对应 getMemories(检索)与 addMemories(写入)这两个底层步骤。

要特别说明 user_id:它把记忆限定在某个用户/实体维度上,是记忆读写作用域的关键。同一份记忆库中,不同 user_id 之间彼此隔离,只有传入一致的实体标识,才能命中正确的记忆(更完整的实体维度见下文 Mem0ConfigSettings 字段表)。

模式二:包装模型 + streamText(流式输出)

把记忆增强能力套用到流式场景同样直接:

import { streamText } from "ai";
import { createMem0 } from "@mem0/vercel-ai-provider";

const mem0 = createMem0();

const result = streamText({
  model: mem0("gpt-5-mini", { user_id: "alice" }),
  prompt: "What should I cook for dinner tonight?",
});

for await (const chunk of result.textStream) {
  process.stdout.write(chunk);
}

要点有两个:

  • 记忆检索发生在流式开始之前Mem0GenericLanguageModel.doStream 会先执行 processMemories 拿到增强后的 prompt,再去调用底层模型真正的流式接口(mem0-generic-language-model.ts),因此首字节延迟包含了一次记忆检索的开销;
  • 对话会被异步沉淀回 Mem0。usage-patterns 文档将包装模型中的写入描述为 fire-and-forget(非阻塞)调用,即存储失败只记录日志、不阻断响应。需要留意的是:当前仓库快照的 processMemories 实现把 addMemories 包在 try/catch 中执行,写失败不会影响生成结果,这与文档描述的语义一致;具体是否 await、是否阻塞首包,请以你所安装的实际发布版本为准。文档同时提醒:由于写入是异步的,存在"最新一轮对话尚未入库"的短暂窗口期,若追求强一致,应改用下面的独立工具函数并显式 await addMemories

模式三:独立工具函数,全权掌控记忆生命周期

当你希望精确控制"何时检索、何时存储",或 LLM Provider 已单独初始化、不想被包装时,可使用 retrieveMemoriesaddMemories。它们把记忆生命周期拆成三步,见 mem0-utils.ts

与 OpenAI 配合

import { openai } from "@ai-sdk/openai";
import { generateText } from "ai";
import { retrieveMemories, addMemories } from "@mem0/vercel-ai-provider";

const user_id = "alice";
const prompt = "Suggest a weekend trip";

// Step 1: 检索记忆,返回可直接注入 system 的格式化字符串
const memories = await retrieveMemories(prompt, {
  user_id: user_id,
});

// Step 2: 把记忆作为 system 上下文生成回答
const { text } = await generateText({
  model: openai("gpt-5-mini"),
  prompt,
  system: memories,
});

console.log(text);

// Step 3: 将本轮对话沉淀为新的记忆
await addMemories(
  [
    { role: "user", content: [{ type: "text", text: prompt }] },
    { role: "assistant", content: [{ type: "text", text }] },
  ],
  { user_id: user_id }
);

与 Anthropic 配合

模式完全一致,只是 LLM 与模型 ID 不同:

import { anthropic } from "@ai-sdk/anthropic";
import { generateText } from "ai";
import { retrieveMemories, addMemories } from "@mem0/vercel-ai-provider";

const prompt = "Help me plan my exercise routine";
const config = { user_id: "bob" };

const memories = await retrieveMemories(prompt, config);

const { text } = await generateText({
  model: anthropic("claude-sonnet-4-20250514"),
  prompt,
  system: memories,
});

await addMemories(
  [
    { role: "user", content: [{ type: "text", text: prompt }] },
    { role: "assistant", content: [{ type: "text", text }] },
  ],
  config
);

值得注意 retrieveMemories 的返回值是"格式化好的 system prompt 字符串"而非数组——示例里直接把它放进了 system 字段。如果空结果,函数返回 ""(见 mem0-utils.ts),不会污染 system 上下文。

四个工具函数如何选

函数 返回值 适用场景
retrieveMemories(prompt, config?) 格式化好的 system prompt 字符串 直接注入 generateText / streamTextsystem 参数
getMemories(prompt, config?) 原始记忆 数组 需要对记忆做程序化处理(过滤、排序、计数等)
searchMemories(prompt, config?) 完整搜索 响应(results + relations 等) 需要相似度分数、关联关系或完整元数据
addMemories(messages, config?) 写入 API 的响应 把消息沉淀为新记忆

四个函数的第一个参数都是 LanguageModelV3Prompt | string,第二个参数都是可选的 Mem0ConfigSettings(见 memory-utilities.md)。实现上,getMemoriessearchMemories 会先把 prompt 拍平成纯文本字符串(flattenPrompt),再调用内部的 searchInternalMemories,最终 POST 到 {host}/v3/memories/search/addMemories 则把消息经 convertToMem0Format 转换后 POST 到 {host}/v3/memories/add/(默认 host 为 https://api.mem0.ai,见 mem0-utils.ts)。

一个实用的代码级细节:flattenPrompt 对多模态内容做占位化处理——file 类型内容在作为检索 query 时被替换为 [PDF document][Markdown document][Image][File attachment] 等描述符(mem0-utils.ts),既避免把二进制内容喂给记忆检索,又能让 AI 记住"这轮讨论过一份 PDF"。

模式四:generateObject 结构化输出

记忆增强不限于自由文本。用 generateObject 可拿到带记忆上下文的类型化结构化结果:

import { generateObject } from "ai";
import { createMem0 } from "@mem0/vercel-ai-provider";
import { z } from "zod";

const mem0 = createMem0();

const { object } = await generateObject({
  model: mem0("gpt-5-mini", { user_id: "alice" }),
  prompt: "Suggest a meal plan for today",
  schema: z.object({
    breakfast: z.string(),
    lunch: z.string(),
    dinner: z.string(),
    snacks: z.array(z.string()),
    notes: z.string().describe("Personalization notes based on known preferences"),
  }),
});

console.log(object);
// { breakfast: "Avocado toast (you mentioned loving it)", lunch: "...", ... }

文档说明该集成的 defaultObjectGenerationMode"json",即结构化输出开箱即用。它的记忆注入路径与文本生成完全一致——真正带来差异的是 schema 中的字段约束,例如示例里给 notes 加上了 describe("Personalization notes based on known preferences"),引导模型把检索到的偏好写进结构化字段,让"个性化结论"可被程序直接消费。

模式五:多 Provider 配置

包装模型支持在创建 Provider 时切换底层 LLM。文档与仓库代码给出的 provider 一览:

Provider 配置值 必需环境变量
OpenAI(默认) "openai" OPENAI_API_KEY
Anthropic "anthropic" ANTHROPIC_API_KEY
Google "google" GOOGLE_GENERATIVE_AI_API_KEY
Groq "groq" GROQ_API_KEY
Cohere "cohere" COHERE_API_KEY
import { createMem0 } from "@mem0/vercel-ai-provider";

// OpenAI(默认,无需传 provider)
const mem0 = createMem0(); // defaults to "openai"
const model = mem0("gpt-5-mini", { user_id: "alice" });

// Anthropic
const mem0A = createMem0({ provider: "anthropic" });
const modelA = mem0A("claude-sonnet-4-20250514", { user_id: "alice" });

// Google
const mem0G = createMem0({ provider: "google" });
const modelG = mem0G("gemini-2.0-flash", { user_id: "alice" });

// Groq
const mem0Gr = createMem0({ provider: "groq" });
const modelGr = mem0Gr("llama-3.3-70b-versatile", { user_id: "alice" });

// Cohere
const mem0C = createMem0({ provider: "cohere" });
const modelC = mem0C("command-r-plus", { user_id: "alice" });

如果不想依赖环境变量,可以显式传 API Key:

const mem0 = createMem0({
  provider: "openai",
  apiKey: "sk-xxx",       // OpenAI API key
  mem0ApiKey: "m0-xxx",   // Mem0 API key
});

底层切换逻辑在 mem0-provider-selector.tsMem0ClassSelector.supportedProviders = ["openai", "anthropic", "cohere", "groq", "google", "gemini"],传入不支持的 provider 会直接抛 Model not supported。注意这里的历史遗留细节:"gemini" 别名仍存在于支持列表中,但推荐值统一用 "google",两套 skill 文档都明确指出使用 "google" 而非 "gemini"。创建出的底层模型由 provider-response-provider.tsMem0AITextGenerator)负责实际转发。

模式六:Next.js API Route 实战

将以上模式落地到服务端,最典型的是 Next.js App Router 的聊天接口。

包装模型版本

// app/api/chat/route.ts
import { streamText } from "ai";
import { createMem0 } from "@mem0/vercel-ai-provider";

const mem0 = createMem0();

export async function POST(req: Request) {
  const { messages, user_id } = await req.json();

  const lastMessage = messages[messages.length - 1];

  const result = streamText({
    model: mem0("gpt-5-mini", { user_id }),
    prompt: lastMessage.content,
  });

  return result.toDataStreamResponse();
}

代码量很少,因为记忆的检索与存储都被包装模型接管了:取最后一条用户消息作为 prompt,返回 AI SDK 标准的数据流响应(toDataStreamResponse),客户端可无缝对接 useChat

独立工具函数版本(控制力更强)

当你想在流式响应的同时异步落库、或显式控制检索粒度时:

// app/api/chat/route.ts
import { openai } from "@ai-sdk/openai";
import { streamText } from "ai";
import { retrieveMemories, addMemories } from "@mem0/vercel-ai-provider";

export async function POST(req: Request) {
  const { messages, user_id } = await req.json();
  const lastMessage = messages[messages.length - 1];

  // 检索相关记忆
  const memories = await retrieveMemories(lastMessage.content, {
    user_id,
  });

  // 流式返回(把记忆作为 system 注入)
  const result = streamText({
    model: openai("gpt-5-mini"),
    prompt: lastMessage.content,
    system: memories,
  });

  // 后台沉淀本轮对话(fire-and-forget)
  result.text.then(async (text) => {
    await addMemories(
      [
        { role: "user", content: [{ type: "text", text: lastMessage.content }] },
        { role: "assistant", content: [{ type: "text", text }] },
      ],
      { user_id }
    );
  });

  return result.toDataStreamResponse();
}

这里用 result.text.then(...)addMemories 挂到流式结果完成后执行,是"后台落库不阻塞首包"的典型写法:客户端先收到流,等文本生成完,再把这轮 user/assistant 消息作为新记忆写入。

内部机制:一次带记忆的调用到底发生了什么

understanding 内部流程有助于判断延迟来源与调试记忆命中问题。包装模型的调用路径如下(usage-patterns 文档 + mem0-generic-language-model.ts 相互印证):

1. doGenerate(options) / doStream(options) 被调用
2. processMemories(messagesPrompts, mem0Config):
   a. addMemories(messagesPrompts, mem0Config)
      --> POST {host}/v3/memories/add/(写入本轮对话)
   b. await getMemories(messagesPrompts, mem0Config)
      --> POST {host}/v3/memories/search/(用拍平的 prompt 检索)
      --> 返回记忆数组
   c. 把记忆格式化为 system 消息文本
   d. 把 system 消息 prepend 到 messagesPrompts 数组最前面
   e. 返回 { memories, messagesPrompts }
3. 通过 Mem0ClassSelector.createProvider() 创建底层 LLM
4. 调用 model.doGenerate(updatedOptions) / model.doStream(updatedOptions)
5. 返回结果

几个值得展开的实现细节:

记忆写入先于检索发生。从当前源码看,processMemories 会先把传入 prompt(含历史对话)addMemories 落库,再执行 getMemories 检索——这保证了"上一轮刚说过的话"在本轮检索时即可命中,代价是文档强调的写入异常被 try/catch 吞掉、只打日志(mem0-generic-language-model.ts)。无论哪种发布形态,对外保证一致:记忆写入失败不会让 LLM 调用失败

检索端实体标识在 v3 契约中进 filters。源码注释明确写到 "v3: entity IDs go inside the filters object, not as top-level params",user_id / app_id / agent_id / run_id 会被合并进 filters 对象后随 search 请求发送(mem0-utils.ts)。

记忆注入格式。被检索出的记忆会拼成一个 system 消息,插入到 prompt 数组第 0 位,格式为:

{
  role: "system",
  content: "System Message: These are the memories I have stored. Give more weightage to the question by users and try to answer that first. You have to modify your answer based on the memories I have provided. If the memories are irrelevant you can ignore them. Also don't reply to this section of the prompt, or the memories, they are only for your reference. The System prompt starts after text System Message: \n\n Memory: ... \n\n Memory: ... \n\n"
}

这段模板同时出现在 mem0-generic-language-model.tsretrieveMemories 的实现里(mem0-utils.ts),两条代码路径保持一致。模板本身包含了三条关键行为指令:优先回答用户问题、根据记忆调整答案、记忆无关时可忽略且不要回复记忆部分。此外,只有检索结果非空时才注入 system 消息——无记忆时 processMemories 会原样返回 prompt(mem0-generic-language-model.ts),避免给每次调用都附加无意义的 system 开销。

响应中附带记忆来源(仅非流式路径)。在 doGenerate 中,若命中记忆,返回结果会追加一个 type: "source" 的条目,把命中记忆原文放进 providerMetadata.mem0.memories,方便上层做引用溯源或展示"依据的记忆"(mem0-generic-language-model.ts)。doStream 则直接透传底层流式响应,不追加额外结构。

自定义配置:host、检索质量与配置合并优先级

自定义 Mem0 API host

无论是自托管 Mem0 还是走代理,都可覆盖默认的 https://api.mem0.ai

// 包装模型级别
const mem0 = createMem0({
  mem0Config: {
    host: "https://my-mem0-instance.example.com",
  },
});

// 或独立工具函数级别
const memories = await retrieveMemories(prompt, {
  user_id: "alice",
  host: "https://my-mem0-instance.example.com",
});

host 字段会直接影响最终请求 URL(${baseUrl}/v3/memories/search/${baseUrl}/v3/memories/add/),同时 Mem0ProviderSettings 也支持自定义 baseURLheaders 与自定义 fetch 实现(mem0-provider.ts),可自由接入代理或做请求级中间件。

记忆过滤与重排

检索质量由三个参数控制,作用于每次模型调用:

const mem0 = createMem0();
const model = mem0("gpt-5-mini", {
  user_id: "alice",
  top_k: 10,          // 最多检索 10 条记忆
  threshold: 0.8,     // 只保留相似度 score >= 0.8 的记忆
  rerank: true,       // 对检索结果启用重排
});

对应 mem0-types.tsMem0ConfigSettingstop_k / threshold / rerank 字段:searchInternalMemories 会把 top_k、可选的 thresholdrerank 透传进 search 请求体。usage-patterns 文档注释给出的默认检索条数为 5;作为参考,当前快照中 searchInternalMemories 的签名默认参数为 10(mem0-utils.ts),不同版本间存在默认值差异,显式传 top_k 是最稳妥的做法。threshold 用于过滤低相似度噪音,rerank 用于在初检后做二次精排。

Provider 专属配置(organization / project 等)

SDK 特有的设置通过 config 字段下发:

const mem0 = createMem0({
  provider: "openai",
  config: {
    organization: "org-xxx",
    project: "proj-xxx",
  },
});

Mem0ProviderSettings.config 的类型是 OpenAI / Anthropic / Cohere / Groq / Google 五家 provider settings 的联合类型(mem0-types.ts),最终随 provider 创建一起传入底层模型。

默认配置与每调用覆盖的合并顺序

可在 Provider 级设置对所有模型生效的默认记忆配置:

const mem0 = createMem0({
  mem0Config: {
    user_id: "alice",
    top_k: 10,
  },
});

// 这些调用自动继承 mem0Config 中的 user_id 与 top_k
const { text } = await generateText({
  model: mem0("gpt-5-mini"),
  prompt: "Hello",
});

mem0() 的第二个参数(每调用 settings)会合并覆盖默认值:

// 覆盖本次调用的 user_id
const model = mem0("gpt-5-mini", { user_id: "bob" });

合并顺序为:

config.mem0Config(Provider 默认)< settings(每次调用覆盖)

这一优先级在 mem0-generic-language-model.ts 中以对象展开顺序直接实现:{ mem0ApiKey, ...this.config.mem0Config, ...this.settings },后展开者胜出。所以一个常见的多租户写法是:Provider 级 mem0Config 放通用阈值与 top_k,路由层每次调用只覆盖 user_id

完整 Mem0ConfigSettings 字段速查

两个模式(包装模型与独立工具函数)共享同一份可选配置结构(mem0-types.ts):

字段 类型 说明
user_id string 把记忆限定到某个用户
app_id string 把记忆限定到某个应用
agent_id string 把记忆限定到某个 Agent
run_id string 把记忆限定到某次会话/运行
metadata Record<string, any> 自定义元数据
filters Record<string, any> 自定义检索过滤器
infer boolean 是否启用推断
page number 分页页码
page_size number 每页条数
mem0ApiKey string Mem0 API Key,缺省回落到 MEM0_API_KEY 环境变量
top_k number 检索条数上限
threshold number 最低相似度阈值
rerank boolean 是否启用重排
host string 自定义 Mem0 API 地址,默认 https://api.mem0.ai

实践要点与常见坑

综合 usage-patterns.mdSKILL.md 的提示,实战中值得注意:

  • 始终提供实体标识user_id / agent_id / app_id / run_id 之一)。缺少实体作用域时记忆无法被正确限定,检索结果会跨实体串味;
  • 独立工具函数需要显式 API Key:在 config 中传 mem0ApiKey,或设置 MEM0_API_KEY 环境变量(loadApiKey 会优先取 config、再回落环境变量);
  • 版本兼容性:本集成面向 Vercel AI SDK v5 的 V3 Provider 契约,当前源码中 specificationVersion / provider.specificationVersion 均为 v3,实现 ProviderV3 / LanguageModelV3 接口,不兼容 AI SDK v3 / v4。接入前请确认 ai 包版本;
  • "gemini" 别名是历史遗留:provider 选择器虽仍接受它,但请统一使用 "google"(见上文多 Provider 一节);
  • 记忆读写的键不同:写入(add)将实体 ID 作为顶层字段发送,而检索(search)在 v3 契约中将实体 ID 放进 filters,这是 mem0-utils.ts 内部自动处理的两个不同请求形状,不需要你手工拼接;
  • 多模态消息的检索降级:prompt 中的 PDF / Markdown / 图片内容在作为检索 query 时会被替换成占位描述符,而写入端 convertToMem0Format 则会尽力还原为 pdf_url / mdx_url / image_url 结构,保证记忆库能理解"这段对话涉及的文件类型"。

若想深入验证上述行为,仓库在 integrations/vercel-ai-sdk/tests 下提供了覆盖记忆核心路径、Provider 契约、工具函数与输出生成的测试用例(如 memory-core.test.tsv3-provider-contract.test.ts 等),可结合测试断言理解每个参数在真实调用链中的生效位置。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.74 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.81 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
595
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.63 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
518
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
389