How I Built an AI Fantasy Name Generator

How I Built an AI Fantasy Name Generator

Overview

As part of my website's mission, I want to leverage code to help writers write better stories. In this pursuit, I am committed to building free tools accessible from my website. 

Given the recent boom of offerings in the universe of GenAI (Generative Artificial Intelligence), I decided to leverage the power of AI to build these tools.

After considering various ideas, I decided to build a Fantasy name generator powered by Claude AI Opus, Anthropic's most powerful commercial generative model available.

In this article, we'll explore how I built this tool, from a high-level overview of the technologies down to the technical architectural details.

Why I Built a Fantasy Name Generator

First and foremost, I built a name generator that I can use for my stories. Some name generators out there are good, but I found many others repetitive and flavorless. Even the good ones seem to be using a database as a means of storing and retrieving names, and as such, names often re-appear or sound similar to one another.

I chose to build a name generator because it would:

  1. Help me build a foundational infrastructure that connects to AI, which can be reused in future tools.
  2. Bring value to writers, including myself.

The reason this tool differs from other generators is that, while with other generators you are "spinning the wheel" until you find a good name, landing back on the same ones time and time again, with this tool you are:

  • Likely to find suitable names within a few tries.
  • Are unlikely to find duplicate names, given the tool is powered by AI rather than a database.
  • Can produce a range of names with different flavors.

Finding the perfect name in the first attempt is still uncommon. However, given my own experience of how frustrating "spinning the wheel" feels when generating names with existing generators, and jumping from one website to the other, I thought that introducing the AI component (from one of the best AI models available), would improve the process of finding suitable character names, making it faster and less frustrating. After all, we don't want to give writers more opportunity to procrastinate; find your names and go write your stories!

One AI to Rule Them All

Before writing any code, I had to pick an AI model across the many options available on the market today. My priorities were:

  1. Best names — to allow my tool to compete with popular name generators, it must match or exceed the quality of names they generate.
  2. Cost-effective — given this tool is self-funded, bringing costs down means it can be sustained over time.

After trying various models, including GPT 3.5 and 4 from OpenAI, LLaMa models from Facebook, and Anthropic's Claude models, I picked Claude Opus.

The quality of names generated by Opus is excellent. For instance, when asked to generate 5 names for Dwarven characters, it returned these:

  1. Grunark
  2. Thorbrim
  3. Dulgur
  4. Kazrador
  5. Balgorn

They look great! I moved on without hesitation, as code is modular, and the specific AI used behind the scenes can always be changed later!

Starting Small

Keeping in line with my recent post on Ikigai, I decided to start small. I created the smallest possible building blocks to accomplish the generation of names. This approach is known as building the POC (proof of concept). A successful POC gives me a reason to believe the idea can be done. 

The Backend

Web applications are commonly designed in two parts: frontend and backend.

As defined by AWS (Amazon Web Services):

The frontend is what your users see and includes visual elements like buttons, checkboxes, graphics, and text messages. It allows your users to interact with your application. The backend is the data and infrastructure that make your application work. It stores and processes application data for your users.

Initially, I built a simple backend that connects my computer to Claude AI. For those interested, I used Quarkus, a modern Java development framework:

Quarkus to Claude Architecture

In this simple system, my computer asks Quarkus to generate several names, which will, in turn, ask Claude Opus. Quarkus communicates with Opus via prompts, which we'll explore later.

The Frontend

The next step is to create our frontend. This comprises two screens. The fantasy race selector screen:

Selector

And the name generator screen:

Generator

To build the screens, the architecture is expanded to accommodate the frontend. The website is built in SvelteKit configured to run in NodeJS mode. This is what our final architecture looks like:

SvelteKit to Quarkus Architecture

Prompt Engineering

The term Prompt Engineering, coined shortly after the release of OpenAI's generative models, is the science of crafting the most optimal prompts for text AI models. An ideal prompt is as short as it can be (which avoids confusing the AI with unnecessary information) while including all of the necessary context (for example, write as if you were a lawyer). 

In the case of my name generator, I wanted to create a prompt that would result in the most unique and original names:

Create {number} highly creative and unique first names for High Fantasy characters belonging to the race of {fantasyRace}. They must not have appeared in any major TV series, movie or book. Your response should only contain a valid JSON schema such as [\"First\", \"Second\"] and nothing else.
Let's break the prompt down:
  1. The first part asks for several names belonging to the fantasy race requested by the user. This flexibility is crucial in allowing our backend to reuse the same code and prompt for multiple races and a range of names.
  2. Next, we're asking Opus to avoid names that appear in popular media. Testing has shown that, without this constraint, the AI will often return names that are most commonly associated with the requested fantasy race, such as Gloin and Thorin for dwarves, which are characters from the popular book The Hobbit.
  3. Last, we ask the AI to return names as JSON, a format that is easily understandable by computers. This helps us in processing them in both the backend and frontend.

Strategies for Achieving Reliability

To keep up with the standards of other name generators, the tool must be available 24/7. Unfortunately, computing is an unstable world. Internet requests from our frontend to our backend can fail, and so can the ones from our backend to Claude. And so can the response to each request.

To combat this, we employ several strategies, which we'll explore next.

Self-Healing Backend

In my article on how websites work, I explain what websites are and why it's a big deal to keep them online 24/7. This concept is known as reliability.

A modern technology that increases reliability is Kubernetes. Let's look at a practical example to understand how it works.

Suppose you are working on an important project on your computer. An error occurs and your computer shuts off. To resume your work promptly, you must switch the computer back on. Before self-healing technologies like Kubernetes, the same was required of software programs. When a web application crashed,  engineers had to manually reboot them.

In our architecture, there are multiple points of failure:

  1. Our frontend's SvelteKit server can crash.
  2. Our Quarkus backend can crash.
  3. The website itself can crash — however, since the website is loaded by the user's device, crashes are dealt with by the device itself.

In scenarios 1 and 2, Kubernetes will immediately restart the application, and help us achieve better reliability. But that's not the end of it! We can configure Kubernetes to make sure X number of instances of our application are online.

Say, for example, that a Quarkus backend can handle 1,000 requests per second. By telling Kubernetes to create two instances, we can scale our system to handle 2,000. Our name generator is not expected to see any traffic close to these numbers. It is, however, a major benefit of the technology.

Retries

Let's imagine Claude is experiencing an incident and is unavailable.

This will cause an error when our system asks Claude to generate names. At this stage, we don't know whether the request has failed due to network issues, or whether Claude is down (which in this case, it is). We'll therefore retry the operation.

If we receive another error from Claude, we'll wait some time and try again — usually fractions of a second. If we still receive an error, we'll wait even longer, maybe twice as long as the first time, and try again. We'll repeat this process several times (5 or 10 is usually the reasonable limit — remember, our user is waiting while this happens). This is called progressive backoff, and gives Claude time to recover, while not overloading it with requests (which could cause further issues on their end). All the while, we'll setting a threshold beyond which we say "that's enough", and show our user an error message. 

Storing Names

Another strategy to improve the experience of our tool is to keep a list of names ready to go (in reality, a Queue, but we'll refer to it as a List for simplicity).

This means that, when our user asks for names, we don't go to Claude (which could take 2-3 seconds). Instead, we retrieve several names from our stored list. In doing so, we remove them from the list, so that no two users get the same names.

This achieves three benefits:

  • The user gets the names instantly.
  • The names are only returned to one user — they are then removed from the list, making each name generation unique.
  • If Claude is down, we can still return names until our list runs out.

For this to work, we need to keep our list of names continuously full:

Name Queue Architecture

Why not request, say, a million names? That would surely prevent us from going to Claude for months or experiencing any issues if Claude is down.

Well, because generating a million names is expensive. There is a tradeoff involved: stability versus cost. We don't want to generate tons of names nobody will ever use.

Let's imagine a simple scenario to showcase how all the parts fit together:

  1. The backend asks Claude for 20 names.
  2. A user retrieves 5 names through our frontend.
  3. The backend immediately asks Claude for 5 more names, refilling our list to make sure it always has 20 names available.

Ensuring that 20 names are available at any given time means that up to 4 users can request names simultaneously and receive them instantly. A fifth user, also requesting at the same moment, will have to wait a few seconds. However, a few seconds later, 20 more names will be available.

In practice, there are separate caches for each fantasy race, backed by a Java ArrayBlockingQueue, and a virtual thread responsible for populating them:

Screenshot 2024 04 28 112947

Damage Control: In Case of Errors

Not all errors can be avoided. Assuming we have a completely stable infrastructure, Claude can always have downtime, and our list of names can run out.

If this happens, there is nothing left to do other than show a user-friendly error message:

Error

Limiting Requests

AI is expensive, and names are limited. We must ensure that one user does not consume all the names available. If, for example, 1000 names are available each month, we want to allow multiple users to retrieve them.

To ensure this, multiple mechanisms across the frontend and backend prevent users from generating more than 3 names within a minute. For security reasons, we'll not delve into the technical details.

Security

A name generator is a fairly innocent tool since it doesn't drive critical life decisions for people, like a mortgage rate comparison tool would.

Still, security mechanisms must be in place to prevent access to the tool for users with malicious intentions. Once again, we'll not delve into these details, but we'll look at a crucial security mechanism at a high level: the genuine user check.

Checking for Genuine Users With Recaptcha

Tools can be categorized as one of three:

  1. To be used by people.
  2. To be used by code (for example, our backend using Claude AI).
  3. To be used by both.

This tool falls into the first bucket: it's meant to be used by people and not code.

Therefore, the tool integrates with Google's Recaptcha tool, which, according to its documentation:

reCAPTCHA is a free service from Google that helps protect websites from spam and abuse. A “CAPTCHA” is a turing test to tell human and bots apart. It is easy for humans to solve, but hard for “bots” and other malicious software to figure out. By adding reCAPTCHA to a site, you can block automated software while helping your welcome users to enter with ease.

Conclusion

In this article, we explored the new Fantasy name generator tool available on the website, exploring the technical details of its implementation.

If you would like to share any ideas for tools that can help you write better stories, don't hesitate to contact me!

technical
Andrea Cerasoni in Rome, Italy
Andrea Cerasoni

I'm Andrea, a Software Engineer, Technical Editor, and aspiring Fantasy Author. I'm originally from Rome, Italy, but am currently based in Glasgow, United Kingdom. I read and write classic Fantasy: the sword-and-shield, dragons, and wizards kind. In my articles, I talk about writing fantasy fiction, productivity, coding, building a website or platform, establishing a personal brand, and more!

Want to share feedback on my articles? Contact me!