Building a Multi-Agent Ghost Story: How Kiro’s Hybrid Development Changed Everything

Published: (December 5, 2025 at 12:54 PM EST)
5 min read
Source: Dev.to

Source: Dev.to

Overview

I built a gothic ghost story with five independent AI agents that debate in real‑time using Kiro IDE. Each agent was created with a different development approach—vibe coding, spec‑driven, and steering docs—yet they function together as a coherent family. This “Frankenstein” magic stitches incompatible paradigms into something unexpectedly powerful.

Tech Stack: Next.js, Groq (llama‑3.3‑70b), Azure TTS, Kiro IDE

Live Demo:

Source Code:

How to try: Click “Click to begin” and watch the five AI personalities debate your choices.

“The agents genuinely disagree, creating emergent storytelling that’s never the same twice.”


The Challenge

When building a game with five AI agents that need to:

  • Maintain distinct personalities
  • Debate with each other in real‑time
  • Stay consistent across 50+ generations
  • Create authentic conflict (not forced agreement)

traditional AI development quickly becomes a nightmare of prompt engineering, context management, and constant tweaking.


Kiro IDE’s Solution

Kiro IDE lets you combine multiple development paradigms in a single project and automatically stitches them together. Below is a breakdown of how each ghost character was built.

Vibe‑Coding (Emotional Character)

Conversation with Kiro

Me: I need a maternal ghost character
Kiro: What's her role?
Me: She's the mother. Gentle, prioritizes family harmony
Kiro: [Generates initial personality]
Me: More poetic. Less formal. Under 30 words per response.
Kiro: [Refines to final version]

Result: A fully‑formed character with emotional depth in about five minutes.

Why it works

  • Fast iteration on “feeling”
  • Hard to specify “maternal warmth” formally
  • Natural language captures nuance better than technical specs

Spec‑Driven (Logical Character)

const harlan = {
  name: 'Harlan',
  personality: 'scientific, amnesiac, logical but confused',
  systemPrompt: `You are Dr. Harlan Voss, the scientist ghost 
  with fragmented memories. You analyze problems logically but 
  struggle to remember emotional context. When debating, you 
  cite facts but defer to family on emotional matters. Keep 
  responses under 30 words. Use technical language mixed with 
  uncertainty.`
};

Why it works

  • Rock‑solid consistency
  • Debuggable (“Line 47 violates spec”)
  • Ideal for logical, technical characters

Steering Docs (Rule‑Based Character)

File: .kiro/steering/ghost-agent-rules.md

## Inter‑Agent Debate Protocol

- Each agent MUST respond independently
- Agents can disagree – conflict is good for drama
- Mira often sides with emotional choices
- Harlan provides logical analysis but defers to family
- Selene demands honesty, Theo seeks forgiveness

Why it works

  • Prevents personality mix‑ups across many generations
  • Defines relationships between agents, not just individual traits
  • Generates authentic conflict instead of forced agreement

Model Context Protocol (MCP) Server

A custom MCP server acts as a blockchain‑style ledger for eternal vows.

File: app/api/mcp/vows/route.ts

// Simple in‑memory vow ledger (simulates blockchain MCP)
const vows = new Map([
  ['theo-marry-selene', {
    person: 'Theo',
    vow: 'Marry Selene',
    kept: false,
    timestamp: '2039-01-15'
  }],
  ['theo-return', {
    person: 'Theo',
    vow: 'Return to make amends',
    kept: true,
    timestamp: '2039-06-20'
  }],
]);

export async function POST(req: NextRequest) {
  const { action, person, vow } = await req.json();

  if (action === 'check') {
    const key = `${person.toLowerCase()}-${vow.toLowerCase().replace(/\s+/g, '-')}`;
    const record = vows.get(key);

    if (record) {
      return NextResponse.json({
        found: true,
        ...record,
        message: record.kept
          ? `✓ Vow kept: ${record.person} did ${record.vow} on ${record.timestamp}`
          : `✗ Vow broken: ${record.person} failed to ${record.vow}`
      });
    }

    return NextResponse.json({
      found: false,
      message: 'No record of this vow in the ledger'
    });
  }
}

In‑Game Integration

File: HallwayScene.tsx

const handleCheckVow = async () => {
  try {
    const response = await fetch('/api/mcp/vows', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        action: 'check',
        person: 'Theo',
        vow: 'Return'
      })
    });

    const result = await response.json();
    setVowResult(result.message);

    // Selene speaks the eternal record
    speechService.speak(result.message, 'selene');
  } catch (error) {
    console.error('Failed to check vow:', error);
  }
};

Player Experience

When players click “Check Theo’s Vow” in the Hallway scene, the API checks the ledger. Because the request uses vow: 'Return' while the ledger stores 'Return to make amends', the response is:

No record of this vow in the ledger

Why MCP fits

  • Development‑time testing directly in Kiro IDE
  • Separation of concerns: vow logic lives on a dedicated server
  • Easy extensibility for more vows or characters
  • Adds narrative depth through interactive verification

Real‑Time Debate Flow

When the player asks for a hint, all five agents are invoked in parallel:

// Invoke all 5 agents in parallel
const debatePromises = Object.keys(GHOST_AGENTS).map(async (ghostName) => {
  const response = await invokeGhostAgent(ghostName, context, apiKey);
  return { ghost: ghostName, message: response };
});

const debate = await Promise.all(debatePromises);

Example Debate

Player: “I need help with the tapestry puzzle”

  • Elara: “Focus on love and emotional memories, dear one.”
  • Harlan: “I… categories. Logic. But family transcends data.”
  • Mira: “The happy ones! When we played together!”
  • Theo: “Your family moments define you, brother.”
  • Selene: “Truth matters. Match honestly, not hopefully.”

Consensus (Elara): “Look for the emotional connections in each photo—love, joy, and family bonds will guide you.”

Because the agents genuinely disagree, each playthrough yields a unique narrative.


Development Efficiency

TaskTraditional ApproachWith Kiro IDE
Create character personality2 hours (multiple prompt iterations)~5 minutes (natural conversation)
Maintain context across generationsFrequent manual prompt engineeringAutomatic context preservation
Resolve conflicts between agentsManual stitching of promptsSteering docs act as “stitches”

Result: Five distinct AI personalities built in under an hour instead of 10+ hours.


The Frankenstein Metaphor

Each agent was built using a different method:

  • 🎨 Vibe‑coded Elara – fluid, emotional
  • 📋 Spec‑driven Harlan – rigid, logical
  • 📖 Steering‑enforced Mira – rule‑based simplicity

Although they shouldn’t work together, steering documents keep the chimera coherent, allowing authentic conflict while preventing chaos.

Key takeaway: By mixing development paradigms and letting Kiro handle the integration, you can create complex multi‑agent systems that are greater than the sum of their parts.

Back to Blog

Related posts

Read more »