Skip to content

Learning Machine

LearningMachine coordinates multiple memory store types under a single API. It retrieves relevant context before each LLM call and extracts new learnings after each turn — keeping agents adaptive without making the system opaque.

ts
import { LearningMachine } from 'personaforge';

Quick start

ts
import { LearningMachine } from 'personaforge';
import { SqliteAgentDb } from 'personaforge';
import { createAgent } from 'personaforge';

const db = new SqliteAgentDb({ path: './agent.db' });

// All five stores auto-created using the db backend
const machine = new LearningMachine({ db });

const agent = createAgent({
  name: 'adaptive-assistant',
  instructions: 'Help users. Use your memory to personalise responses.',
  model: 'gpt-4o-mini',
  apiKey: process.env.OPENAI_API_KEY!,
  hooks: {
    buildSystemPrompt: async (base, ctx) => {
      // Inject remembered context into every run
      const memory = await machine.buildContext({
        userId:    ctx.userId,
        sessionId: ctx.sessionId,
        message:   ctx.prompt,
      });
      return memory ? `${base}\n\n${memory}` : base;
    },
    afterRun: async (result) => {
      // Extract and persist new learnings from this turn
      await machine.process({
        messages:  result.messages,
        userId:    result.userId,
        sessionId: result.sessionId,
      });
      return result;
    },
  },
});

Store types

Each store is opt-in — use only what the task requires:

StorePurpose
userProfileStructured user attributes (name, preferences, language)
userMemoryUnstructured user memories (free-text facts per user)
sessionContextPer-session summary, current goal, and plan
entityMemoryMemories about companies, projects, people (named entities)
learnedKnowledgeReusable insights and patterns across all users
decisionLogLog of agent decisions for auditability and reflection

LearningMachine API

ts
// Build a context string to inject into the system prompt
const context = await machine.buildContext({
  userId: 'user-42',
  sessionId: 'sess-xyz',
  message: 'What were we working on last time?',
  namespace: 'default',     // optional: scope entity/knowledge queries
});

// Raw recall — returns one key per store (useful for inspection/testing)
const recalled = await machine.recall({ userId: 'user-42', sessionId: 'sess-xyz' });
// recalled.userProfile, recalled.userMemory, recalled.sessionContext...

// Process a completed turn and persist learnings.
// `messages` is required. The base implementation is effectively a no-op —
// extend it with custom stores to plug in LLM-based extraction.
await machine.process({
  messages,
  userId: 'user-42',
  sessionId: 'sess-xyz',
});

// Get callable tools the agent can use to update its own memory.
// getTools() is synchronous and returns bare callables (LearningTool[]) —
// plain functions, NOT framework Tools.
const tools = machine.getTools({ userId: 'user-42' });
// Returns: [addMemory, updateMemory, deleteMemory, updateContext, addEntityFact,
//   addEntityEvent, saveKnowledge, searchKnowledge, logDecision, searchDecisions]

LearningMachineConfig

ts
interface LearningMachineConfig {
  /** Structured user profile store */
  userProfile?: UserProfileStore;
  /** Unstructured user memory store */
  userMemory?: UserMemoryStore;
  /** Per-session context store */
  sessionContext?: SessionContextStore;
  /** Entity memory store */
  entityMemory?: EntityMemoryStore;
  /** Learned knowledge store */
  learnedKnowledge?: LearnedKnowledgeStore;
  /** Decision log store */
  decisionLog?: DecisionLogStore;
  /** Curator for pruning and deduplicating memories */
  curator?: Curator;
  /** Optional db backend — any unspecified store is auto-created */
  db?: AgentDb;
  /** Default namespace for entity/knowledge stores (default: 'global') */
  namespace?: string;
  debug?: boolean;
}

Self-updating memory tools

Give the agent tools to update its own memory during a run:

ts
import { tool } from 'personaforge';
import { z } from 'zod';

const machine = new LearningMachine({ db });

// getTools() is synchronous and returns bare callables (LearningTool[]), not
// framework Tools — wrap each with tool() before passing to createAgent.
// (Most take a single string argument; widen the schema per tool as needed.)
const memoryTools = machine.getTools({ userId: 'user-42' }).map((fn) =>
  tool({
    name: fn.name,                          // e.g. 'addMemory', 'saveKnowledge'
    description: `Learning memory tool: ${fn.name}`,
    parameters: z.object({ input: z.string() }),
    execute: async ({ input }) => String(await fn(input)),
  }),
);

const agent = createAgent({
  name: 'personal-assistant',
  instructions: 'Help the user. Use memory tools to save important facts.',
  model: 'gpt-4o',
  apiKey: process.env.OPENAI_API_KEY!,
  tools: memoryTools,   // agent can call addMemory, addEntityFact, saveKnowledge, etc.
});

Where to go next

  • Memory — underlying memory stores (InMemoryMemoryStore, DbMemoryStore).
  • Session — session continuity underlying sessionContext.
  • Eval — measure whether learning is improving outcomes.

Released under the MIT License.