back to projects
2025-01-06 in-development

Fabula

A constraint-satisfaction system for procedural story generation

#prolog#story-generation#narrative-ai#metainterpreter
→ GitHub

What is Fabula?

Fabula is a bookkeeping system for stories. It tracks the state of your narrative—characters, locations, events, what each character knows, what the reader knows—and tells you when something doesn't add up.

Write a scene where two characters have a conversation? Fabula checks: are they in the same place? Do they know each other? Introduce a mysterious letter? Fabula remembers: that letter needs to be opened eventually, or you've got a loose thread.

The Human-AI-Algorithm Triad

Fabula acts as the "algorithm/bookkeeper" in a collaborative storytelling system:

Role Responsibility
Human Creative direction, high-level plot decisions, aesthetic judgment
AI (LLM) Prose generation, dialogue, scene details, creative suggestions
Fabula State tracking, constraint validation, implication surfacing

The AI generates, the human guides, and Fabula ensures nothing breaks.

Why Prolog?

Stories are fundamentally about relationships and rules:

  • If character A knows a secret, and A tells B, then B knows the secret
  • If the gun appears in Act 1, it must fire by Act 3 (Chekhov's constraint)
  • If two characters are enemies, their dialogue should reflect tension

Prolog's logic programming paradigm naturally expresses these constraints. Fabula uses a metainterpreter pattern—Prolog reasoning about Prolog—to:

  1. Track narrative state as facts
  2. Express story rules as logical implications
  3. Query for violations and loose threads
  4. Surface consequences of plot decisions

Core Concepts

Story State

% Characters exist in locations
at(alice, library, chapter_1).
at(bob, garden, chapter_1).

% Characters possess items
has(alice, mysterious_letter).

% Knowledge tracking
knows(alice, secret_password).
knows_about(reader, mysterious_letter).  % Reader has seen this

Narrative Operations

% Moving a character
move(Character, From, To, Chapter) :-
    at(Character, From, Chapter),
    retract(at(Character, From, Chapter)),
    assert(at(Character, To, Chapter)).

% Conversation requires co-location
can_converse(A, B, Chapter) :-
    at(A, Location, Chapter),
    at(B, Location, Chapter),
    A \= B.

Constraint Validation

% Chekhov's Gun: introduced items must be used
loose_thread(Item) :-
    introduced(Item, Chapter),
    \+ used(Item, _),
    current_chapter(Current),
    Current > Chapter + 2.

% Knowledge consistency
plot_hole(Character, Info) :-
    uses_knowledge(Character, Info, Scene),
    \+ knows(Character, Info, Scene).

Workflow

  1. Author writes scene (or AI generates draft)
  2. Scene is parsed into narrative operations
  3. Fabula validates operations against current state
  4. Violations surfaced → author fixes or acknowledges
  5. State updated → ready for next scene

Current Status

Fabula is in active development. Current focus:

  • Core metainterpreter for state tracking
  • Basic constraint library (co-location, knowledge, item tracking)
  • Integration protocol for LLM-generated scenes

Related Research

This project emerged from experiments on procedural story generation, specifically investigating how constraints affect LLM creativity. See:

$ exploring absorption points, seeding strategies, and creative constraints in language models