Story2BoardStory2Board
cases

How MAGNET Keeps Long Stories Coherent with Character Agents, World State, and Dynamic Goals

Story2Board Team··14 min read
How MAGNET Keeps Long Stories Coherent with Character Agents, World State, and Dynamic Goals

Long stories usually do not fail because a model cannot write a decent sentence. They fail because the story forgets itself. A character repeats the same move, a goal drifts, a room changes shape, and by page 30 the text still sounds fluent even though the narrative has lost its spine.

That is the problem MAGNET is trying to solve.

If you want the shortest version, here it is: MAGNET keeps long stories coherent by splitting generation into separate roles, forcing each role to look at the same world state, and updating that world state after the narrator picks the actions that actually belong in the story. The paper also adds a second layer, Atlas, which checks long-form stories for hallucinations by turning them into graphs and comparing scenes against each other.

We read the paper and the code bundle side by side. The interesting part is not that the system uses multiple agents. A lot of papers say that. The interesting part is that MAGNET makes story state explicit, and then uses that state to control both narration and pacing.

Why long-form stories drift

Single-shot prompting works fine when the task is short. Ask a model for a paragraph, a scene summary, or a one-page beat sheet, and it usually stays inside the rails.

Long-form narrative is different. The model has to keep track of at least four things at once:

  • who each character is
  • what each character wants right now
  • what has already happened in the scene
  • how the larger story should move next

If any one of those gets fuzzy, the whole story starts to wobble. The characters become generic. The plot repeats. A promise made earlier disappears without a clean payoff.

MAGNET attacks that by not asking one model to do all of it in one pass.

The MAGNET loop

The core generation loop is simple enough to fit in one diagram.

Rendering diagram…

That loop matters because each step has a different job.

The character agent is not trying to write prose. It proposes an action, something concrete and observable.

The critic is not trying to improve style. It checks whether the action is specific, plausible, in character, and useful for the current goal.

The narrator is not trying to invent a new plot. It chooses which actions become canonical story events and turns them into one paragraph.

And the environment is not a passive notebook. It commits the selected state changes so the next step sees a story that actually remembers what happened.

In the repo, that logic shows up in pipeline.py, agents.py, and environment.py. The code is not fancy. That is the point. The system works because the roles are narrow.

for step in range(max_steps):
    proposals = each_character_proposes_action(goal, world_state)
    accepted = critic_filters(proposals, world_state)
    paragraph = narrator_writes_scene(accepted, world_state)
    world_state.commit(accepted.world_updates)

    if goal_completed_or_stale:
        goal = narrator_generates_next_goal(world_state)

That is the whole thing at a high level. The rest of the paper is about making each box useful enough to hold up in long stories.

World state is the memory that matters

The key design choice in MAGNET is that story state is not just hidden in a prompt. It lives in a graph.

In the code bundle, environment.py stores the world in a networkx.DiGraph with a root node, character nodes, and one node per state key. That means the system can carry forward things like current_goal, goal_history, and other state variables without relying on the model to remember them from text alone.

Rendering diagram…

This is the part that makes the system feel more like a story machine than a prompt chain.

If the state only exists in text, it becomes fragile. The next model call has to reconstruct the story from prose, and that is where detail starts leaking out. If the state is explicit, the narrator and critic can read from the same source of truth every time.

That design shows up clearly in environment.py:

  • reset() initializes the world graph
  • get_world_vars() turns the graph back into a flat dictionary for prompts
  • step_selected_actions() commits only the actions the narrator selected
  • protected keys like current_goal and goal_history do not get overwritten by random updates

That last bit matters more than it sounds. It keeps the story from mutating its own spine.

Dynamic goals keep the story moving

The paper does not let the story sit on one goal forever.

There are two pacing controls baked into the loop:

  • if a goal has not been completed for about 15 steps, it gets refreshed
  • after about 40 steps, the system forces a shift into a new domain

That second rule is easy to miss, but it is the thing that keeps a long story from looping in place. A character can only circle the same conflict for so long before the reader can feel the repeat.

Rendering diagram…

In the repo, this logic lives in pipeline.py, where stale goals and arc shifts are handled in separate helper methods. That is a good sign. It means the pacing policy is not buried in a prompt. It is part of the runtime.

Why does that matter?

Because long-form narrative needs rhythm. Not just continuity. Rhythm.

You want a story to keep moving without feeling like it is changing the subject every two paragraphs. A goal refresh lets the story adapt when it stalls. A domain shift lets it open a new chapter when the current conflict has paid off.

How MAGNET evaluates its own output

The paper evaluates stories in two different ways.

The first is editorial and rubric-based evaluation. The paper uses an LLM editor to annotate story-level, chapter-level, and sentence-level issues, then uses pairwise rubric scoring to compare systems.

The second is Atlas, which is a graph-based hallucination detector for long-form stories. Atlas is not just another judge prompt. It builds scene graphs, extracts entities and relations, and checks inconsistencies between the current scene and earlier scenes.

Rendering diagram…

The headline numbers are the ones worth remembering:

  • at 100 pages, MAGNET reduced editorial annotations by 41 compared with the single-model baseline
  • at 100 pages, hallucinations dropped by 50 percent compared with the single-model baseline
  • compared with IBSEN, the reductions were 34 editorial annotations and 45 percent hallucinations
  • at 20 pages, all three systems had zero hallucinations, but MAGNET still scored better in the rubric and editorial layers

The DPO stage is also worth noting. The paper uses 1,012 training preference examples and 53 evaluation examples to improve the character action model.

That is not a huge dataset. It does not need to be. The important part is that the preference data is targeted at one narrow job, choosing better character actions.

What the code actually implements

One thing we checked carefully: the repository does not expose an Atlas implementation.

That is not a criticism. It is just the boundary between the paper and the code bundle. The repo is strong on generation and action quality, and lighter on the Atlas evaluation side.

Here is the clean split:

  • run_pipeline.py wires the models together, runs the story, and exports the final world graph
  • pipeline.py runs the main generation loop and handles stale goals and arc shifts
  • agents.py contains CharacterAgent and NarratorAgent
  • environment.py owns the networkx.DiGraph world state
  • memory.py adds optional retrieval-backed memory
  • generate_dpo_dataset.py builds preference data from rollouts
  • train_dpo.py trains the LoRA DPO adapter
  • rubric_eval_pipeline.py covers editorial and rubric-style evaluation
  • eval_pipeline.py provides another evaluation entry point

That makes the repo useful in a product sense. It gives you a generation system, a preference-data path, and a way to score output. It does not give you a full Atlas clone, so do not write that into a blog post unless you are describing the paper, not the repo.

What Story2Board should borrow from this paper

We should not copy MAGNET because it is fashionable to say "multi-agent". We should copy the parts that make long stories less fragile.

The first thing to borrow is the split between local action and global narration. A character-level decision model and a story-level integrator are not the same thing. Treating them as the same thing is how you get flat, generic output.

The second thing to borrow is explicit state. Story2Board already thinks in terms of scene continuity and shot relationships. A state graph gives us a place to put that structure so it can be inspected, updated, and scored.

The third thing to borrow is pacing control. A long storyboard or long narrative needs a policy for when to refresh a stuck goal and when to move the arc into a new domain. That is exactly the kind of thing a product can expose to users in a controlled way.

The fourth thing is evaluation. The paper does not just say the stories are better. It measures why they are better. That is the kind of loop we want if we are serious about improving output instead of just generating more of it.

If you want a closer Story2Board parallel, the best companion reads are probably our posts on training-free storyboard consistency, narrative purpose, and vector search that actually helps an agent.

Limitations, because the paper has them

The paper is good, but it is not magic.

  • it is expensive, because multiple model calls happen per timestep
  • it depends on closed-source models in the main setup
  • the judge layers can still carry bias
  • Atlas depends on enough story detail to build a meaningful graph
  • the evaluation is strong, but it is still limited to the stories and lengths the paper tested

And there is one more limitation that matters for readers of this blog: the paper and the repository are not the same thing. The paper gives us MAGNET plus Atlas. The repo gives us MAGNET-style generation, DPO data, and rubric evaluation. That distinction is worth being honest about.

The short answer for product people

If you only care about the product takeaway, it is this:

Long-form stories get better when the system stops pretending that one prompt can carry everything.

MAGNET works because it separates the jobs, stores state outside the prompt, and refreshes the story when it starts to stall. That is a useful blueprint for Story2Board, even if we do not copy the exact architecture.

FAQ

Is MAGNET just a bigger prompt chain?

No. Prompt chains still rely on one model thread to remember too much. MAGNET splits the work into action, critique, narration, and state commit, then uses the world graph as shared memory.

Why does the world state need to be a graph?

Because the system is not only tracking text. It is tracking characters, goals, relations, and state changes over time. A graph makes those connections explicit and queryable.

What does the dynamic goal refresh actually do?

It keeps the story from getting stuck. If the current goal has stalled for too long, MAGNET replaces it with a feasible next goal. If the story runs long enough, it forces a domain shift so the arc does not repeat itself forever.

Is Atlas implemented in the repo you inspected?

No, not in the code bundle we reviewed. The paper describes Atlas, but the repository content we inspected focuses on generation, preference data, and rubric-style evaluation.

References

Aluru, A., Ho, C., Hammouri, M., Luo, K., Malik, M., Lagasse, R., Bahuguna, A., & Sharma, V. (2026). From personas to plot: Character-grounded multi-agent story generation for long-form narratives. arXiv. https://arxiv.org/abs/2607.00918

Han, S., Chen, L., Lin, L.-M., Xu, Z., & Yu, K. (2024). IBSEN: Director-actor agent collaboration for controllable and interactive drama script generation. arXiv. https://arxiv.org/abs/2407.01093

Related Posts

Ready to create your storyboard?

Turn your ideas into professional storyboards with Story2Board — the intelligent director assistant.

Try Story2Board Free