AI Content Generation (Tier 2)¶
MAID's Tier-2 AI pipeline turns short builder prompts into validated game content — rooms, NPCs, items, monsters, quests and lore — emitted as YAML that drops cleanly into a content pack.
This guide covers everything from first-run setup to in-game generation, content-pack template overrides, quality review, and offline workflows.
Overview¶
Tier 2 is built around three layers:
- Generation engine (
maid_engine.ai.generation.engine) — orchestrates schema lookup, prompt assembly, provider call, validation, retry, and YAML serialization. - Prompt assembler & templates — content-type-specific prompt builders
in
maid_engine.ai.generation.templatesplus the basePromptAssembler. - Quality & consistency checks — rule-based quality scoring
(
QualityChecker) and cross-entity lore consistency (LoreConsistencyChecker) that run without an LLM round-trip.
Generation can be invoked three ways:
- CLI:
maid ai generate ...(best for scripted/batch work) - In-game:
@ai.generate ...(best for builders during play-testing) - Programmatic:
ContentGenerationEngine.generate(GenerationRequest(...))
Prerequisites¶
- Install MAID with provider extras as needed:
- Configure a provider via environment variables:
- (Optional) Set generation defaults — see Configuration reference.
If no provider is configured you can still produce skeleton content (see Working offline).
Quick start¶
# Generate a single room and print the YAML
uv run maid ai generate room "Mossy Hollow"
# Use a style preset and zone
uv run maid ai generate npc "Old Bob" --style pastoral --zone fishing_village
# Pipe to a file (or use --output)
uv run maid ai generate item "Lantern of Whispers" \
--output content/items/lantern.yaml
CLI commands¶
All AI commands live under maid ai:
| Command | Purpose |
|---|---|
maid ai generate <type> <name> |
Generate one piece of content (multi-doc YAML for area/dungeon). |
maid ai describe <entity-file> |
Generate or improve a description for an existing entity YAML file. |
maid ai review <file-or-dir> |
LLM-powered review (quality / balance / consistency focus). |
maid ai populate <area-file> |
Add NPCs/items to an existing area as a multi-doc YAML. |
maid ai connect <area1> <area2> |
Emit an exits patch connecting two areas. |
maid ai balance <file-or-dir> |
Report counts and level distribution for combat content. |
maid ai schema <type> |
Print the expected schema for a content type. |
maid ai estimate <type> <name> |
Estimate token cost without calling a provider. |
maid ai validate-output <file> |
Validate generated YAML via the engine's ContentValidator. |
maid ai cache <stats\|clear> |
Inspect or clear the response cache. |
maid ai generate¶
maid ai generate <content_type> <name> [options]
Arguments:
content_type One of: room, npc, item, monster, quest, lore, area, dungeon.
name Name/title for the generated content.
Options:
-o, --output PATH Output file path (default: stdout).
-p, --provider TEXT LLM provider name (mock/anthropic/openai/ollama/gemini/chatjimmy).
-m, --model TEXT Model override.
--temperature FLOAT Temperature (0.0–2.0).
--style TEXT epic | gritty | whimsical | horror | pastoral.
--zone TEXT Zone name for grounding.
--level-range TEXT Level range, e.g. '5-10'.
--tags TEXT Comma-separated tags.
--theme TEXT Thematic guidance.
--context PATH Additional context file (repeatable).
--world-context / --no-world-context Inject existing world state (default: on).
--few-shot / --no-few-shot Include few-shot examples (default: on).
--validate / --no-validate Run schema validation (default: on).
--interactive / --no-interactive Interactive accept/regen review.
--max-tokens INT Max response tokens.
--retries INT Max retry/repair attempts (default: 2).
--seed INT Random seed for reproducibility.
--dry-run Print the assembled prompt instead of calling the LLM.
--format TEXT yaml | json (default: yaml).
--pack TEXT Content-pack context (loads pack template overrides).
Supported content_type values: room, npc, item, monster, quest,
lore, area, dungeon. The area and dungeon types are routed to
the dedicated multi-entity generators (AreaGenerator / DungeonGenerator)
and emit multi-document YAML covering the area room, its NPCs, items and
(for dungeons) the boss + loot. When validation fails after all retries,
the CLI exits with a non-zero status code and prints the offending
issues — suitable for use in CI pipelines.
Style presets¶
| Preset | Tone | Vocabulary |
|---|---|---|
epic |
heroic, grand, mythic | elevated, archaic flourishes |
gritty |
harsh, grounded, ambiguous | blunt, sensory, lived-in |
whimsical |
playful, light, mischievous | bouncy, alliterative |
horror |
dread-soaked, uncanny | visceral, hushed, foreboding |
pastoral |
warm, peaceful, reflective | natural, unhurried |
Content packs can override or extend these — see Content pack integration.
Interactive review mode¶
Interactive mode prints the generated YAML and prompts you to accept
(y/yes writes to --output if set, otherwise prints the YAML) or
discard (anything else, including the default n). It does not
run the QualityChecker automatically and does not offer a regenerate
loop — re-run the command for a fresh generation. Use maid ai review
afterwards if you want a quality pass.
Cost estimation and budgets¶
# Estimate tokens/cost for a single generation without calling the provider
uv run maid ai estimate room "Forge Hall" --provider anthropic
# Enforce a daily token cap (engine-wide)
export MAID_AI_GENERATION_BUDGET_DAILY_TOKENS=200000
The engine refuses to call the provider once the configured budget is exhausted; CI pipelines should set tight budgets to fail closed.
Response cache¶
ContentGenerationEngine deduplicates identical prompts via a
filesystem-backed cache. Inspect or clear it with:
uv run maid ai cache stats # show entries, size, hit/miss counts
uv run maid ai cache clear # remove every cached entry
Only stats and clear are supported. The CLI's --cache-dir option
defaults to ~/.maid/ai-cache; the engine itself reads
MAID_AI_GENERATION_CACHE_DIR (default .maid/ai_cache, project-relative)
when constructed via settings, so pass --cache-dir to the CLI when you
need it to inspect a non-default cache location.
Working offline¶
When no provider is configured, the CLI falls back to a built-in
MockProvider seeded with deterministic skeleton output produced by
SkeletonGenerator. The CLI prints a warning and proceeds with the
skeleton so scripts and CI can still produce valid YAML.
from maid_engine.ai.generation.skeleton import SkeletonGenerator
gen = SkeletonGenerator()
print(gen.generate_yaml("room", "Empty Cell"))
Skeleton output is valid against ContentSchema with the correct
_id, _meta and required components (DescriptionComponent with
name, short_desc, long_desc for all entity types; HealthComponent
additionally for npc/monster), so downstream loaders treat it
identically to AI output (just with TODO: placeholders to fill in).
To force the mock path explicitly use --provider mock.
In-game @ai commands¶
When the engine is running with a configured provider, builders can invoke generation interactively. All commands require BUILDER access level.
| Command | Description |
|---|---|
@ai.generate <type> <name> [description] |
Generate content; result is sent to your session as YAML. |
@ai.describe <target> |
Generate (or regenerate) a description for an existing entity. |
@ai.populate [room\|area] |
Populate the current room/area with a small batch (NPCs + items, plus a monster for area). |
@ai.review [target] |
Run the quality checker against a target (or current room). |
Examples:
> @ai.generate room "Sunken Atrium" "marble columns half-flooded"
[YAML follows]
> @ai.describe here
[generated description YAML]
> @ai.populate area
[YAML for an NPC, an item, and a monster]
> @ai.review here
Quality score: 0.84
[warning] (variety) description: Word 'water' is repeated 5 times.
Suggestions:
- Vary vocabulary — replace repeated words with synonyms.
Output is never auto-applied to world state — copy the YAML into a
content pack and load it normally with maid data load. This keeps an
audit trail and avoids accidental persistence.
If no provider is configured, in-game commands fall back to skeleton
output and prefix the response with [no AI provider configured —
emitting skeleton].
Content pack integration¶
Content packs can customize prompts via the PromptTemplateProvider protocol:
from maid_engine.ai.generation.templates.pack_override import (
PackTemplateResolver,
PromptTemplateProvider,
)
class MyPackPrompts:
def get_system_identity(self) -> str | None:
return "You are a content designer for the Ironpeak setting..."
def get_style_overrides(self) -> dict[str, str] | None:
return {"epic": "Style: EPIC IRONPEAK.\nTone: industrial heroism..."}
def get_few_shot_examples(self, content_type: str):
if content_type == "npc":
return [{"user": "Smith Korr", "assistant": "..."}]
return None
def get_world_context_extra(self) -> str | None:
return "Note: Ironpeak's gods are silent; magic comes from forgework."
Register on pack load:
async def on_load(self, engine):
resolver: PackTemplateResolver = engine.world.custom_data.setdefault(
"ai:template_resolver", PackTemplateResolver()
)
resolver.register_provider(MyPackPrompts())
Resolution rules:
- System identity / world-context extra — first non-
Nonevalue wins (provider order is registration order). - Style presets — engine defaults are merged with all provider overrides; later providers win on key conflicts.
- Few-shot examples — first provider returning a non-
Nonelist wins.
Quality review¶
QualityChecker runs five categories of rule-based checks:
| Category | What it catches |
|---|---|
description |
Missing/short/long descriptions, placeholder text. |
naming |
Missing or placeholder names; unusually short/long names. |
completeness |
Missing required fields per content type. |
variety |
Words repeated above a threshold in descriptions. |
balance |
NPC/monster health invariants (current ≤ maximum, maximum > 0). |
from maid_engine.ai.generation.quality import QualityChecker
report = QualityChecker().check(data, "npc")
print(report.score) # 0.0–1.0
for issue in report.issues:
print(issue.severity, issue.category, issue.message)
Severity penalties: info=0.02, warning=0.08, error=0.20.
Lore consistency¶
LoreConsistencyChecker operates across a set of entities and reports
issues that only emerge when entities are viewed together:
- Name conflicts — two entities sharing a name (case-insensitive).
- Dangling references —
*_id/*_ref/destination/targetfields pointing at unknown entities. - Level mismatches — NPCs/monsters in the same zone with a level spread > 10.
- Zone mismatches — quests whose declared zone differs from the zones of the entities they reference.
from maid_engine.ai.generation.lore_check import LoreConsistencyChecker
checker = LoreConsistencyChecker()
for entity_id, data in entities.items():
checker.add_entity(entity_id, data)
for issue in checker.check():
print(issue.severity, issue.entity_id, issue.field, issue.issue)
Run this after every batch generation as part of CI to catch worldbuilding drift before content lands in the live game.
Configuration reference¶
All Tier-2 settings live under the MAID_AI_GENERATION_ prefix
(see maid_engine.config.settings.AIGenerationSettings):
| Variable | Default | Meaning |
|---|---|---|
MAID_AI_GENERATION_DEFAULT_STYLE |
epic |
Style preset when none specified. |
MAID_AI_GENERATION_MAX_RETRIES |
2 |
Validation-failure repair attempts. |
MAID_AI_GENERATION_DEFAULT_TEMPERATURE |
0.7 |
Provider temperature. |
MAID_AI_GENERATION_DEFAULT_MAX_TOKENS |
2000 |
Output token cap. |
MAID_AI_GENERATION_ENABLE_WORLD_CONTEXT |
true |
Inject world context block. |
MAID_AI_GENERATION_ENABLE_FEW_SHOT |
true |
Include few-shot examples. |
MAID_AI_GENERATION_CACHE_ENABLED |
true |
Cache provider responses. |
MAID_AI_GENERATION_CACHE_DIR |
.maid/ai_cache |
Cache directory. |
MAID_AI_GENERATION_CACHE_TTL_HOURS |
24 |
Cache entry lifetime. |
MAID_AI_GENERATION_BUDGET_DAILY_TOKENS |
unset | Daily generation token cap. |
MAID_AI_GENERATION_BUDGET_MONTHLY_TOKENS |
unset | Monthly token cap. |
MAID_AI_GENERATION_DEFAULT_PROVIDER |
unset | Override MAID_AI_DEFAULT_PROVIDER for generation. |
MAID_AI_GENERATION_DEFAULT_MODEL |
unset | Force a specific model. |
Provider-level settings (MAID_AI_ANTHROPIC_API_KEY, etc.) are documented
separately in AI Configuration Reference.
See also¶
- AI Configuration Reference — provider-level setup.
- AI Provider Testing — testing with mock and real providers.
- Building Commands Reference — full reference
for builder commands, including
@ai.*. - NPC Dialogue Guide — Tier-1 (live) AI integration.