← All projects

03 · GM sponsored · 2025

Policy analytics

An on-device AI analyst for congressional data

PolicyAI is a plain-English assistant built into a web app that tracks congressional bills, lobbying and campaign finance. Analysts ask a question in plain English and get an answer drawn from curated data, and nothing is ever sent to the cloud.

Complete · Fall 2025

Program
Wayne State capstone
Sponsor
General Motors
My role
Backend; built the AI assistant
When
Sep – Dec 2025

How an answer is made

Two pipelines meet at the model. On the left, public records are cleaned and curated into Gold tables. On the right, a question is turned into a search that pulls only the Gold rows it needs. The language model on the same machine answers from those rows and cites them.

Try an example question

 

Gold · billsBill summaryLatest action

At a glance

What it is

3public data sources: Congress.gov, Senate lobbying filings and the FEC
7curated Gold tables the assistant searches
20Bparameter language model, running locally
0cloud calls when you ask a question

The capstone team's web app tracked congressional bills, lobbyist spending and campaign contributions. I was in charge of the backend, and I built PolicyAI, the assistant that lets anyone ask about that data in plain English instead of writing SQL or building a report. It lives on every page of the app as a chat window.

The journey

From a chat box to a grounded analyst

  1. September 2025

    The problem: the data was there, the answers weren't

    Policy teams need quick answers from congressional data: what a bill does, who is lobbying on it, how much a campaign has raised. The app already collected all of it, but getting an answer out meant knowing exactly where to look, or writing SQL.

    The goal for my part was simple to say: ask a question in plain English, get a correct answer drawn from the data.

  2. The foundation

    Curated data to stand on

    The app's data follows a Medallion design. Bronze holds raw pulls from public APIs: Congress.gov for bills, members and laws, the Senate's lobbying disclosures, and the FEC for campaign finance. Silver cleans and types them. Gold joins them into tables an analyst can trust. For example, each bill appears once with its summary, whether it became law, its latest action and a readable ID like “H.R. 10-119”.

    That Gold layer is what the assistant answers from, so the model only ever sees clean, curated records.

  3. Late September

    A fluent model that knew nothing about us

    The first version was a plain chat model. It could talk about Congress in general, but it had never seen our database, so it had nothing to ground an answer in. That's the gap retrieval closes: find the right rows first, then answer only from them.

  4. October

    Retrieval, and a bug that looked like a bad model

    I built a first retrieval prototype over exported tables, then moved to a stronger embedding model. Search still came back wrong: a query for bill H.R. 3746 returned S. 3949.

    The model wasn't the problem. The stored vectors and the question's vector came from differently sized embeddings. Once both sides matched, H.R. 3746 ranked first out of 715 bills.

    What I foundWhen search results look random, check that both sides were embedded the same way before blaming the model.

  5. November

    Search inside the Gold tables

    I moved search into the database itself. Every row in seven Gold tables got a 768-number embedding stored next to it in Postgres with pgvector. A question is embedded the same way, each table returns its closest rows, and the model receives them as numbered sources with instructions to cite them. The old CSV path was deleted.

    Queries are read-only, search is limited to the curated Gold tables, and a user's question is never written to the database. One practical fix along the way: some bill summaries were long enough to crash the embedder, so row text is capped at 12,000 characters.

  6. November

    Everything on device

    I set one rule for the stack: every model runs locally, and nothing is pulled from the network when someone asks a question. The language model (a 20-billion-parameter open model) and the embedding model run through Ollama, in Docker next to Postgres, the API and the web app.

    The whole system runs on one machine. Questions, data and answers never leave it.

    What I foundKeeping it local came down to architecture, not sacrifice. Public data comes in through ingestion, and at question time nothing goes out.

  7. December 2025

    The result

    A working assistant, built into every page of the app, that answers questions about bills, lobbying and campaign finance in plain English from the Gold tables, running entirely on device. We demoed it to the client as part of the capstone. Here's how it got there:

    1. v1 · ChatA plain chat model. Fluent, but it had never seen our data.
    2. v2 · CSV searchFirst retrieval prototype over exported tables.
    3. v3 · Better embeddingsA stronger embedding model, and a bug that made search look random.
    4. v4 · Gold searchSearch moved into the Gold tables in Postgres, one row per source.
    5. v5 · On deviceModel, search, data and app in Docker on one machine.

If I picked it up again

What I'd build next

  • An evaluation set of real analyst questions with known answers, so accuracy is measured rather than spot-checked.
  • A vector index on each Gold table, so search stays fast as the tables grow.
  • More of the Gold layer made searchable, so every table the app tracks can be asked about.

Contact

Hiring for a software role?

I'm looking for full-time software, backend and systems roles. Email is the fastest way to reach me.

pranch555@gmail.com