Persona Lifecycle & Adaptive Social Mind Architecture
LLM-backed AI persona engine for simulations, virtual agents, and interactive applications.
Each PlasmaEngine instance is one simulated person โ they have a personality, mood, fatigue, memory, a social influence score, and a relationship graph centered on themselves. They read messages, decide whether to respond, call external tools, and produce in-character text replies.
Works in any TypeScript environment (Node.js, Electron, browser, etc.). Requires an OpenAI or Anthropic API key supplied at runtime.
PlasmaEngine (one instance per persona)
โโโ PersonaCore โ static traits: personality, skills, values, influence score
โโโ EmotionCore โ dynamic mood: valence, arousal, stress, confidence, motivation
โโโ FatigueCore โ energy, mental fatigue, burnout risk; work/rest simulation
โโโ MemoryCore โ episodic/semantic/emotional memories with decay + retrieval
โโโ RelationshipGraph โ ego-centric graph; edges updated by every interaction
โ
โโโ MessageRouter โ engagement decision engine (MUST / SHOULD / CAN / SKIP)
โโโ ConversationManager โ conversation state and history
โโโ ResponseGenerator โ agentic loop: LLM โ tool calls โ result โ final reply
โ
โโโ MCPClient โ tool registry; register handlers at startup
โโโ PromptBuilder โ assembles system prompt from all live state
โโโ LLMProvider โ OpenAI / Anthropic abstraction
npm install plasma-engine
# install your chosen LLM provider:
npm install openai # for OpenAI
npm install @anthropic-ai/sdk # for Anthropicimport { PlasmaEngine, fromPreset } from 'plasma-engine';
const engine = new PlasmaEngine({
persona: {
id: 'dev-jisu',
name: '๊น์ง์',
role: 'Frontend Developer',
influenceScore: fromPreset('senior').score, // 58
influenceLabel: 'Senior',
personality: {
openness: 0.75, conscientiousness: 0.65,
extraversion: 0.45, agreeableness: 0.70,
neuroticism: 0.30, ambition: 0.65,
},
background: '5-year frontend developer. React and TypeScript specialist.',
skills: [
{ name: 'React', level: 'expert', domain: 'frontend' },
{ name: 'TypeScript', level: 'advanced', domain: 'frontend' },
{ name: 'UI Design', level: 'intermediate', domain: 'design' },
],
communicationStyle: { formality: 0.35, verbosity: 0.55, directness: 0.6, humor: 0.4, language: 'en' },
relationships: [
{ personaId: 'lead-1', name: 'Team Lead', trust: 0.7, rapport: 0.6, influenceScore: 99, explicitType: 'superior' },
{ personaId: 'dev-2', name: 'Park Minsu', trust: 0.8, rapport: 0.8, influenceScore: 40 },
],
values: ['code quality', 'autonomy', 'learning'],
},
llm: {
provider: 'anthropic',
apiKey: process.env.ANTHROPIC_API_KEY!,
},
debug: false,
});Plug in any callable action โ databases, APIs, task queues, etc.:
engine.registerTool(
{
name: 'get_task_queue',
description: 'List tasks currently assigned to this persona.',
inputSchema: { type: 'object', properties: {} },
},
async () => myApp.getTasksFor(engine.persona.id)
);const decision = engine.routeMessage({
id: 'msg-001',
conversationId: 'channel-general',
senderId: 'lead-1',
senderName: 'Team Lead',
senderInfluenceScore: 99,
content: 'Jisu, is the sprint review ready?',
timestamp: Date.now(),
mentions: ['dev-jisu'],
isDirectMessage: false,
});
// decision.action: 'MUST_RESPOND' | 'SHOULD_RESPOND' | 'CAN_RESPOND' | 'SKIP'
if (decision.action !== 'SKIP') {
setTimeout(async () => {
const reply = await engine.respond(message);
myApp.sendMessage(engine.persona.id, reply);
}, decision.delayMs);
}engine.applyGameEvent({ type: 'praised', intensity: 0.9 });
engine.applyGameEvent({ type: 'deadline_missed', intensity: 0.7 });
engine.applyGameEvent({ type: 'promoted', intensity: 1.0 });engine.advanceTime(8, false); // 8 hours of work
engine.advanceTime(6, true); // 6 hours of rest
engine.startNewDay();
engine.startNewWeek();engine.on('emotion:changed', (state) => ui.updateMoodIndicator(state.mood));
engine.on('fatigue:changed', (state) => ui.updateEnergyBar(state.energy));
engine.on('engagement:decided', (d) => console.log(d.action, d.reasoning));
engine.on('relationship:updated', (edge) => updateGraphNode(edge));
engine.on('memory:added', (mem) => console.log('Memory:', mem.content));
engine.on('response:generated', ({ response }) => console.log(response));Instead of hardcoded job titles, Plasma uses a 0โ100 influence score:
import { INFLUENCE_PRESETS, fromPreset, deriveModifiers } from 'plasma-engine';
INFLUENCE_PRESETS.intern // 5
INFLUENCE_PRESETS.senior // 58
INFLUENCE_PRESETS.team_manager // 80
INFLUENCE_PRESETS.ceo_founder // 99
const jisu = fromPreset('senior', 'Senior'); // โ { score: 58, label: 'Senior' }
const mods = deriveModifiers(58);
// mods.decisionAuthority โ 0.52
// mods.deferenceToHigher โ 0.44
// mods.maxEffectiveHoursPerDay โ 11.5| Metric | Range | Meaning |
|---|---|---|
trust |
0โ1 | Reliability and honesty |
rapport |
0โ1 | Warmth and social comfort |
respect |
0โ1 | Admiration of competence/character |
tension |
0โ1 | Conflict and friction |
familiarity |
0โ1 | How well they know each other |
Auto-derived types: close_friend ยท friend ยท colleague ยท rival ยท acquaintance ยท stranger
| Method | Description |
|---|---|
routeMessage(msg) |
Feed a message โ returns EngagementDecision |
respond(msg, opts?) |
Generate and record a response string |
logInteraction(targetId, name, event) |
Record an interaction (updates relationship graph) |
setRelationshipType(id, name, type, meta?) |
Set explicit relationship classification |
getEgoGraph() |
Export graph for visualization |
applyGameEvent(event) |
Trigger emotional/memory update |
advanceTime(hours, isResting?) |
Simulate passage of time |
registerTool(tool, handler) |
Register an external tool |
serialize() |
Full serialisable state for save/load |
.on(event, handler) |
Subscribe to engine events |
const saved = engine.serialize();
localStorage.setItem('persona-dev-jisu', JSON.stringify(saved));
// Full restore API planned for v0.3Persona Lifecycle & Adaptive Social Mind Architecture
LLM ๊ธฐ๋ฐ AI ํ๋ฅด์๋ ์์ง โ ๊ฐ์ ยทํผ๋ก๋ยท๊ธฐ์ตยท์ธ๊ฐ๊ด๊ณ๋ฅผ ๊ฐ์ง ๊ฐ์ ์ธ๊ฒฉ์ด ์ํฉ์ ๋ง๊ฒ ์์จ์ ์ผ๋ก ํ๋จํ๊ณ ๋ํํฉ๋๋ค.
PlasmaEngine ์ธ์คํด์ค ํ๋๊ฐ ๊ณง ํ ๋ช
์ ๊ฐ์ ์ธ๊ฒฉ์
๋๋ค. ์ฑ๊ฒฉ, ๊ธฐ๋ถ, ํผ๋ก๋, ๊ธฐ์ต, ์ฌํ์ ์ํฅ๋ ฅ ์ ์, ๊ทธ๋ฆฌ๊ณ ๊ด๊ณ ๊ทธ๋ํ๋ฅผ ๊ฐ์ง๋ฉฐ, ๋ฉ์์ง๋ฅผ ์ฝ๊ณ ์๋ต ์ฌ๋ถ๋ฅผ ์ค์ค๋ก ํ๋จํด ์บ๋ฆญํฐ์ ๋ง๋ ์๋ต์ ์์ฑํฉ๋๋ค.
TypeScript ํ๊ฒฝ์ด๋ผ๋ฉด ์ด๋์๋ ๋์ํฉ๋๋ค (Node.js, Electron, ๋ธ๋ผ์ฐ์ ๋ฑ). OpenAI ๋๋ Anthropic API ํค๊ฐ ํ์ํฉ๋๋ค.
PlasmaEngine (ํ๋ฅด์๋๋น 1๊ฐ ์ธ์คํด์ค)
โโโ PersonaCore โ ์ ์ ํน์ฑ: ์ฑ๊ฒฉ, ์คํฌ, ๊ฐ์น๊ด, ์ํฅ๋ ฅ ์ ์
โโโ EmotionCore โ ๋์ ๊ฐ์ : ๊ฐ๊ฐ, ๊ฐ์ฑ๋, ์คํธ๋ ์ค, ์์ ๊ฐ, ๋๊ธฐ๋ถ์ฌ
โโโ FatigueCore โ ์๋์ง, ์ ์ ํผ๋ก, ๋ฒ์์ ์ํ; ๊ทผ๋ฌด/ํด์ ์๋ฎฌ๋ ์ด์
โโโ MemoryCore โ ์ํผ์๋/์๋ฏธ๋ก ์ /๊ฐ์ ๊ธฐ์ต (๊ฐ์ + ๊ฒ์)
โโโ RelationshipGraph โ ์๊ธฐ์ค์ฌ ๊ทธ๋ํ; ๋ชจ๋ ์ํธ์์ฉ์ผ๋ก ๊ฐฑ์
โ
โโโ MessageRouter โ ์ฐธ์ฌ ๊ฒฐ์ ์์ง (MUST / SHOULD / CAN / SKIP)
โโโ ConversationManager โ ๋ํ ์ํ ๋ฐ ํ์คํ ๋ฆฌ
โโโ ResponseGenerator โ ์์ด์ ํธ ๋ฃจํ: LLM โ ๋๊ตฌ ํธ์ถ โ ๊ฒฐ๊ณผ โ ์ต์ข
์๋ต
โ
โโโ MCPClient โ ๋๊ตฌ ๋ ์ง์คํธ๋ฆฌ; ์์ ์ ํธ๋ค๋ฌ ๋ฑ๋ก
โโโ PromptBuilder โ ๋ชจ๋ ๋ผ์ด๋ธ ์ํ๋ฅผ ์์คํ
ํ๋กฌํํธ๋ก ์กฐํฉ
โโโ LLMProvider โ OpenAI / Anthropic ์ถ์ํ
npm install plasma-engine
# ์ฌ์ฉํ LLM ํ๋ก๋ฐ์ด๋ ์ค์น:
npm install openai # OpenAI
npm install @anthropic-ai/sdk # Anthropicimport { PlasmaEngine, fromPreset } from 'plasma-engine';
const engine = new PlasmaEngine({
persona: {
id: 'dev-jisu',
name: '๊น์ง์',
role: 'Frontend Developer',
influenceScore: fromPreset('senior').score, // 58
influenceLabel: '๊ณผ์ฅ',
personality: {
openness: 0.75, conscientiousness: 0.65,
extraversion: 0.45, agreeableness: 0.70,
neuroticism: 0.30, ambition: 0.65,
},
background: '5๋
์ฐจ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์. React์ TypeScript ์ ๋ฌธ๊ฐ.',
skills: [
{ name: 'React', level: 'expert', domain: 'frontend' },
{ name: 'TypeScript', level: 'advanced', domain: 'frontend' },
{ name: 'UI Design', level: 'intermediate', domain: 'design' },
],
communicationStyle: {
formality: 0.35, verbosity: 0.55,
directness: 0.6, humor: 0.4, language: 'ko',
},
relationships: [
{ personaId: 'lead-1', name: 'ํ์ฅ', trust: 0.7, rapport: 0.6, influenceScore: 99, explicitType: 'superior' },
{ personaId: 'dev-2', name: '๋ฐ๋ฏผ์', trust: 0.8, rapport: 0.8, influenceScore: 40 },
],
values: ['code quality', 'autonomy', 'learning'],
},
llm: {
provider: 'openai',
apiKey: process.env.OPENAI_API_KEY!,
},
debug: false,
});DB, API, ์์ ํ ๋ฑ ์ํ๋ ์ก์ ์ ์์ ๋กญ๊ฒ ์ฐ๊ฒฐํ ์ ์์ต๋๋ค:
engine.registerTool(
{
name: 'get_task_queue',
description: '์ด ํ๋ฅด์๋์๊ฒ ํ ๋น๋ ์์
๋ชฉ๋ก์ ๊ฐ์ ธ์ต๋๋ค.',
inputSchema: { type: 'object', properties: {} },
},
async () => myApp.getTasksFor(engine.persona.id)
);const decision = engine.routeMessage({
id: 'msg-001',
conversationId: 'channel-general',
senderId: 'lead-1',
senderName: 'ํ์ฅ',
senderInfluenceScore: 99,
content: '์ง์์จ, ์ค๋ ์คํ๋ฆฐํธ ๋ฆฌ๋ทฐ ์ค๋น๋๋์?',
timestamp: Date.now(),
mentions: ['dev-jisu'],
isDirectMessage: false,
});
// decision.action: 'MUST_RESPOND' | 'SHOULD_RESPOND' | 'CAN_RESPOND' | 'SKIP'
if (decision.action !== 'SKIP') {
setTimeout(async () => {
const reply = await engine.respond(message);
myApp.sendMessage(engine.persona.id, reply);
}, decision.delayMs);
}engine.applyGameEvent({ type: 'praised', intensity: 0.9 });
engine.applyGameEvent({ type: 'deadline_missed', intensity: 0.7 });
engine.applyGameEvent({ type: 'promoted', intensity: 1.0 });engine.advanceTime(8, false); // 8์๊ฐ ๊ทผ๋ฌด
engine.advanceTime(6, true); // 6์๊ฐ ํด์
engine.startNewDay();
engine.startNewWeek();engine.on('emotion:changed', (state) => ui.updateMoodIndicator(state.mood));
engine.on('fatigue:changed', (state) => ui.updateEnergyBar(state.energy));
engine.on('engagement:decided', (d) => console.log(d.action, d.reasoning));
engine.on('relationship:updated', (edge) => updateGraphNode(edge));
engine.on('memory:added', (mem) => console.log('๊ธฐ์ต:', mem.content));
engine.on('response:generated', ({ response }) => console.log(response));์ง๊ธ ์ด๋ฆ ๋์ 0~100 ์ํฅ๋ ฅ ์ ์๋ฅผ ์ฌ์ฉํฉ๋๋ค:
import { INFLUENCE_PRESETS, fromPreset, deriveModifiers } from 'plasma-engine';
INFLUENCE_PRESETS.intern // 5
INFLUENCE_PRESETS.senior // 58
INFLUENCE_PRESETS.team_manager // 80
INFLUENCE_PRESETS.ceo_founder // 99
const jisu = fromPreset('senior', '๊ณผ์ฅ'); // โ { score: 58, label: '๊ณผ์ฅ' }
const mods = deriveModifiers(58);
// mods.decisionAuthority โ 0.52
// mods.deferenceToHigher โ 0.44
// mods.maxEffectiveHoursPerDay โ 11.5| ์งํ | ๋ฒ์ | ์๋ฏธ |
|---|---|---|
trust |
0โ1 | ์ ๋ขฐ๋ |
rapport |
0โ1 | ์น๋ฐ๊ฐ |
respect |
0โ1 | ์กด๊ฒฝ |
tension |
0โ1 | ๊ฐ๋ฑ |
familiarity |
0โ1 | ์น์ํจ |
์๋ ๋ถ๋ฅ: close_friend ยท friend ยท colleague ยท rival ยท acquaintance ยท stranger
| ๋ฉ์๋ | ์ค๋ช |
|---|---|
routeMessage(msg) |
๋ฉ์์ง ์์ โ EngagementDecision ๋ฐํ |
respond(msg, opts?) |
์๋ต ๋ฌธ์์ด ์์ฑ ๋ฐ ๊ธฐ๋ก |
logInteraction(targetId, name, event) |
์ํธ์์ฉ ๊ธฐ๋ก (๊ด๊ณ ๊ทธ๋ํ ์ ๋ฐ์ดํธ) |
setRelationshipType(id, name, type, meta?) |
๊ด๊ณ ์ ํ ๋ช ์์ ์ค์ |
getEgoGraph() |
์๊ฐํ์ฉ ๊ทธ๋ํ ๋ด๋ณด๋ด๊ธฐ |
applyGameEvent(event) |
๊ฐ์ /๊ธฐ์ต ์ ๋ฐ์ดํธ ํธ๋ฆฌ๊ฑฐ |
advanceTime(hours, isResting?) |
์๊ฐ ๊ฒฝ๊ณผ ์๋ฎฌ๋ ์ด์ |
registerTool(tool, handler) |
์ธ๋ถ ๋๊ตฌ ๋ฑ๋ก |
serialize() |
์ ์ฅ/๋ก๋์ฉ ์ ์ฒด ์ง๋ ฌํ |
.on(event, handler) |
์์ง ์ด๋ฒคํธ ๊ตฌ๋ |
const saved = engine.serialize();
localStorage.setItem('persona-dev-jisu', JSON.stringify(saved));
// ์ ์ฒด ๋ณต์ API๋ v0.3์์ ๊ณํ ์ค