A Python SDK for creating and simulating AI personas with emotional states, personality traits, and memory
Project description
Personaut PDK
A Python SDK for creating and simulating AI personas with emotional states, personality traits, memories, and relationships.
Overview
Personaut PDK enables you to create rich, psychologically-grounded AI personas that can participate in:
- Conversations: Multi-party dialogues with realistic emotional dynamics
- Surveys: Persona-driven questionnaire responses
- Outcome Analysis: Simulation-based prediction of behavioral outcomes
- Live Interactions: Real-time chat with modality-specific interfaces
Features
- 🎭 36 Emotional States - Fine-grained emotional modeling across 6 categories
- 🧠 17 Personality Traits - Based on the 16PF psychological model
- 📍 Situational Facts - Structured context with 8 categories and LLM extraction
- 💾 Vector Memory - Semantic memory retrieval with trust-gated access
- 🔗 Relationships - Trust-based relationship dynamics
- 🎯 Triggers & Masks - Context-aware behavioral modifications
- 🚀 Live Server - FastAPI backend + Flask UI for interactive sessions
Installation
pip install personaut
Optional Dependencies
# For Gemini model provider
pip install personaut[gemini]
# For AWS Bedrock provider
pip install personaut[bedrock]
# For live interaction server
pip install personaut[server]
# For development
pip install personaut[dev]
# Install everything
pip install personaut[all]
Quick Start
Creating an Individual
import personaut
# Create an individual with personality traits and emotional state
sarah = personaut.create_individual(
name="Sarah",
traits={"warmth": 0.8, "dominance": 0.4, "sensitivity": 0.7},
emotional_state={"cheerful": 0.6, "curious": 0.5},
)
# Update emotional state
sarah.change_emotion("anxious", 0.3)
# Access traits
print(sarah.get_high_traits()) # [('warmth', 0.8), ...]
# Get dominant emotion (respects active mask)
print(sarah.get_dominant_emotion()) # ('cheerful', 0.6)
Using Masks for Contextual Behavior
from personaut.masks import PROFESSIONAL_MASK
# Add a professional mask
sarah.add_mask(PROFESSIONAL_MASK)
sarah.activate_mask("professional")
# Emotional state is now filtered through the mask
modified_state = sarah.get_emotional_state() # Suppresses strong emotions
# Get raw state without mask
raw_state = sarah.get_raw_emotional_state()
Running a Conversation Simulation
import personaut
# Create individuals
sarah = personaut.create_individual(name="Sarah")
mike = personaut.create_individual(name="Mike")
# Create situation
situation = personaut.situation.create_situation(
type=personaut.types.modality.TEXT_MESSAGE,
description='Catching up after a long time'
)
# Create and run simulation
simulation = personaut.simulation.create_simulation(
situation=situation,
individuals=[sarah, mike],
type=personaut.simulations.types.CONVERSATION,
style=personaut.simulations.styles.SCRIPT
)
simulation.run(num=5, dir='./output/')
Situational Facts
from personaut.facts import FactExtractor, LLMFactExtractor
# Regex-based extraction (fast, deterministic)
extractor = FactExtractor()
ctx = extractor.extract(
"A busy coffee shop in downtown Miami around 3pm. "
"80% capacity with a line of 5 people."
)
print(ctx.get_value("venue_type")) # "coffee shop"
print(ctx.get_value("capacity_percent")) # 80
# LLM-based extraction (richer, more nuanced)
llm_extractor = LLMFactExtractor(llm_client=your_client)
ctx = await llm_extractor.extract(
"We grabbed coffee at the corner spot. Super packed, great vibe."
)
# Generate embedding text
print(ctx.to_embedding_text())
Live Interactive Chat
import personaut
from personaut.server import LiveInteractionServer
# Create server and add individual
server = LiveInteractionServer()
server.add_individual(sarah)
# Start server
server.start(api_port=8000, ui_port=5000)
# Access:
# - UI: http://localhost:5000
# - API: http://localhost:8000/docs
Emotional System
The emotion system models 36 discrete emotions organized into 6 categories:
| Category | Emotions |
|---|---|
| Anger/Mad | hostile, hurt, angry, selfish, hateful, critical |
| Sad/Sadness | guilty, ashamed, depressed, lonely, bored, apathetic |
| Fear/Scared | rejected, confused, submissive, insecure, anxious, helpless |
| Joy/Happiness | excited, sensual, energetic, cheerful, creative, hopeful |
| Powerful/Confident | proud, respected, appreciated, important, faithful, satisfied |
| Peaceful/Calm | content, thoughtful, intimate, loving, trusting, nurturing |
from personaut.emotions import EmotionalState, ANXIOUS, HOPEFUL
state = EmotionalState()
state.change_emotion(ANXIOUS, 0.6)
state.change_emotion(HOPEFUL, 0.8)
# Query dominant emotion
dominant, value = state.get_dominant() # ('hopeful', 0.8)
Personality Traits
Based on the 16PF model with 17 traits that influence emotional transitions:
from personaut.traits import create_trait, WARMTH, EMOTIONAL_STABILITY
# High warmth = more approachable, friendly
warmth = create_trait(trait=WARMTH, value=0.8)
# High emotional stability = less reactive to stress
stability = create_trait(trait=EMOTIONAL_STABILITY, value=0.7)
Memory System
Store and retrieve memories with emotional context and trust-gated access:
from personaut.memory import (
create_individual_memory,
create_shared_memory,
create_private_memory,
InMemoryVectorStore,
search_memories,
)
# Individual memory with situational context
memory = create_individual_memory(
owner_id="sarah_123",
description="Met Alex at the coffee shop",
context=ctx, # SituationalContext from facts extraction
salience=0.8,
)
# Shared memory between multiple people
shared = create_shared_memory(
description="Team dinner at the Italian restaurant",
participant_ids=["sarah_123", "mike_456"],
perspectives={
"sarah_123": "Great food, but Mike was late",
"mike_456": "Traffic was terrible",
},
)
# Private memory with trust threshold
private = create_private_memory(
owner_id="sarah_123",
description="My anxiety about the presentation",
trust_threshold=0.8, # Only shared with high-trust individuals
)
# Store and search memories
store = InMemoryVectorStore()
store.store(memory, embedding=[...]) # Vector from embedding model
results = search_memories(
store=store,
query="coffee meetings",
embed_func=my_embed_function,
trust_level=0.5, # Filters private memories
)
Masks and Triggers
Masks modify emotional expression based on context. Triggers activate responses when conditions are met:
from personaut.masks import (
create_mask,
PROFESSIONAL_MASK,
STOIC_MASK,
)
from personaut.triggers import (
create_emotional_trigger,
create_situational_trigger,
)
# Apply professional mask in office settings
if PROFESSIONAL_MASK.should_trigger("Going to an office meeting"):
modified_state = PROFESSIONAL_MASK.apply(emotional_state)
# Suppresses anger, boosts composure
# Create custom mask
interview_mask = create_mask(
name="interview",
emotional_modifications={"anxious": -0.3, "content": 0.2},
trigger_situations=["interview", "formal"],
)
# Emotional trigger: activate stoic mask when anxiety is high
anxiety_trigger = create_emotional_trigger(
description="High anxiety response",
rules=[{"emotion": "anxious", "threshold": 0.8, "operator": ">"}],
response=STOIC_MASK,
)
if anxiety_trigger.check(emotional_state):
calmed_state = anxiety_trigger.fire(emotional_state)
# Situational trigger: increase anxiety in dark spaces
dark_trigger = create_situational_trigger(
description="Dark space phobia",
keywords=["dark", "basement", "cave"],
response={"anxious": 0.3, "helpless": 0.2},
)
Relationships
Model trust dynamics between individuals and query relationship networks:
from personaut.relationships import (
create_relationship,
RelationshipNetwork,
get_trust_level,
TrustLevel,
)
# Create relationship with asymmetric trust
rel = create_relationship(
individual_ids=["sarah", "mike"],
trust={"sarah": 0.8, "mike": 0.5}, # Sarah trusts Mike more
history="Roommates in college for 2 years",
relationship_type="friends",
)
# Query trust
trust = rel.get_trust("sarah", "mike") # 0.8
mutual = rel.get_mutual_trust("sarah", "mike") # 0.65
# Update trust after an event
rel.update_trust("mike", "sarah", 0.2, "helped during crisis")
# Check trust level
level = rel.get_trust_level("sarah", "mike")
if level == TrustLevel.HIGH:
# Sarah will share private memories with Mike
# Build a relationship network
network = RelationshipNetwork()
network.add_relationship(rel)
network.add_relationship(create_relationship(["mike", "carol"], trust={"mike": 0.7, "carol": 0.6}))
# Find connection path
path = network.find_path("sarah", "carol") # ['sarah', 'mike', 'carol']
path_trust = network.calculate_path_trust(path) # Trust decays along path
Situations
Define the context for simulations with modality, location, and structured context:
from datetime import datetime
from personaut.situations import (
create_situation,
SituationContext,
create_environment_context,
)
from personaut.types.modality import Modality
# Create a situation
situation = create_situation(
modality=Modality.IN_PERSON,
description="Meeting at a coffee shop to discuss a project",
time=datetime.now(),
location="Miami, FL",
context={"atmosphere": "relaxed"},
)
# Query modality characteristics
if situation.is_synchronous():
# Real-time communication expected
traits = situation.get_modality_traits()
print(f"Visual cues: {traits['visual_cues']}")
# Build structured context with validation
ctx = create_environment_context(
lighting="dim",
noise_level="quiet",
indoor=True,
private=True,
)
result = ctx.validate()
if result.valid:
# Context data meets schema requirements
pass
Documentation
| Document | Description |
|---|---|
| PERSONAS.md | Main agent guidelines |
| docs/EMOTIONS.md | Emotion system reference |
| docs/TRAITS.md | Trait system reference |
| docs/FACTS.md | Situational context and fact extraction |
| docs/MEMORY.md | Memory system and vector storage |
| docs/PROMPTS.md | Prompt generation |
| docs/SIMULATIONS.md | Simulation types |
| docs/LIVE_INTERACTIONS.md | Server architecture |
| docs/STYLE_GUIDE.md | Code conventions |
Development
# Clone repository
git clone https://github.com/personaut/python-pdk.git
cd python-pdk
# Install with dev dependencies
pip install hatch
hatch shell
# Run tests
hatch test
# Run linters
hatch fmt
# Type check
hatch run type
See CONTRIBUTING.md for full development guidelines.
Requirements
- Python 3.10+
- See pyproject.toml for dependencies
License
Apache License 2.0 - see LICENSE for details.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file personaut-0.3.2.tar.gz.
File metadata
- Download URL: personaut-0.3.2.tar.gz
- Upload date:
- Size: 469.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
788476840d2ca09f58448da63cfcd8ae8ab23a7a1e0c8ca12952e93373c659c2
|
|
| MD5 |
34c1d5a9c368047df40d5fa17328957c
|
|
| BLAKE2b-256 |
301ebd303d13149036c94c8f8c211d23cde5eaf064a17bbe68b1b684b4b9ef50
|
Provenance
The following attestation bundles were made for personaut-0.3.2.tar.gz:
Publisher:
publish.yml on Personaut/python-pdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
personaut-0.3.2.tar.gz -
Subject digest:
788476840d2ca09f58448da63cfcd8ae8ab23a7a1e0c8ca12952e93373c659c2 - Sigstore transparency entry: 975538938
- Sigstore integration time:
-
Permalink:
Personaut/python-pdk@a9f43903cb0f3f4cc68c2ad135b6233332593b62 -
Branch / Tag:
refs/tags/v0.3.2 - Owner: https://github.com/Personaut
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a9f43903cb0f3f4cc68c2ad135b6233332593b62 -
Trigger Event:
release
-
Statement type:
File details
Details for the file personaut-0.3.2-py3-none-any.whl.
File metadata
- Download URL: personaut-0.3.2-py3-none-any.whl
- Upload date:
- Size: 392.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
55fb1f9e569b77aa98ffea092499cd70d27e73e74cebb99005845f211aec8acf
|
|
| MD5 |
e8a05c6b0079cc2d1155b9b3a42f6710
|
|
| BLAKE2b-256 |
d7ab735265f4def30b38290a493c4bc477de94c3bbda829000c373d5f6f0dfb6
|
Provenance
The following attestation bundles were made for personaut-0.3.2-py3-none-any.whl:
Publisher:
publish.yml on Personaut/python-pdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
personaut-0.3.2-py3-none-any.whl -
Subject digest:
55fb1f9e569b77aa98ffea092499cd70d27e73e74cebb99005845f211aec8acf - Sigstore transparency entry: 975538939
- Sigstore integration time:
-
Permalink:
Personaut/python-pdk@a9f43903cb0f3f4cc68c2ad135b6233332593b62 -
Branch / Tag:
refs/tags/v0.3.2 - Owner: https://github.com/Personaut
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a9f43903cb0f3f4cc68c2ad135b6233332593b62 -
Trigger Event:
release
-
Statement type: