Chapter 9: Creating an AI-powered application
This chapter introduces building Streamlit features with LLM prompts and responses, controllable structured interactions, sequential state, and editable tables.
After this chapter you will be able to
- LLM-powered application features. Explain how connecting an application to a large language model can enable product features through natural-language prompts and responses that previously required more specialized techniques or extensive programming.
- Fact Frenzy capabilities. Describe the planned Fact Frenzy trivia app and state that it uses artificial intelligence to generate trivia questions, evaluate player answers, and vary the quizmaster's style.
- Fact Frenzy app concept. Describe Fact Frenzy as a trivia game that asks players a set of questions and uses AI to evaluate their answers.
- AI trivia game requirements. Explain the required Fact Frenzy features: AI-generated trivia questions, free-text player responses, AI evaluation with the correct answer, score tracking, selectable question difficulty, and quizmaster speaking styles.
- Two-column game layout. Describe the proposed Fact Frenzy interface as a two-column layout, identifying the controls in the left column and the game content in the right column.
- Fact Frenzy core game loop. Describe the linear Fact Frenzy loop: retrieve a trivia question with an LLM, show it to the player, have the LLM evaluate the player’s answer, report whether it was right, and repeat for another question until the game ends.
- App-owned flow and LLM roles. Distinguish the app’s responsibility for the overall game flow from the LLM’s roles in retrieving trivia questions and evaluating player answers.
- LLM role in Fact Frenzy. Explain that a Large Language Model powers Fact Frenzy by generating trivia questions, evaluating player responses, and giving the quizmaster personality.
- LLM text-generation capabilities. Describe an LLM as an AI system trained on vast amounts of text to process and generate human-like, coherent, contextually appropriate text for varied tasks.
- LLM trivia question generation. Explain how an LLM lets Fact Frenzy generate a large and diverse supply of trivia questions on demand instead of requiring the developers to maintain a large question bank.
- Open-ended answer evaluation. Describe how an LLM can interpret a player's free-text answer and evaluate it, allowing Fact Frenzy to ask open-ended questions rather than being limited to answer matching for multiple-choice questions.
- OpenAI account and API key. Describe how to sign in or create an OpenAI account, navigate to Settings and API Keys, and create a new secret key using the default options.
- API key protection. Explain that OpenAI displays a newly created secret key only once, so it must be copied when created and kept secure.
- OpenAI client setup. Describe how to install OpenAI's Python library and instantiate an OpenAI client with the API key obtained previously before making API calls.
- Chat prompt message structure. Explain that a prompt for this chat-completions request is a list of message dictionaries, each with role and content, and distinguish the system and user roles used in the example.
- Chat completions request flow. Trace the call to the chat completions endpoint for text generation: pass the messages and selected model, receive a ChatCompletion object, and access its generated text response.
- LLM communication encapsulation. Explain why Fact Frenzy puts all interaction with the external LLM in an Llm class rather than scattering OpenAI calls throughout the app. Describe how this separation isolates LLM logistics from the rest of the application.
- Llm class interface. Describe the responsibilities of Llm.__init__ and ask. State that initialization creates an OpenAI client from the supplied API key, while ask is the public method that receives prompt information and returns the LLM response.
- Game backend responsibility. Explain the role of the Game class as the single backend class called directly by the frontend, and describe why it will be important for tracking game state without confusion.
- LLM question generation delegation. Describe how a Game instance initializes an Llm object with an API key and how ask_llm_for_question delegates trivia-question generation to that Llm object.
- Minimal Game frontend. Describe how main.py creates a Game instance, calls its ask_llm_for_question method, and displays the resulting trivia question in the Streamlit app.
- Machine-readable answer evaluation. Explain why Fact Frenzy needs the LLM to return both whether a player's answer is correct and, when it is not, the correct answer in a reliably usable form.
- Free-form response parsing problem. Describe why free-form natural-language LLM replies cannot reliably drive application logic when evaluating trivia answers.
- Structured Outputs schema. Explain how OpenAI Structured Outputs makes LLM responses programmatically reliable by requiring them to conform to a supplied schema. Describe the AnswerEvaluation schema and its is_correct boolean and correct_answer string fields.
- Schema-enabled LLM calls. Trace how the Llm.ask method supports both regular text and Structured Outputs: schema defaults to None, while a supplied schema uses beta.chat.completions.parse with response_format and returns message.parsed. State that the caller passes the AnswerEvaluation class itself, not an instance, and receives an instance that follows that schema.
- Answer evaluation prompt flow. Describe how ANSWER_PROMPT separates the system and user messages, inserts the asked {question} and player's {answer}, and instructs the LLM to judge correctness and provide the correct answer. Trace how Game.ask_llm_to_evaluate_answer sends this populated prompt with AnswerEvaluation, and how the frontend uses evaluation.is_correct and evaluation.correct_answer after Submit.
- Rerun-induced question mismatch. Explain why submitting “Ottawa” can be evaluated against a newly displayed question: clicking Submit triggers a Streamlit rerun, game.ask_llm_for_question runs again before the Submit block, and the new question is paired with the existing text-input answer. State that the LLM and Structured Outputs are not at fault; the next section must manage state to retain the originally asked question.
- Four-state game flow. Describe Fact Frenzy's four sequential states—GET_QUESTION, ASK_QUESTION, EVALUATE_ANSWER, and STATE_RESULT—and what happens in each state.
- Game status transitions. Explain how the Game class uses a status attribute and transition methods to control the sequence: obtain_question advances to ASK_QUESTION, accept_answer advances to EVALUATE_ANSWER, and evaluate_answer advances to STATE_RESULT.
- Session-persisted game instance. Explain why the Game instance is stored in st.session_state: Streamlit reruns the whole app, so storing it there preserves the same game's status and current values across reruns.
- Conditional frontend reruns. Trace how main.py branches on game.status to show the appropriate widgets or waiting UI, and uses st.rerun() after an action changes state so the next branch can execute.
- Fact Frenzy gameplay gaps. Describe the game features Fact Frenzy currently has and the mechanics it lacks. State that it can ask a player an AI-generated question and evaluate the answer, but it has no score or defined game start and end.
- Score and completion tracking. Describe how the Game class tracks a player's score and determines when a game is over. Explain that each LLM-judged correct answer adds one point, every evaluated answer increments the completed-question count, and is_over checks that count against max_questions.
- New game session initialization. Explain how the New Game button starts or restarts a game by creating a new Game instance in st.session_state.game and then calling st.rerun. Describe why putting that work in start_new_game prevents it from running automatically when the page loads.
- Probabilistic LLM responses. Explain that unlike deterministic regular code, an LLM produces text probabilistically and can give different responses to the same or similar prompts. Relate variability to the task: it can be desirable for creative question generation but undesirable when predictable or exact output is needed.
- Temperature and top_p controls. Describe how temperature and top_p change next-token selection in an LLM. Explain that higher temperature flattens token probabilities, while higher top_p expands the cumulative-probability set of tokens considered, and connect moderately high temperature and high top_p to more diverse trivia questions.
- In-game question exclusion. Describe how including previously asked questions in the prompt tells the LLM what to avoid, helping guarantee that a question is not repeated within one game. Explain that the prompt can reinforce this by explicitly instructing the model never to ask the same question twice.
- Distinct-question prompting strategy. Explain how the question-generation prompt reduces repeated trivia questions by assigning the LLM a quizmaster role, requesting a unique category and topic, supplying a seed, and listing questions already asked. Describe how the game uses a timestamp-derived integer seed and its stored questions when requesting another question.
- Cyclic multi-question game state. Trace how Fact Frenzy changes from a one-pass state sequence into a cycle by returning from STATE_RESULT to GET_QUESTION. Describe that Game stores generated questions in self.questions, uses max_questions set to 5 as the game limit, and uses proceed_to_next_question to reset the status for another question.
- Callback-driven next-question interface. Describe how the frontend displays the current question number from len(game.questions), the maximum question count, and the score, then shows a Next Question button when the game is not over. Distinguish st.button(on_click=game.proceed_to_next_question) from an if st.button pattern by explaining that its callback runs before the button-triggered rerun, so no manual st.rerun is needed for this transition.
- AI quizmaster personalities. Explain why Fact Frenzy can use an LLM to give its quizmaster a personality, such as mimicking the style of different characters when asking trivia questions.
- Editable settings data editor. Explain how st.data_editor turns the Fact Frenzy settings data into an editable, spreadsheet-like UI and returns the current edited settings. Describe why fixed rows and an explicit widget key are used for this editor.
- Popover settings module. Describe the settings.py design: option lists and default_settings are defined centrally, and settings_editor places the data editor inside an st.popover and returns the edited settings dictionary. Explain why the defaults are stored as a dictionary whose values are one-element lists.
- Settings-driven LLM questions. Trace how the current Quizmaster and Difficulty settings reach LLM-generated questions: Game receives and stores settings, get_setting extracts a value from its one-element list, and ask_llm_for_question inserts those values into the question prompt. Describe how main.py initializes defaults, renders the editor in the side column, and calls modify_settings on each in-game rerun.
What it builds on
- st.session_state (chapter 4) · The prior introduction of st.session_state for preserving values across Streamlit reruns.
- Stating our concept: A unit converter (chapter 3) · Stating an app concept before expanding it into requirements.
- Defining the requirements (chapter 3) · Turning an app concept into explicit functional requirements.
- Defining what the app won’t do (chapter 3) · Defining what an app will not do to control scope.
- Accessing an API key safely with st.secrets (chapter 5) · The earlier practice of handling external-service API keys safely with Streamlit secrets.
- Using the requests library to connect to an API (chapter 5) · The idea of connecting a Python app to an external API.
- Connecting PostgreSQL to the app (chapter 8) · The earlier Database class pattern for encapsulating communication with an external database.
- Splitting the app into services (chapter 8) · The idea of splitting an app into focused service-like components.
Section by section
Introduction
This chapter introduces building Streamlit features with LLM prompts and responses, controllable structured interactions, sequential state, and editable tables.
- LLM-powered application features: Explain how connecting an application to a large language model can enable product features through natural-language prompts and responses that previously required more specialized techniques or extensive programming.
Why it matters: LLMs let an app use language understanding and generation as part of its functionality. - LLM interaction control: Describe the chapter's focus on engineering prompts and tuning LLM interactions to achieve desired results while controlling cost, including obtaining responses in a custom parseable format with Structured Outputs.
Why it matters: Useful AI features require outputs that are both suitable for the app to consume and affordable to generate. - Chapter implementation topics: State that the chapter also covers managing state in a sequential Streamlit app and using st.data_editor to create editable tables.
Why it matters: These Streamlit techniques support interactive application flows alongside the AI functionality.
Key terms: LLM, large language model, prompts, responses, natural language, prompt engineering, Structured Outputs, custom parseable format, LLM interactions, state
9.1 Fact Frenzy: An AI trivia game
This section introduces Fact Frenzy, an AI-powered Streamlit trivia game that generates questions, evaluates answers, and supports varied quizmaster styles.
- Fact Frenzy capabilities: Describe the planned Fact Frenzy trivia app and state that it uses artificial intelligence to generate trivia questions, evaluate player answers, and vary the quizmaster's style.
Why it matters: These three AI-driven capabilities define the application developed throughout the chapter. - Trivia as AI application: Explain why trivia is presented as a suitable first AI-powered Streamlit application: its core interactions naturally let AI generate questions, assess answers, and present varied quizmaster behavior.
Why it matters: The game provides a concrete setting for learning several practical ways to use AI in a Streamlit app.
Key terms: Fact Frenzy, AI-powered Streamlit app, artificial intelligence, quizmaster styles, trivia
9.1.1 Stating the concept and requirements
This section defines Fact Frenzy’s AI trivia-game concept, its required features, and the deliberately limited scope for the hands-on project.
- Fact Frenzy app concept: Describe Fact Frenzy as a trivia game that asks players a set of questions and uses AI to evaluate their answers.
Why it matters: This concept establishes the app that the rest of the chapter implements. - AI trivia game requirements: Explain the required Fact Frenzy features: AI-generated trivia questions, free-text player responses, AI evaluation with the correct answer, score tracking, selectable question difficulty, and quizmaster speaking styles.
Why it matters: These requirements define the concrete user experience and implementation work for the game. - Intentional project scope: Distinguish Fact Frenzy’s in-scope AI and Streamlit learning focus from excluded features: persistent storage of questions, answers, and scores; user management; and selectable question categories. Explain why AI is explicitly named in the requirements despite the usual preference to avoid implementation language.
Why it matters: Keeping these features out of scope preserves a small project focused on LLM interaction, state management, and new Streamlit elements.
Key terms: Fact Frenzy, trivia game, AI, AI model, free-text response, difficulty level, quizmaster speaking styles, score, out of scope, persistent storage
9.1.2 Visualizing the user experience
This section sketches Fact Frenzy’s two-column interface and shows how its AI Quizmaster personality appears in a standard free-form trivia game.
- Two-column game layout: Describe the proposed Fact Frenzy interface as a two-column layout, identifying the controls in the left column and the game content in the right column.
Why it matters: The layout separates game setup controls from the question-and-answer play area. - Quizmaster personality presentation: Explain that the Quizmaster setting uses AI to mimic different characters’ speaking styles, illustrated by Gollum’s distinctive hissing voice and phrases such as “my precious.”
Why it matters: The Quizmaster setting adds personality to generated questions while leaving the underlying trivia interaction familiar. - Free-form trivia interaction: Describe Fact Frenzy as a standard question-and-answer trivia game in which the player enters a free-form text answer, despite the AI personality gimmick.
Why it matters: This distinguishes the game’s familiar answer flow from its AI-generated and personality-styled presentation.
Key terms: two-column layout, New Game button, Quizmaster, Difficulty, score, AI, Gollum, speaking styles, my precious, question-and-answer trivia game
9.1.3 Brainstorming the implementation
This section defines Fact Frenzy’s linear game loop, where the app owns game flow while an LLM supplies and evaluates trivia interactions.
- Fact Frenzy core game loop: Describe the linear Fact Frenzy loop: retrieve a trivia question with an LLM, show it to the player, have the LLM evaluate the player’s answer, report whether it was right, and repeat for another question until the game ends.
Why it matters: This loop is the backbone that later game features extend. - App-owned flow and LLM roles: Distinguish the app’s responsibility for the overall game flow from the LLM’s roles in retrieving trivia questions and evaluating player answers.
Why it matters: Separating orchestration from AI tasks makes clear what the application must still control. - Optional game settings: State that difficulty level and quizmaster selection are later enhancements, while the diagram represents the core Fact Frenzy flow without those additions.
Why it matters: This separates the essential game loop from optional customization features.
Key terms: Fact Frenzy, LLM, game flow, logic loop, overall game flow, retrieve a trivia question, evaluate the answer, difficulty level, quizmaster, bells and whistles
9.2 Using AI to generate trivia questions
This section introduces LLMs as the AI foundation of Fact Frenzy, explaining their text capabilities and role as the game’s quizmaster.
- LLM role in Fact Frenzy: Explain that a Large Language Model powers Fact Frenzy by generating trivia questions, evaluating player responses, and giving the quizmaster personality.
Why it matters: This establishes why an LLM is the central AI component of the trivia game. - LLM text-generation capabilities: Describe an LLM as an AI system trained on vast amounts of text to process and generate human-like, coherent, contextually appropriate text for varied tasks.
Why it matters: Understanding what LLMs can do explains why one can role-play as a quizmaster rather than serve only one narrow function. - Examples of LLM providers: State that prominent LLM examples include OpenAI’s GPT series, Anthropic’s Claude, and Google’s Gemini.
Why it matters: These examples show that LLMs are a category of systems rather than a single product.
Key terms: Large Language Model, LLM, Fact Frenzy, quizmaster, human-like text, contextually appropriate text, OpenAI, GPT, Anthropic, Claude
9.2.1 Why use an LLM in Fact Frenzy?
This section explains why Fact Frenzy uses an LLM to generate trivia, judge open-ended answers, and make the game more entertaining while acknowledging its limitations.
- LLM trivia question generation: Explain how an LLM lets Fact Frenzy generate a large and diverse supply of trivia questions on demand instead of requiring the developers to maintain a large question bank.
Why it matters: On-the-fly question generation keeps the game varied without requiring a manually maintained catalog of questions. - Open-ended answer evaluation: Describe how an LLM can interpret a player's free-text answer and evaluate it, allowing Fact Frenzy to ask open-ended questions rather than being limited to answer matching for multiple-choice questions.
Why it matters: Free-text evaluation makes trivia interactions more natural than restricting every player response to predefined choices. - LLM game personality: Explain the LLM's entertainment role in Fact Frenzy: beyond factual tasks, it can use creativity and humor, including mimicking quizmaster character styles to give the game personality.
Why it matters: A creative quizmaster voice can make the game more engaging rather than making it only a factual question-and-answer tool. - Limits of LLM evaluation: State that LLMs are not omniscient: they generate responses from patterns in training data and can make mistakes, particularly on nuanced or ambiguous answers, even though they are an appropriate balance of intelligence, versatility, and entertainment for this game.
Why it matters: Recognizing that model judgments can be fallible helps set realistic expectations for AI-powered gameplay.
Key terms: LLM, generate questions on the fly, free-text user inputs, open-ended trivia questions, evaluate player responses, multiple-choice questions, comic relief provider, quizmasters, personality, mimic the style
9.2.2 Setting up an OpenAI API key
This section explains how to obtain and safely retain an OpenAI secret API key so the Fact Frenzy app can connect to OpenAI’s LLM service.
- OpenAI account and API key: Describe how to sign in or create an OpenAI account, navigate to Settings and API Keys, and create a new secret key using the default options.
Why it matters: The app needs this key to authenticate its requests to OpenAI. - API key protection: Explain that OpenAI displays a newly created secret key only once, so it must be copied when created and kept secure.
Why it matters: Losing the one-time-visible key means you cannot retrieve it later, while exposing it could let others use your OpenAI account. - OpenAI usage tiers: State that a new account may receive free credits but the free tier has severe usage limits, and explain why the section recommends a paid tier for serious AI development while noting that this chapter's usage is inexpensive.
Why it matters: Understanding usage limits and costs helps readers choose an account tier that will support their development work.
Key terms: OpenAI account, Settings, API Keys, secret key, OpenAI API, OpenAI API key, free credits, free tier, usage limits, paid tier
9.2.3 Calling the OpenAI API in Python
This section verifies an OpenAI API connection in Python by creating a client, sending a chat-completions prompt, and reading the generated text.
- OpenAI client setup: Describe how to install OpenAI's Python library and instantiate an OpenAI client with the API key obtained previously before making API calls.
Why it matters: The client is the application’s connection point for sending requests to OpenAI. - Chat prompt message structure: Explain that a prompt for this chat-completions request is a list of message dictionaries, each with role and content, and distinguish the system and user roles used in the example.
Why it matters: Message roles let an app separately set model behavior and provide the request the model should answer. - Chat completions request flow: Trace the call to the chat completions endpoint for text generation: pass the messages and selected model, receive a ChatCompletion object, and access its generated text response.
Why it matters: This request-and-response flow is the basic operation the trivia game uses to obtain LLM-generated content. - Model selection tradeoffs: Explain why the example selects gpt-5.1: it is a fast GPT-5-family model whose reasoning can be turned off, while model price and capabilities can change and should be checked against current needs.
Why it matters: Choosing a model involves practical tradeoffs among speed, quality features, and cost.
Key terms: openai, API key, client, prompt, messages, role, content, system, user, chat completions endpoint
9.2.4 Writing an LLM class
This section encapsulates OpenAI communication in an Llm class so the rest of Fact Frenzy can request LLM responses through a maintainable interface.
- LLM communication encapsulation: Explain why Fact Frenzy puts all interaction with the external LLM in an Llm class rather than scattering OpenAI calls throughout the app. Describe how this separation isolates LLM logistics from the rest of the application.
Why it matters: A dedicated boundary makes the LLM integration easier to maintain, test, or replace without changing unrelated app code. - Llm class interface: Describe the responsibilities of Llm.__init__ and ask. State that initialization creates an OpenAI client from the supplied API key, while ask is the public method that receives prompt information and returns the LLM response.
Why it matters: This interface gives game code a simple way to obtain an LLM response without managing the client or request construction itself. - Conditional message construction: Describe how ask delegates creation of the chat messages list to construct_messages using user_msg and sys_msg. Explain that sys_msg defaults to None, so a system-role message is included only when one is provided, and that construct_messages is a static method because it does not depend on object state.
Why it matters: Conditional message construction lets the app use a system instruction when needed without requiring one for every LLM request.
Key terms: Llm class, external LLM, encapsulation, Llm, __init__, OpenAI client, API key, ask, construct_messages, user_msg
9.2.5 The Game class
This subsection introduces an initial Game backend class that owns game logic and asks an LLM, acting as a quizmaster, to generate a trivia question.
- Game backend responsibility: Explain the role of the Game class as the single backend class called directly by the frontend, and describe why it will be important for tracking game state without confusion.
Why it matters: Keeping backend responsibilities in Game gives the trivia app a clear place to manage its evolving game state. - LLM question generation delegation: Describe how a Game instance initializes an Llm object with an API key and how ask_llm_for_question delegates trivia-question generation to that Llm object.
Why it matters: This delegation gives the app a simple backend path from its API key to an AI-generated trivia question. - Quizmaster prompt constraints: State the instructions in the question-generation prompt: the system message makes the LLM behave as a quizmaster, while the user message requests a question with no answer choices and no revealed answer.
Why it matters: These prompt constraints make the generated output fit a trivia game instead of prematurely giving players the solution.
Key terms: Game class, backend logic, frontend, game state, Hub class, Game, Llm, __init__, API key, ask_llm_for_question
9.2.6 Calling the Game class in the app
This subsection wires the Game class into a minimal Streamlit frontend that securely calls the OpenAI API and displays a generated trivia question.
- Minimal Game frontend: Describe how main.py creates a Game instance, calls its ask_llm_for_question method, and displays the resulting trivia question in the Streamlit app.
Why it matters: This is the point where the game backend becomes a visible AI-powered application for the player. - Secret configuration and app launch: Explain how the OpenAI API key is stored in .streamlit/secrets.toml and how to run main.py so Streamlit can locate that secrets directory.
Why it matters: Keeping the API key out of the application code protects a credential while still making it available to the app.
Key terms: Game, ask_llm_for_question, main.py, st.write, st.container, .streamlit, secrets.toml, OpenAI API key, streamlit run main.py
9.3 Using AI to evaluate answers
This section explains why evaluating trivia answers requires machine-readable LLM output rather than free-form text.
- Machine-readable answer evaluation: Explain why Fact Frenzy needs the LLM to return both whether a player's answer is correct and, when it is not, the correct answer in a reliably usable form.
Why it matters: The app must use the evaluation result to give feedback and control game behavior, not merely show text to a player. - Free-form response parsing problem: Describe why free-form natural-language LLM replies cannot reliably drive application logic when evaluating trivia answers.
Why it matters: Humans can infer meaning from varied wording, but code needs a consistent, machine-friendly way to identify the result. - Display versus application logic: Distinguish the earlier use of an LLM response that could be displayed as-is from answer evaluation, where the response must also support programmatic actions such as score decisions.
Why it matters: Whether an app only displays generated text or must make decisions from it determines how strictly its output needs to be structured.
Key terms: LLM, evaluating answers, correct answer, parse, LLM response, machine-friendly, display, increment the player's score
9.3.1 Structured Outputs
This section uses OpenAI Structured Outputs and a Pydantic schema to evaluate trivia answers reliably, then exposes a Streamlit rerun bug that requires state management.
- Structured Outputs schema: Explain how OpenAI Structured Outputs makes LLM responses programmatically reliable by requiring them to conform to a supplied schema. Describe the AnswerEvaluation schema and its is_correct boolean and correct_answer string fields.
Why it matters: A schema lets the game consume an LLM evaluation as dependable fields rather than trying to interpret free-form text. - Pydantic model role: Describe why AnswerEvaluation subclasses Pydantic BaseModel: it defines a typed schema and inherits validation, serialization, and type-checking functionality. Distinguish this role from a dataclass, which lacks BaseModel's complex validation-related features.
Why it matters: Using BaseModel provides the schema and validation behavior that integrates with Structured Outputs. - Schema-enabled LLM calls: Trace how the Llm.ask method supports both regular text and Structured Outputs: schema defaults to None, while a supplied schema uses beta.chat.completions.parse with response_format and returns message.parsed. State that the caller passes the AnswerEvaluation class itself, not an instance, and receives an instance that follows that schema.
Why it matters: This conditional API wrapper preserves existing text calls while allowing evaluation calls to return typed objects. - Answer evaluation prompt flow: Describe how ANSWER_PROMPT separates the system and user messages, inserts the asked {question} and player's {answer}, and instructs the LLM to judge correctness and provide the correct answer. Trace how Game.ask_llm_to_evaluate_answer sends this populated prompt with AnswerEvaluation, and how the frontend uses evaluation.is_correct and evaluation.correct_answer after Submit.
Why it matters: The prompt, backend method, and frontend must work together for a player submission to become useful game feedback. - Rerun-induced question mismatch: Explain why submitting “Ottawa” can be evaluated against a newly displayed question: clicking Submit triggers a Streamlit rerun, game.ask_llm_for_question runs again before the Submit block, and the new question is paired with the existing text-input answer. State that the LLM and Structured Outputs are not at fault; the next section must manage state to retain the originally asked question.
Why it matters: Recognizing rerun order prevents the game from grading a valid answer against the wrong question.
Key terms: Structured Outputs, AnswerEvaluation, is_correct, correct_answer, schema, pydantic, BaseModel, data validation, type hints, dataclass
9.4 Moving through game states
This section implements Fact Frenzy as a stateful sequential game that persists a Game instance across Streamlit reruns and advances through question, answer, evaluation, and result states.
- Four-state game flow: Describe Fact Frenzy's four sequential states—GET_QUESTION, ASK_QUESTION, EVALUATE_ANSWER, and STATE_RESULT—and what happens in each state.
Why it matters: Naming the states makes it possible to ensure that the game performs the right action and shows the right interface at each point. - Game status transitions: Explain how the Game class uses a status attribute and transition methods to control the sequence: obtain_question advances to ASK_QUESTION, accept_answer advances to EVALUATE_ANSWER, and evaluate_answer advances to STATE_RESULT.
Why it matters: Keeping transitions in the central Game class gives the app a clear, controlled way to progress through a sequential interaction. - Session-persisted game instance: Explain why the Game instance is stored in st.session_state: Streamlit reruns the whole app, so storing it there preserves the same game's status and current values across reruns.
Why it matters: Without a persisted Game instance, a rerun could discard the current question and break the sequential game flow. - Conditional frontend reruns: Trace how main.py branches on game.status to show the appropriate widgets or waiting UI, and uses st.rerun() after an action changes state so the next branch can execute.
Why it matters: Conditional rendering plus explicit reruns lets the interface move cleanly from an LLM operation to the next player-facing step. - Single LLM request control: Explain that LLM calls must occur only in their corresponding states and only once per question, both to preserve correct question-answer matching and to avoid unnecessary cost.
Why it matters: Controlling when LLM requests run prevents repeated calls from corrupting game logic and spending money unnecessarily.
Key terms: GET_QUESTION, ASK_QUESTION, EVALUATE_ANSWER, STATE_RESULT, game states, Game class, status attribute, obtain_question, accept_answer, curr_question
9.5 Game mechanics: Keeping score, a New Game button, and Game Over
This section introduces the missing game mechanics that will turn Fact Frenzy from a single AI question-and-answer interaction into a game with scoring and defined start and end points.
- Fact Frenzy gameplay gaps: Describe the game features Fact Frenzy currently has and the mechanics it lacks. State that it can ask a player an AI-generated question and evaluate the answer, but it has no score or defined game start and end.
Why it matters: Identifying these gaps explains why scorekeeping, a New Game button, and Game Over states are needed next. - Planned game mechanics: State that the section will address scorekeeping and the definition of game start and end, including New Game and Game Over mechanics. Explain that these mechanics provide the structure missing from the current interaction.
Why it matters: These mechanics establish a playable game flow rather than an isolated question-and-answer exchange.
Key terms: Fact Frenzy, score, New Game, Game Over
9.5.1 Keeping score
This section adds basic scoring, game-over detection, and a session-state-driven New Game interface to Fact Frenzy.
- Score and completion tracking: Describe how the Game class tracks a player's score and determines when a game is over. Explain that each LLM-judged correct answer adds one point, every evaluated answer increments the completed-question count, and is_over checks that count against max_questions.
Why it matters: These fields give the game a measurable result and a defined endpoint. - New game session initialization: Explain how the New Game button starts or restarts a game by creating a new Game instance in st.session_state.game and then calling st.rerun. Describe why putting that work in start_new_game prevents it from running automatically when the page loads.
Why it matters: Session state plus a rerun lets a click establish a fresh game and immediately update the displayed interface. - Context-sensitive game interface: Describe how the frontend changes according to whether a game exists, is in progress, or is over. Explain that the main column stays blank before a game begins, the button becomes a primary “Start new game” action before play but a less prominent “Restart game” action during play, and the final score appears when is_over is true.
Why it matters: Matching controls and content to the current game state keeps the interface focused on the player's likely next action.
Key terms: score, evaluate_answer, num_questions_completed, max_questions, is_over, start_new_game, st.session_state.game, Game, st.rerun, new_game_button
9.6 Including multiple questions
This introduction frames the need to turn Fact Frenzy from a one-question demo into a multi-question game and previews an LLM-related challenge.
- Multi-question game need: Explain why Fact Frenzy needs multiple questions, noting that its existing score system and start/end states are not enough to make a game that asks only one question feel like a real game.
Why it matters: Multiple questions make the existing game mechanics meaningful across an actual round of play. - LLM challenge preview: Describe the section’s transition to an LLM-related challenge that must be explored before implementing multiple questions in Fact Frenzy.
Why it matters: Recognizing the upcoming challenge prepares you for why multi-question support needs more than simply repeating the current question flow.
Key terms: Fact Frenzy, score system, game’s start and end, LLM-related challenge
9.6.1 Response variability, or lack thereof
This section explains why LLM trivia responses vary and how Fact Frenzy can increase question diversity while avoiding repeats within a game.
- Probabilistic LLM responses: Explain that unlike deterministic regular code, an LLM produces text probabilistically and can give different responses to the same or similar prompts. Relate variability to the task: it can be desirable for creative question generation but undesirable when predictable or exact output is needed.
Why it matters: Understanding that LLM output is variable helps you deliberately design for diverse trivia rather than assume the same prompt will always behave the same way. - Temperature and top_p controls: Describe how temperature and top_p change next-token selection in an LLM. Explain that higher temperature flattens token probabilities, while higher top_p expands the cumulative-probability set of tokens considered, and connect moderately high temperature and high top_p to more diverse trivia questions.
Why it matters: These controls provide practical levers for balancing predictable output against varied questions in an AI-powered game. - In-game question exclusion: Describe how including previously asked questions in the prompt tells the LLM what to avoid, helping guarantee that a question is not repeated within one game. Explain that the prompt can reinforce this by explicitly instructing the model never to ask the same question twice.
Why it matters: A trivia game with multiple questions needs an explicit safeguard against asking players the same question again during a game. - Structured prompt randomness: Describe ways to increase cross-game question diversity by injecting structured randomness into prompts, such as randomly chosen categories, topics, and starting letters, asking the LLM to choose a category and topic, or adding a random seed. State that prompt engineering requires experimentation because results can be surprising and no technique is a guaranteed formula.
Why it matters: Changing prompt inputs gives the game more routes to varied questions instead of relying only on the model's built-in randomness. - Hallucination risk: State that LLMs can hallucinate by generating plausible-sounding but false or fabricated content because they rely on probabilistic patterns rather than factual understanding. Explain that external information sources can reduce but cannot eliminate this risk, so Fact Frenzy may occasionally generate an inaccurate question or answer.
Why it matters: Recognizing hallucination risk prevents treating AI-generated trivia as automatically factual.
Key terms: deterministic, probabilistic, variability, LLM, tokens, probability distribution, temperature, top_p, cumulative probability, prompt
9.6.2 Implementing multiple questions
This section turns Fact Frenzy into a multi-question AI trivia game by varying prompts, cycling game state, using a callback-driven Next Question button, and considering LLM costs.
- Configurable LLM request parameters: Describe how the Llm.ask method accepts optional temperature and top_p arguments and builds an llm_args dictionary that is unpacked into the appropriate OpenAI call. State that reasoning models may not permit explicit temperature or top_p settings.
Why it matters: Configurable request parameters let an app tune generation behavior without duplicating API-call code. - Distinct-question prompting strategy: Explain how the question-generation prompt reduces repeated trivia questions by assigning the LLM a quizmaster role, requesting a unique category and topic, supplying a seed, and listing questions already asked. Describe how the game uses a timestamp-derived integer seed and its stored questions when requesting another question.
Why it matters: A multi-question trivia game needs varied questions rather than the same response on every round. - Cyclic multi-question game state: Trace how Fact Frenzy changes from a one-pass state sequence into a cycle by returning from STATE_RESULT to GET_QUESTION. Describe that Game stores generated questions in self.questions, uses max_questions set to 5 as the game limit, and uses proceed_to_next_question to reset the status for another question.
Why it matters: Cycling state while retaining the question history is what lets one game contain several questions. - Callback-driven next-question interface: Describe how the frontend displays the current question number from len(game.questions), the maximum question count, and the score, then shows a Next Question button when the game is not over. Distinguish st.button(on_click=game.proceed_to_next_question) from an if st.button pattern by explaining that its callback runs before the button-triggered rerun, so no manual st.rerun is needed for this transition.
Why it matters: Using a callback makes the state update available when Streamlit redraws the page after a Next Question click. - LLM cost management: Explain that OpenAI LLM usage is charged by input plus output tokens and that price varies by model, with gpt-5.1 cited at $1.25 per one million tokens at the time of writing. Describe practical cost reductions: shorter prompts or outputs, output-token limits, batching requests, cheaper models, reviewing pricing, or requiring users to provide their own API key.
Why it matters: An LLM-powered app can create unexpectedly large production costs when many users generate requests.
Key terms: Llm, ask, temperature, top_p, llm_args, dictionary unpacking, system prompt, quizmaster, seed, already_asked
9.7 Adding AI personalities
This section introduces adding selectable LLM-driven quizmaster personalities to make Fact Frenzy less mechanical and more engaging.
- AI quizmaster personalities: Explain why Fact Frenzy can use an LLM to give its quizmaster a personality, such as mimicking the style of different characters when asking trivia questions.
Why it matters: LLMs can add a more engaging presentation layer without changing the game’s established trivia flow. - Player-selected quizmaster character: Describe the proposed player choice: players select which character the AI quizmaster should take, and the quizmaster asks questions in that selected character’s style.
Why it matters: Letting players choose the quizmaster style turns an LLM capability into a visible game setting.
Key terms: LLM, GPT-5.1, quizmaster, personality, players, character
9.7.1 Adding game settings
This section adds editable quizmaster and difficulty settings to Fact Frenzy and passes their current values into the LLM question prompt.
- Editable settings data editor: Explain how st.data_editor turns the Fact Frenzy settings data into an editable, spreadsheet-like UI and returns the current edited settings. Describe why fixed rows and an explicit widget key are used for this editor.
Why it matters: This lets players change game behavior without editing Python code. - Constrained setting choices: Describe how st.column_config.SelectBoxColumn constrains the Quizmaster and Difficulty columns to required choices instead of asking players to type arbitrary values. State that st.data_editor also supports other editable and display-only column configurations.
Why it matters: Constrained choices make settings easier to use and prevent unsupported values from reaching the game. - Popover settings module: Describe the settings.py design: option lists and default_settings are defined centrally, and settings_editor places the data editor inside an st.popover and returns the edited settings dictionary. Explain why the defaults are stored as a dictionary whose values are one-element lists.
Why it matters: Keeping the editor and its defaults in a small module makes the UI reusable and keeps settings readable. - Settings-driven LLM questions: Trace how the current Quizmaster and Difficulty settings reach LLM-generated questions: Game receives and stores settings, get_setting extracts a value from its one-element list, and ask_llm_for_question inserts those values into the question prompt. Describe how main.py initializes defaults, renders the editor in the side column, and calls modify_settings on each in-game rerun.
Why it matters: The setting controls only matter when their latest values are propagated into the prompt used to generate questions.
Key terms: st.data_editor, default_settings, num_rows='fixed', key='settings_editor', reruns, st.column_config, SelectBoxColumn, required, CheckboxColumn, DatetimeColumn
9.8 Summary
This summary recaps how Fact Frenzy combines OpenAI LLM calls, Streamlit state and UI components, response control, and cost-conscious design into an AI-powered app.
Where this is used later
- Go forth and develop! (chapter 12) · The Fact Frenzy AI trivia game
- Nibby: A customer service bot (chapter 10) · Python applications can call an LLM to provide AI-driven behavior.
- Retrieval Augmented Generation (chapter 10) · The use of an LLM to generate application content.
- Restricting the bot to customer support (chapter 10) · The use of an OpenAI API key for making cloud LLM requests.
- Creating a base prompt (chapter 10) · A base prompt uses system-level instructions to constrain bot behavior.
- A one-node LLM graph (chapter 10) · The roles of user, assistant, and system messages in an LLM interaction.
- What is Retrieval Augmented Generation? (chapter 10) · Retrieval Augmented Generation is introduced as a way to supplement an LLM's knowledge with external sources.
- Introduction to LangGraph and LangChain (chapter 10) · The idea that an AI application can handle multiple questions rather than one isolated response.
Concepts explained in this chapter
- temperatureTemperature is a language-model sampling parameter that controls how broadly probability is spread across possible next tokens.
- top-p filteringtop_p is a language-model sampling parameter that limits token selection to the smallest set whose cumulative probability reaches a specified threshold.
- tokenA token is a chunk of text, such as a word or word fragment, processed as an input or generated as an output by a language model.
- promptA prompt is an input supplied to a language model, typically consisting of one or more conversational messages.
- language modelAn LLM is a large language model that generates or transforms text from input sequences.
- st.data_editorst.data_editor is a Streamlit widget that displays editable tabular or collection data and returns the user's edits.
- structured dataStructured Outputs is an OpenAI API feature that constrains a model response to conform to a developer-provided schema.
- st.column_configst.column_config is Streamlit's API for configuring the behavior and presentation of columns in data editor and dataframe views.
- st.popoverst.popover is a Streamlit layout element that reveals a small popup panel when its associated trigger is activated.
- callbackA callback is a function supplied to another component for execution in response to a later event.
- cost optimizationCost optimization is the practice of reducing the resources or money required to operate a system while preserving required outcomes.
- state cycleA state cycle is a state-machine structure in which transitions eventually return to an earlier state and can repeat.
- settings editorA settings editor is a user-interface pattern for viewing and changing configurable application options.
- hallucinationAn AI hallucination is a generated response that presents invented or unsupported information as if it were factual.
- probability distributionA probability distribution assigns likelihoods to the possible values or events of a random variable.
- varianceVariability is the degree to which a model produces different outputs for the same or similar inputs.
- BaseModelBaseModel is pydantic's base class for declaring validated, schema-driven data models.
- Pydanticpydantic is a Python data-validation library that uses type annotations to validate and serialize data against declared schemas.
- state managementState management is the practice of retaining and updating application data that determines behavior across interactions or executions.
- conditional logicBranching logic is control flow that selects among alternative execution paths based on conditions or decisions.
- OpenAI API SDKThe OpenAI Python module is a client library for accessing OpenAI APIs from Python programs.
- buttonA button is a Streamlit user-interface control that can initiate an application action when activated.
- machine stateA game state is a distinct phase of a game that determines the actions and interface elements currently applicable.
- status elementA status element is a user-interface component that indicates the progress or activity of a long-running operation.
- endpointAn endpoint is an API-specific address identifying a particular operation or resource.
- GPT modelGPT-5.1 is an OpenAI large language model in the GPT-5 family that supports configurable reasoning behavior.
- messageA user message contains the instruction or request submitted on behalf of the person interacting with a language model.
- API keyAn API key is a secret credential that authenticates software when it accesses an application programming interface.
- dependencyAn external dependency is software or a remote service outside a component's implementation that the component relies on for functionality.
- contractAn API is a defined interface through which software components programmatically exchange data or invoke functionality.
- sessionst.session_state is Streamlit's session-scoped state store for retaining values across reruns of an app session.
- stateA graph state is the structured mutable data associated with a graph execution, including the messages for a conversation thread.
- .streamlit directoryThe .streamlit directory is a project directory containing Streamlit configuration files and related settings.
- secrets.tomlsecrets.toml is a Streamlit configuration file that stores application secrets such as API keys outside source code.
- streamlit runThe streamlit run command is a command-line interface command that launches a Streamlit application from a Python source file.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
