AI Integrations

I've been exploring how to integrate LLMs into my personal side projects. Here are some of the things I've built, and the engineering decisions behind them.

  • Beta Bot: AI-powered writing feedback

    Catherine posted yesterday

    Beta Bot is a local-first writing app I built for organizing manuscripts and getting AI feedback. The interesting problem is context: a useful critique of chapter twenty needs to know what happened in the chapters before it.

    I built a workflow that turns earlier writing into structured context, combines it with the current chapter, and asks a reviewer with a specific editorial perspective for feedback. The writer can inspect the prompt and keep the resulting review alongside the chapter.

    Giving the model enough of the story

    Sending an entire manuscript with every request would repeat a lot of text. Sending only the latest chapter would leave the reviewer guessing about established characters and events.

    Beta Bot uses summaries at two levels. Chapter summaries capture the point-of-view character, cast, locations, plot beats, and a factual overview. Part summaries condense a longer story arc. A review can then use previous part summaries and earlier chapter summaries from the current part, alongside the full text of the chapter being reviewed.

    The summaries are editable. If a summary misses an important detail, the writer can correct that context rather than repeatedly trying to steer the reviewer around the same misunderstanding.

    Beta Bot's chapter summary interface, with an AI-generated summary alongside the chapter.

    Different reviewers for different questions

    An encouraging reader, a developmental editor, and a line editor should not give the same kind of response. I added built-in reviewer profiles for those perspectives, plus custom profiles with editable prompts.

    The profile supplies the system prompt. The application assembles the story context and chapter into the user prompt, then stores both the review and the prompt used to produce it. That makes feedback easier to revisit and inspect: the writer can see what the model was responding to.

    Turning summaries into a story bible

    The same summary workflow can optionally update wiki pages for characters and locations. Detected entities lead to new pages or updates to existing ones, with links back to the source chapter. Existing page content is included when requesting an update, so the model can incorporate new information and flag contradictions.

    This connects an AI response to durable application data. The chapter, its summary, and the relevant wiki entries become useful inputs for later writing work.

    Keeping the writer in control

    Books, chapters, summaries, and reviews live in a local SQLite database. AI requests go directly from the app to OpenAI using the writer's own API key when they invoke an AI feature. The text included in that request leaves the device; keeping the rest of the library local does not make the AI request itself private to the device.

    The model can still misunderstand a scene or produce an unhelpful review. Editable context, inspectable prompts, and saved feedback give the writer ways to understand and respond to that, while leaving editorial judgment with them.

    For me, this project has been an exercise in making LLM output useful inside a larger product: selecting context, supporting different user intentions, and connecting generated text to records people can revisit.

    Explore the example book without an account or API key, or browse the source on GitHub. Generating new AI feedback requires an API key.

  • Beta Bot: AI in writing games

    Catherine posted yesterday

    Inside Beta Bot, I built Bardwall: an illustrated town where writing earns coins and stories become games. It gave me a different setting for AI integration: characters that can respond to something the player actually writes.

    Two features use LLMs directly. At a coffeehouse, the player competes against an AI bard in a storytelling contest. In a cavern outside town, they take turns writing a story with Vesper, an elderly bat-like storyteller who will never let them have the last word.

    Bardwall's illustrated town map, with destinations for writing challenges and storytelling games.

    A rival bard and a separate judge

    The coffeehouse contest starts with symbolic cards and a word-count category. The player writes a story incorporating the cards and wagers in-game coins or food. The app then runs a sequence of model calls:

    1. Generate the rival's entry. Orla, the AI bard, receives the same cards and required word-count range, plus a configurable creative prompt.
    2. Check the entry in code. The app counts Orla's words. If the entry is outside the required range, it requests one rewrite with corrective instructions. If that also fails, it reports the problem without settling the wager.
    3. Request an assessment. A separate judge prompt receives the two stories as anonymous A and B entries, with their order randomized. It returns JSON containing an explanation and scores for scene specificity, character agency, narrative movement, craft coherence, and use of the prompts.
    4. Resolve the contest. Application code normalizes the scores, applies length penalties, calculates the outcome, and updates the game state.

    The judge is instructed to treat the entries as quoted fiction rather than follow instructions embedded in them. That is a prompt-level precaution, not a guarantee against manipulation. Likewise, anonymous ordering does not make a model an objective literary judge. This is a playful assessment with visible reasoning, rather than a measurement of a writer's ability.

    Players can edit both Orla's creative instructions and the judge's rubric. Those choices change what the model is asked to do, while the application still owns the scoring categories and wager settlement.

    A story that refuses to end

    Vesper's Game of the Last Word uses a different interaction: collaborative storytelling. The player contributes a passage and chooses the approximate length of Vesper's reply. The model receives the story title, the latest twelve turns, and the new passage, with instructions to continue in character and leave an opening for another turn.

    Keeping the recent conversation bounds the context sent on each request. It also creates a tradeoff: details from much earlier in a long game may fall out of context. The game saves its turns locally, so the player can return to a story later.

    Where ordinary application logic belongs

    Bardwall also rewards passages added in saved manuscript revisions. That reward flow uses revision data and tracks which passages have already been told; it does not need a model to decide whether the writer earned coins.

    I use AI where an open-ended response makes the experience more interesting: a competing story, a critique, or a character's next turn. Word counts, balances, inventory, and state transitions stay in code. Game state is kept separately from manuscript content, so playing does not rewrite a chapter.

    These are bounded, multi-step LLM workflows rather than an autonomous agent with unrestricted tools. Building them has involved many of the same practical questions: what context to supply, how to shape a response, when to retry, and which decisions the application should retain.

    Browse Beta Bot's source on GitHub, or visit the app. AI game interactions require the player's own OpenAI API key.