Building Your First AI Persona with Python’s Personaut PDK
Give your AI agents a soul — complete with personality, emotions, and psychological grounding.

Building Your First AI Persona with Python’s Personaut PDK
Give your AI agents a soul — complete with personality, emotions, and psychological grounding.
The Problem with Generic AI Agents
We’ve all been there. You ask ChatGPT to “act like a friendly barista” and it gives you a response that sort of works — but lacks any real personality depth. It doesn’t get nervous when the café is busy. It doesn’t light up when talking about a new roast. It doesn’t remember that you had a rough day yesterday.
That’s because personality isn’t just a system prompt. It’s a dynamic interplay of traits, emotions, and context — and that’s exactly what the Personaut PDK was built to model.
In this article, you’ll learn how to:
- Create a psychologically-grounded AI persona
- Define personality traits using the 16PF model
- Manage a 36-emotion state system
- Generate persona-aware LLM prompts that actually feel different
Installation
pip install personaut
That’s it. No API keys needed for everything in this article.
Step 1: Creating Your First Individual
The Individual is the core building block of Personaut. Think of it as a fully modeled character with an identity, personality, and emotional state.
import personaut
# Create a simulated persona — fully tracked with emotions and traits
sarah = personaut.create_individual(
name="Sarah C",
traits={"warmth": 0.85, "liveliness": 0.7, "reasoning": 0.6},
emotional_state={"cheerful": 0.6, "hopeful": 0.4}
)
print(f"Name: {sarah.name}")
print(f"Type: {sarah.individual_type}")
print(f"Traits: {sarah.traits}")
print(f"Emotional State: {sarah.emotional_state}")
Three Types of Individuals
Personaut distinguishes between three types of participants:
# 1. SIMULATED — Full AI persona with state tracking
barista = personaut.create_individual(
name="Marco",
traits={"warmth": 0.9, "liveliness": 0.8},
emotional_state={"cheerful": 0.7}
)
# 2. HUMAN — A real person being tracked (has memory, no simulated emotions)
customer = personaut.create_human(name="Alex")
# 3. NONTRACKED — Background character (no state, no memory)
bystander = personaut.create_nontracked_individual(name="Random Customer")
This distinction is critical for simulations: the system only generates prompts and emotional drift for simulated individuals, while tracking interaction history for human participants.
Step 2: The 17-Factor Personality Model
Personaut’s trait system is inspired by Raymond Cattell’s 16 Personality Factor (16PF) model, extended to 17 factors. Each trait exists on a spectrum from 0.0 to 1.0, where the poles represent opposite behavioral tendencies:
TraitLow Pole (≤ 0.3)High Pole (≥ 0.7)WarmthReserved, DistantFriendly, OutgoingReasoningConcreteAbstract, AnalyticalDominanceAccommodatingAssertive, DirectLivelinessSerious, RestrainedEnthusiastic, ExpressiveEmotional StabilityReactive, ChangeableEven-tempered, CalmOpennessConventionalCurious, Experimental
Here’s how to explore the full trait system:
from personaut import TraitProfile, Trait, WARMTH, DOMINANCE, EMOTIONAL_STABILITY
# Create a detailed personality profile
architect_traits = TraitProfile(traits={
"warmth": 0.3, # Reserved
"reasoning": 0.9, # Highly analytical
"emotional_stability": 0.8, # Calm under pressure
"dominance": 0.7, # Direct communicator
"liveliness": 0.2, # Serious demeanor
"openness": 0.85, # Very curious
"sensitivity": 0.4, # Utilitarian
"vigilance": 0.6, # Somewhat suspicious
})
# Access trait constants for programmatic use
print(f"Warmth constant: {WARMTH}")
print(f"Dominance constant: {DOMINANCE}")
The magic happens when these traits influence how the persona responds to emotions — a concept called trait-emotion coefficients.
Step 3: The 36-Emotion State System
Personaut models 36 discrete emotions organized into 6 categories:
from personaut import EmotionalState, EmotionCategory, Emotion
from personaut import ANXIOUS, CHEERFUL, HOPEFUL, get_emotions_in_category, get_category
# Create an emotional state
state = EmotionalState()
# Set emotions directly (absolute assignment)
state.change_emotion("cheerful", 0.7)
state.change_emotion("hopeful", 0.5)
state.change_emotion("anxious", 0.2)
# Check the dominant emotion — this drives prompt tone
dominant = state.get_dominant_emotion()
print(f"Dominant emotion: {dominant}") # cheerful
# Explore emotion categories
joy_emotions = get_emotions_in_category("joy")
print(f"Joy category contains: {joy_emotions}")
# Get which category an emotion belongs to
category = get_category("anxious")
print(f"'anxious' belongs to: {category}") # Fear
How Traits Influence Emotions
This is where Personaut gets interesting. Traits don’t just describe behavior — they mathematically influence how emotions drift over time:
from personaut import TraitProfile
profile = TraitProfile(traits={
"warmth": 0.9,
"emotional_stability": 0.3, # Reactive!
"sensitivity": 0.8, # Highly sensitive
})
# Calculate how this personality modifies emotional responses
modifier = profile.calculate_emotion_modifier("anxious")
print(f"Anxiety modifier for sensitive personality: {modifier}")
# A high-sensitivity, low-stability person has amplified anxiety responses
Step 4: Generating Persona-Aware Prompts
This is the payoff. The PromptManager translates all of this psychological modelling into natural language system prompts that you can feed to any LLM:
from personaut.prompts import PromptManager
# Create two very different personas
barista = personaut.create_individual(
name="Marco",
traits={"warmth": 0.9, "liveliness": 0.8, "sensitivity": 0.7},
emotional_state={"cheerful": 0.7, "hopeful": 0.5}
)
architect = personaut.create_individual(
name="Diana",
traits={"warmth": 0.3, "reasoning": 0.9, "dominance": 0.7},
emotional_state={"confident": 0.6, "curious": 0.4}
)
# Generate conversation prompts
pm = PromptManager()
barista_prompt = pm.generate(barista, template_name="conversation")
architect_prompt = pm.generate(architect, template_name="conversation")
print("=" * 60)
print("BARISTA PROMPT:")
print("=" * 60)
print(barista_prompt)
print()
print("=" * 60)
print("ARCHITECT PROMPT:")
print("=" * 60)
print(architect_prompt)
The output will show dramatically different prompts — the barista’s will emphasize warmth, enthusiasm, and emotional expressiveness, while the architect’s will focus on analytical precision, directness, and reserved emotional display.
Using the PromptBuilder for Fine-Grained Control
For advanced use cases, the PromptBuilder gives you complete control over prompt assembly:
from personaut.prompts import PromptBuilder
builder = PromptBuilder()
prompt = (builder
.with_individual(barista)
.with_emotional_state(barista.emotional_state)
.using_template("conversation")
.section_order(["identity", "personality", "emotional_state", "guidelines"])
.build())
print(prompt)
Step 5: Putting It All Together
Let’s build a complete example that creates a persona, modifies their emotional state in response to events, and generates evolving prompts:
import personaut
from personaut.prompts import PromptManager
def main():
# Create our persona
alex = personaut.create_individual(
name="Alex Rivera",
traits={
"warmth": 0.7,
"liveliness": 0.6,
"emotional_stability": 0.5,
"sensitivity": 0.7,
"openness": 0.8,
},
emotional_state={"cheerful": 0.5, "curious": 0.4}
)
pm = PromptManager()
# --- Morning: Alex is cheerful and curious ---
print("🌅 MORNING STATE")
print(f" Dominant emotion: {alex.emotional_state.get_dominant_emotion()}")
morning_prompt = pm.generate(alex, template_name="conversation")
print(f" Prompt preview: {morning_prompt[:200]}...")
print()
# --- Event: Alex receives critical feedback ---
print("📝 EVENT: Received harsh criticism on their work")
alex.emotional_state.change_emotion("cheerful", 0.1)
alex.emotional_state.change_emotion("anxious", 0.7)
alex.emotional_state.change_emotion("frustrated", 0.5)
print(f" Dominant emotion: {alex.emotional_state.get_dominant_emotion()}")
afternoon_prompt = pm.generate(alex, template_name="conversation")
print(f" Prompt preview: {afternoon_prompt[:200]}...")
print()
# --- Event: A friend offers support ---
print("💬 EVENT: Friend offers encouragement")
alex.emotional_state.change_emotion("anxious", 0.3)
alex.emotional_state.change_emotion("hopeful", 0.6)
alex.emotional_state.change_emotion("grateful", 0.5)
print(f" Dominant emotion: {alex.emotional_state.get_dominant_emotion()}")
evening_prompt = pm.generate(alex, template_name="conversation")
print(f" Prompt preview: {evening_prompt[:200]}...")
if __name__ == "__main__":
main()
What You’ve Learned
In this article, we’ve covered the foundational building blocks of the Personaut PDK:
- ✅ Individuals — Three types of participants with different tracking depths
- ✅ Traits — 17-factor personality model that shapes behavior
- ✅ Emotions — 36 discrete emotions across 6 categories with intensity tracking
- ✅ Trait-Emotion Coefficients — How personality amplifies or dampens emotional responses
- ✅ Prompt Generation — Translating psychological state into natural language LLM instructions
What’s Next
In the next article, we’ll explore how to make personas react dynamically to their environment using Masks (contextual behavior overrides) and Triggers (automated emotional responses) — and use them to generate full multi-turn conversation simulations.
Found this useful? Follow me for the next article in this series, where we build emotion-driven conversation simulations that feel alive.
The Personaut PDK is open-source and available on PyPI and GitHub.
메타데이터
- post_id
- 76ea45c04004
- slug
- building-your-first-ai-persona-with-pythons-personaut-pdk-76ea45c04004
- url
- https://ai.plainenglish.io/building-your-first-ai-persona-with-pythons-personaut-pdk-76ea45c04004
- canonical_url
- https://ai.plainenglish.io/building-your-first-ai-persona-with-pythons-personaut-pdk-76ea45c04004
- author_url
- https://medium.com/@empadev64
- status
- ok
- fetched_at
- 2026-07-13 06:23:13