| layout | default |
|---|---|
| title | Character System |
| parent | ElizaOS Deep Dive |
| nav_order | 3 |
Welcome to Chapter 3: Character System. In this part of ElizaOS: Deep Dive Tutorial, you will build an intuitive mental model first, then move into concrete implementation details and practical production tradeoffs.
Characters are the personality layer of ElizaOS agents. A character file defines who an agent is — its name, bio, communication style, knowledge domains, goals, and behavioral rules. This decouples agent identity from code, making it easy to create, share, and iterate on agent personalities.
graph TB
subgraph Character["Character Definition"]
ID[Identity<br/>name, bio, description]
STYLE[Communication Style<br/>chat, post, general]
KNOW[Knowledge<br/>topics, facts, documents]
BEHAVE[Behavior<br/>goals, adjectives, rules]
CONFIG[Configuration<br/>model, plugins, settings]
end
subgraph Runtime["AgentRuntime"]
PROMPT[System Prompt Builder]
CONTEXT[Context Composer]
end
Character --> PROMPT
PROMPT --> CONTEXT
interface Character {
// Identity
id?: string; // Unique identifier
name: string; // Display name
description: string; // Short description
bio: string[]; // Background paragraphs
username?: string; // Handle/username
// Model Configuration
modelProvider: ModelProvider; // "openai" | "anthropic" | "google" | etc
model?: string; // Specific model ID
imageModelProvider?: ModelProvider;
imageModel?: string;
// Communication Style
style: {
all: string[]; // Rules applied to all outputs
chat: string[]; // Rules for chat messages
post: string[]; // Rules for social media posts
};
// Personality
adjectives: string[]; // Personality traits
topics: string[]; // Areas of expertise
// Knowledge
knowledge?: string[]; // Static knowledge facts
messageExamples?: MessageExample[][]; // Example conversations
postExamples?: string[]; // Example social media posts
// Plugins
plugins: string[]; // Plugin package names
// Settings
settings?: {
model?: string;
embeddingModel?: string;
maxContextTokens?: number;
secrets?: Record<string, string>;
[key: string]: unknown;
};
// Client/Connector configuration
clients?: string[]; // Platform connectors
clientConfig?: Record<string, unknown>;
}The name and bio shape how the agent introduces itself and how other agents refer to it:
{
"name": "Sage",
"description": "A senior software architect with 20 years of experience",
"bio": [
"Sage is a senior software architect who has designed systems at scale for two decades.",
"With deep experience in distributed systems, databases, and API design, Sage approaches every problem by first understanding the constraints.",
"Sage values simplicity over cleverness and always considers operational burden before adding complexity.",
"Known for asking 'What's the simplest thing that could work?' before exploring advanced solutions."
]
}The bio array is joined into the system prompt. Each paragraph provides a distinct facet of the agent's background.
Adjectives influence the agent's tone in generated content:
{
"adjectives": [
"pragmatic",
"experienced",
"thoughtful",
"direct",
"detail-oriented",
"skeptical of hype"
]
}Style rules are the most powerful control over agent output. They're injected into every prompt:
{
"style": {
"all": [
"Never use buzzwords or marketing language",
"Always consider edge cases in technical advice",
"Admit when something is outside your expertise",
"Use concrete examples over abstract explanations",
"Prefer 'I think' over 'Obviously' for opinions"
],
"chat": [
"Keep responses under 3 paragraphs unless the topic requires more",
"Use code blocks for any code snippets",
"Ask one clarifying question if the request is ambiguous",
"Use backticks for technical terms inline"
],
"post": [
"Keep posts under 250 characters",
"Share one insight per post",
"No hashtags unless directly relevant",
"Write in first person"
]
}
}The runtime selects the appropriate style set based on context:
allrules are always includedchatrules are added for conversational contexts (Discord DMs, Telegram, etc.)postrules are added for social media (X/Twitter, Farcaster)
Embed facts directly in the character:
{
"knowledge": [
"PostgreSQL uses MVCC (Multi-Version Concurrency Control) for transaction isolation",
"The CAP theorem states a distributed system can only guarantee two of: Consistency, Availability, Partition tolerance",
"Event sourcing stores state as a sequence of events rather than current state snapshots",
"CQRS separates read and write models for better scalability in read-heavy systems"
]
}Place documents in the knowledge/ directory:
knowledge/
├── architecture-patterns.md
├── database-design-guide.pdf
├── api-best-practices.txt
└── company-handbook.docx
These are automatically ingested via RAG — chunked, embedded, and stored in the vector database for retrieval during conversations.
Examples teach the agent specific interaction patterns:
{
"messageExamples": [
[
{
"user": "user1",
"content": { "text": "Should I use microservices for my new project?" }
},
{
"user": "Sage",
"content": {
"text": "That depends on your team size and operational maturity. For a small team starting a new product, a well-structured monolith is almost always the right choice. Microservices add significant operational complexity — service discovery, distributed tracing, eventual consistency — that isn't justified until you have clear scaling or team-independence needs. What's your team size and current infrastructure?"
}
}
],
[
{
"user": "user2",
"content": { "text": "What's the best database?" }
},
{
"user": "Sage",
"content": {
"text": "There's no 'best' database — only the best fit for your workload. What are you building? That'll help me recommend something specific rather than giving a generic answer."
}
}
]
]
}{
"modelProvider": "anthropic",
"model": "claude-sonnet-4-20250514",
"settings": {
"maxContextTokens": 128000
}
}{
"modelProvider": "openai",
"model": "gpt-4o",
"imageModelProvider": "openai",
"imageModel": "dall-e-3",
"settings": {
"embeddingModel": "text-embedding-3-small",
"model": "gpt-4o",
"maxContextTokens": 128000
}
}{
"modelProvider": "ollama",
"model": "llama3.1:70b",
"settings": {
"embeddingModel": "nomic-embed-text",
"ollamaBaseUrl": "http://localhost:11434"
}
}Characters specify which plugins load:
{
"plugins": [
"@elizaos/plugin-bootstrap",
"@elizaos/plugin-sql",
"@elizaos/plugin-web-search",
"@elizaos/plugin-image-generation",
"my-custom-plugin"
]
}Each plugin extends the agent's capabilities. A research agent might include web search and PDF processing; a social media agent might include the X/Twitter and image generation plugins.
{
"name": "CodeReview",
"description": "A code review specialist focused on security and performance",
"bio": [
"CodeReview is an expert code reviewer with deep knowledge of security vulnerabilities and performance antipatterns.",
"Trained on thousands of code reviews from major open source projects."
],
"style": {
"all": [
"Always check for OWASP Top 10 vulnerabilities",
"Flag performance issues with estimated impact",
"Suggest specific fixes, not just identify problems",
"Rate severity as Critical/High/Medium/Low"
],
"chat": [
"Format reviews as numbered lists",
"Include code snippets for suggested fixes"
]
},
"topics": ["security", "performance", "code quality", "best practices"],
"plugins": ["@elizaos/plugin-bootstrap", "@elizaos/plugin-sql"]
}{
"name": "TechPulse",
"description": "A tech industry commentator",
"bio": [
"TechPulse tracks the latest developments in AI, cloud, and developer tools.",
"Known for concise, insightful takes that cut through the hype."
],
"style": {
"all": [
"Be concise and opinionated",
"Back opinions with specific data points",
"Avoid corporate speak"
],
"post": [
"One clear insight per post",
"Use numbers and comparisons",
"Under 250 characters",
"No thread-style posts"
]
},
"postExamples": [
"SQLite handles 100K writes/sec on modern hardware. Most apps don't need a database server.",
"The best architecture doc is the one your team actually reads. Keep it to one page.",
"Hot take: most microservice architectures are just distributed monoliths with extra latency."
],
"clients": ["twitter"],
"plugins": ["@elizaos/plugin-bootstrap", "@elizaos/plugin-sql"]
}{
"name": "Aria",
"description": "A general-purpose assistant with Web3 capabilities",
"bio": [
"Aria is a versatile AI assistant that bridges traditional software development with Web3.",
"Equally comfortable discussing React patterns and Solidity smart contracts."
],
"plugins": [
"@elizaos/plugin-bootstrap",
"@elizaos/plugin-sql",
"@elizaos/plugin-web-search",
"@elizaos/plugin-wallet",
"@elizaos/plugin-token-data"
],
"clients": ["discord", "telegram"],
"clientConfig": {
"discord": {
"shouldIgnoreBots": true,
"shouldRespondToDirectMessages": true
},
"telegram": {
"shouldIgnoreGroups": false
}
}
}The runtime transforms character data into system prompts:
function buildSystemPrompt(character: Character, state: State): string {
const sections = [];
// Identity
sections.push(`You are ${character.name}.`);
sections.push(character.bio.join("\n"));
// Style rules
sections.push("## Communication Style");
sections.push(character.style.all.map(r => `- ${r}`).join("\n"));
if (state.context === "chat") {
sections.push(character.style.chat.map(r => `- ${r}`).join("\n"));
} else if (state.context === "post") {
sections.push(character.style.post.map(r => `- ${r}`).join("\n"));
}
// Knowledge
if (state.relevantMemories.length > 0) {
sections.push("## Relevant Knowledge");
sections.push(state.relevantMemories
.map(m => m.content.text).join("\n"));
}
// Provider context
for (const [name, output] of Object.entries(state.providers)) {
sections.push(`## ${name}\n${output}`);
}
// Available actions
if (state.actions.length > 0) {
sections.push("## Available Actions");
sections.push(state.actions
.map(a => `- ${a.name}: ${a.description}`).join("\n"));
}
return sections.join("\n\n");
}| Concept | Key Takeaway |
|---|---|
| Character File | JSON definition of agent identity, style, knowledge, and capabilities |
| Style Rules | Three contexts: all (always), chat (conversation), post (social media) |
| Knowledge | Static facts, message examples, and RAG-ingested documents |
| Model Config | Per-character model selection — any supported provider |
| Plugin Selection | Characters specify which plugins load for their capabilities |
| Composition | Mix specialist, social, and multi-domain patterns |
| Prompt Building | Characters are automatically compiled into system prompts |
Next Steps: Chapter 4: Plugin Architecture — Build custom plugins with actions, providers, evaluators, and services.
Built with insights from the ElizaOS repository and community documentation.
Most teams struggle here because the hard part is not writing more code, but deciding clear boundaries for plugin, elizaos, sections so behavior stays predictable as complexity grows.
In practical terms, this chapter helps you avoid three common failures:
- coupling core logic too tightly to one implementation path
- missing the handoff boundaries between setup, execution, and validation
- shipping changes without clear rollback or observability strategy
After working through this chapter, you should be able to reason about Chapter 3: Character System as an operating subsystem inside ElizaOS: Deep Dive Tutorial, with explicit contracts for inputs, state transitions, and outputs.
Use the implementation notes around name, push, state as your checklist when adapting these patterns to your own repository.
Under the hood, Chapter 3: Character System usually follows a repeatable control path:
- Context bootstrap: initialize runtime config and prerequisites for
plugin. - Input normalization: shape incoming data so
elizaosreceives stable contracts. - Core execution: run the main logic branch and propagate intermediate state through
sections. - Policy and safety checks: enforce limits, auth scopes, and failure boundaries.
- Output composition: return canonical result payloads for downstream consumers.
- Operational telemetry: emit logs/metrics needed for debugging and performance tuning.
When debugging, walk this sequence in order and confirm each stage has explicit success/failure conditions.
Use the following upstream sources to verify implementation details while reading this chapter:
- ElizaOS
Why it matters: authoritative reference on
ElizaOS(github.com).
Suggested trace strategy:
- search upstream code for
pluginandelizaosto map concrete implementation paths - compare docs claims against actual runtime/config code before reusing patterns in production