DEV Community

Mukunda Rao Katta
Mukunda Rao Katta

Posted on

Named Persona Registry: Keep Your Agent Identities Consistent Across Every Session

The product had three agents: a customer support agent, a technical documentation agent, and a sales agent. Each had different names, different tones, different capabilities listed in the system prompt.

The problem: three separate code paths assembled three separate system prompts. Developers updated the customer support persona in one place and forgot to update the fallback path. For two weeks, the fallback persona introduced itself as "Maya" while the primary introduced itself as "Alex." Support tickets mentioned the inconsistency.

llm-persona is a named registry for agent personas. One definition. Many uses. No drift.


The Shape of the Fix

from llm_persona import PersonaRegistry, Persona

registry = PersonaRegistry()

registry.define(Persona(
    name="Maya",
    role="Customer Support Specialist",
    company="Acme Corp",
    tone="helpful, concise, empathetic",
    capabilities=[
        "Look up order status",
        "Process refunds under $50",
        "Escalate complex issues",
    ],
    constraints=[
        "Never promise specific delivery dates",
        "Never approve refunds over $50 without approval",
    ],
    greeting="Hi, I'm Maya from Acme support.",
))

# Build system prompt
system = registry.get("Maya").build_system_prompt()

# Or access specific fields
print(registry.get("Maya").greeting)
# "Hi, I'm Maya from Acme support."
Enter fullscreen mode Exit fullscreen mode

Define once. Use everywhere. Change the definition in one place.


What It Does NOT Do

llm-persona does not manage conversation history or state. It provides persona definition and system prompt generation. The actual conversation is your responsibility.

It does not validate that the model actually behaves according to the persona. It defines the instructions; whether the model follows them depends on prompt quality and model capability.

It does not handle persona switching mid-conversation. Each persona is for a fresh context. Switching personas mid-conversation requires careful prompt construction that this library does not help with.


Inside the Library

Persona is a dataclass:

@dataclass
class Persona:
    name: str
    role: str
    company: str | None = None
    tone: str = "helpful and professional"
    capabilities: list[str] = field(default_factory=list)
    constraints: list[str] = field(default_factory=list)
    greeting: str | None = None
    custom_sections: dict[str, str] = field(default_factory=dict)

    def build_system_prompt(self) -> str:
        lines = [f"You are {self.name}, a {self.role}"]
        if self.company:
            lines[0] += f" at {self.company}."
        else:
            lines[0] += "."

        lines.append(f"Tone: {self.tone}")

        if self.capabilities:
            lines.append("You can:\n" + "\n".join(f"- {c}" for c in self.capabilities))

        if self.constraints:
            lines.append("Do not:\n" + "\n".join(f"- {c}" for c in self.constraints))

        for section_name, content in self.custom_sections.items():
            lines.append(content)

        return "\n\n".join(lines)
Enter fullscreen mode Exit fullscreen mode

PersonaRegistry.define() stores the persona by name. get(name) retrieves it. list_names() returns all registered names. Personas can be loaded from YAML or JSON files via PersonaRegistry.from_file(path).

Template fields: persona fields can contain {variable} placeholders. persona.build_system_prompt(company="Acme", user_name="John") fills them. Useful for personas that need to reference per-session context.


When to Use It

Use it when you have more than one agent persona in your product. The registry pattern pays off immediately: one definition, many uses, no drift.

Use it for persona-heavy products. Customer support bots, sales assistants, persona-based education tools. Any product where the agent "is" a specific character with a defined identity.

Use it with agent-context-builder for the full system prompt. The persona defines the core identity sections. The context builder adds tool descriptions, capability flags, and per-session context on top.

Skip it for single-persona products where the system prompt never changes. If you have one agent with one fixed persona, a constant string is simpler.


Install

pip install git+https://github.com/MukundaKatta/llm-persona
Enter fullscreen mode Exit fullscreen mode
from llm_persona import PersonaRegistry, Persona
import yaml

# Load personas from config file
with open("personas.yaml") as f:
    personas_config = yaml.safe_load(f)

registry = PersonaRegistry()
for name, config in personas_config.items():
    registry.define(Persona(name=name, **config))

# In your request handler
def handle_request(agent_id: str, user_message: str) -> str:
    persona = registry.get(agent_id)

    response = client.messages.create(
        model="claude-sonnet-4-6",
        system=persona.build_system_prompt(),
        messages=[{"role": "user", "content": user_message}],
        max_tokens=1024,
    )

    return response.content[0].text
Enter fullscreen mode Exit fullscreen mode

Sibling Libraries

Library What it solves
agent-context-builder Build full system prompts from named sections
prompt-template-version Version and fingerprint prompt templates
prompt-cache-warmer Pre-warm Anthropic cache for persona system prompts
agent-fn-registry Registry for tools associated with each persona
conversation-codec Persist persona-specific conversation history

The full persona stack: llm-persona defines the identity, agent-context-builder adds context-specific sections, prompt-cache-warmer keeps the combined prompt warm in Anthropic's cache.


What's Next

Persona versioning: tag each persona definition with a version. Track which version handled each conversation. When you update a persona, old conversations were handled by v1, new ones by v2. This is essential for A/B testing persona changes.

Inheritance: Persona(name="MayaPremium", parent="Maya", capabilities=[...extra...]) that inherits from a base persona and adds or overrides fields. This avoids duplicating the base persona definition for each variant.

YAML schema validation: a JSON schema for the persona YAML format with a validate_file() helper that gives clear error messages when the config is malformed. Right now loading a malformed YAML will crash with a KeyError or pydantic-less dict mismatch.


Built as part of the agent-stack family: composable Python primitives for production LLM agents.

Top comments (0)