Building and Testing a Production LLM-Powered Quiz Application

google · Education · 2023 · source Agentic: no · promotional bias: low · source-backed · extracted with prompt 0.5.5 (schema v2)

Generated from extraction.json — do not edit by hand. Every claim cites verbatim source evidence; stated means the quote directly asserts the claim, implied means it supports it indirectly.

Problem

Domain: LLM-powered quiz generation

Google's Quizaic team needed to turn a static trivia app constrained by fixed English-only content into a dynamic quiz system that could generate questions, languages, images, and hosted multiplayer sessions on demand, while handling the unreliability, latency, malformed outputs, hallucinations, model drift, and validation burden introduced by generative AI.

Constraints:

Architecture

Quizaic uses a Flutter client for web/mobile quiz creation and play, a Python Flask API server on Cloud Run, Firestore for document-shaped quiz/session/result data and real-time browser updates, and Vertex AI for Gemini quiz generation plus Imagen topic-image generation. The API prompts Gemini for structured JSON quiz content, post-processes malformed responses, and starts quiz and image generation in parallel. A validator pipeline uses an LLM to judge generated quiz assertions after first measuring validator accuracy on Open Trivia data. Defensive engineering wraps the system with placeholders, retries, fallback imagery, model-version pinning, prompt/version pairing, batching, caching, and regression testing across model changes.

Diagram confidence: explicit (topology described by the source).

flowchart LR
  flutter-client[Flutter client] --> cloud-run-api[Cloud Run API]
  flutter-client --> firestore[Firestore]
  cloud-run-api --> vertex-ai-gemini[Vertex AI Gemini]
  cloud-run-api --> vertex-ai-imagen[Vertex AI Imagen]
  cloud-run-api --> firestore
  vertex-ai-gemini --> json-post-processor[JSON post-processor]
  json-post-processor --> cloud-run-api
  vertex-ai-imagen --> fallback-image-handler[Fallback image handler]
  fallback-image-handler --> cloud-run-api
  cloud-run-api --> validation-pipeline[Validation pipeline]
  validation-pipeline --> vertex-ai-gemini
  validation-pipeline --> open-trivia-benchmark[Open Trivia benchmark]
  session-host[Session host] --> flutter-client
  session-host --> firestore
  player-clients[Player clients] --> flutter-client
  player-clients --> firestore
  model-version-gate[Model version gate] --> vertex-ai-gemini
  model-version-gate --> validation-pipeline
Drag to pan · scroll or use +/− to zoom · reconstructed from the source's described component topology

Components

Component Role Interacts with
Flutter client Cross-platform web/mobile UI for creating, hosting, playing, and viewing quizzes. Cloud Run API, Firestore
Cloud Run API Python Flask backend for quiz APIs, generator calls, parsing, hosting actions, and application logic. Flutter client, Vertex AI Gemini, Vertex AI Imagen, Firestore, Validation pipeline
Firestore NoSQL document backend for quizzes, sessions, results, personas, and real-time state updates. Flutter client, Cloud Run API
Vertex AI Gemini LLM service used to generate structured quiz questions and answers from topic prompts. Cloud Run API, JSON post-processor, Validation pipeline
Vertex AI Imagen Image-generation model used to create topic-specific quiz imagery. Cloud Run API, Fallback image handler
JSON post-processor Defensive cleanup and parsing layer for malformed model output such as markdown-wrapped JSON or conversational prefixes. Vertex AI Gemini, Cloud Run API
Fallback image handler Degrades gracefully to the Quizaic logo when image generation fails while preserving quiz functionality. Vertex AI Imagen, Cloud Run API
Validation pipeline LLM-as-validator process for assessing quiz accuracy, creating confidence scores, and running regression tests. Cloud Run API, Vertex AI Gemini, Open Trivia benchmark
Open Trivia benchmark Known-answer trivia dataset used to estimate the accuracy of candidate validator models. Validation pipeline
Session host Quiz host experience that starts synchronous or asynchronous games, advances questions, and reveals results. Flutter client, Firestore
Player clients Anonymous browser clients that join sessions, answer questions, and receive real-time updates and end-state effects. Flutter client, Firestore
Model version gate Operational discipline for pinning model versions and advancing them only after testing. Vertex AI Gemini, Validation pipeline

Data flow

A creator chooses a topic, difficulty, question count, language, and generator. The Flutter UI creates a placeholder quiz while the Cloud Run Flask API calls Vertex AI Gemini for quiz JSON and Imagen for a topic image in parallel. Firestore stores quizzes, sessions, participants, and results, then streams real-time updates to the host and players during synchronous play. Post-processing strips non-JSON prefixes or markdown wrapping before parsing. For quality checks, generated multiple-choice questions are decomposed into truth assertions, batched, shuffled, and sent to a validator LLM. The validator's answers are compared with known true/false labels to estimate quiz confidence and can feed background validation or model/version regression reports.

4 supporting quotes
“the Gen part well first of all this is what we have for the app um we have two tier like so we have a UI server that um host the UI and we use flutter which I'll talk about then we have a separate API server uh it's a python flask application running on cloud run um and”transcript, stated
“the UI and we use flutter which I'll talk about then we have a separate API server uh it's a python flask application running on cloud run um and that python is talking to the llm um on the llm site we are using Gemini as a model uh so Gemini is the model we use to generate the quizzes uh and imag”transcript, stated
“we are using Gemini as a model uh so Gemini is the model we use to generate the quizzes uh and imag gen is the model we use to generate the image on that topic uh and this is all running on Google Google Cloud um weex AI um which is the which is the AI”transcript, stated
“anything we change on the server site this state gets updated in the browser automatically without having without us having to do anything so that was also a good choice for us so that's very quick explanation of what we use then we have the typical data”transcript, stated

Why this architecture

The architecture keeps ordinary application concerns ordinary: Flutter handles cross-platform UI, Cloud Run hosts containerized services with autoscaling and logs, and Firestore maps naturally to quiz documents while streaming session state. Generative AI is isolated behind Vertex AI calls and defensive parsing because the app cannot trust structured output. Quiz and image generation run independently so image failures do not block play, and validation is separated from interactive generation because users should not wait for a second LLM pass. The validator exists because syntactic checks only prove the quiz parses; correctness requires measuring whether answers are true.

4 supporting quotes
“we chose to use flatter for the client because we want to have a mobile app and also a web application uh and flutter for those of you who might not know it's a it's it's a platform for multiplatform applications from Google and it uses a language called”transcript, stated
“client side so it work pretty well for us um for hosting the UI and API servers we are using Cloud run which is the um container service on Google cloud and it's the default place that I always deploy my code to because it gives you flexibility uh with the containers but it's”transcript, stated
“you don't want to blindly store this quote unquote uh Json output in a database and start serving it only to find out that the user gets you know a very weird experience so you want to parse the output check it's it exists it looks reasonable and it has the”transcript, stated
“my my um idea in my mind is we'll store the quiz as it is fire off this validation as as a background thread again using our good old software techniques that we've been using for 50 40 years and then once the validation is done we can attach the little”transcript, stated

Models used: Palm · Gemini Pro · Gemini Ultra · Gemini 1.5 Flash · Imagen version 1 · Imagen version 2

Scale

Alternatives considered (roads not taken)

Continue with Open Trivia as the content source

Rejected because: It was limited in categories, question count, language, format, images, and required tedious manual curation to expand.

“approach um I'm a bit of a perfectionist and I didn't really like how this was turning out um there was a limited list of topics if you noticed in that previous slide there was maybe 25 or 30 categories and that was it uh and I wanted a much broader”transcript, stated
“want to expand that content you've got to do work you've got to do the tedious work of actually handcrafting good trivia questions which is going to slow you down and maybe prevent you from ever really realizing this application then in March 2023 we all saw this sort of explosion that came”transcript, stated

Generate only the quiz before requesting the image

Rejected because: Quiz and image generation are independent slow LLM calls, so starting both at the same time reduces wait time.

“you can parallelize calls like we don't wait till the quiz is generated before we start asking for the image we can start them both going at the same time uh we can cache common responses so all those things we've been doing for a long time uh still apply”transcript, stated
“parallelize calls like we don't wait till the quiz is generated before we start asking for the image we can start them both going at the same time uh we can cache common responses so all those things we've been doing for a long time uh still apply and you should still”transcript, stated

Trust prompted JSON output directly

Rejected because: The model sometimes wrapped or prefixed JSON, so parsing needed post-processing and defensive checks.

“it would prefix the Json with here's your Json like thank you you just blew up my app and then I realized it's not its fault I need to be sure I deal with the fact that I might not get perfect Json so I strip out anything that comes before the beginning of”transcript, stated
“not its fault I need to be sure I deal with the fact that I might not get perfect Json so I strip out anything that comes before the beginning of the Json and then uh of course I got something after the document I won't keep going with this story but um you can imagine uh why”transcript, stated

Use the LLM for free-form answer grading

Rejected because: Traditional fuzzy matching was faster, cheaper, and good enough for the user-interface path.

“I know I'll ask the llm bad idea because number one that's a lot of propagation delay and latency in the user interface”transcript, stated
“a python library or whatever language you're working on which does Levenstein distance or fuzzy matching things like that it may not be as good as the solution you want but it's probably going to be good enough um another example was just this this little um”transcript, stated

Use the LLM for logo image editing and profanity filtering

Rejected because: Conventional image editing and standard profanity filters were more appropriate than slow, costly LLM calls.

“tried everything I was talking to meta and meta said why don't you just use Photoshop or something like that I was like yes why don't I so sometimes there's a good oldfashioned way to do things you don't have to do everything with the new cool stuff and that avoids an expensive call to uh to what”transcript, stated
“question was are we taking the user input and putting it straight into the model not exactly I have a I'm using a profanity filter uh which is minimal but you want to do that kind of thing and I haven't bullet hardened it you know to be perfect”transcript, stated

Always use direct provider SDKs

Rejected because: The team found API churn across Palm, Gemini, and other providers made a higher-level abstraction such as LangChain worth considering.

“and now we will probably use Gemini flash um Palm had it's own API and libraries and then when we switched to Gemini we had to use new libraries um so it's a very fast changing landscape not not just within Google but also outside Google as well and you need to kind”transcript, stated
“with bunch of different places so my idea changed and now I kind of prefer Lang chain because they have the right level of abstractions right uh but it's up to what you prefer yeah and by the way um there's no right answer here I'm not advocating everyone here should use Lang chain there's always”transcript, stated

Patterns

structured-output-contract (interface)

The quiz generator prompts Gemini to return JSON with questions, responses, and correct answers, then pairs that prompt with output parsing logic.

“different prompts so normally we started with something simple like give me a trivia quiz on this topic um but then we don't want free text so we add it like give me in Json and give me this category so you have to play with it to get the results you want but in the end this is the kind of prompt”transcript, stated
“you see that we get a Json response with a question responses and the correct response”transcript, stated

fallback-degradation (resilience)

Image generation can fail without failing the quiz; the app falls back to the Quizaic logo so the core workflow continues.

“quizzes or no image these are questions you have to ask yourself as an application developer my answer would be no I can't live without a quiz I can live without an image so if the image generation fails I put the quic logo there as the image for the quiz is it perfect no do do you get the”transcript, stated
“if the image generation fails I put the quic logo there as the image for the quiz”transcript, stated

prompt-versioning (prompt-ops)

Prompts are treated as code and versioned together with the parsing logic that expects their output shape.

“manage your prompts like code it is critical part of your application now um version your prompts”transcript, stated
“the output parsing and the prompting kind of go hand inand so you want to think of those two as a that you you're matching up”transcript, stated

eval-driven-development (quality)

The team measures prompt and model changes with validator accuracy, generated-quiz accuracy, and regression test reports before adopting new models.

“you need to have something like this where you can basically measure things in whatever way whatever you need to measure”transcript, stated
“run a test suite and it'll use uh the the preferred model to generate thousands of quizzes and give us a report”transcript, stated

llm-as-judge (evaluation)

An LLM validator checks generated quiz assertions and produces a confidence signal for quiz accuracy.

“we should use an llm to evaluate the llm output as far as its accuracy goes”transcript, stated
“call to the llm and generates a quiz as you've seen twice we take that quiz and we send it to the validator and the validator asks the LM to validate the quiz accuracy and based on that result we get back something to attach to the quiz so think of this as a confidence rating we're not going to”transcript, stated

batch-offline-processing (throughput)

Validation decomposes quiz questions into assertions and batches them into one API call, and the app batches prompts where possible.

“assertions batch them because your quiz is going to have multiple questions and again you get the benefit of getting everything done in one API call”transcript, stated
“batch your prompts wherever it makes sense to get away with you know sort of minimizing the round trips”transcript, stated

latency-optimization (performance)

The app uses placeholders, progress indicators, background validation, and parallel quiz/image generation to mask slow model calls.

“these llms are slow and you need to build something into your user interface to condition the user to understand you are working on it”transcript, stated
“I'm not going to Advocate that we do this process interactively because I don't want to make the user wait for the evaluation”transcript, stated

model-routing (model-ops)

The app kept Palm, Gemini Pro, Ultra, Flash, and Imagen variants available for testing and quality comparison as models evolved.

“we initially started using pal which was the model that came out from Google in a in March 2023”transcript, stated
“Gemini came along with Pro and Ultra versions and that's the latest one that we are using right now”transcript, stated

safety-policy-enforcement (safety)

The team explicitly reviews model safety settings because commercial safety filters can block legitimate quiz topics.

“functionality without having to lose the whole workflow yes so that's a good that's a good um compromise I think sometimes you'll find the llm is too cautious a lot of the commercial llms are the providers are struggling with making sure they don't do the wrong thing however you define the wrong”transcript, stated
“look look carefully at the safety settings in your application or your API calls”transcript, stated

Tradeoffs

Dynamic content generation

Chose Gemini-generated quizzes and Imagen-generated images over static Open Trivia content.

Rationale: Generative models removed constraints on topic, language, format, and imagery that made the original app hard to expand.

“revisit the problems that we initially had with this application so if you remember we had limited set of topics now with Gen we can do pretty much any topic we had limited questions and answers now again everything like it's pretty much like unlimited questions and answers we had Limited Format”transcript, stated
“free form we can we can have pretty much any format we want it used to be English only now it's any language um there were no images now we can generate images on demand they might not be great but at least it's it's an image um that we can use and expanding quiz content was very difficult before”transcript, stated

Image failure handling

Chose fallback image and keep quiz flow over fail the whole quiz when image generation fails.

Rationale: The generated quiz is core functionality; the image improves experience but is not required to play.

“questions you have to ask yourself as an application developer my answer would be no I can't live without a quiz I can live without an image so if the image generation fails I put the quic logo there as the image for the quiz is it perfect no do do you get the functionality without having to lose”transcript, stated
“do you get the functionality without having to lose the whole workflow yes”transcript, stated

Validator latency

Chose background confidence scoring over interactive validation before showing the quiz.

Rationale: A second LLM pass improves confidence but would make users wait, so it should attach scores after generation.

“I'm not going to Advocate that we do this process interactively because I don't want to make the user wait”transcript, stated
“once the validation is done we can attach the little confidence score to the quiz”transcript, stated

Abstraction layer

Chose consider LangChain-style standardization over provider SDKs everywhere.

Rationale: Direct libraries give more control, but provider and model churn created repeated interface work.

“there's always a trade-off when you use an abstraction layer you lose a certain amount of control”transcript, stated
“not having to code to specific interfaces it outweighs the loss of control”transcript, stated

Model upgrades

Chose pin and advance model versions after tests over always use latest model alias.

Rationale: Model changes can improve quality or break behavior, so upgrades should be treated like software dependencies.

“if you just use a name that refers to whatever the latest model is then you leave your application open”transcript, stated
“test and make sure you're getting exactly what you want and only advance and pin it to the version you specifically want”transcript, stated

Gotchas (what bit them)

A precise prompt still occasionally produced non-JSON output.

Root cause: LLMs can add conversational prefixes or markdown despite explicit JSON-only instructions.

Resolution: Post-process and parse defensively instead of trusting the prompt contract alone.

“it would prefix the Json with here's your Json like thank you you just blew up my app”transcript, stated
“even though I got the prompt to be 98% accurate I still need to add the code to to make sure it does the right thing”transcript, stated

The same prompt and model can return different answers.

Root cause: LLM calls are non-deterministic and models can also be updated under the same name.

Resolution: Expect inconsistent output, pin model versions, and run tests before adopting new model versions.

“except the uncertainty of an llm this is a bit repetitive so I'm going to kind of go through this quickly but the same prompt same model can give you different output the same prompt uh same model can the model can get updated to give you different output and so on so just expect the unexpected”transcript, stated
“your API calls um and decide for yourself what's the you know tolerable level of of safety for your application pinning model versions is really important because if you just use a name that refers to whatever the latest model is then you leave your application open to the fact that it's”transcript, stated

More prompt rules sometimes made quiz quality worse.

Root cause: The model did not monotonically improve as the prompt became more verbose.

Resolution: Find the minimal prompt that works, then measure every additional constraint before keeping it.

“the more where was your prompt gets then the results get worse as well”transcript, stated
“figure out the minimal prompt that works and kind of build on that”transcript, stated

Multilingual prompting was fragile.

Root cause: Placing the language phrase in the wrong part of the prompt made the model apply it to the topic instead of the quiz.

Resolution: Treat prompt wording and placement as production logic and test generated output in target languages.

“have to add the words in Swedish to my prompt and so got up in front of the audience and I added in Swedish and I put it in the wrong place in the sentence I said generate a quiz based on this topic fill in the topic in Swedish and it interpreted in Swedish as applying to the topic and my topic”transcript, stated
“these things are very finicky and they're very literal whatever you tell them to do they're going to do”transcript, stated

Syntactic validation does not prove quiz correctness.

Root cause: A quiz can parse as JSON and still be off-topic, inaccurate, or have wrong answers.

Resolution: Use semantic validation with LLM-as-judge assertions and regression reports.

“then there's a semantic question is this a good quiz is this an accurate quiz”transcript, stated
“you can compare them to the known values and decide if the uh quiz makes sense or not whether the quiz is accurate or not”transcript, stated

LLM calls can create user-visible latency and failure modes.

Root cause: Generation calls are slow and may return empty results, false answers, no output, or safety-filter rejections.

Resolution: Use progress UI, retries, background work, fallback images, and safety-setting review.

“to having consistency so if you call something you expect the same response but with llms that's not true and llm calls are slow so it's not like normal API call I mean sometimes it's really quick but sometimes you need to wait a little bit to get an answer sometimes doesn't respond”transcript, stated
“you get some empty results can you live with no quizzes or no image”transcript, stated

Evaluation

Quizaic validates at three levels: syntactic checks for parseable JSON, structure checks for expected question/answer counts and image presence, and semantic checks where a validator LLM judges generated quiz assertions. Validator quality is first measured against Open Trivia known answers; generated quizzes are decomposed into true/false assertions, batched, shuffled, and compared with expected labels. The team intends to run background confidence scoring and regression suites across model or app versions.

Metrics: parseable JSON · expected quiz structure · image exists · validator accuracy on Open Trivia · Palm validator accuracy around 80% · Gemini Ultra validator accuracy around 94% · Gemini Pro generated quiz accuracy around 70% · Gemini Ultra generated quiz accuracy around 91% · confidence score · model-version regression report

5 supporting quotes
“switch gears to testing and validation so unit and functional tests are as important as ever as we've discussed uh it's easy to check the existence or the format of a quiz so that's pretty straightforward is it a parsable Json document which of course you have you want to do you don't want to”transcript, stated
“we should use an llm to evaluate the llm output as far as its accuracy goes”transcript, stated
“assessing the quality of quizzes it was assessing the quality of an llm to judge a quiz and what we found was uh Gemini Ultra is about 94% accurate if you give it a random quiz and it tells you an answer for for it it judges its accuracy it's about 94% correct so that gives us the first part of”transcript, stated
“quizzes Which was disappointing and meta found that the as a as an assessor as a validator it was it was 80% but the actual quality of its quizzes was only 70% so it's two different quantities we're talking about the good news is looking at this same problem with quizzes generated by Gemini Ultra”transcript, stated
“only 70% so it's two different quantities we're talking about the good news is looking at this same problem with quizzes generated by Gemini Ultra has jumped up to 91% now don't take these uh numbers too literally because they change every day but the point is Gemini Ultra has proven to be a very”transcript, stated

Outcomes

The demo ran with more than 100 live participants and 15-second answer windows. The project moved from roughly 25-30 fixed categories and a few thousand static questions to arbitrary generated topics, languages, formats, and imagery. Validation numbers included around 80% Palm answer-judging accuracy, 94% Gemini Ultra judging accuracy, 70% Gemini Pro quiz accuracy, and 91% Gemini Ultra quiz accuracy.

4 supporting quotes
“revisit the problems that we initially had with this application so if you remember we had limited set of topics now with Gen we can do pretty much any topic we had limited questions and answers now again everything like it's pretty much like unlimited questions and answers we had Limited Format”transcript, stated
“then we'll get into the Gen part well first of all this is what we have for the app um we have two tier like so we have a UI server that um host the UI and we use flutter which I'll talk about then we have a separate API server uh it's a python flask application running on cloud run um and that”transcript, stated
“which is a Nole database that I'll talk about um and fire store has this nice feature of real time updates so it basically gives you free real time updates to your browser and we use that in the application to to give you updates uh about the state so that's kind of the architecture of the”transcript, stated
“once I had access to this large language model I didn't come up with the version you're looking at today but I came up with a pretty respectable prototype in seven weeks I didn't know Dart and I had to pick up flutter on the Fly so that all slowed me down a little bit but the point is I went from”transcript, stated