From f964d4de9b4645041b6cba1fbdabf14988de47c7 Mon Sep 17 00:00:00 2001 From: p-sw Date: Tue, 19 May 2026 23:24:34 +0900 Subject: [PATCH] feat: add baseSystemPrompt --- src/conversation.ts | 8 +++++--- src/persona.ts | 8 +++++--- src/types.ts | 1 + tests/conversation.test.ts | 26 ++++++++++++++++++++++++++ tests/persona.test.ts | 12 ++++++++++++ 5 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/conversation.ts b/src/conversation.ts index a956cac..d65ec86 100644 --- a/src/conversation.ts +++ b/src/conversation.ts @@ -20,14 +20,16 @@ export function formatMessageHistory(input: { .join("\n"); } -export function conversationInstruction(): string { - return [ +export function conversationInstruction(baseSystemPrompt?: string): string { + const parts = [ + ...(baseSystemPrompt === undefined ? [] : [baseSystemPrompt]), "You are controlling the persona, not a generic assistant.", "Use the send_message tool conceptually: return one or more outgoing messages.", "Unless the persona strongly prefers otherwise, keep each outgoing message to at most one sentence.", "Prefer short, natural, chat-like wording and allow splitting one thought into multiple messages.", 'If mandatory memory says "기억이 없음", the persona may naturally wonder about missing context instead of pretending to remember.', - ].join("\n"); + ]; + return parts.join("\n"); } export async function buildMandatoryConversationContext(input: { diff --git a/src/persona.ts b/src/persona.ts index 09d234d..fe7142d 100644 --- a/src/persona.ts +++ b/src/persona.ts @@ -68,6 +68,7 @@ export class Persona { private readonly mode: Mode; private readonly readyPromise: Promise; private availabilitySnapshot?: ScheduledAvailabilitySnapshot; + readonly baseSystemPrompt: string | undefined; constructor( displayName: string, @@ -88,6 +89,7 @@ export class Persona { this.options = second ?? {}; } this.memory = this.options.memory ?? new InMemoryMemoryStore(); + this.baseSystemPrompt = this.options.baseSystemPrompt; this.readyPromise = this.initialize(); } @@ -247,7 +249,7 @@ export class Persona { mode: "reply", context, ...(userMessage === undefined ? {} : { userMessage }), - instruction: conversationInstruction(), + instruction: conversationInstruction(this.baseSystemPrompt), }), ); @@ -281,7 +283,7 @@ export class Persona { now: toIso(input.datetime), mode: "reply", context: latestContext, - instruction: conversationInstruction(), + instruction: conversationInstruction(this.baseSystemPrompt), })), ); } @@ -320,7 +322,7 @@ export class Persona { now: toIso(input.datetime), mode: "start-conversation", context, - instruction: conversationInstruction(), + instruction: conversationInstruction(this.baseSystemPrompt), }), ); await this.emit("persona.conversation.started", { diff --git a/src/types.ts b/src/types.ts index 60341a4..2a62b60 100644 --- a/src/types.ts +++ b/src/types.ts @@ -160,6 +160,7 @@ export interface PersonaOptions { models?: PersonaModels; debug?: DebugHook; now?: DateTimeInput; + baseSystemPrompt?: string; } export interface BoxBrainMemoryStore { diff --git a/tests/conversation.test.ts b/tests/conversation.test.ts index 6569251..257c36f 100644 --- a/tests/conversation.test.ts +++ b/tests/conversation.test.ts @@ -124,4 +124,30 @@ describe('Conversation API', () => { expect(mode).toBe('start-conversation'); expect(started.messages).toEqual(['오늘 좀 조용하네.']); }); + + it('includes baseSystemPrompt at the start of the instruction when provided', async () => { + const memory = new InMemoryMemoryStore(); + let captured: ReplyGenerationInput | undefined; + const persona = new Persona('Mina', 'Mina likes quiet cafes.', { + memory, + now: '2026-05-01T10:00:00.000Z', + baseSystemPrompt: 'You are a helpful assistant. Always be kind.', + models: { + conversation: { + async generateReply(input) { + captured = input; + return { messages: ['Hello!'] }; + }, + }, + }, + }); + await persona.ready(); + + await persona.sendMessage({ + datetime: '2026-05-01T12:00:00.000Z', + messageHistory: [{ sender: 'user', time: '2026-05-01T12:00:00.000Z', content: 'Hi' }], + }); + + expect(captured?.instruction.startsWith('You are a helpful assistant. Always be kind.')).toBe(true); + }); }); diff --git a/tests/persona.test.ts b/tests/persona.test.ts index d27fd4f..5f91268 100644 --- a/tests/persona.test.ts +++ b/tests/persona.test.ts @@ -29,6 +29,18 @@ describe('Persona initialization', () => { expect(debug).toContain('persona.initialized'); }); + it('exposes baseSystemPrompt on the persona instance when provided', async () => { + const memory = new InMemoryMemoryStore(); + const persona = new Persona('Hana', 'Hana is a cheerful barista.', { + memory, + now: '2026-05-01T10:00:00.000Z', + baseSystemPrompt: 'You are a helpful assistant. Always be kind.', + }); + + await persona.ready(); + expect(persona.baseSystemPrompt).toBe('You are a helpful assistant. Always be kind.'); + }); + it('loads an existing persona space by space id without creating another space', async () => { const memory = new InMemoryMemoryStore(); const created = new Persona('Joon', 'Joon is a freelance designer.', { memory, now: '2026-05-01T10:00:00.000Z' });