Skip to the writeup

Tunnel

Hack the North / September 2025Source code

We won 2 awards at Hack the North.

Tunnel

AI agents for simulated market research.

Describe a product, collect reactions from simulated customers, and talk through their feedback.

350 synthetic personas108 cities
Drag to explore
Persona 255Toronto, Canada

Thomas Anderson

Digital Marketing Specialist

Real Estate / Gardening / Stock Market / Passive Income

Tech adoption 6/10Price sensitivity 2/10

Explore the project’s actual persona dataset. These are generated profiles.

A focus group
you can call.

You have an idea for something. You can probably come up with a few reasons someone would want it. It’s harder to work out why they’d ignore it, or what you’d have to change before they’d care.

I built Tunnel with Dev, Suneru, and Haresh, and we won 2 awards at Hack the North. We made a world of AI personas you can pitch an idea to, then talk to the ones whose responses you want to understand.

The whole thing lives on a globe. Each point is a generated person with a job, interests, a location, and their own response to your idea. Click one and you can read what they thought. If their answer leaves you with more questions, you can call them.

The prototype in use
Choosing a focus group, reading its reactions, and opening a conversation.
01

Find an audience

We start with the idea you type in. Tunnel identifies its niche, then looks for personas whose backgrounds have something to do with it. A tool for developers should reach people who write code. A consumer product might need a very different group.

Each profile has structured fields for occupation, industry, interests, demographics, and traits like price sensitivity. We turn those fields into a short text document and send the documents to Cohere’s rerank API, with the niche and original idea as the query.

We use Cohere’s rerank-english-v3.0 model and request the top results, capped at the number of available profiles. It returns document indices and relevance scores. We map those indices back to the original profiles and keep their persona IDs through the rest of the pipeline, from opinion generation to the points on the globe.

That score answers a fairly narrow question: how relevant is this profile to the idea? It doesn’t tell us whether the person will like it. We ask that separately.

Your idea
product + audience
Persona profiles
work + interests + traits
Cohere rerankProfile relevance to the idea

A smaller group to start with

02

Give each person
a response

Once we have a group, we make a separate Cohere Command R request for each persona. The prompt includes that person’s background and the product idea. We ask for their level of attention, sentiment, a reason, and a comment with any suggestions. Each call has a 700 token output limit, which keeps the response short enough to read and parse.

The response comes back as JSON. We validate that attention is one of three values and that sentiment is a number between zero and one. Keeping those fields separate lets the interface show a quick overview while still giving you the actual explanation.

The API starts the requests together using Promise.all. Each request catches its own errors, so one failed response won’t reject the entire group. The endpoint waits for the group before returning the results. That saves us from waiting for every persona in sequence, though it also means a slow request can hold up the batch.

Once the responses arrive, the interface reveals five every 600 milliseconds and updates the globe and counts together. Those are completed responses being displayed gradually. Each one carries its persona ID so the point, written feedback, and eventual call all refer to the same profile.

One response per persona

personaId
Keep the same identity
attention
full / partial / ignore
sentiment
0 to 1
reason
Why they reacted this way
comment
What they would change
InterestedUnsureNot interested
03

Okay, but why?

A written response can leave you with more questions. We added a way to call that persona and ask them directly.

We used Vapi for the voice conversation. Before the call starts, we build a system prompt from the selected persona, the idea you pitched, and the reaction they already gave. If they were skeptical about the price, the call should start with that concern.

The prompt asks the agent to hold onto its initial opinion unless you address the specific objection. Otherwise it’s too easy to get a completely different answer just by opening a new conversation.

The standalone call view reads a context object from sessionStorage containing the persona, reaction, and product idea. It uses that object to build the system prompt before starting the Vapi session. The browser listens for call, speech, and transcript events from Vapi. Those events update the call controls and the transcript while you talk. The voice session gets the same persona context as the written response, so you can go straight to a follow up question.

A call with Simon Lee, a simulated product owner in Amsterdam, with his earlier reaction and live transcription visible
A persona’s initial reaction stays visible during the call.
04

Try it again

You can save the responses you want to work on, then ask Tunnel to revise the idea using that feedback. We send the original pitch and the selected comments back to Cohere and put the revised version into the input.

From there, we run it through the existing focus group again. Keeping the group gives you a way to compare responses without also changing everyone you’re asking. When you’re ready, the global view sends the idea to all the loaded personas.

We used Auth0’s Management API to create the synthetic identities and store their profile fields as metadata. MongoDB holds the analysis sessions, including the prompt, selected people, opinions, and interface state. Each session has user and project IDs, with a compound index on that pair for workspace lookups. Autosave waits two seconds after a change before saving, resetting the timer if another change comes in. That lets you return to the same experiment without saving on every edit.

Original ideaFocus group reactionsFeedback you chooseRevised idea

Back to the same group ↵

What I’d want to test next

I’d want to put these responses next to interviews with real people. The profiles and opinions are generated, so a positive result tells us how the model reacted to a description. It doesn’t establish that someone would buy the product.

The hackathon version also substitutes a fallback response if an individual model call fails. I’d make those failures visible and exclude them from the results before using the scores to compare ideas.