FishMem

Vercel AI SDK

Give Vercel AI SDK agents long-term memory by retrieving FishMem facts into the prompt, or exposing search as a tool.

The @fishmem/ai-sdk adapter connects FishMem to the Vercel AI SDK (ai package). There are two patterns: retrieve relevant memories and inject them into the prompt before the model runs, or expose memory search as a tool() the model can call on demand.

Install

npm install ai @fishmem/ai-sdk fishmem

Set your key in the environment. Both the adapter and the fishmem client read FISHMEM_API_KEY by default.

export FISHMEM_API_KEY="fm_..."

Pattern A — Retrieve and inject

Search FishMem before the turn, prepend the results to the system prompt, then write the exchange back afterward. This keeps the model grounded in what it already knows about the user without any tool round-trips.

Recall before the turn

recall.ts
import { FishMem } from "fishmem";

const memory = new FishMem({ apiKey: process.env.FISHMEM_API_KEY });

export async function recall(query: string, userId: string) {
  const { results } = await memory.search(query, { user_id: userId, top_k: 5 });
  if (results.length === 0) return "";
  const facts = results.map((m) => `- ${m.memory}`).join("\n");
  return `What you know about this user:\n${facts}`;
}

Inject and generate

chat.ts
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import { FishMem } from "fishmem";
import { recall } from "./recall";

const memory = new FishMem({ apiKey: process.env.FISHMEM_API_KEY });

export async function chat(userId: string, prompt: string) {
  const context = await recall(prompt, userId);

  const { text } = await generateText({
    model: openai("gpt-4o"),
    system: `You are a helpful assistant.\n\n${context}`,
    prompt,
  });

  // Persist the exchange so the next turn can recall it.
  await memory.add(
    [
      { role: "user", content: prompt },
      { role: "assistant", content: text },
    ],
    { user_id: userId },
  );

  return text;
}

The same pattern works with streamText — call recall before streaming, and memory.add once the stream completes.

Pattern B — Memory as a tool

Instead of injecting upfront, give the model a searchMemory tool so it can pull memories only when it decides they're relevant. This costs fewer recall calls on turns that don't need memory, at the price of an extra round-trip when they do.

memory-tool.ts
import { generateText, tool } from "ai";
import { openai } from "@ai-sdk/openai";
import { FishMem } from "fishmem";
import { z } from "zod";

const memory = new FishMem({ apiKey: process.env.FISHMEM_API_KEY });

export async function chatWithTool(userId: string, prompt: string) {
  const searchMemory = tool({
    description:
      "Search the user's long-term memory for relevant facts before answering.",
    parameters: z.object({
      query: z.string().describe("What to look up in memory."),
    }),
    execute: async ({ query }) => {
      const { results } = await memory.search(query, {
        user_id: userId,
        top_k: 5,
      });
      return results.map((m) => m.memory);
    },
  });

  const { text } = await generateText({
    model: openai("gpt-4o"),
    system: "Use searchMemory when the user references past context.",
    prompt,
    tools: { searchMemory },
    maxSteps: 4,
  });

  await memory.add(
    [
      { role: "user", content: prompt },
      { role: "assistant", content: text },
    ],
    { user_id: userId },
  );

  return text;
}

Always pass user_id (and agent_id / run_id where relevant) so memories stay isolated per user. See scoping.

Next steps

On this page