Inspiration

At an elderly care center, I learned that patience is a form of respect. As a wealth management intern at a bank, I saw how much technical language surrounds decisions that ordinary families make every day. Studying for the Financial Risk Manager (FRM) exam brought those two experiences together. I was spending months learning to measure financial risk, while retirees were being asked to judge the same risks in a single conversation with a salesperson, with their life savings on the table. A phrase like “guaranteed income” sounds reassuring. To understand it, you need to know who stands behind the guarantee, what it costs each year, and what happens if you need your money back in year three. That takes a closer look. I built Second Look so anyone can take that closer look. Someone who spent forty years saving shouldn’t have to learn the seller’s vocabulary before they can understand the product.

What it does

Second Look has eight screens: Welcome, Your situation, Explore products, Compare, Check an offer, History, My products and My account. Every screen is reachable from one row of large buttons, with no sidebar and no hidden menus. One label, six tiers. Twelve product categories, from insured savings and Treasury bills to annuities, stock funds and Bitcoin, each get exactly one of six labels on a nautical scale: ⚓ Safe Harbor, Calm Bay, Coastal Waters, Open Sea, Storm Waters, and Uncharted Waters. Each label carries a wave icon, a tier number and a word, so the meaning never depends on color. Beside the label sit two separate badges, one for getting your money out and one for cost, because similar risk does not mean similar fees or similar access. Every product’s worst historical year is shown as what it would have done to $10,000. Your situation. Five questions ask about age, income, when the money might be needed, how the person would react to a drop, and their main goal. Four published rules turn those answers into “Worth thinking about” notes. For example, someone who needs their money within five years sees a note on any product that locks it up for years. Check an offer. A person can paste a pitch, pick a sample, or simply say it out loud. Second Look returns a plain summary, verified warning signs, the label, the badges, and three to five questions to ask before signing. Every fact is tagged “Found in the offer” or treated as unknown, and a “Show me the numbers” panel shows each step of the classification and where each input came from. My products. Signed-in users can list what they already own and get “Things worth a look”: findings from seven published rules, such as money locked up that they may need soon, yearly costs above 1%, a Safe Harbor account earning less than inflation, a CD ending within 60 days, or bank deposits above the $250,000 FDIC limit at a single bank. History. Users pick a start year from 1928 onward and a time period, then see how $10,000 would have moved through stocks, Treasury bonds and Treasury bills, including the losing years along the way. Voice, both ways. Every product card has a Read aloud button. Offer results have Listen to the result. The default speed is slower than normal, and captions always show the exact words being spoken. Numbers are converted to words first (“five thousand six hundred twenty dollars”), so the voice and the captions always match.

How we built it

I built Second Look solo in Python 3.13 and Streamlit, with Altair for charts, Cartesia for voice, Supabase for optional accounts, and live data from FRED. The core design decision is that the AI reads, and the code decides. Offer analysis runs in three separate stages, each in its own file. Extract (claude_helper.py, prompts.py). The language model receives the offer inside tags, so it treats the text as material to examine rather than instructions to follow. It also receives the allowed list of product categories and a fixed JSON shape. It must quote the offer’s exact words (25 words at most) for every fact, or answer “unknown.” It is explicitly forbidden from assigning a tier, calling a product safe or good, or using its own memory for any number. Verify (checks.py). Python confirms that every quote really appears in the pasted text, allowing for differences in capital letters, spacing and curly versus straight quote marks. Any fact whose quote can’t be found becomes unknown. A red flag with invented wording doesn’t count. A category that isn’t on the list becomes “Not rated yet.” Additional guards catch an offer claiming FDIC or government backing for a product type that has none, and they replace any summary sentence that sounds like advice (“buy,” “best,” “safe investment”) with a neutral one. Classify (scoring.py). A deterministic, five-step rule assigns the label. Step 1: is it backed by the U.S. government? Then the worst calendar-year loss is compared against published thresholds of 10%, 25% and 50%. Products whose worst year was above 50% are split by whether standard protections exist. A guaranteed return more than double today’s live Treasury bill rate is placed off the map with a scam warning, and missing facts produce “Not rated yet” instead of a guess. The same principle governs the portfolio check. The seven rules produce the findings. The AI may only rewrite them in plain words, and it receives no names, nicknames or company names. Before anything is shown, Python scans the AI’s wording. If it contains any dollar amount or percent that isn’t in the findings, or the words buy, sell, best or recommended, it is discarded and the user sees the rule-based findings alone. Live data with honest fallbacks. live_data.py pulls the 3-month Treasury bill rate, the 10-year Treasury rate, national average savings and 12-month CD rates, and CPI inflation from FRED. Good downloads are cached for six hours. Failures are never cached. If FRED is down, the app shows saved figures, labeled “saved figure” with their as-of date, so stale data never passes as fresh data. Every number in products.py has a source and an as-of date. Privacy by design. Guests save nothing. Accounts are opt-in with a consent checkbox. Each browser session gets its own Supabase client, and row-level security in the database double-checks that people can only reach their own rows. Saved offer checks store the category, label, badges and summary, but never the pasted text. Users can download all of their data as one file, or delete their account and everything in it by typing DELETE. Demos use only a fictional persona, Rosa, and six fictional sample offers. Built to survive a live demo. Every external service returns a calm, plain-language message instead of crashing: if the voice fails, the captions still appear, and if the AI is unavailable, the samples still work. A one-time script, save_demo_cache.py, records a real AI reading of a sample offer and all of the voice files. A large “Demo replay” switch then runs the full experience with no internet connection, while the label is still recomputed live by scoring.py every time. Accessibility as a requirement, not a polish step. The base text size is 22px. All text was checked at 7:1 contrast or better, which meets the strictest WCAG level. Animations are turned off, buttons span the full width, gauge bars are hidden from screen readers because a word always says the same thing, and every layout collapses cleanly to a single column on a phone. Tested. Five test scripts (check_scoring, check_safety, check_voice, check_portfolio and check_replay) cover every tier boundary (10% gives Calm Bay, 10.1% gives Coastal Waters), invented quotes, false insurance claims, advice words, AI wording that introduces new numbers, and service failures. They run without API keys or credits.

Challenges we ran into

Making simplicity honest. One label can’t capture every risk. If I had used only the label, a fixed annuity and a savings account could look misleadingly close. Pairing each tier with separate access and cost badges, dollar examples, sources and a step-by-step explanation let me keep the label simple without hiding anything. Not trusting fluent output. A well-written AI answer can still contain claims the offer never made. Requiring exact quotes, then checking those quotes in code, gave me a concrete test instead of a hope. It doesn’t prove every interpretation is right, but it makes invented evidence very hard to slip through unnoticed. Letting the AI help without letting it advise. For the portfolio explanation, I wanted warm, readable wording without the risk of a model inventing a number or nudging someone to sell. The answer was to treat the AI’s output like untrusted input: every figure must trace back to a rule’s finding, or the text isn’t shown. Unreliable services during a live demo. Live data ages and APIs fail at the worst moment. Dated sources, uncached errors, saved fallbacks and a fully offline replay mode mean the app stays understandable even when everything external is down.

Accomplishments that we're proud of

I’m proud that the most important decision in the app is the most inspectable one. The label never comes from a salesperson, and it never comes from the AI. It comes from a short, published rule that anyone can read, test, and argue with. I’m proud of the evidence trail. Users can see the exact words quoted from their offer, which facts came from the offer and which came from the reference data, and each step behind a label. When the facts aren’t there, the app says “Not rated yet” instead of guessing. I’m also proud of the insight the product makes visible: two products with similar market risk can carry very different fees and very different access to your money. Second Look puts those differences side by side, in dollars. As a solo builder, bringing together financial risk reasoning, AI extraction with verification, live government data, voice in and out, privacy-respecting accounts, and an accessible interface, then testing all of it, was the accomplishment I care about most.

What we learned

Financial literacy is partly an interface problem. A 43% loss is abstract, but “$10,000 would have become $5,620” is not. A lock-up period means little until it is connected to when someone says they’ll need the money. Trust requires several distinct checks. Finding a quote in an offer proves those words were written; it doesn’t prove the seller’s claim is true. That distinction shaped the whole architecture: the offer provides evidence, the reference data provides facts, and the rules make the decision. Historical returns need their journey. Showing both annual losses and compounded growth explains why an attractive 20-year result can still involve years that would frighten someone into selling at the worst moment.

What's next for Second Look: Read Between the Returns

My next priorities are comprehension testing with older adults, and review of the classification framework by qualified financial professionals and fiduciary advisors. I also plan to build a broader evaluation set of misleading, incomplete and adversarial offers, including prompt-injection attempts hidden in sales text. I want to expand multilingual support, starting with Spanish and Chinese, and make the line between category-level assumptions and the verified terms of an individual offer even clearer. I’d welcome partnerships with AI companies for API credits, technical mentorship, and support evaluating financial-language extraction and accessible voice experiences. Longer term, I hope to bring Second Look to senior centers, libraries and credit unions, so people can use it at the moment it matters most: before they commit their savings.

Built With

  • cartertesia
  • chatgpt
  • claude
  • grok
  • python
  • streamlit
Share this project:

Updates

Submission history