Status: Consolidated Reference Document
Version: 2.0.1-consolidated
Date: 2026-01-26
Based On: Character Card V2 (malfoyslastname/character-card-spec-v2), Character Card V3
(kwaroran/character-card-spec-v3)
This document is a downstream consolidation of the upstream specifications:
- Character Card V2 Specification — https://github.com/malfoyslastname/character-card-spec-v2
- Character Card V3 Specification — https://github.com/kwaroran/character-card-spec-v3
This consolidated document is supplementary and does not supersede the upstream specifications. Where discrepancies exist between this document and the upstream sources, the upstream sources are authoritative. This document represents the idiosyncrasies of the upstream specifications as-is, including:
- Deviations from common best practices and de facto standards
- Inconsistencies in field naming conventions (snake_case vs camelCase)
- Differences in keyword definitions between V2 and V3
- Optional fields that are implementation-dependent
- Behavioral ambiguities documented in Section 12
Implementers SHOULD consult the upstream repositories for the latest updates and errata.
- Scope
- Normative References
- Terms and Definitions
- Conformance Keywords
- Embedding Methods
- Data Structures
- Character Card Fields
- Lorebook (Character Book)
- Decorators
- Curly Braced Syntax (Macros)
- Placeholder Substitution
- Unresolved Ambiguities
- Version Compatibility
This specification defines the data format for Character Cards, a structured representation of AI chatbot characters used across various frontend applications. Character Cards encapsulate character identity, personality, behavioral instructions, example dialogues, and supplementary metadata.
This specification covers:
- Three specification versions: V1, V2, and V3
- Embedding methods for storing Character Cards within image and archive files
- Required and optional fields for character definition
- Lorebook (world book) entries for contextual information injection
- Macro substitution and decorator systems for advanced prompt engineering
This specification does NOT cover:
- Transport protocols for Character Card distribution
- Authentication or authorization mechanisms
- Specific AI model prompt formatting requirements
- Rendering or display requirements for frontends
This specification is self-contained. The following references are informational:
- PNG Specification (ISO/IEC 15948): Defines the PNG image format and its chunk structure, including tEXt chunks used for embedding Character Card data.
- APNG Specification: Extension to PNG for animated images; follows the same chunk structure.
- ZIP File Format (PKWARE APPNOTE): Defines the archive format used for CHARX files.
- JSON (ECMA-404): JavaScript Object Notation, the data interchange format used for Character Card serialization.
- ECMAScript Regular Expressions: The regular expression syntax used in lorebook key matching when regex mode is enabled.
- Base64 (RFC 4648): The encoding scheme used for embedding JSON data in image metadata.
- ISO 639-1: Language tags used in multilingual creator notes.
- Unicode UTF-8 (RFC 3629): Character encoding for all string values.
For the purposes of this specification, the following terms and definitions apply:
Software that reads, writes, or processes Character Card objects. This includes frontends, editors, and conversion tools.
An application that uses Character Cards to conduct conversations with AI models. Examples include SillyTavern, RisuAI, and Agnai.
An application or component used to create, modify, and export Character Card objects.
A structured data object containing the definition of an AI chatbot character, including its name, description, personality, and behavioral instructions.
A collection of entries containing contextual information that is conditionally inserted into prompts based on trigger conditions. Also known as "World Book," "World Info," or "Memory Book" in various implementations.
The text sent to an AI language model, constructed from Character Card fields, conversation history, and lorebook entries.
The conversation history between the user and the AI character, consisting of alternating user and assistant messages.
The initial message displayed at the start of a conversation, originating from the character rather than the user.
An alternative message variant at the same position in the conversation. For greetings, this refers to alternate initial messages.
Metadata intended for human readers (users and other creators), not for inclusion in AI prompts.
A structured annotation within lorebook content that modifies the behavior of that entry. Decorators
begin with @@ and appear at the start of the content field.
A macro substitution syntax using double curly braces (e.g., {{char}}) that is replaced with
dynamic values at runtime. Also known as "macros."
A technique where partial assistant responses are provided to the AI model to guide its output. Supported by some API providers.
A binary file (image, audio, video, etc.) associated with a Character Card, such as character portraits or background images.
A conversation involving multiple AI characters simultaneously, as opposed to a one-on-one conversation.
The key words in this specification are to be interpreted as follows:
| Keyword | Synonyms | Definition |
|---|---|---|
| MUST | REQUIRED, SHALL | An absolute requirement of the specification. Non-conformance constitutes a specification violation. |
| MUST NOT | SHALL NOT | An absolute prohibition. Non-conformance constitutes a specification violation. |
| SHOULD | RECOMMENDED | A strong recommendation. Deviations are permitted only when the implications are fully understood and carefully considered. |
| SHOULD NOT | NOT RECOMMENDED | A strong discouragement. The behavior may be acceptable in specific circumstances when implications are understood. |
| MAY | OPTIONAL | Truly optional. Implementations may include or omit this item freely. |
| MAY NOT | Truly optional to exclude. The item is not forbidden. | |
| IN ANY CASE | The described action takes precedence over other rules in the specification, even if it creates a conflict. |
Character Card data may be embedded using multiple methods. This section defines each method and its requirements.
Character Card objects MAY be stored as standalone JSON files.
Requirements:
- The file extension SHOULD be
.json. - The file MUST contain a valid JSON object conforming to the Character Card structure.
- UTF-8 encoding MUST be used.
Considerations:
- Standalone JSON files lack associated imagery and are discouraged for distribution due to reduced user-friendliness.
Character Card V1 and V2 objects MAY be embedded in PNG or APNG files.
Requirements:
- The Character Card JSON MUST be serialized as a UTF-8 string.
- The UTF-8 string MUST be encoded using Base64.
- The Base64 string MUST be stored in a PNG
tEXtchunk with the keywordchara.
Note: V1 specification references "Chara EXIF metadata field." This is a misnomer; PNG files do
not contain EXIF metadata. Implementations MUST use tEXt chunks.
Character Card V3 objects MAY be embedded in PNG or APNG files.
Requirements:
- The Character Card JSON MUST be serialized as a UTF-8 string.
- The UTF-8 string MUST be encoded using Base64.
- The Base64 string MUST be stored in a PNG
tEXtchunk with the keywordccv3.
Backward Compatibility:
- Applications MAY include a backfilled V2-compatible representation in the
charachunk. - When backfilling V2, applications SHOULD add a warning to the
creator_notesfield indicating that the card is V3 and should be used with a V3-compatible application. - When importing, if both
charaandccv3chunks are present, applications SHOULD use theccv3chunk. - The backfilled
charachunk SHOULD be discarded on import whenccv3is present.
PNG/APNG files MAY contain embedded assets as additional tEXt chunks.
Requirements:
- The
tEXtchunk keyword MUST bechara-ext-asset_:{path}where{path}is the asset path. - The chunk value MUST be Base64-encoded binary data.
- Assets are accessed via the URI
__asset:{path}.
Recommendation: This method is provided for legacy compatibility. New implementations SHOULD use CHARX format for assets.
WEBP embedding is NOT covered by this specification due to technical ambiguities in the V1 specification. Implementations MAY support WEBP but behavior is undefined.
CHARX is an archive format for Character Card V3 objects with embedded assets.
Structure:
- A CHARX file is a ZIP archive.
- The ZIP archive MUST contain a
card.jsonfile at the root containing the CharacterCardV3 object. - The ZIP archive MUST NOT be encrypted.
- File names and paths within the archive SHOULD use only ASCII characters.
Asset Organization:
Assets SHOULD be organized as follows:
| Asset Type | Directory Path |
|---|---|
| Images (.png, .avif, .jpg, .webp) | assets/{type}/images/ |
| Audio (.mp3, .wav, .ogg) | assets/{type}/audio/ |
| Video (.mp4, .webm) | assets/{type}/video/ |
| Live2D models | assets/{type}/l2d/ |
| 3D models (.mmd, .obj) | assets/{type}/3d/ |
| AI models (.safetensors, .ckpt, .onnx) | assets/{type}/ai/ |
| Fonts (.otf, .ttf) | assets/{type}/fonts/ |
| Code (.lua, .js) | assets/{type}/code/ |
| Other | assets/{type}/other/ |
The {type} value corresponds to the asset's usage context (e.g., icon, background, emotion).
Application-Specific Data:
- Applications MAY store additional JSON files at the archive root for application-specific data.
Rejection:
- Applications MAY reject CHARX files that are too large, corrupted, invalid ZIP archives, or encrypted.
This section defines the data structures using an Interface Definition Language (IDL). The IDL is C-like with the following conventions:
| Syntax | Meaning |
|---|---|
String |
A UTF-8 encoded string of variable length |
Integer |
A signed integer (precision implementation-defined, minimum 32-bit) |
Float |
A floating-point number (precision implementation-defined, minimum 64-bit IEEE 754) |
Boolean |
A boolean value (true or false) |
Array<T> |
A dynamically-sized ordered collection of elements of type T |
Record<K, V> |
An associative map with keys of type K and values of type V |
Optional<T> |
A value that may be present (of type T) or absent |
Literal<V> |
A string field that MUST have the exact value V |
Union<A, B> |
A value that may be either type A or type B |
Enum { A, B, C } |
A string value restricted to one of the listed options |
struct CharacterCardV1 {
String name;
String description;
String personality;
String scenario;
String first_mes;
String mes_example;
}Field Default: All fields are mandatory and MUST default to empty string (""), not null
or absent.
struct CharacterCardV2 {
Literal<"chara_card_v2"> spec;
Literal<"2.0"> spec_version;
CharacterCardV2Data data;
}
struct CharacterCardV2Data {
// V1 fields (inherited)
String name;
String description;
String personality;
String scenario;
String first_mes;
String mes_example;
// V2 additions
String creator_notes;
String system_prompt;
String post_history_instructions;
Array<String> alternate_greetings;
Optional<CharacterBook> character_book;
Array<String> tags;
String creator;
String character_version;
Record<String, Any> extensions;
}
struct CharacterBook {
Optional<String> name;
Optional<String> description;
Optional<Integer> scan_depth;
Optional<Integer> token_budget;
Optional<Boolean> recursive_scanning;
Record<String, Any> extensions;
Array<CharacterBookEntry> entries;
}
struct CharacterBookEntry {
Array<String> keys;
String content;
Record<String, Any> extensions;
Boolean enabled;
Integer insertion_order;
Optional<Boolean> case_sensitive;
Optional<String> name;
Optional<Integer> priority;
Optional<Integer> id;
Optional<String> comment;
Optional<Boolean> selective;
Optional<Array<String>> secondary_keys;
Optional<Boolean> constant;
Optional<Enum { "before_char", "after_char" }> position;
}struct CharacterCardV3 {
Literal<"chara_card_v3"> spec;
Literal<"3.0"> spec_version;
CharacterCardV3Data data;
}
struct CharacterCardV3Data {
// Inherited from V2
String name;
String description;
String personality;
String scenario;
String first_mes;
String mes_example;
String creator_notes;
String system_prompt;
String post_history_instructions;
Array<String> alternate_greetings;
Array<String> tags;
String creator;
String character_version;
Record<String, Any> extensions;
// Changed from V2
Optional<Lorebook> character_book;
// V3 additions
Optional<Array<Asset>> assets;
Optional<String> nickname;
Optional<Record<String, String>> creator_notes_multilingual;
Optional<Array<String>> source;
Array<String> group_only_greetings;
Optional<Integer> creation_date;
Optional<Integer> modification_date;
}
struct Asset {
String type;
String uri;
String name;
String ext;
}
struct Lorebook {
Optional<String> name;
Optional<String> description;
Optional<Integer> scan_depth;
Optional<Integer> token_budget;
Optional<Boolean> recursive_scanning;
Record<String, Any> extensions;
Array<LorebookEntry> entries;
}
struct LorebookEntry {
Array<String> keys;
String content;
Record<String, Any> extensions;
Boolean enabled;
Integer insertion_order;
Optional<Boolean> case_sensitive;
Boolean use_regex;
Optional<Boolean> constant;
Optional<String> name;
Optional<Integer> priority;
Optional<Union<Integer, String>> id;
Optional<String> comment;
Optional<Boolean> selective;
Optional<Array<String>> secondary_keys;
Optional<Enum { "before_char", "after_char" }> position;
}struct LorebookExport {
Literal<"lorebook_v3"> spec;
Lorebook data;
}Applications supporting multiple versions SHOULD use:
typedef CharacterCard = Union<CharacterCardV1, CharacterCardV2, CharacterCardV3>;This section specifies the semantics and requirements for each field.
| Property | Value |
|---|---|
| Type | String |
| Required | Yes |
| Default | "" |
| Used in Prompt | Yes (as substitution target) |
The character's name. Used to identify the character and as the replacement value for {{char}}
placeholders (unless overridden by nickname in V3).
| Property | Value |
|---|---|
| Type | Optional<String> |
| Required | No |
| Default | Absent |
| Used in Prompt | Yes (as substitution target) |
An alternative display name. When present and non-empty, the placeholders {{char}}, <char>, and
<bot> MUST be replaced with the nickname value instead of name.
| Property | Value |
|---|---|
| Type | String |
| Required | Yes |
| Default | "" |
| Used in Prompt | SHOULD be included by default |
A description of the character. SHOULD be included in prompts by default.
Alternative UI Labels:
- ZoltanAI: "Personality"
- Agnai: "Persona Attributes"
- SillyTavern: "Description"
| Property | Value |
|---|---|
| Type | String |
| Required | Yes |
| Default | "" |
| Used in Prompt | SHOULD be included by default |
A concise summary of the character's personality traits. SHOULD be included in prompts by default.
Alternative UI Labels:
- ZoltanAI: "Summary"
- SillyTavern: "Personality summary"
| Property | Value |
|---|---|
| Type | String |
| Required | Yes |
| Default | "" |
| Used in Prompt | SHOULD be included by default |
The context and circumstances of the conversation. SHOULD be included in prompts by default.
| Property | Value |
|---|---|
| Type | String |
| Required | Yes |
| Default | "" |
| Used in Prompt | Yes (as initial message) |
The character's first message (greeting) in a conversation.
Requirements:
- The character MUST send the first message in a conversation.
- The first message MUST be the value of
first_mes.
| Property | Value |
|---|---|
| Type | Array<String> |
| Required | Yes (V2+) |
| Default | [] |
| Used in Prompt | Yes (as alternative initial messages) |
Alternative first messages. Applications MUST provide a mechanism (swipes) to select among
first_mes and each element of alternate_greetings.
| Property | Value |
|---|---|
| Type | Array<String> |
| Required | Yes |
| Default | [] |
| Used in Prompt | Yes (in group chat contexts) |
Additional greetings available only in group chat contexts. These greetings SHOULD NOT appear in one-on-one conversations.
| Property | Value |
|---|---|
| Type | String |
| Required | Yes |
| Default | "" |
| Used in Prompt | Conditionally |
Example conversations demonstrating the character's speech patterns and behavior.
Format:
<START>
{{user}}: [user message]
{{char}}: [character response]
<START>
{{user}}: [another user message]
{{char}}: [another response]
Requirements:
- The
<START>marker indicates the beginning of a new example conversation segment. - Applications MAY transform
<START>(e.g., into a system message reading "Start a new conversation"). - Example conversations SHOULD be included in prompts by default until actual conversation history fills the context window.
- When context space is limited, examples SHOULD be pruned to make room for actual conversation.
- This pruning behavior MAY be user-configurable.
| Property | Value |
|---|---|
| Type | String |
| Required | Yes (V2+) |
| Default | "" |
| Used in Prompt | Yes |
Character-specific system prompt override.
Requirements:
- If non-empty, applications MUST replace the default/global system prompt with this value by default.
- If empty, applications MUST use the default/global system prompt.
- Applications MUST support the
{{original}}placeholder, which is replaced with the system prompt that would have been used without this override. - Applications MAY provide mechanisms to override or supplement this field, but such overrides MUST NOT be the default behavior.
| Property | Value |
|---|---|
| Type | String |
| Required | Yes (V2+) |
| Default | "" |
| Used in Prompt | Yes |
Instructions inserted after the conversation history, also known as "jailbreak" or "UJB" (User Jailbreak) in some applications.
Requirements:
- If non-empty, applications MUST replace the default post-history instructions with this value by default.
- If empty, applications MUST use the default post-history instructions.
- Applications MUST support the
{{original}}placeholder. - Applications MAY provide mechanisms to override or supplement this field, but such overrides MUST NOT be the default behavior.
| Property | Value |
|---|---|
| Type | String |
| Required | Yes (V2+) |
| Default | "" |
| Used in Prompt | MUST NOT |
Notes from the character creator intended for users and other creators.
Requirements:
- MUST NOT be included in prompts sent to AI models.
- SHOULD be prominently displayed to users (at least one paragraph SHOULD be visible).
V3 Behavior:
- If
creator_notes_multilingualis present and contains a matching language, that localized version takes precedence. - If no matching language is found,
creator_notesis used as the fallback.
| Property | Value |
|---|---|
| Type | Optional<Record<String, String>> |
| Required | No |
| Default | Absent |
| Used in Prompt | MUST NOT |
Localized versions of creator notes. Keys MUST be valid ISO 639-1 language tags (e.g., "en",
"ja").
Selection Algorithm:
- Check for exact match with application's language setting.
- Check for language-only match (ignoring region/script).
- Fall back to
creator_notes.
| Property | Value |
|---|---|
| Type | String |
| Required | Yes (V2+) |
| Default | "" |
| Used in Prompt | MUST NOT |
The name or identifier of the character's creator.
| Property | Value |
|---|---|
| Type | String |
| Required | Yes (V2+) |
| Default | "" |
| Used in Prompt | MUST NOT |
Version identifier for the character card. MAY be used for display and sorting.
| Property | Value |
|---|---|
| Type | Array<String> |
| Required | Yes (V2+) |
| Default | [] |
| Used in Prompt | SHOULD NOT |
Categorization tags for the character.
Requirements:
- No restrictions on tag string content.
- SHOULD NOT be used in prompt engineering.
- MAY be used for frontend sorting and filtering.
- Filtering SHOULD be case-insensitive.
| Property | Value |
|---|---|
| Type | Optional<Array<String>> |
| Required | No |
| Default | Absent |
| Used in Prompt | MUST NOT |
URLs or identifiers indicating the origin of the character card.
Requirements:
- Elements SHOULD be HTTP/HTTPS URLs or platform-specific identifiers.
- This field SHOULD NOT be user-editable.
- Applications MAY provide UI to open source URLs.
- Applications SHOULD only append to this array and SHOULD NOT modify or remove existing elements unless the element was added by the same application.
- Applications MAY remove elements if they significantly impair performance or are harmful.
| Property | Value |
|---|---|
| Type | Optional<Integer> |
| Required | No |
| Default | Absent |
| Used in Prompt | MUST NOT |
Unix timestamp (seconds since 1970-01-01 00:00:00 UTC) of character card creation. Applications SHOULD set this when creating a new character and SHOULD NOT modify it thereafter.
| Property | Value |
|---|---|
| Type | Optional<Integer> |
| Required | No |
| Default | Absent |
| Used in Prompt | MUST NOT |
Unix timestamp (seconds since 1970-01-01 00:00:00 UTC) of last modification. Applications SHOULD update this when saving changes.
| Property | Value |
|---|---|
| Type | Record<String, Any> |
| Required | Yes (V2+) |
| Default | {} |
| Used in Prompt | Application-dependent |
A container for application-specific or experimental data.
Requirements:
- MUST default to an empty object.
- MAY contain any arbitrary JSON-serializable key-value pairs.
- Applications MUST NOT destroy unknown key-value pairs when importing and exporting.
- Applications SHOULD namespace keys to prevent conflicts:
- Preferred:
"appname/key"(e.g.,"agnai/voice") - Acceptable:
"appname_key"(e.g.,"agnai_voice") - Acceptable:
"appname": { "key": value }(e.g.,"agnai": { "voice": ... })
- Preferred:
| Property | Value |
|---|---|
| Type | Optional<Array<Asset>> |
| Required | No |
| Default | See below |
| Used in Prompt | No (visual/audio resources) |
Binary assets associated with the character.
Default Value (when absent or undefined):
[
{
"type": "icon",
"uri": "ccdefault:",
"name": "main",
"ext": "png"
}
]The special URI ccdefault: indicates the default behavior:
- For PNG/APNG embedding: the host image itself
- For CHARX: implementation-defined
Asset Object Structure:
| Field | Type | Description |
|---|---|---|
type |
String |
The asset category (see below) |
uri |
String |
Location of the asset |
name |
String |
Identifier for the asset |
ext |
String |
File extension (lowercase, e.g., "png") |
URI Schemes:
| Scheme | Description |
|---|---|
ccdefault: |
Default/embedded asset |
embeded:// |
Asset embedded in CHARX or PNG extension chunks |
__asset:{path} |
Asset in PNG extension chunk at {path} |
http://, https:// |
Remote asset |
Asset Types:
| Type | Description |
|---|---|
icon |
Character portrait/avatar |
background |
Conversation background image |
user_icon |
User's avatar when using this character |
emotion |
Character expression/emotion sprite |
Behavior:
- For
icon: Applications SHOULD use the firsticonasset withnameequal to"main"as the default character avatar. - For
background: Applications SHOULD use the firstbackgroundasset withnameequal to"main"as the default background. - For
user_icon: Applications SHOULD use the asset as the user's avatar. - For
emotion: Thenamefield identifies the emotion (e.g.,"happy","sad").
Applications MAY define custom asset types. Custom types SHOULD be prefixed with x_ to
avoid conflicts with future specification additions.
Applications MAY ignore assets they do not support but MUST preserve unrecognized assets on export.
Format Support: Applications SHOULD support at least PNG, JPEG, and WebP formats. If uri
is ccdefault:, the ext field SHOULD be ignored.
| Version | Value |
|---|---|
| V2 | "chara_card_v2" |
| V3 | "chara_card_v3" |
Applications SHOULD NOT consider a card as conforming to a specification version if this field does not match exactly.
| Version | Value |
|---|---|
| V2 | "2.0" |
| V3 | "3.0" |
V3 Versioning Behavior:
- Future minor versions (e.g.,
"3.1") may be parsed as floats for comparison. - Applications SHOULD NOT reject cards with higher
spec_versionvalues. - Applications SHOULD alert users when loading cards from newer specification versions.
- Applications SHOULD fill missing fields with default values when importing newer versions.
The lorebook (called character_book in V2 and character_book in V3) provides contextual
information entries that are conditionally inserted into prompts.
| Property | Value |
|---|---|
| Type | Optional<String> |
| Used in Prompt | SHOULD NOT |
Identifier for the lorebook. Not used in prompt engineering.
| Property | Value |
|---|---|
| Type | Optional<String> |
| Used in Prompt | SHOULD NOT |
Description or notes about the lorebook.
| Property | Value |
|---|---|
| Type | Optional<Integer> |
The number of recent messages to scan when checking for key matches. If not specified, implementation-defined default applies.
| Property | Value |
|---|---|
| Type | Optional<Integer> |
Maximum token count for all inserted lorebook entries combined. When exceeded, lower-priority entries are removed.
| Property | Value |
|---|---|
| Type | Optional<Boolean> |
When true, lorebook entry content may trigger other lorebook entries (i.e., keys are matched
against inserted content, not just conversation history).
| Property | Value |
|---|---|
| Type | Record<String, Any> |
| Default | {} |
Application-specific lorebook data. Same preservation requirements as character-level extensions.
| Property | Value |
|---|---|
| Type | Array<LorebookEntry> |
| Required | Yes |
The lorebook entries. MAY be an empty array.
| Property | Value |
|---|---|
| Type | Array<String> |
| Required | Yes |
Trigger words or patterns. An entry matches if the chat log contains any of these keys (subject to other conditions).
Standard Mode (use_regex = false or absent):
- Keys are matched as literal substrings.
- Matching is case-insensitive by default (see
case_sensitive).
Regex Mode (use_regex = true, V3 only):
- Keys are interpreted as regular expression patterns.
- Applications MAY use only the first key for performance reasons.
| Property | Value |
|---|---|
| Type | Optional<Array<String>> |
Secondary trigger words used with selective mode. When selective is true, the entry matches
only if at least one primary key AND at least one secondary key are found.
V3 Note: Ignored when use_regex is true.
| Property | Value |
|---|---|
| Type | Optional<Boolean> |
| Default | false |
When true, enables secondary key requirement (see secondary_keys).
| Property | Value |
|---|---|
| Type | String |
| Required | Yes |
The text to insert into the prompt when the entry matches.
Requirements:
- If the entry matches, the content MUST be added to the prompt exactly once (regardless of how many times keys match).
- If content is empty, nothing is added to the prompt.
- Content MAY contain decorators (V3), which are processed and removed before insertion.
| Property | Value |
|---|---|
| Type | Boolean |
| Required | Yes |
When false, the entry MUST NOT match IN ANY CASE.
| Property | Value |
|---|---|
| Type | Integer |
| Required | Yes |
Determines the relative order of matched entries in the prompt. Lower values are inserted earlier (higher in the prompt).
When entries have equal insertion_order, the result is implementation-defined.
If priority is not specified, entries with lower insertion_order MAY be removed first when
reaching token_budget.
| Property | Value |
|---|---|
| Type | Optional<Boolean> |
| Default | false |
When true, key matching is case-sensitive. When false or absent, matching is case-insensitive.
| Property | Value |
|---|---|
| Type | Optional<Boolean> (V2), Implementation-required (V3) |
| Default | false |
When true, the entry MUST match regardless of key matches, subject to enabled and budget
constraints.
V3 Note: Implementation of this field is REQUIRED in V3 (was optional in V2).
| Property | Value |
|---|---|
| Type | Boolean |
| Required | Yes (V3) |
When true, keys are interpreted as regular expression patterns instead of literal strings.
Implementation Notes:
- Applications MAY limit regex complexity for performance.
- Applications MAY ignore entries with malicious regex patterns (e.g., ReDoS-vulnerable).
- The
re2regex engine is RECOMMENDED for server-side implementations.
| Property | Value |
|---|---|
| Type | Optional<String> |
| Used in Prompt | SHOULD NOT |
Human-readable identifier for the entry. Not used in prompt engineering.
| Property | Value |
|---|---|
| Type | Optional<Union<Integer, String>> |
| Used in Prompt | SHOULD NOT |
Machine identifier for the entry. V2 specifies Integer; V3 allows Integer or String.
| Property | Value |
|---|---|
| Type | Optional<String> |
| Used in Prompt | SHOULD NOT |
Notes about the entry. Not used in prompt engineering.
| Property | Value |
|---|---|
| Type | Optional<Integer> |
Priority for budget-constrained removal. Lower values are removed first when token_budget is
reached.
| Property | Value |
|---|---|
| Type | Optional<Enum { "before_char", "after_char" }> |
Position relative to character definitions.
"before_char": Insert before character definition fields."after_char": Insert after character definition fields.
Decorators are a V3 feature providing inline modifiers for lorebook entry behavior. They appear at
the beginning of the content field.
@@decorator_name [value]
Rules:
- Decorators start with
@@and end with a newline. - Values are space-separated from the decorator name.
- Multiple values are comma-separated (no spaces).
- Boolean decorators (flags) have no value.
- All decorators MUST appear before any non-decorator content.
- Decorators MUST be removed from content before prompt insertion.
- Unrecognized decorators MUST be ignored.
- If multiple decorators of the same name appear, only the first is considered (unless specified otherwise).
Fallback Syntax:
Fallback decorators use @@@ and follow their primary decorator:
@@primary_decorator value
@@@fallback_decorator fallback_value
If the application does not recognize @@primary_decorator, it checks @@@fallback_decorator.
Multiple fallbacks are checked in order.
Value: Integer
Behavior: The entry SHOULD NOT match until the chat log's assistant message count exceeds
the specified value.
If message counting is not possible, the entry SHOULD NOT match until the Nth user input is received.
Value: Integer
Behavior: The entry SHOULD NOT match unless the chat log's assistant message count is
divisible by the specified value.
If message counting is not possible, the fallback behavior is implementation-defined.
Value: None (flag)
Behavior: Once the entry matches, it SHOULD remain active IN ANY CASE for subsequent
messages, even if keys no longer match.
Value: None (flag)
Behavior: Once the entry matches, it SHOULD NOT match again IN ANY CASE for subsequent
messages.
Value: None (flag)
Behavior: The entry SHOULD match IN ANY CASE, regardless of key matches or other
conditions.
Value: None (flag)
Behavior: The entry SHOULD NOT match IN ANY CASE, unless @@activate is also present.
Useful for disabling entries or as fallback targets.
Value: Integer
Behavior: Insert the content at the specified depth in the chat log (0 = most recent, higher =
earlier).
Special Case (depth = 0):
- If
@@roleisassistantand the environment supports prefill, insert as a prefill message. - Multiple prefill entries are concatenated in
insertion_order.
If chat-based insertion is not possible:
- If depth > total message count: insert in a low-priority position.
- Otherwise: insert in a high-priority position.
Ignored if @@position is present.
Value: Integer
Behavior: Same as @@depth but measures depth in tokens rather than messages. Suitable for
non-chat contexts.
Ignored if @@position is present.
Value: Integer
Behavior: Same as @@depth but counting from the oldest message. Equivalent to
@@depth <total_messages - value>.
Ignored if @@position or @@depth is present.
Value: Integer
Behavior: Same as @@instruct_depth but counting from the beginning. Equivalent to
@@instruct_depth <total_tokens - value>.
Ignored if @@position or @@instruct_depth is present.
Value: String
Behavior: Insert content at a named position in the prompt structure.
| Value | Position |
|---|---|
after_desc |
After the description field |
before_desc |
Before the description field |
personality |
In the personality section |
scenario |
In the scenario section |
Applications MAY support additional positions. Unrecognized positions SHOULD be ignored.
Value: "assistant", "system", or "user"
Behavior: Treat the content as having the specified role. Relevant for chat-format APIs.
Value: Integer
Behavior: Override the lorebook's scan_depth for this entry only.
Value: Integer
Behavior: Same as @@scan_depth but measured in tokens rather than messages.
Value: Integer
Behavior: The entry MUST NOT match unless the active greeting index equals the specified
value.
- Index 0 =
first_mes - Index 1+ = corresponding element of
alternate_greetings
Ignored if greeting detection is not possible.
Value: String
Behavior: The entry SHOULD NOT match unless the active user icon's name equals the
specified value.
Ignored if user icon detection is not possible.
Value: None (flag)
Behavior: The entry SHOULD NOT match when context is at maximum capacity, or SHOULD be
trimmed first when maximum context is reached.
Ignored if context limit detection is not possible.
Value: Comma-separated strings
Behavior: The entry SHOULD NOT match unless the chat log contains at least one of these
additional keys (in addition to the primary keys requirement).
This decorator MAY appear multiple times (values accumulate).
When use_regex is true, values are treated as regex patterns.
Value: Comma-separated strings
Behavior: The entry SHOULD NOT match if the chat log contains any of these keys.
Ignored when use_regex is true.
Value: String ("post_history_instructions" or "system_prompt")
Behavior: Applications MAY disable the specified UI-level prompt when this entry is active.
Applications MAY define additional UI prompt types.
When converting V3 to V2 format, all decorators SHOULD be removed from content.
Curly Braced Syntax (CBS), also called macros, provides dynamic value substitution in text fields.
Aliases: <char>, <bot>, <BOT> (V1 only)
Replacement: The character's nickname (if present and non-empty) or name.
Aliases: <USER> (V1 only)
Replacement: The user's display name or current persona name.
Replacement: One randomly selected value from the comma-separated list.
Escape: Use :: for literal commas (e.g., {{random:a::b,c}} may produce "a,b" or "c").
Replacement: One value from the comma-separated list, selected to be consistent across identical conditions within the same context.
Applications SHOULD make effort to return the same value for identical {{pick:...}}
expressions in the same generation context.
Replacement: A random integer from 1 to N (inclusive).
Alternate Syntax: {{roll:dN}} or {{roll:DN}} (e.g., {{roll:d6}} = {{roll:6}}).
Replacement: Empty string.
Behavior: The content A is discarded entirely. SHOULD NOT be used for lorebook key
matching.
10.3.2 {{hidden_key:A}}
Replacement: Empty string.
Behavior: Same as {{// A}} but the value SHOULD be considered for recursive lorebook
scanning.
Replacement: Empty string (in prompts).
Behavior: The content A MAY be displayed to the user as an inline comment in the UI but
MUST NOT be sent to the AI model. SHOULD NOT be used for lorebook key matching.
Replacement: The string A with characters in reverse order.
Example: {{reverse:Hello}} → "olleH"
Context: Only valid within system_prompt and post_history_instructions fields.
Replacement: The default/global value that would have been used if the character-specific field
were empty.
This section defines the processing requirements for placeholder substitution.
The following fields MUST have placeholders replaced:
| Field | V1 | V2 | V3 |
|---|---|---|---|
description |
✓ | ✓ | ✓ |
personality |
✓ | ✓ | ✓ |
scenario |
✓ | ✓ | ✓ |
first_mes |
✓ | ✓ | ✓ |
mes_example |
✓ | ✓ | ✓ |
alternate_greetings |
— | ✓ | ✓ |
group_only_greetings |
— | — | ✓ |
system_prompt |
— | ✓ | ✓ |
post_history_instructions |
— | ✓ | ✓ |
Lorebook content |
— | ✓ | ✓ |
- Decorators are parsed and removed from lorebook content (V3).
- All CBS macros are evaluated and replaced.
- Character/user placeholders (
{{char}},{{user}}, etc.) are replaced.
Placeholder matching MUST be case-insensitive (e.g., {{CHAR}}, {{Char}}, and {{char}} are
equivalent).
Behavior for unrecognized placeholders is undefined. Applications MAY:
- Leave them unchanged
- Remove them
- Display a warning
This section documents ambiguities in the upstream specifications that cannot be resolved within this consolidated document.
Source: V1 Specification
Issue: Whether {{user}} and <USER> should be replaced inside the name field is explicitly
marked as "UNSPECIFIED."
Recommendation: Applications SHOULD NOT perform substitution in the name field to avoid
circular references.
Source: V1 Specification
Issue: WEBP embedding is explicitly not covered "due to technical ambiguities."
Recommendation: Implementations requiring WEBP support should define their own embedding mechanism or convert to PNG.
Source: V1 Specification
Issue: A default value for the user's name "MUST exist" but no specific default is mandated.
Recommendation: Common defaults include "User", "You", or "Anon".
Issue: Behavior when multiple entries have the same insertion_order is not specified.
Recommendation: Implementations SHOULD use a stable secondary sort (e.g., by array index).
Source: V3 Specification
Issue: The V3 spec defines **MAY** NOT with the adjective "forbidden" but states it means
"truly optional." This is contradictory.
Clarification: Based on context, **MAY** NOT should be interpreted as equivalent to **MAY**
(the item is optional to omit).
Source: V2 Specification
Issue: The {{original}} placeholder description for post_history_instructions incorrectly
references "system_prompt" in the replacement text.
Clarification: {{original}} in post_history_instructions should be replaced with the default
post-history instructions, not the system prompt.
Source: V3 Specification
Issue: The TypeScript interface shows group_only_greetings: Array<string> (required), but
behavioral text does not mandate a default.
Recommendation: Treat as required with default value [].
Source: V1 Specification
Issue: V1 refers to "Chara" EXIF metadata, but PNG uses tEXt chunks. The chunk keyword is
chara (lowercase in practice).
Clarification: Use lowercase chara for the tEXt chunk keyword.
Source: V3 Specification
Issue: When multiple entries with @@depth 0 and @@role assistant exist, they are
concatenated "in insertion_order," but it's unclear if this means ascending or descending.
Recommendation: Concatenate in ascending insertion_order (lower values first).
Source: V3 Specification
Issue: States secondary_keys "SHOULD be ignored" when use_regex is true, but
@@additional_keys works with regex. Inconsistent behavior.
Recommendation: Follow specification literally; use @@additional_keys for regex scenarios
requiring secondary conditions.
function detectVersion(data):
if data.spec == "chara_card_v3":
return "V3"
if data.spec == "chara_card_v2":
return "V2"
if data.name exists AND data.data does not exist:
return "V1"
return "Unknown"
To upgrade a V1 card to V2:
{
"spec": "chara_card_v2",
"spec_version": "2.0",
"data": {
"name": "<V1.name>",
"description": "<V1.description>",
"personality": "<V1.personality>",
"scenario": "<V1.scenario>",
"first_mes": "<V1.first_mes>",
"mes_example": "<V1.mes_example>",
"creator_notes": "",
"system_prompt": "",
"post_history_instructions": "",
"alternate_greetings": [],
"tags": [],
"creator": "",
"character_version": "",
"extensions": {}
}
}To upgrade a V2 card to V3:
- Copy all V2
datafields to V3data. - Set
specto"chara_card_v3". - Set
spec_versionto"3.0". - Rename
character_bookentries to conform to V3Lorebookstructure. - Add
use_regex: falseto all lorebook entries. - Add
group_only_greetings: [].
To downgrade a V3 card to V2:
- Copy compatible fields from V3
datato V2data. - Set
specto"chara_card_v2". - Set
spec_versionto"2.0". - Remove all decorators from lorebook entry content.
- Remove V3-only fields:
assets,nickname,creator_notes_multilingual,source,group_only_greetings,creation_date,modification_date. - Remove
use_regexfrom lorebook entries. - Convert lorebook entry
idfrom string to integer if necessary.
Warning: V3-to-V2 downgrade is lossy. Advanced features will be lost.
For future versions beyond V3:
- Applications SHOULD NOT reject cards with
spec_versiongreater than"3.0". - Applications SHOULD alert users that the card was created with a newer specification.
- Applications SHOULD fill missing fields with defaults.
- Applications SHOULD ignore unrecognized fields rather than failing.
- Applications MUST preserve unrecognized fields on export.
// ============================================================
// Primitive Types
// ============================================================
typedef String; // UTF-8 encoded string, variable length
typedef Integer; // Signed integer, minimum 32-bit
typedef Float; // IEEE 754 double precision (64-bit)
typedef Boolean; // true or false
typedef Any; // Any JSON-serializable value
// ============================================================
// Generic Types
// ============================================================
typedef Array<T>; // Dynamic array of type T
typedef Optional<T>; // T or absent
typedef Record<K, V>; // Map with key type K, value type V
typedef Literal<V>; // Exact string value V
typedef Union<A, B>; // Either A or B
typedef Enum { values... }; // One of enumerated string values
// ============================================================
// Character Card V1
// ============================================================
struct CharacterCardV1 {
String name;
String description;
String personality;
String scenario;
String first_mes;
String mes_example;
}
// ============================================================
// Character Card V2
// ============================================================
struct CharacterCardV2 {
Literal<"chara_card_v2"> spec;
Literal<"2.0"> spec_version;
CharacterCardV2Data data;
}
struct CharacterCardV2Data {
String name;
String description;
String personality;
String scenario;
String first_mes;
String mes_example;
String creator_notes;
String system_prompt;
String post_history_instructions;
Array<String> alternate_greetings;
Optional<CharacterBook> character_book;
Array<String> tags;
String creator;
String character_version;
Record<String, Any> extensions;
}
struct CharacterBook {
Optional<String> name;
Optional<String> description;
Optional<Integer> scan_depth;
Optional<Integer> token_budget;
Optional<Boolean> recursive_scanning;
Record<String, Any> extensions;
Array<CharacterBookEntry> entries;
}
struct CharacterBookEntry {
Array<String> keys;
String content;
Record<String, Any> extensions;
Boolean enabled;
Integer insertion_order;
Optional<Boolean> case_sensitive;
Optional<String> name;
Optional<Integer> priority;
Optional<Integer> id;
Optional<String> comment;
Optional<Boolean> selective;
Optional<Array<String>> secondary_keys;
Optional<Boolean> constant;
Optional<Enum { "before_char", "after_char" }> position;
}
// ============================================================
// Character Card V3
// ============================================================
struct CharacterCardV3 {
Literal<"chara_card_v3"> spec;
Literal<"3.0"> spec_version;
CharacterCardV3Data data;
}
struct CharacterCardV3Data {
String name;
String description;
String personality;
String scenario;
String first_mes;
String mes_example;
String creator_notes;
String system_prompt;
String post_history_instructions;
Array<String> alternate_greetings;
Array<String> tags;
String creator;
String character_version;
Record<String, Any> extensions;
Optional<Lorebook> character_book;
Optional<Array<Asset>> assets;
Optional<String> nickname;
Optional<Record<String, String>> creator_notes_multilingual;
Optional<Array<String>> source;
Array<String> group_only_greetings;
Optional<Integer> creation_date;
Optional<Integer> modification_date;
}
struct Asset {
String type;
String uri;
String name;
String ext;
}
struct Lorebook {
Optional<String> name;
Optional<String> description;
Optional<Integer> scan_depth;
Optional<Integer> token_budget;
Optional<Boolean> recursive_scanning;
Record<String, Any> extensions;
Array<LorebookEntry> entries;
}
struct LorebookEntry {
Array<String> keys;
String content;
Record<String, Any> extensions;
Boolean enabled;
Integer insertion_order;
Optional<Boolean> case_sensitive;
Boolean use_regex;
Optional<Boolean> constant;
Optional<String> name;
Optional<Integer> priority;
Optional<Union<Integer, String>> id;
Optional<String> comment;
Optional<Boolean> selective;
Optional<Array<String>> secondary_keys;
Optional<Enum { "before_char", "after_char" }> position;
}
struct LorebookExport {
Literal<"lorebook_v3"> spec;
Lorebook data;
}
// ============================================================
// Union Type for Multi-Version Support
// ============================================================
typedef CharacterCard = Union<CharacterCardV1, CharacterCardV2, CharacterCardV3>;| Decorator | Value Type | Description |
|---|---|---|
@@activate_only_after |
Integer | Match only after N messages |
@@activate_only_every |
Integer | Match only every N messages |
@@keep_activate_after_match |
— | Stay active after first match |
@@dont_activate_after_match |
— | Deactivate after first match |
@@activate |
— | Always match |
@@dont_activate |
— | Never match (unless @@activate present) |
@@depth |
Integer | Insert at message depth |
@@instruct_depth |
Integer | Insert at token depth |
@@reverse_depth |
Integer | Insert at reverse message depth |
@@reverse_instruct_depth |
Integer | Insert at reverse token depth |
@@position |
String | Insert at named position |
@@role |
String | Set message role |
@@scan_depth |
Integer | Override scan depth for this entry |
@@instruct_scan_depth |
Integer | Override scan depth (tokens) |
@@is_greeting |
Integer | Match only for specific greeting |
@@is_user_icon |
String | Match only for specific user icon |
@@ignore_on_max_context |
— | Skip when context is full |
@@additional_keys |
String,... | Require additional keys |
@@exclude_keys |
String |