Skip to content

Work

Rabbithole

Research turns into forty tabs and a chat thread you can't scroll back through. We built a version where every answer is a node you can branch from.

When
November 2025
What
Neo Hackathon 2025, 3rd place
Team
Three of us, equal parts

The idea

Chat is linear, and research isn't. You ask about subsea cables, wonder who pays for them, then want to know how repairs work. Three questions later the first thread is buried. In Rabbithole you ask from any card, or highlight a sentence and ask about that. Each answer becomes a child node on a canvas, so the shape of what you explored stays visible.

How it's put together

The frontend is Next.js with React Flow, laid out top-down with dagre. The backend is FastAPI on Modal. Answers come from OpenAI's search-enabled GPT-4o, so they're grounded in the web, and they come back as structured output: a title, the answer, two suggested follow-ups and a few subtopics. Sessions are saved to Postgres on Supabase, with Google sign-in and shareable links.

The tree is just nodes and edges. Each edge remembers the question that created it and any text you highlighted. Rather than storing nodes as rows, every operation saves a snapshot of the whole graph as JSON. It's simple, and it gives each session a history you can replay.

What the model sees

Pick a node to ask a follow-up from it. Only the highlighted path is sent as context. The tree is an example; the rule is the real one from the code.

Sent to the model, oldest first

  1. User: How do subsea cables work?
    Assistant: Subsea cables + the answer text
  2. User: How is a cable actually laid?
    Assistant: Laying a cable + the answer text
  3. User: How do they pick the route?
    Assistant: (new answer generated here)

Left out: 5 other nodes on the map.

Choosing context

This was the interesting design question. When you ask from a node, we walk the edges back to the root and replay that path as a conversation: each ancestor's question, then its title and answer. Siblings and cousins are left out. That keeps answers focused on the branch you're on, and the prompt stays small even when the map gets large. The cost is that the model can't connect ideas across branches. That's what grouping is for.

There are two smaller guards. If a new question overlaps 85% or more (by words) with one of the last three on the path, the prompt asks for a fresh angle instead of repeating itself. And a user's custom instructions are fenced off in their own tag with a hard cap on length.

Grouping related ideas

Each node is embedded with OpenAI's small embedding model, then clustered with k-means. Picking k is the fiddly part: we try a few values and keep the best silhouette score if it's above 0.2. Otherwise we fall back to an elbow rule (keep adding clusters while the error still drops by more than 8%). A small model names each cluster in two or three words, and the canvas colours nodes to match. “Auto mode” uses the same embeddings to attach a new question under whichever existing node it's closest to.

What we didn't finish

  • An agent mode with Exa search over MCP that would add nodes to the canvas as it researched. It's built, but it's switched off in the shipped version.
  • The embedding cache lives in memory, so every cold start on Modal throws it away. Auto mode also embeds nodes one at a time.
  • Two clusters that get the same generated name collide, and one disappears.
  • Plenty of hackathon leftovers: debug prints, comments that disagree with the code, a README behind the app.

We didn't get it in front of real researchers in any serious way, so I can't tell you whether the map beats a good notes app.

Tehseen and a teammate presenting Rabbithole to a packed room at Neo Hackathon, with the app on screen
demo day at Neo, Nov 2025

Back to all work