Career Services runs one dashboard for the entire student body. Every VT student sees the same event list regardless of whether they're a CS sophomore chasing Software Engineering or a finance junior gunning for investment banking. We wanted a Career Services tool that starts from your resume and your actual skill gaps, not a generic feed, and the Deloitte × Databricks "Campus Career Navigator" track gave us a real lakehouse to build it on.
What it does Sign in with Google, upload a resume (parsed by a Databricks-hosted LLM into a skill profile), and answer 8 questions. HireUp builds a For You dashboard: events, recruiter visits, clubs, and opportunities, all ranked by how much they close your specific skill gaps, plus a Gap-to-Goal roadmap you can export to your calendar. A tool-using chat agent answers career questions and can morph the dashboard live. Ask "how do I pivot into investment banking?" and it opens a new goal tab built from a real skill-gap query, not a canned response. Recruiter Radar shows who's actually coming to campus, Event Prep generates a pitch and talking points before you walk into an info session, "/" opens semantic search over events and roles, and Career Services gets an aggregate Admin Insights page with a Genie "ask the data" box. Nobody's individual data, gold tables only.
How we built it Next.js 16 (App Router) on Vercel in front of Databricks Free Edition: Unity Catalog + Delta for the shared lakehouse, six UC Functions as the agent's only tools (called through the AI SDK, max 6 tool steps), Foundation Model APIs for the LLM and embeddings, Vector Search for semantic search, Lakebase Postgres for per-student state (chat sessions, saved items, roadmap, goal tabs), and Genie for the admin NL-query box. Deterministic scoring and ranking live in plain TypeScript with unit tests, never left to the model. Every ID the agent surfaces gets validated against Unity Catalog server-side before render, and an output guard strips any [ID] chip the model invents.
Challenges we ran into The Databricks Statement Execution API costs about 500 to 900ms of round trip per call, even for SELECT 1. The fix wasn't faster queries, it was fewer of them: a shared TTL cache, one joined query instead of N+1, and a single plan_for_path tool that replaced a 5-call pivot sequence. A cold SQL warehouse adds about 14s to the first query, so we added a warm-up ping fired the moment the landing page loads. One serving model (gpt-oss-120b) returned typed reasoning and text content blocks instead of a plain string, which silently broke the AI SDK's parser until we normalized it in a fetch middleware. Free Edition blocks the Vector Search HYBRID reranker, so we pinned queries to ANN and built a SQL ILIKE fallback for when the index isn't configured at all. Resume extraction fails on some real (non-fixture) resumes with no clean root cause yet. Multi-column layout, a rate limit, or a model refusal on real PII are all still on the table, so we shipped a manual-entry fallback rather than block onboarding on it. Next's built-in image optimizer (no sharp installed) silently flattened a transparent hero asset's alpha channel to a grey matte, an easy one to chase your tail on until you diff the served bytes against the source file.
Accomplishments that we're proud of We shipped all 7 MUST features and all 4 SHOULD features (Recruiter Radar, Event Prep, semantic search plus calendar export, Admin Insights and Genie) instead of cutting to a bare MVP. The grounding architecture held up under a live agent eval (golden set plus behavior tests logged to MLflow). And the performance pass took warm dashboard loads from 2.5 to 2.8s down to about 1.6s, and a pivot chatof cutting to a bare MVP. The grounding architecture held up under a live agent eval (golden set plus behavior tests logged to MLflow). And the performance pass took warm dashboard loads from 2.5 to 2.8s down to about 1.6s, and a pivot chat turn from about 11s and 5 to 6 tool calls down to 5.5s on a single tool call, measured live against the real workspace, not estimated.
What we learned Latency in a lakehouse-backed app is a network-call-count problem before it's a query-optimization problem. We learned to design the data-fetch shape around that from the start next time. Grounding an LLM agent well means treating it as a planner over a small, validated tool surface and never trusting its output directly. The server-side ID validation and output guard did more for trust than any prompt engineering did. And small infrastructure gaps (a missing sharp, one model's nonstandard response shape) break things in ways unit tests never catch, so live checklists against the real workspace earned their place in the process.
What's next for HireUp Ship F12 (path comparison), wire up the AI/BI dashboard link and a real scheduled Jobs run for external ingestion (O*NET, BLS, Greenhouse/Lever), build the nightly agent_turns Lakebase to Delta copy so the MLflow eval can run against real chat traffic instead of a golden set alone, round out Playwright e2e coverage, and, the actual goal, get it in front of VT Career Services for a real pilot.
Built With
- ai
- auth.js-(nextauth-v5)-with-google-oauth
- databricks
- databricks-sql-statement-execution-api
- endpoints
- lakebase
- next.js-16-(app-router)
- python
- react-19
- serving
- shadcn/ui-on-base-ui-primitives
- sql
- tailwind-css-v4
- typescript
- unity-catalog-/-delta-lake-(shared-lakehouse)
- vercel
Log in or sign up for Devpost to join the conversation.