Skip to content

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:

  1. Generation engine (maid_engine.ai.generation.engine) — orchestrates schema lookup, prompt assembly, provider call, validation, retry, and YAML serialization.
  2. Prompt assembler & templates — content-type-specific prompt builders in maid_engine.ai.generation.templates plus the base PromptAssembler.
  3. 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

  1. Install MAID with provider extras as needed:
    uv sync --extra openai     # or --extra ollama, --extra gemini, etc.
    
  2. Configure a provider via environment variables:
    MAID_AI_DEFAULT_PROVIDER=anthropic
    MAID_AI_ANTHROPIC_API_KEY=sk-ant-...
    
  3. (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

maid ai generate room "Crypt Entrance" --interactive

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-None value 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-None list 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 / target fields 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