AI NPC Framework

Description

AI NPC Brain is a Unity-focused system that turns NPCs into LLM-driven characters: they chat in character, remember facts about the player, track relationship stats (affinity, trust, fear, respect, flags), and can suggest game actions as structured events. Those suggestions are validated against a whitelist (your event catalog) before anything runs, so the model can’t arbitrarily trigger unknown gameplay.

It ships with pluggable backends (OpenAI, local Ollama, and a mock fallback for offline/UI testing), file-based persistence for conversation and memory, sample UI, demo scene, and editor tools (e.g. animation/catalog setup).

Technical Features

  • LLM-backed NPC dialogue — NPCs reply in character from a persona you define (ScriptableObject + JSON), with OpenAI, local Ollama, or an offline mock for testing.
  • Persistent memory — Stores facts about the player across sessions (file-based), with limits so prompts stay bounded.
  • Relationship model — Tracks affinity, trust, fear, respect, narrative flags, and stage; each turn can apply clamped deltas from the model.
  • Structured, safe game events — The model returns JSON; only catalog-whitelisted events can fire, with typed args, optional allowed values, and a confidence threshold.
  • Conversation history — Rolling turn history in the prompt (configurable depth) plus relevant memories retrieval.
  • World context — worldStateCompact on the brain injects free-form game state into prompts (time, location, inventory snippets, etc.).
  • Unity integration — Central AiNpcBrain component with UnityEvents (OnNpcReply, OnEmotionChanged, OnAnyNpcEventPayload, etc.) for UI, animation, and gameplay hooks.
  • Animation bridge — NpcAnimationMap maps semantic animation IDs to Animator triggers; NpcAnimationReceiver plays them from validated events.
  • UI workflow — Sample TMP chat UIs (simple and scroll-view), optional window IDs and WindowRegistryRuntime for opening shop/quest/relationship panels.
  • Editor tooling — Tools > AI NPC Brain (e.g. NPC Animation Setup) to sync Animator triggers with the animation map and event catalog.
  • Demo content — Demo scene, sample assets, and prefabs so buyers can press Play after wiring an API key or Ollama.
  • Unity 6 + URP oriented — Documented for Unity 6 and URP, with standard dependencies (Input System, Newtonsoft JSON, TextMesh Pro).

Documentation

Architecture Overview

Architecture Overview

High-Level Flow

Player types message
        │
        ▼
   AiNpcBrain.SendAsync()
        │
        ├─ 1. Append player turn to ConversationState
        ├─ 2. Query relevant memories from IMemoryStore
        ├─ 3. IPromptPolicy.BuildPrompt() → (system, developer, user)
        ├─ 4. INpcModelProvider.GenerateAsync() → raw LLM text
        ├─ 5. IResponseValidator.TryParseAndValidate() → NpcModelResponse
        ├─ 6. Apply relationship delta (clamped)
        ├─ 7. Store new memory facts (bounded)
        ├─ 8. Append NPC turn, persist to disk
        ├─ 9. FireEvents() → validate against EventCatalog → dispatch payloads
        └─ 10. Fire Unity events (OnNpcReply, OnEmotionChanged)
                    │
        ┌───────────┴───────────┐
        ▼                       ▼
  NpcAnimationReceiver    AureliaDemoReceiver
  (triggers Animator)     (opens/closes UI panels)

Core Components

AiNpcBrain (MonoBehaviour)

The central orchestrator. Lives on the NPC's GameObject. Accepts player text, drives the full pipeline, and emits results via UnityEvents.

References (assigned in Inspector): - PersonaDefinition — who the NPC is - NpcBrainConfig — how the brain behaves - NpcEventCatalog — what events are allowed - NpcAnimationMap — what animations are available (optional) - NpcUiBindings — canonical window IDs for this NPC (optional)

Internal dependencies (created in Awake): - IMemoryStoreJsonFileMemoryStore - IPromptPolicyDefaultPromptPolicy - IResponseValidatorDefaultResponseValidator - INpcModelProviderOpenAiChatProvider, OllamaProvider, MockProvider

Provider System

Three providers implement INpcModelProvider:

Provider Backend When Used
OpenAiChatProvider OpenAI /chat/completions Cloud LLM (default)
OllamaProvider Ollama /api/generate Local LLM
MockProvider Keyword matching Offline fallback

The brain picks a primary provider based on NpcBrainConfig.providerMode, then falls back to the other if the primary fails, and finally to MockProvider as a last resort.

Prompt System

DefaultPromptPolicy builds a three-part prompt:

Section Content
System Immutable rules: stay in character, return JSON only, PG-13 content
Developer NPC persona, relationship state, known facts, recent conversation, world state, event catalog, animation guidance, output JSON schema
User Player's utterance + metadata (player ID, location, time of day)

Response Validation

DefaultResponseValidator parses the raw LLM output: 1. Strips optional code fences (```json ... ```) 2. Deserializes via JsonUtility.FromJson<NpcModelResponse>() 3. Validates required fields (reply_text must exist) 4. Clamps/sanitizes values (emotion tag whitelist, text length cap, importance 0–1)

Event System

The LLM suggests events as part of its JSON response. Before any event fires:

  1. Confidence gate — event must meet fireConfidenceThreshold
  2. NormalizationAiNpcBrain.NormalizeEventSuggestion() fills in NPC-specific IDs (shop_id, quest_board_id, etc.)
  3. EventGate validation — checks that the event exists in the catalog, all required args are present, types match, values are in the allowed list, and no extra args are included
  4. Dispatch — validated JSON payload is fired via OnAnyNpcEventPayload UnityEvent

Memory System

JsonFileMemoryStore persists per-NPC, per-player state to disk:

{persistentDataPath}/AiNpcBrain/{playerId}/{npcId}.json

Each file contains: - RelationshipState — affinity, trust, fear, respect, flags, stage - List<Turn> — rolling conversation history (max 30 turns) - List<MemoryFact> — long-lived facts (max 50)


Data Flow Diagram

ScriptableObject Assets (design-time)
┌─────────────────┐  ┌──────────────┐  ┌────────────────┐  ┌────────────────┐
│PersonaDefinition│  │NpcBrainConfig│  │NpcEventCatalog │  │NpcAnimationMap │
└────────┬────────┘  └──────┬───────┘  └───────┬────────┘  └───────┬────────┘
         │                  │                   │                   │
         └──────────────────┴─────────┬─────────┴───────────────────┘
                                      │
                                      ▼
                               ┌─────────────┐
                               │ AiNpcBrain  │ (MonoBehaviour on NPC)
                               └──────┬──────┘
                                      │
                    ┌─────────────────┼─────────────────┐
                    ▼                 ▼                  ▼
            ┌──────────────┐  ┌─────────────┐   ┌──────────────┐
            │PromptPolicy  │  │MemoryStore  │   │ ModelProvider│
            │(builds prompt)│  │(load/save)  │   │(calls LLM)  │
            └──────────────┘  └─────────────┘   └──────────────┘

Runtime State (per player)
┌──────────────────────────────────────────────────┐
│ ConversationState                                │
│  ├─ RelationshipState (affinity, trust, ...)     │
│  ├─ List<Turn> (conversation history)            │
│  └─ List<MemoryFact> (learned facts)             │
│                                                  │
│  Persisted to: {persistentDataPath}/AiNpcBrain/  │
└──────────────────────────────────────────────────┘

Namespace Structure

Namespace Location Contents
AiNpcBrain Runtime/, Scripts/ All runtime classes, interfaces, and DTOs
AiNpcBrain.Editor Editor/ Editor windows, custom inspectors, sync utilities
(global) Scripts/DemoCursorUnlock.cs Demo utility (no namespace)

Folder Structure

Assets/
├── Documentation/          ← You are here
├── Editor/                 ← Editor-only scripts (custom inspectors, tools)
├── Runtime/                ← Core library
│   ├── Core/               ← AiNpcBrain, DTOs, interfaces, ScriptableObjects
│   ├── Dialogue/           ← Prompt building, response validation
│   ├── Memory/             ← JSON file persistence
│   ├── Npc/                ← NPC UI bindings
│   ├── Providers/          ← LLM provider implementations
│   ├── Samples/            ← Sample chat UI controllers
│   └── UI/                 ← WindowPanelId component
├── Scripts/                ← Demo-specific scripts (animation, receivers)
├── Samples/                ← Pre-built ScriptableObject assets for Aurelia demo
├── Prefabs/UI/             ← Chat message prefab
├── Scenes/                 ← Demo scene
├── Mixamo Character/       ← Character model + animations
└── Settings/               ← URP rendering settings

Extension Points

The system uses interfaces for its core strategies. You can replace any of them:

Interface Default Implementation Purpose
INpcModelProvider OpenAiChatProvider, OllamaProvider, MockProvider Add a new LLM backend (Anthropic, Gemini, etc.)
IPromptPolicy DefaultPromptPolicy Change how prompts are assembled
IResponseValidator DefaultResponseValidator Change how LLM output is parsed/validated
IMemoryStore JsonFileMemoryStore Switch to a database, cloud storage, etc.

To use a custom implementation, modify AiNpcBrain.Awake() to instantiate your class instead of the default.