
We are building an NPC dialogue generator for game writers and narrative designers. It ingests a character profile, location, and emotional state, then returns voice-consistent lines that fit the scene. This cuts hours from early prototyping and gives writers a starting block they can refine rather than facing a blank page.
What you'll need
Before starting, grab the following items:
- Python 3.10 or newer installed locally.
- An Oxlo.ai API key from https://portal.oxlo.ai. Oxlo.ai uses flat per-request pricing, so sending long character bibles with every prompt does not inflate your bill the way token-based metering would. See https://oxlo.ai/pricing for plan details.
- The OpenAI SDK installed with
pip install openai. Oxlo.ai is fully OpenAI API compatible, so this is the only client you need.
Step 1: Configure the API Client
Create a file named npc_agent.py and initialize the client. I point the base URL to Oxlo.ai and load the API key from an environment variable. I use llama-3.3-70b here because it follows creative instructions tightly, handles roleplay well, and has no cold starts on Oxlo.ai.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=os.getenv("OXLO_API_KEY", "YOUR_OXLO_API_KEY")
)
MODEL = "llama-3.3-70b"
Step 2: Design the System Prompt
The system prompt acts as the style guide and contract for the agent. It defines the role, output format, and creative guardrails. I keep it strict so the model does not drift into prose narration or add markdown formatting that would break a game engine import pipeline. Keeping the prompt in its own variable also lets non-technical writers tweak voice without touching Python.
SYSTEM_PROMPT = """You are a senior narrative designer writing NPC dialogue for a video game.
Rules:
- Respond with exactly one line of in-character dialogue.
- Do not add narration, stage directions, or quotation marks.
- Match the character's dialect, vocabulary, and emotional state provided in the user prompt.
- Keep the line under 150 characters so it fits standard UI text boxes.
- If the character is a merchant, mention prices or goods. If a guard, mention duty or threats."""
Step 3: Build the Dialogue Generator
Next, I write a reusable function that accepts a character sheet and scene context, formats them into a user message, and calls the Oxlo.ai chat endpoint. I set temperature to 0.8 to introduce natural variety between runs without letting the model hallucinate facts outside the provided context. I strip whitespace from the returned content so the line drops cleanly into a CSV or JSONL pipeline.
def generate_line(name, role, emotion, location, recent_event):
user_message = f"""Character: {name}
Role: {role}
Emotion: {emotion}
Location: {location}
Recent event: {recent_event}
Write one line of dialogue."""
response = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
temperature=0.8,
max_tokens=60,
)
return response.choices[0].message.content.strip()
Step 4: Orchestrate a Scene
Real scenes involve multiple speakers reacting to the same event. I create a small loop that passes each character's context to the generator and prints a formatted transcript. In a production pipeline, you would write this output to a JSONL file for import into Unity, Unreal, or a localization spreadsheet. For now, printing to the terminal lets us read the flow instantly and adjust the system prompt if any voice feels off.
scene_characters = [
{
"name": "Greta",
"role": "blacksmith",
"emotion": "suspicious",
"location": "forge",
"recent_event": "A stranger asked about the Duke's sword."
},
{
"name": "Finn",
"role": "street urchin",
"emotion": "excited",
"location": "forge doorway",
"recent_event": "He saw the stranger drop a silver coin."
},
{
"name": "Sergeant Yao",
"role": "city guard",
"emotion": "authoritative",
"location": "forge entrance",
"recent_event": "He is searching for a wanted thief."
},
]
def run_scene(characters):
print("--- SCENE START ---")
for char in characters:
line = generate_line(
char["name"],
char["role"],
char["emotion"],
char["location"],
char["recent_event"]
)
print(f"{char['name']}: {line}")
print("--- SCENE END ---")
if __name__ == "__main__":
run_scene(scene_characters)
Run it
Save the file and run the script from your terminal:
python npc_agent.py
When I ran this against Oxlo.ai, the transcript looked like this:
--- SCENE START ---
Greta: I don't know nothing about no noble blades, and I'd advise you to stop asking.
Finn: Mister, you dropped something shiny. Can I keep it? Please?
Sergeant Yao: Nobody leaves until I check every pocket and pack. Thieves don't hide forever.
--- SCENE END ---
Each line respects the role and emotional constraints. Because Oxlo.ai charges a flat rate per request, sending these detailed character sheets does not inflate the cost the way token-based pricing would. You can iterate on voice all afternoon without watching the meter run.
Wrap-up and next steps
This agent gives narrative designers a repeatable way to draft NPC lines that stay in character. Two concrete ways to extend it:
- Add voice acting by piping the dialogue strings through Oxlo.ai's Kokoro TTS endpoint at
/audio/speech. You can assign different voices to characters and preview the scene aloud. - Deploy the generator as a FastAPI service and cache character profiles in Redis. Game clients can then request fresh lines dynamically based on live world state, all backed by Oxlo.ai's flat per-request pricing.

