9 Creating an AI-powered application
This chapter introduces how to build an AI-powered Streamlit application by creating Fact Frenzy, a trivia game that uses a Large Language Model to generate questions, evaluate free-text answers, and add personality to the game experience. The app demonstrates how LLMs can replace traditional hand-built trivia databases and rigid multiple-choice logic by producing questions on demand and interpreting user responses. It also shows how to connect Python code to OpenAI through an API key, wrap LLM interactions in a reusable class, and keep prompts separate from application logic for easier maintenance and experimentation.
A major focus is making LLM responses usable inside a real application. The chapter explains prompt design, system and user messages, and the need for structured, machine-readable outputs when the app must act on an AI response rather than merely display it. To evaluate answers reliably, the app uses a Pydantic schema with OpenAI Structured Outputs so the LLM returns fields such as whether an answer is correct and what the correct answer is. The chapter also addresses practical LLM behavior, including response variability, repeated questions, temperature, top-p, random seeds, previously asked questions, hallucinations, and cost considerations.
The chapter then develops Fact Frenzy into a complete Streamlit game by managing sequential state with a central Game class and st.session_state. It walks through solving rerun-related bugs, cycling through game states, displaying spinners during LLM calls, tracking score, adding a New Game button, supporting multiple questions, and ending the game with a final score. Finally, it adds user-configurable settings with st.data_editor and st.popover, letting players choose difficulty and a quizmaster personality that the LLM mimics in its questions, resulting in a more polished and entertaining AI-powered Streamlit app.
A UI sketch of Fact Frenzy
The flow of logic in our AI-based trivia game
Creating a secret key in your OpenAI account.
The app can now successfully call the OpenAI API to obtain a trivia question (see chapter_09/in_progress_01 in the GitHub repo for the full code).
A problem with session state causes our answer to be matched with the wrong question (see chapter_09/in_progress_02 in the GitHub repo for the full code).
Our game consists of four sequential states.
Conditional branching in the frontend based on the game's state.
The question-answer mismatch problem has been resolved (see chapter_09/in_progress_03 in the GitHub repo for the full code).
Keeping score, a New Game button, and Game Over (see chapter_09/in_progress_04 in the GitHub repo for the full code).
How temperature and top_p affect the creativity and predictability of an LLM's output.
To implement multiple questions, we now cycle through the game states in a loop.
A working multi-question trivia game (see chapter_09/in_progress_05 in the GitHub repo for the full code).
A simple output of st.data_editor.
st.data_editor with one of the columns showing a SelectBoxColumn.
A final version of Fact Frenzy with editable settings (see chapter_09/in_progress_06 in the GitHub repo for the full code).
Summary
- A Large Language Model (LLM) is an AI system designed to process and generate human-like text.
- LLMs can perform both creative and analytical tasks.
- OpenAI, one of the most popular LLM providers, allows developers to access its GPT series through an API.
- The openai library provides a convenient way to call the OpenAI API in Python.
- You can pass a conversation to OpenAI API's chat completion endpoint with messages tagged as "system", "assistant" or "user", causing the model to complete the conversation in a logical way.
- Structured Outputs is a feature provided by OpenAI that ensures the model will generate a response that adheres to a given schema.
- A common pattern in linear Streamlit apps is to implement conditional branching logic based on a variable that's held in st.session_state.
- You can vary parameters such as temperature and top_p to affect the creativity and predictability of responses generated by an LLM.
- Injecting randomness into the prompt is a good way to ensure we get different responses to similar prompts.
- It's important to optimize cost in LLM-based applications. You can do this by having the LLM process fewer input and output tokens, reducing the number of prompts, using cheaper models, or even having users bear the cost by requiring them to supply their own API key.
- st.data_editor provides a way to create editable tables in Streamlit apps.
- st.column_config lets you configure columns to be of certain editable and non-editable types in st.data_editor and st.dataframe.
- st.popover displays a small pop-up screen triggered by a click on an associated button.
Build Python Web Apps with Streamlit ebook for free