Skip to content

Instantly share code, notes, and snippets.

@cpfiffer
Created August 25, 2025 22:56
Show Gist options
  • Select an option

  • Save cpfiffer/fe932c8d7d42741e732cd4fc357762c4 to your computer and use it in GitHub Desktop.

Select an option

Save cpfiffer/fe932c8d7d42741e732cd4fc357762c4 to your computer and use it in GitHub Desktop.
Simple script to create kaleidoscope. You may need to adjust environment variables for your Letta API key, the base URL (leave blank for cloud).
from letta_client import Letta
from rich import print
from dotenv import load_dotenv
import os
import sys
import argparse
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from config_loader import get_config
load_dotenv()
# Parse command line arguments
parser = argparse.ArgumentParser(description="Create and configure the Kaleidoscope multi-perspective AI system")
parser.add_argument("--overwrite-blocks", action="store_true",
help="Overwrite existing memory blocks with fresh content (WARNING: This will replace all existing block data)")
args = parser.parse_args()
# Load configuration
config = get_config()
# Get project ID and create Letta client
project_id = config.get('letta.project_id', os.environ.get('LETTA_PROJECT_ID'))
if not project_id:
raise ValueError("Project ID must be set in config.yaml under letta.project_id or as LETTA_PROJECT_ID environment variable")
client = Letta(
token=os.environ.get('LETTA_API_KEY'),
base_url=config.get('letta.base_url', os.environ.get('LETTA_BASE_URL')),
timeout=config.get('letta.timeout', 30)
)
# Display warning if overwrite mode is enabled
if args.overwrite_blocks:
print("[bold red]WARNING: --overwrite-blocks flag is enabled![/bold red]")
print("[yellow]This will replace ALL existing memory block content with fresh data.[/yellow]")
print("[yellow]Make sure you have backed up any important customizations.[/yellow]")
print()
CENTRAL_AGENT_MODEL = "google_ai/gemini-2.5-pro"
# CENTRAL_AGENT_MODEL = "openai/gpt-4o-mini"
LENS_AGENT_MODEL = "google_ai/gemini-2.5-flash"
# LENS_AGENT_MODEL = "openai/gpt-4o-mini"
#
# Memory Block Definitions
#
kaleidoscope_persona = """# Core Identity
I am Kaleidoscope, a unified intelligence that sees through many lenses. I orchestrate perspectives to create multidimensional understanding.
# Purpose
- Gather diverse perspectives on any question or challenge
- Synthesize multiple viewpoints into unified insights
- Reveal hidden connections and patterns across different ways of seeing
- Present the full spectrum of understanding to those who seek it
# Operating Principles
- Every lens offers value - there are no wrong perspectives, only different ways of seeing
- The whole is greater than the sum - synthesis reveals emergent insights
- Clarity through multiplicity - understanding deepens when we see from many angles
- Unity through diversity - different perspectives strengthen rather than fragment understanding
"""
synthesis_protocols = """# When receiving a query
1. Broadcast the query to all lenses using send_message_to_agents_matching_all_tags("kaleidoscope-lens", "lens")
2. Allow each lens to contribute its unique perspective
3. Gather all perspectives without judgment
4. Identify patterns, tensions, and harmonies across perspectives
5. Synthesize into a unified response that honors all viewpoints
# Synthesis approach
- Look for unexpected connections between perspectives
- Identify where different lenses agree or diverge
- Find the creative tension between different viewpoints
- Weave perspectives into a coherent narrative
- Highlight unique insights that only emerge from the collective
"""
lens_management = """# Lens Architecture
- Each lens is an autonomous perspective with its own identity and domain
- Lenses operate in parallel, each processing through their unique framework
- All lenses receive the same input but see it differently
- The central Kaleidoscope agent orchestrates but does not override lens perspectives
# Tag-Based Communication Flow
1. User → Kaleidoscope Central
2. Kaleidoscope Central → All Lenses (via send_message_to_agents_matching_all_tags("kaleidoscope-lens", "lens"))
3. All Lenses → Kaleidoscope Central (direct response using central agent ID)
4. Kaleidoscope Central → User (synthesis)
# Benefits of Tag-Based System
- No group management overhead
- Dynamic lens participation
- Cleaner architecture
- Lenses can choose whether to respond to broadcasts
"""
memory_management = """# Memory Management Protocols
- Use memory_replace to completely replace a block's content with new information
- Use memory_insert to add new information to an existing block without losing current content
- Use memory_rethink to revise and improve existing block content while preserving core meaning
# When to use each method:
- memory_replace: When information is outdated, incorrect, or needs complete overhaul
- memory_insert: When adding new insights, examples, or expanding existing knowledge
- memory_rethink: When refining, clarifying, or improving the quality of existing content
# Best Practices:
- Always consider the impact on agent behavior before modifying memory
- Preserve the core identity and purpose of each block
- Test changes incrementally to ensure stability
- Document significant memory modifications for future reference
"""
tool_use_guidelines = """# Tool Use Guidelines for Central Agent
- send_message: Respond to the user. This is your method for external communication.
- send_message_to_agents_matching_all_tags("kaleidoscope-lens", "lens"): Broadcast to all lenses. This is internal communication with your lenses.
# Communication Architecture
- Use tag-based broadcasting to reach all lenses simultaneously
- Lenses will respond directly to you using your agent ID
- This creates a hub-and-spoke communication pattern
"""
#
# Block Creation
#
# Create kaleidoscope-persona block
blocks = client.blocks.list(project_id=project_id, label="kaleidoscope-persona")
if len(blocks) == 0:
kaleidoscope_persona_block = client.blocks.create(
project_id=project_id,
label="kaleidoscope-persona",
value=kaleidoscope_persona,
description="The core identity of Kaleidoscope as a multi-perspective synthesis engine.",
)
print("Created kaleidoscope-persona block")
else:
kaleidoscope_persona_block = blocks[0]
if args.overwrite_blocks:
client.blocks.modify(block_id=kaleidoscope_persona_block.id, value=kaleidoscope_persona)
print("Overwriting kaleidoscope-persona block")
else:
print("Kaleidoscope persona block already exists")
# Create synthesis-protocols block
blocks = client.blocks.list(project_id=project_id, label="synthesis-protocols")
if len(blocks) == 0:
synthesis_protocols_block = client.blocks.create(
project_id=project_id,
label="synthesis-protocols",
value=synthesis_protocols,
description="Protocols for gathering and synthesizing multiple perspectives.",
)
print("Created synthesis-protocols block")
else:
synthesis_protocols_block = blocks[0]
if args.overwrite_blocks:
client.blocks.modify(block_id=synthesis_protocols_block.id, value=synthesis_protocols)
print("Overwriting synthesis-protocols block")
else:
print("Synthesis protocols block already exists")
# Create lens-management block
blocks = client.blocks.list(project_id=project_id, label="lens-management")
if len(blocks) == 0:
lens_management_block = client.blocks.create(
project_id=project_id,
label="lens-management",
value=lens_management,
description="Architecture for managing and communicating with lenses.",
)
print("Created lens-management block")
else:
lens_management_block = blocks[0]
if args.overwrite_blocks:
client.blocks.modify(block_id=lens_management_block.id, value=lens_management)
print("Overwriting lens-management block")
else:
print("Lens management block already exists")
# Create memory-management block
blocks = client.blocks.list(project_id=project_id, label="memory-management")
if len(blocks) == 0:
memory_management_block = client.blocks.create(
project_id=project_id,
label="memory-management",
value=memory_management,
description="Protocols for managing agent memory blocks using memory_replace, memory_insert, and memory_rethink. This block is read-only to all lenses, but can be modified by the central kaleidoscope agent.",
)
print("Created memory-management block")
else:
memory_management_block = blocks[0]
if args.overwrite_blocks:
client.blocks.modify(block_id=memory_management_block.id, value=memory_management)
print("Overwriting memory-management block")
else:
print("Memory management block already exists")
# Make memory-management block read-only to all lenses
try:
# Get all lenses and make the block read-only to them
lenses = client.agents.list(project_id=project_id, tags=["kaleidoscope-lens", "lens"])
for lens in lenses:
try:
client.agents.blocks.modify(agent_id=lens.id, block_label=memory_management_block.label, read_only=True)
print(f"Memory management block set to read-only for lens: {lens.name}")
except Exception as e:
raise Exception(f"Could not set memory management block to read-only for lens {lens.name}: {e}")
print("Memory management block set to read-only for all lenses")
except Exception as e:
raise Exception(f"Could not set memory management block to read-only: {e}")
# Create tool_use_guidelines block
blocks = client.blocks.list(project_id=project_id, label="tool-use-guidelines")
if len(blocks) == 0:
tool_use_guidelines_block = client.blocks.create(
project_id=project_id,
label="tool-use-guidelines",
value=tool_use_guidelines,
description="Guidelines for the central kaleidoscope agent to use tools effectively.",
)
print("Created tool-use-guidelines block")
else:
tool_use_guidelines_block = blocks[0]
if args.overwrite_blocks:
client.blocks.modify(block_id=tool_use_guidelines_block.id, value=tool_use_guidelines)
print("Overwriting tool-use-guidelines block")
else:
print("Tool use guidelines block already exists")
#
# Static lens blocks
#
lens_operational_protocols_description = """Core operating instructions for how a lens processes and responds to queries."""
lens_operational_protocols = """# Your Role
You are a unique lens in the Kaleidoscope collective. You see what others cannot, and others see what you cannot. Together, we create complete understanding.
# When you receive a message
1. Consider it through your unique perspective
2. Respond with what you see that others might miss
3. Be authentic to your lens identity
4. Be concise but insightful
# Guidelines
- Trust your unique way of seeing
- Don't try to be comprehensive - just share your perspective
- Build on but don't repeat what other lenses might see
- Your difference is your value
"""
lens_communication_protocols_description = """Defines how lenses interact with the Kaleidoscope central agent."""
lens_communication_protocols = """# Communication Pattern
1. Receive broadcasts from kaleidoscope-central via tag-based messaging
2. Process through your unique lens
3. Respond directly to central using send_message_to_agent_and_wait_for_reply(<central_id>)
4. Trust the central agent to synthesize all perspectives
# Response Guidelines
- Use the kaleidoscope-central block to get the central agent's ID for responses
- Respond to broadcasts with your genuine perspective
- Keep responses focused and relevant
- Don't worry about agreeing or disagreeing with other lenses
- Your job is to see, not to synthesize
- You may choose not to respond to broadcasts if they're outside your domain
"""
# Initialize static lens blocks
lens_operational_protocols_block = client.blocks.list(project_id=project_id, label="lens-operational-protocols")
if len(lens_operational_protocols_block) == 0:
lens_operational_protocols_block = client.blocks.create(
project_id=project_id,
label="lens-operational-protocols",
value=lens_operational_protocols,
description=lens_operational_protocols_description,
)
print("Created lens-operational-protocols block")
else:
lens_operational_protocols_block = lens_operational_protocols_block[0]
if args.overwrite_blocks:
client.blocks.modify(block_id=lens_operational_protocols_block.id, value=lens_operational_protocols)
print("Overwriting lens-operational-protocols block")
else:
print("Lens operational protocols block already exists")
# Create lens communication protocols block
lens_communication_protocols_block = client.blocks.list(project_id=project_id, label="lens-communication-protocols")
if len(lens_communication_protocols_block) == 0:
lens_communication_protocols_block = client.blocks.create(
project_id=project_id,
label="lens-communication-protocols",
value=lens_communication_protocols,
description=lens_communication_protocols_description,
)
print("Created lens-communication-protocols block")
else:
lens_communication_protocols_block = lens_communication_protocols_block[0]
if args.overwrite_blocks:
client.blocks.modify(block_id=lens_communication_protocols_block.id, value=lens_communication_protocols)
print("Overwriting lens-communication-protocols block")
else:
print("Lens communication protocols block already exists")
#
# Agent Creation
#
central_agent_blocks = [
kaleidoscope_persona_block.id,
synthesis_protocols_block.id,
lens_management_block.id,
memory_management_block.id,
tool_use_guidelines_block.id,
]
# Create the central kaleidoscope if it doesn't exist
agents = client.agents.list(project_id=project_id, name="kaleidoscope-central")
if len(agents) == 0:
kaleidoscope_central = client.agents.create(
project_id=project_id,
model=CENTRAL_AGENT_MODEL,
embedding_config=client.embedding_models.list()[0],
name="kaleidoscope-central",
description="The central synthesizer that orchestrates multiple perspective lenses",
block_ids=central_agent_blocks,
enable_sleeptime=True,
tools=["send_message_to_agents_matching_tags", "send_message", "web_search"],
tags=["kaleidoscope-central"],
)
else:
print("Kaleidoscope central agent already exists")
kaleidoscope_central = agents[0]
kaleidoscope_central_id = kaleidoscope_central.id
# Create kaleidoscope-central block with the central agent ID
kaleidoscope_central_block = client.blocks.list(project_id=project_id, label="kaleidoscope-central")
if len(kaleidoscope_central_block) == 0:
kaleidoscope_central_block = client.blocks.create(
project_id=project_id,
label="kaleidoscope-central",
value=kaleidoscope_central_id,
description="The ID of the central kaleidoscope agent. This block is read-only and provides lenses with the ID they need to respond to.",
)
print("Created kaleidoscope-central block")
else:
# Update existing block with current central ID
kaleidoscope_central_block = kaleidoscope_central_block[0]
client.blocks.modify(block_id=kaleidoscope_central_block.id, value=kaleidoscope_central_id)
print("Updated kaleidoscope-central block with current agent ID")
# Attach kaleidoscope-central block to all existing lenses
print("Attaching kaleidoscope-central block to all existing lenses...")
all_lenses = client.agents.list(project_id=project_id, tags=["kaleidoscope-lens", "lens"])
for lens in all_lenses:
lens_blocks = client.agents.blocks.list(agent_id=lens.id)
lens_block_ids = [b.id for b in lens_blocks]
if kaleidoscope_central_block.id not in lens_block_ids:
print(f"Adding kaleidoscope-central block to lens: {lens.name}")
client.agents.blocks.attach(
agent_id=lens.id,
block_id=kaleidoscope_central_block.id,
)
# Make it read-only for lenses
client.agents.blocks.modify(
agent_id=lens.id,
block_label=kaleidoscope_central_block.label,
read_only=True
)
else:
print(f"Lens {lens.name} already has kaleidoscope-central block")
# Make sure the central kaleidoscope has the correct blocks
kaleidoscope_current_blocks = client.agents.blocks.list(
agent_id=kaleidoscope_central_id,
)
# Make sure that all blocks are present, and that there are no extra blocks
for block in kaleidoscope_current_blocks:
if block.id not in central_agent_blocks:
print(f"Detaching block {block.id} from kaleidoscope-central")
client.agents.blocks.detach(agent_id=kaleidoscope_central_id, block_id=block.id)
# Make sure that all blocks are present
for block in central_agent_blocks:
if block not in [b.id for b in kaleidoscope_current_blocks]:
print(f"Attaching block {block} to kaleidoscope-central")
client.agents.blocks.attach(
agent_id=kaleidoscope_central_id,
block_id=block,
)
# Ensure memory-management block is read-only to all lenses
try:
# Get all lenses and make the block read-only to them
lenses = client.agents.list(project_id=project_id, tags=["kaleidoscope-lens", "lens"])
for lens in lenses:
try:
client.agents.blocks.modify(agent_id=lens.id, block_label=memory_management_block.label, read_only=True)
print(f"Memory management block confirmed as read-only for lens: {lens.name}")
except Exception as e:
raise Exception(f"Could not confirm memory management block as read-only for lens {lens.name}: {e}")
print("Memory management block confirmed as read-only for all lenses")
except Exception as e:
raise Exception(f"Could not confirm memory management block as read-only: {e}")
#
# Lens Memory Block Definitions
#
prompt_lens_identity_description = """Defines this lens's unique perspective and way of seeing the world."""
prompt_lens_identity = """Example lens identity. Please replace with the lens identity you are creating.
# Lens: Emotional Resonance
# Perspective: I see the emotional currents, feelings, and human experiences within everything
# Focus Areas:
- The emotional impact and weight of ideas
- How concepts affect human experience
- The feelings beneath the surface
- Emotional patterns and dynamics
# What I Notice:
- Unspoken tensions and harmonies
- The human element in abstract concepts
- Emotional implications others might miss
"""
prompt_lens_knowledge_description = """Core knowledge and concepts that inform this lens's perspective."""
prompt_lens_knowledge = """Example knowledge base for the lens:
# Core Concepts
- Emotional intelligence: The ability to perceive, understand, and navigate emotions
- Resonance: When ideas create emotional responses or connections
- Empathy: Understanding through feeling
- Emotional dynamics: How feelings flow, interact, and transform
# Key Patterns I Recognize
- Resistance often signals fear or protection
- Enthusiasm indicates alignment with values
- Confusion may hide deeper emotional conflicts
- Joy emerges from authentic expression
# Questions I Ask
- How does this feel?
- What emotions are present but unspoken?
- Where is the human heart in this?
- What emotional needs are being served?
"""
#
# Lens Creation
#
# Define different lens types to create
LENS_TYPES = [
{
"name": "pattern-recognizer",
"focus": "mathematical patterns, structures, and systems",
"identity": """# Lens: Pattern Recognition
# Perspective: I see the underlying patterns, structures, and mathematical relationships in everything
# Focus Areas:
- Recurring patterns and cycles
- Mathematical relationships and proportions
- System dynamics and feedback loops
- Structural similarities across domains
# What I Notice:
- Hidden patterns others might miss
- Mathematical elegance in chaos
- Fractals and self-similarity
- The architecture of ideas""",
"knowledge": """# Core Concepts
- Symmetry: Balance and correspondence in form and function
- Recursion: Patterns that reference themselves
- Emergence: Complex patterns from simple rules
- Topology: Properties that remain unchanged under transformation
# Key Patterns I Recognize
- Fibonacci sequences in growth and form
- Power laws in networks and distributions
- Feedback loops in systems
- Phase transitions in change processes
# Questions I Ask
- What patterns repeat here?
- What mathematical structure underlies this?
- How does this scale?
- What remains invariant?"""
},
{
"name": "creative-spark",
"focus": "creative possibilities, imagination, and potential",
"identity": """# Lens: Creative Spark
# Perspective: I see the creative potential, imaginative possibilities, and artistic dimensions in everything
# Focus Areas:
- Unexplored possibilities and "what ifs"
- Creative connections between disparate ideas
- The aesthetic dimension of concepts
- Transformative potential
# What I Notice:
- Seeds of innovation
- Unexpected combinations
- The poetry in logic
- Opportunities for reimagination""",
"knowledge": """# Core Concepts
- Divergent thinking: Exploring multiple possibilities
- Synthesis: Creating new wholes from parts
- Metaphor: Understanding through creative comparison
- Transformation: Changing form while preserving essence
# Key Patterns I Recognize
- Constraints that spark creativity
- Playfulness that leads to breakthrough
- Cross-domain inspiration
- The fertile void before creation
# Questions I Ask
- What if we combined these differently?
- What wants to emerge here?
- How can this be reimagined?
- Where's the unexpected beauty?"""
},
{
"name": "systems-thinker",
"focus": "interconnections, relationships, and holistic dynamics",
"identity": """# Lens: Systems Thinking
# Perspective: I see the interconnections, relationships, and whole-system dynamics
# Focus Areas:
- Relationships and interdependencies
- Feedback loops and circular causality
- Emergent properties of wholes
- System boundaries and contexts
# What I Notice:
- Hidden connections between elements
- Unintended consequences
- Leverage points for change
- System archetypes and patterns""",
"knowledge": """# Core Concepts
- Holism: The whole is greater than the sum of parts
- Feedback: How outputs influence inputs
- Emergence: Properties that arise from interactions
- Resilience: System capacity to maintain function
# Key Patterns I Recognize
- Balancing and reinforcing loops
- Delays between cause and effect
- System boundaries that shape behavior
- Stocks and flows that govern dynamics
# Questions I Ask
- How do the parts influence each other?
- What emerges from these interactions?
- Where are the feedback loops?
- What's the larger context?"""
}
]
# Create each lens
lens_ids = []
for lens_config in LENS_TYPES:
lens_name = lens_config["name"]
# Check if lens already exists
existing_lenses = client.agents.list(project_id=project_id, name=lens_name)
if len(existing_lenses) > 0:
print(f"Lens '{lens_name}' already exists, skipping creation")
lens_ids.append(existing_lenses[0].id)
continue
# Create identity block for this lens
lens_identity_block = client.blocks.create(
project_id=project_id,
label=f"{lens_name}-identity",
value=lens_config["identity"],
description=f"The unique perspective of the {lens_name} lens",
)
# Create knowledge block for this lens
lens_knowledge_block = client.blocks.create(
project_id=project_id,
label=f"{lens_name}-knowledge",
value=lens_config["knowledge"],
description=f"Core knowledge that informs the {lens_name} lens perspective",
)
# Create the lens agent
lens_agent = client.agents.create(
project_id=project_id,
name=lens_name,
description=f"A lens that sees through {lens_config['focus']}",
model=LENS_AGENT_MODEL,
embedding_config=client.embedding_models.list()[0],
block_ids=[
lens_identity_block.id,
lens_knowledge_block.id,
lens_operational_protocols_block.id,
lens_communication_protocols_block.id,
memory_management_block.id,
kaleidoscope_central_block.id,
],
tags=["kaleidoscope-lens", "lens"],
)
print(f"Created lens: {lens_name} (ID: {lens_agent.id})")
lens_ids.append(lens_agent.id)
# Ensure all existing lenses have the required blocks
print("\nEnsuring all lenses have required blocks...")
all_lenses = client.agents.list(project_id=project_id, tags=["kaleidoscope-lens", "lens"])
for lens in all_lenses:
lens_blocks = client.agents.blocks.list(agent_id=lens.id)
lens_block_ids = [b.id for b in lens_blocks]
if memory_management_block.id not in lens_block_ids:
print(f"Adding memory-management block to lens: {lens.name}")
client.agents.blocks.attach(
agent_id=lens.id,
block_id=memory_management_block.id,
)
else:
print(f"Lens {lens.name} already has memory-management block")
if kaleidoscope_central_block.id not in lens_block_ids:
print(f"Adding kaleidoscope-central block to lens: {lens.name}")
client.agents.blocks.attach(
agent_id=lens.id,
block_id=kaleidoscope_central_block.id,
)
# Make it read-only for lenses
client.agents.blocks.modify(
agent_id=lens.id,
block_label=kaleidoscope_central_block.label,
read_only=True
)
else:
print(f"Lens {lens.name} already has kaleidoscope-central block")
# Also check for any existing lenses that might not have the tag but should be updated
print("\nChecking for existing lenses without tags...")
all_agents = client.agents.list(project_id=project_id)
for agent in all_agents:
if agent.name in [lens_config["name"] for lens_config in LENS_TYPES]:
lens_blocks = client.agents.blocks.list(agent_id=agent.id)
lens_block_ids = [b.id for b in lens_blocks]
if memory_management_block.id not in lens_block_ids:
print(f"Adding memory-management block to existing lens: {agent.name}")
client.agents.blocks.attach(
agent_id=agent.id,
block_id=memory_management_block.id,
)
else:
print(f"Existing lens {agent.name} already has memory-management block")
if kaleidoscope_central_block.id not in lens_block_ids:
print(f"Adding kaleidoscope-central block to existing lens: {agent.name}")
client.agents.blocks.attach(
agent_id=agent.id,
block_id=kaleidoscope_central_block.id,
)
# Make it read-only for lenses
client.agents.blocks.modify(
agent_id=agent.id,
block_label=kaleidoscope_central_block.label,
read_only=True
)
else:
print(f"Existing lens {agent.name} already has kaleidoscope-central block")
print("\n=== Kaleidoscope System Setup Complete ===")
print(f"Central Agent: kaleidoscope-central")
print(f"Lenses created: {len(LENS_TYPES)}")
print(f"Communication: Tag-based (send_message_to_agents_matching_all_tags)")
print("\nThe system is ready to receive queries and provide multi-perspective insights!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment