Guides / Use cases

Tutoring and education

For tutoring apps, homework helpers and learning platforms: a tutor that knows what each learner has mastered, what they keep mixing up and what they are working towards, across every lesson. Recall stores the lessons, learns about the learner in the background, and gives your tutor and the teacher a learner model your app can use.

What memory adds

Without memory, every lesson starts from the syllabus. With it, the tutor picks up where the learner is:

Without memoryWith Recall
Week 1, fractions lessonThe learner says 1/8 is bigger than 1/4, "because 8 is bigger than 4".
Week 2, homework"Let's start with fractions. A fraction has a numerator and a denominator...""Before we start: last week bigger denominators tripped you up. Which is bigger, 1/6 or 1/3?"
Week 4, the teacher looksTwelve chat logs to read.Mastered: comparing unit fractions. Still mixing up: adding fractions with different denominators. Next: common denominators. Search finds the lesson behind each.

Recall does not teach. Your own model writes the explanations, with your teaching prompt and your safeguards; Recall gives it what this learner knows and gets wrong, and learns from each lesson.

How to model it

In your productIn RecallWhy
A school, per school yearWorkspace, e.g. school-2207-y2026Each school's data stays apart, and the end of the year is one delete.
A learnerPeer learner-5531A pseudonymous id from your own system. Recall learns about the learner from what they write.
Your tutorPeer tutor, with observeMe: falseRecall does not learn from the tutor's own lines, only reads them as context, so they cost less.
A lesson or homework sessionSession, e.g. lesson-40817Context and summaries per lesson; a teacher can see which lesson something came from.
A courseScope course-math-7, given when each session is createdThe course's own view of the learner, so maths progress is not mixed with French.
What the learner knows and gets wrongConclusions and the peer cardLearned in the background. A fact that stops being true is closed, not lost.

A consumer app with no school in between uses one workspace for the app and one peer per learner. With a single subject you can skip the scope and ask the learner peer directly.

Build it

Install the SDK (npm install @recall-memory/sdk or pip install recall-mem) and set RECALL_API_KEY. Call Recall from your server, not from the learner's device. Create the learner, the tutor and a session per lesson, filed under its course.

import { Recall } from "@recall-memory/sdk";

const recall = new Recall({ workspaceId: "school-2207-y2026" });

// A pseudonymous id from your own system: no name, email or class in Recall.
const learner = recall.peer("learner-5531");
// The tutor is your app, so Recall learns about the learner, not from the tutor's own lines.
const tutor = recall.peer("tutor", { configuration: { observeMe: false } });

// One session per lesson or homework session, filed under the course it belongs to.
const course = "course-math-7";
const lesson = recall.session("lesson-40817", { peers: [learner, tutor], scopes: [course] });

Every learner message: one request. turn stores the message and returns the context for the tutor's reply in the same round trip: the lesson so far, a summary of older turns, and what Recall has learned about the learner in earlier lessons. It uses no model, so it is fast and included in the price.

// Each learner message: store it and get back what the tutor should know, in one request.
const { context } = await lesson.turn(
  [learner.message("1/8 is bigger than 1/4, right? Because 8 is bigger than 4.")],
  { peerTarget: learner, tokens: 2000 },
);
const prompt = context.toOpenAI(tutor); // what Recall knows about the learner, then the lesson
// Your model writes the tutor's reply from `prompt`; store it so the lesson stays complete.
await lesson.addMessages([
  tutor.message("One pizza cut into 8 slices, one cut into 4. Which slice is bigger?"),
]);

The learner model, as JSON. Ask in plain language and pass responseFormat: the answer comes back as JSON that matches your schema, ready for a progress screen or for choosing the next exercise. In TypeScript pass a JSON Schema (you get the parsed object) or { jsonSchema, parse } for a typed result; in Python pass a Pydantic class (you get an instance) or a JSON-schema dict (you get the JSON text). scope answers from the course's view of the learner. This is one model call of a few seconds, so run it after a lesson and store the result rather than asking on every page view.

// Before the next lesson: the learner model as JSON your app can show and act on.
// (waitForIdle is for this demo; by the next lesson, learning has long finished.)
await recall.waitForIdle();
type LearnerModel = { mastered: string[]; misconceptions: string[]; nextGoal: string };
const list = { type: "array", items: { type: "string" } };
const learnerModel = {
  jsonSchema: {
    type: "object",
    properties: {
      mastered: { ...list, description: "Skills the learner has shown they can do" },
      misconceptions: { ...list, description: "Mistakes the learner still makes" },
      nextGoal: { type: "string", description: "The one thing to practise next" },
    },
    required: ["mastered", "misconceptions", "nextGoal"],
  },
  // With zod: { jsonSchema: z.toJSONSchema(Schema), parse: (data) => Schema.parse(data) }
  parse: (data: unknown) => data as LearnerModel,
};
const model = await learner.chat(
  "What has this learner mastered, what do they still get wrong, and what should come next?",
  { scope: course, reasoningLevel: "low", responseFormat: learnerModel },
);

Misconceptions that get corrected. Facts in Recall have a start and an end. When a later lesson shows the learner now gets it right, Recall closes the old fact ("thinks 1/8 is bigger than 1/4") and starts a new one, the same way it handles someone moving house. Prompt context and the learner model use what holds now. The old fact stays as history, so chat can answer "what has this learner corrected?", and the dashboard's memory explorer shows when each fact held.

// Two weeks later, in homework, the learner has it right. Recall closes the old fact instead
// of deleting it: context and the learner model show what holds now; chat can say what changed.
const homework = recall.session("homework-40822", { peers: [learner, tutor], scopes: [course] });
await homework.addMessages([
  learner.message("Wait, 1/8 is smaller than 1/4. More slices means smaller slices."),
]);
await recall.waitForIdle(); // demo only, as above
const progress = await learner.chat(
  "Which misconceptions has this learner corrected, and when?",
  { scope: course, reasoningLevel: "low" },
);

The teacher's view. Search finds what the learner wrote about a topic across all their sessions, with the session each message came from, so a teacher can check where a line in the learner model came from. For a written overview of one learner, ask chat as above. Give teachers this view inside your app; they do not need Recall keys.

// The teacher's view: the learner's own words about a topic, across every lesson, with the
// session each came from. Search takes about a second and is included in the price.
const found = await learner.search("fractions pizza slices", { limit: 20 });
const excerpts = found.map((m) => ({ lesson: m.sessionId, at: m.createdAt, said: m.content }));

A learner leaves, or the school year ends. learner.delete() erases the learner's messages and everything learned about them. The tutor's replies in their lessons stay unless you delete those sessions too, and they often quote the learner, so delete both. With a workspace per school year, the end of the year is one loop.

// A learner leaves: delete their sessions (the tutor's replies quote them), then the learner.
for (const s of await (await learner.sessions()).toArray()) await s.delete();
await learner.delete();
// End of the school year: every session, then the workspace (refused while sessions remain).
for (const s of await (await recall.sessions()).toArray()) await s.delete();
await recall.deleteWorkspace();

Settings that matter

SettingUse
observeMe: false on the tutorRecall learns about the learner only. The tutor's lines are read as context, not learned from, so they cost less.
scopes on each session, scope on chatA view of the learner built only from one course's sessions. Both need a workspace key, not a key scoped to one learner.
responseFormatJSON for the learner model. The root must be an object; allOf, not and conditional keywords are not supported. Field descriptions help the model fill each field.
reasoningLevellow (the default) for the learner model, minimal for quick checks such as "has this learner met common denominators?". See Reasoning levels and models.
Workspace reasoning.customInstructionsKeep learning on the subject, for example: "Record what the learner knows, gets wrong and is working on. Do not record family, health or other personal matters." It steers the model that learns; it is not a filter.
tokensThe context budget for the tutor's prompt. Older turns become a summary, so a long lesson stays within it.
Message createdAtWhen you import past homework, pass when it was written, so "corrected in week 3" is dated right.

Cost and speed

Measured from Europe against the hosted API (Frankfurt), October 2026: a turn or context call took 0.3 to 0.7 seconds, a search about 0.9 seconds, and a question at minimal or low 2 to 4.5 seconds. Learning happens in the background and does not hold up the lesson.

Context, search, storage and summaries are included. You pay for learning, $1.75 per million message tokens with every message counting as at least 50, and for questions. A learner with three lessons a week (12 a month), each with 20 short messages of their own and 20 tutor replies of about 80 tokens, a learner model after every lesson and four teacher questions a month:

ItemPer month
Learner messages: 12 × 20 × 50 tokens × $1.75 / 1M$0.0210
Tutor replies, read but not learned from: 12 × 20 × 80 tokens × $1.75 / 1M$0.0336
Learner model at low: 12 × $0.005$0.0600
Teacher questions at low: 4 × $0.005$0.0200
Totalabout $0.13 per learner a month

Answers are cached until the memory they depend on changes, but a cached answer is still billed as one query, so store the learner model instead of asking again. More in Cost and speed.

Privacy and forgetting

Learners are often children. This is practical guidance, not legal advice: check it against the law that applies to you and to the schools you serve, and see Privacy, consent and forgetting.

Recall's own terms are in the privacy policy and the DPA.

Pitfalls

Next: Health, fitness and coaching · Privacy, consent and forgetting