164 lines
5.5 KiB
Markdown
164 lines
5.5 KiB
Markdown
# BoxBrain
|
|
|
|
BoxBrain is a TypeScript framework for designing LLM harnesses that make a persona feel like a real person, backed by IdentityDB memory spaces.
|
|
|
|
This repository was reset from scratch. The current implementation focuses on a clean framework core rather than a concrete chatbot product.
|
|
|
|
## Goals
|
|
|
|
BoxBrain helps API users build an LLM-driven persona with:
|
|
|
|
- persona initialization into an isolated IdentityDB-backed space
|
|
- realistic schedule generation
|
|
- schedule-derived availability (`online`, `do-not-disturb`, `offline`)
|
|
- reply and proactive conversation APIs
|
|
- sleep-time memory extraction into durable facts
|
|
- debug hooks that expose the framework flow and persona reasoning pipeline
|
|
|
|
## Install
|
|
|
|
```bash
|
|
bun install
|
|
```
|
|
|
|
## Core API
|
|
|
|
```ts
|
|
import { Persona, createSqliteIdentityMemoryStore } from 'boxbrain';
|
|
|
|
const memory = await createSqliteIdentityMemoryStore('.data/mina.sqlite');
|
|
|
|
const persona = new Persona(
|
|
'Mina',
|
|
'Mina is a careful student who likes quiet cafes and is preparing for exams.',
|
|
{
|
|
memory,
|
|
models: {
|
|
conversation: yourConversationModel,
|
|
memoryExtraction: yourMemoryExtractionModel,
|
|
},
|
|
debug: (event) => console.log(event),
|
|
},
|
|
);
|
|
|
|
const space = await persona.ready();
|
|
```
|
|
|
|
A persona can also be loaded from an existing space:
|
|
|
|
```ts
|
|
const persona = new Persona(space.id, { memory, models });
|
|
await persona.ready();
|
|
```
|
|
|
|
## Persona initialization
|
|
|
|
`new Persona(displayName, message, options)` creates a new isolated persona space. The seed message is the single freeform place for personality, history, likes, dislikes, relationships, and other facts about the persona.
|
|
|
|
`new Persona(spaceId, options)` loads an existing persona space.
|
|
|
|
If `models.initialization` is provided, BoxBrain asks it for initial facts. If no initialization model is provided, BoxBrain stores a minimal seed fact about the persona. If the model intentionally returns an empty list, no fallback fact is stored.
|
|
|
|
## Schedule API
|
|
|
|
```ts
|
|
await persona.createDailySchedule(now, 'Keep a normal work day.');
|
|
await persona.createMonthlySchedule(now, 'Mostly study, with occasional rest.');
|
|
await persona.deleteSchedulesBefore(cutoff);
|
|
await persona.deleteSchedulesOlderThan(cutoff);
|
|
```
|
|
|
|
`createDailySchedule(datetime, message)` creates tomorrow's schedule in 10-minute blocks. For example, if `datetime` is May 1, the generated daily schedule covers May 2 00:00 through May 3 00:00.
|
|
|
|
`createMonthlySchedule(datetime, message)` creates day-level schedule outlines for the next 30 days.
|
|
|
|
Schedules are stored through the configured memory store. The IdentityDB store records schedule entries as facts under schedule-related topics.
|
|
|
|
## Availability API
|
|
|
|
```ts
|
|
const availability = await persona.getTodayScheduledAvailability(now);
|
|
```
|
|
|
|
Availability is derived from schedule entries and kept in memory rather than persisted separately. The window covers today 00:00 through tomorrow 24:00. When the date changes, BoxBrain rebuilds the snapshot from schedule entries in memory.
|
|
|
|
Schedule activities map to availability roughly as:
|
|
|
|
- `sleep` → `offline`
|
|
- `work`, `study`, `job-search`, `travel`, `commute` → `do-not-disturb`
|
|
- rest, meals, exercise, errands, social time, free time → `online`
|
|
|
|
## Conversation API
|
|
|
|
```ts
|
|
const reply = await persona.sendMessage({
|
|
datetime: now,
|
|
messageHistory: [
|
|
{ sender: 'persona', time: yesterday, content: 'See you later.' },
|
|
{ sender: 'user', time: now, content: 'What are you doing?' },
|
|
],
|
|
});
|
|
|
|
const opener = await persona.startConversation({
|
|
datetime: now,
|
|
messageHistory: [],
|
|
});
|
|
```
|
|
|
|
Before generating a reply, BoxBrain always loads mandatory context:
|
|
|
|
- formatted yesterday/today message history supplied by the API user
|
|
- yesterday, today, and tomorrow schedule entries
|
|
- current schedule-derived availability
|
|
- IdentityDB facts related to the persona and the user
|
|
|
|
If no relevant mandatory memory is found, the model context explicitly says `기억이 없음` so the persona can react naturally instead of pretending to remember.
|
|
|
|
Conversation models return one or more outgoing messages. The framework instruction tells the model to behave like a `send_message` tool and, unless the persona prefers otherwise, keep each message to at most one sentence.
|
|
|
|
If `getLatestMessageHistory` and `models.rewrite` are provided, BoxBrain can detect messages that arrived while a draft was being generated and ask the rewrite model whether to discard and regenerate the stale draft.
|
|
|
|
## sleepMemory
|
|
|
|
```ts
|
|
await persona.sleepMemory({
|
|
datetime: '2026-05-02T00:00:00.000Z',
|
|
messageHistory: messagesFromMay1,
|
|
});
|
|
```
|
|
|
|
`sleepMemory` asks `models.memoryExtraction` to inspect the provided message history, objectivize durable facts, and persist them through the memory store. The recommended cadence is daily around midnight, passing the previous day's messages.
|
|
|
|
## Debug hooks
|
|
|
|
Every major pipeline step can emit a debug event:
|
|
|
|
```ts
|
|
const persona = new Persona('Mina', seed, {
|
|
debug(event) {
|
|
// Write to a file, messenger, trace UI, etc.
|
|
console.log(event.name, event.data);
|
|
},
|
|
});
|
|
```
|
|
|
|
Examples include:
|
|
|
|
- `persona.initialized`
|
|
- `persona.loaded`
|
|
- `persona.schedule.daily.generated`
|
|
- `persona.availability.refreshed`
|
|
- `persona.conversation.context.loaded`
|
|
- `persona.conversation.rewrite.checked`
|
|
- `persona.memory.sleep.persisted`
|
|
|
|
## Development
|
|
|
|
```bash
|
|
bun run test
|
|
bun run check
|
|
bun run build
|
|
```
|
|
|
|
The current test suite covers persona creation/loading, schedule generation/pruning, availability derivation, conversation context assembly, stale-draft rewrite checks, proactive conversation starts, and sleep-memory persistence.
|