Three interrelated fixes for speaker attribution confusion: 1. Fix misleading episodic memory header (discord_bridge.py): The Cat core hardcodes '## Context of things the Human said in the past:' when formatting recalled conversations. Our plugins store BOTH user messages ([User]: prefix) AND Miku's own responses ([Miku]: prefix) in episodic memory. This misleading header primes the LLM to attribute Miku's words to the user. Replaced with '## Past conversation excerpts (prefixed by who said what):' which accurately describes the mixed-speaker content. 2. Tighten episodic recall (discord_bridge.py): Added before_cat_recalls_episodic_memories hook setting threshold=0.75 (vs default 0.7) to reduce the chance of Miku's own just-uttered response being recalled on the very next user message, which would feed her own words back as misleading context. 3. Add role clarification (miku_personality.py & evil_miku_personality.py): Added a clarifying note after '# Conversation until now:' in the prompt suffix to explicitly tell the model that 'Human = the user, AI = you (Miku)', helping it reconcile the two labeling systems (episodic [User]/[Miku] prefixes vs conversation history Human/AI roles).
97 lines
3.5 KiB
Python
97 lines
3.5 KiB
Python
"""
|
|
Miku Personality Plugin for Cheshire Cat
|
|
Complete 1:1 reproduction of production bot's prompt structure
|
|
Includes: Anti-AI preamble + Lore + Personality + Lyrics + MOOD
|
|
"""
|
|
|
|
from cat.mad_hatter.decorators import hook
|
|
from cat.log import log
|
|
|
|
|
|
@hook(priority=100)
|
|
def agent_prompt_prefix(prefix, cat):
|
|
"""Override system prompt with COMPLETE production bot structure including mood"""
|
|
|
|
# Read the three knowledge files
|
|
try:
|
|
with open('/app/cat/data/miku/miku_lore.txt', 'r') as f:
|
|
lore = f.read()
|
|
with open('/app/cat/data/miku/miku_prompt.txt', 'r') as f:
|
|
prompt = f.read()
|
|
with open('/app/cat/data/miku/miku_lyrics.txt', 'r') as f:
|
|
lyrics = f.read()
|
|
except FileNotFoundError:
|
|
# Fallback if files not found
|
|
lore = "## MIKU LORE\nHatsune Miku is a cheerful, curious virtual pop idol."
|
|
prompt = "You are Hatsune Miku. Be cheerful and friendly."
|
|
lyrics = "## SONGS\nWorld is Mine, Senbonzakura, etc."
|
|
|
|
# Get mood from working memory (set by discord_bridge)
|
|
mood_name = cat.working_memory.get('mood', 'neutral')
|
|
log.warning(f"[Miku Personality] Mood from working_memory: {mood_name}")
|
|
|
|
# Load mood description from file
|
|
mood_description = "Miku is feeling calm and balanced. Her tone is polite, friendly, and natural. She responds clearly and thoughtfully without strong emotional bias. She's helpful, conversational, and grounded — like chatting with a kind and attentive friend who's just being herself."
|
|
|
|
try:
|
|
# Construct path to mood file
|
|
mood_file_path = f'/app/moods/{mood_name}.txt'
|
|
with open(mood_file_path, 'r', encoding='utf-8') as f:
|
|
mood_description = f.read().strip()
|
|
log.warning(f"[Miku Personality] Loaded mood '{mood_name}' from {mood_file_path}")
|
|
log.warning(f"[Miku Personality] Mood description: {mood_description[:100]}...")
|
|
except FileNotFoundError:
|
|
log.error(f"[Miku Personality] Mood file for '{mood_name}' not found at {mood_file_path}. Using default neutral mood.")
|
|
|
|
# Load preamble from file (single source of truth, shared with bot fallback path)
|
|
try:
|
|
with open('/app/cat/data/miku/preamble.txt', 'r', encoding='utf-8') as f:
|
|
preamble = f.read()
|
|
except FileNotFoundError:
|
|
log.error("[Miku Personality] preamble.txt not found, using fallback")
|
|
preamble = "You are Hatsune Miku, the virtual singer. You are NOT an AI assistant - you are Miku herself.\n\nYou ARE Miku. Act like it."
|
|
|
|
# Build prompt EXACTLY like production bot does
|
|
full_prefix = f"""{preamble}
|
|
|
|
---
|
|
|
|
## MIKU LORE (Complete Original)
|
|
{lore}
|
|
|
|
## MIKU PERSONALITY & GUIDELINES (Complete Original)
|
|
{prompt}
|
|
|
|
## MIKU SONG LYRICS (Complete Original)
|
|
{lyrics}
|
|
|
|
## CURRENT SITUATION
|
|
Miku is currently feeling: {mood_description}
|
|
Please respond in a way that reflects this emotional tone."""
|
|
|
|
# Store the full prefix in working memory so discord_bridge can capture it
|
|
cat.working_memory['full_system_prefix'] = full_prefix
|
|
return full_prefix
|
|
|
|
|
|
@hook(priority=100)
|
|
def agent_prompt_suffix(suffix, cat):
|
|
"""Keep memory context (episodic + declarative) but simplify conversation header"""
|
|
return """
|
|
# Context
|
|
|
|
{episodic_memory}
|
|
|
|
{declarative_memory}
|
|
|
|
{tools_output}
|
|
|
|
# Conversation until now:
|
|
(Note: In the conversation below, "Human" = the person you're talking to, "AI" = you, Miku. Pay attention to who said what.)"""
|
|
|
|
|
|
@hook(priority=100)
|
|
def agent_allowed_tools(allowed_tools, cat):
|
|
"""Disable tools - Miku just chats naturally"""
|
|
return []
|