-
-
Initial Landing Page
-
Choosing a theme
-
Loading a Theme
-
A First Question
-
Another Question ( there are up to 45 in a full session)
-
Finishing the survey and waiting on a report ( its worth it!)
-
The initial report on areas assessed
-
the strangest results from your answers
-
The Real assessment quite detailed
Y(K)now Your Self
Inspiration
I started with a question that had nothing to do with AI: why is every personality test so boring?
That sounds like a design complaint. It's actually a measurement problem. When people are bored they rush, they satisfice, they park on the middle option — and in the data that's indistinguishable from someone who genuinely is middling. Boredom doesn't just make a test unpleasant. It makes it worse at measuring.
so i wandered what if a personality test was centered around topics i like "formula 1" that might hold more personal meaning and challenge me to care about the questions instead of the rush to finish the test.
So I asked whether you could have both. Keep the psychology strict, and let the world the questions live in be whatever the person actually cares about.
The moment it clicked was an early draft of a question about aesthetic sensitivity. My first attempt at the middle option was:
"Ensure it's not like the god-awful design of last year's car."
Which is completely wrong — and wrong in a really instructive way. Someone who calls last year's car god-awful is having an enormous aesthetic reaction. They're at the same end of the scale as the person who wants it beautiful. The trait is how much visual form registers, not whether you liked what you saw.
That single mistake defined the whole project. Every question has to ladder along one dimension by degree, not by flavour. And if i am honest
i have written other Agent based solutions and ran out of time to incorporate image generation and regretted it.
So i doubled down and ensured it was designed from the ground up to incorporate images ... and thanks google this part was very easy !
What it does
Pick a world you'd happily talk about for an hour. Formula 1. Baking. Astronomy. Woodworking. Or type in your own.
You then answer 45 scenario questions set entirely in that world — a livery sign-off, a tyre call in the rain, a junior mechanic who just cost the team a podium. Five answers each, and none of them with any bias.
Underneath, every single question comes from a hand-written specification that fixes exactly what's being measured and how strongly. The AI dresses that spec in your world. The costume changes. The instrument doesn't.
At the end you get a profile of fifteen traits, a note on where two traits inside the same group pull in opposite directions, and a written interpretation — plus a standalone HTML report you can download and keep, because answers are only held for seven days.
The whole thing is tinted in a colour derived from your theme. Formula 1 is always the same green. Baking is always amber. Every person's session looks different, which is the entire point made visible.
Try it: https://frontend-e44mo5hsaa-uc.a.run.app/
How I built it
Six services on Cloud Run, four of them Google ADK agents.
- Theme Agent — screens what you typed through Model Armor, then for distress, copyrighted characters, real people, and whether the world is rich enough to host fifteen traits. Then tidies it into a slug so "f1", "F1" and "formula one" all land in the same place.
- Question Agent — renders one spec into a themed scenario with five options laddered on a single trait. A mechanical gate checks the output and retries once before giving up.
- Image Agent — writes a scene description from the scenario, generates a 16:9 image with Nano Banana 2 Lite, derives three WebP sizes.
- Analyst Agent — receives the finished numbers and writes the interpretation.
- Scoring Service — plain Python, with no AI in it at all.
- Orchestrator — picks the 45 questions, runs everything, holds the answer key.
- Front End Website — displays the image and question and potential answers.
That fifth one is the decision that stands out the most. The Analyst Agent is architecturally forbidden from doing arithmetic. Scoring is ordinary maths in a service with no model call anywhere in it. An LLM doing that work would give slightly different answers between runs and you'd have no way to explain how a number was produced. The model writes prose about numbers it's handed, and that's all it does.
The answer key never reaches the browser either. Options are shuffled per session and sent out with opaque ids; the mapping lives server-side. If i Shipped the scores to the client and the whole thing is gameable and that defeats the purpose of testing..
Models: gemini-3.7-flash for the reasoning-heavy work, gemini-3.5-flash-lite for image prompts, gemini-3.1-flash-lite-image for the artwork. Firestore for state, Cloud Storage for images, Cloud Scheduler for catalogue refresh.
then there is the dressing or in this case the front end web site, just some Python and html one file , i know its nasty but it works if i had more time than 3 working nights it would be prettier...
Challenges I ran into
Vertex AI now being called Gemini Enterprise Agent Platform. when you're used to the old way things change , billing line items renamed, I learned to verify every model string in Model Garden rather than trusting any guide — including my own from two days earlier. i Guess thats life in SAAS.
Everything 404'd until I found the global endpoint. Gemini 3.x models are served globally, not regionally, while Cloud Run services still deploy to a region. thats painful , but once you accept it you get used to it .... also No NZ Data center come on Google !
The system worked perfectly for exactly one hour after every deploy. I was caching the ID tokens used for service-to-service auth. Tokens expire hourly. Every session start would fail with a 401 that looks exactly like a permissions problem. If I'd deployed the night before a demo, nothing would have worked the next morning.
The image debugging was the good one. I was getting relentless 429s and assumed a rate limit. So I wrote a six-line script calling the image model directly — and while the app was failing, it got 4 out of 6. The model had capacity I wasn't using.
The logs then told me why. Instead of image IDs, the agent was returning things like 'sub-agent-failed', 'IMAGE_GENERATION_FAILED', and once, memorably, an entire scene description of a banquet hall. An LLM was narrating its own failures in prose, differently every time, and my regex was logging all of it identically as "no match".
The fix wasn't more retries. It was making the output mechanical — a literal example, a single FAILED token, a hard cap on output length — and dropping thinking_level to minimal, because writing forty words of scene description does not need a reasoning budget.
Sessions were silently dying at question 14. Items that fail the quality gate get dropped server-side, but the client was calculating its next request from how many items it had, not how many it had asked for. Those two drift apart the moment anything fails, and eventually the client asks for a window it's already consumed. The outgoing question had already faded to zero opacity, so you got a blank screen with no error.
And one purely self-inflicted wound: the report's Highest/Lowest columns used a CSS class called .pair. So did the theme wall tiles — which carry aspect-ratio: 1/1. The report card was being forced square, producing several hundred pixels of beautiful empty nothing.
Accomplishments that I'm proud of
I wrote the instrument before I wrote the app. 272 hand-written specifications across 34 trait facets, each with a construct definition, a concrete behaviour, and a fixed position on the scale. It took a long time and it's why the questions measure anything.
I test the interpretation adversarially. Three profiles go through the Analyst. One is strongly differentiated. One is a perfect mirror of it — every score reflected around the midpoint. And one is a person who pressed the middle button forty-five times.
The mirror test is the clever one. Read both narratives side by side, and any phrase appearing in both is decoration, not description. It catches horoscope language that no single read reveals. The third profile has to open by saying the results are provisional — writing confident prose about a meaningless input is the worst thing an assessment can do.
That testing changed the product. My first Analyst reframed every weakness as a hidden strength. Low self-discipline became "flexible in fast-moving environments". Low cooperation became "an effective advocate against flawed group decisions". I felt it betrayed its purpose delivered unflattery — which means it wasn't discriminating between people at all. Fixing it required an explicit rule that costs get named, not softened.
The report only claims what it can support. No percentiles, because I don't have population data. Scores rank your own traits against each other, the bars diverge from your average rather than an implied population mean, and the report says so in plain language instead of implying more.
Some questions refuse the costume on purpose. Anything touching anxiety or low mood gets asked plainly regardless of the theme you picked. Dressing up a bad day inside something someone loves is unpleasant, and it adds noise exactly where you least want it.
What I learned
Multiple agents are worth it when the work is parallel and involves judgement. They're a liability when it's serial and the interface between them is prose. The Image Agent taught me that: an extra model call, an extra network hop, and three failure modes indistinguishable from each other — all to translate a scenario that already described a scene.
Structured output isn't a nicety. The moment an agent can reply with free text, it eventually will, and your parser will log a paragraph of apology as a missing value.
Theming makes the psychometrics better, not just prettier. Ask someone abstractly whether beauty matters and there's an obviously flattering answer. Ask inside Formula 1 and "I don't care how it looks, I care whether it's quick" is a respectable thing to say. The costume makes the unflattering pole safe to endorse, which is exactly what a good item needs.
Read your own output. I built a mechanical gate that catches option counts, lengths and banned vocabulary. It cannot catch two options that are the same feeling worded differently — which is the failure that matters most. I found a broken item by sitting down and reading all forty-five, and there was no substitute for it.
An error that changes and then stops changing means state got cached. I chased a KeyError for a while that was just the corpse of a permissions problem, held in a module-level variable that had been populated with an empty dict.
What's next for Y(K)now Your Self
Norms. Every completed session already writes a de-identified response vector — trait, answer level, timing, theme, and nothing that identifies a person. Hive-partitioned, so it mounts as a BigQuery external table with one statement. A few hundred responses and I can compute real percentiles instead of relative ones.
Calibrating the specs. Right now each one's difficulty is expert judgement, not a measured value. With real response data I can estimate them properly — and expect to drop 10–20% of them, which is why there are eight per facet rather than four.
The question I most want to answer: does the world you pick change your scores? If someone's Excitement Seeking reads higher under Formula 1 than under gardening, the costume is contributing variance and the might need tightening. I record the theme on every response specifically so this becomes measurable. It's the validity question unique to this whole approach, and it could be something to dive deeper into.
Warmed themes and faster art. Image generation is rate-limited upstream, so a brand new world fills in over several minutes. Pre-warming popular themes offline makes almost every session instant.
Other-report. The single cheapest way to improve a self-report instrument is to ask someone who knows you well to answer the same questions about you. Same specs, second-person framing, and the gap between the two is often more interesting than either alone.
Monetization. Making the images costs a bit in token and real world dollars (storage not so much yet!), if i am to keep this going i will need a way to re attribute or cover its costs.
More auditing. Model armor is there for safety but alot is sanitized by the multi agent setup and their instructions. With longer storage , and other question criteria this could quite easily be used to give reports between execution , tracking failiure rates better from the orchestrator . Load Balancing and then cloud armor could be good next steps
Built With
- adk
- cloud-run
- firebase
- html5
- javascript
- nano-banana
- python
Log in or sign up for Devpost to join the conversation.