Inspiration
We were job-hunting ourselves — learning new languages and tools along the way — and kept running into the same wall: in a field that moves this fast, it's genuinely hard to tell which positions you actually qualify for. Not because the skills aren't there, but because the market itself isn't consistent. Two companies can post what's really the same role under completely different titles, with a different scope, describing the same technology stack in different words. There's no shared vocabulary to compare yourself against.
That's the gap we wanted to close: a place that unifies those criteria, so a candidate can quickly see how their profile stacks up against the market and against how companies actually phrase their job posts — which positions they could realistically apply to right now, and which specific skills would get them to a perfect fit. And on the other side, a place where a company can find profiles that genuinely match their tech stack, and reach out to invite them to interview without wading through mismatched titles and inconsistent requirement lists.
What it does
Rumbo is a matching platform for professional profiles and companies, built around a sequential multi-agent pipeline instead of a chatbot:
A company describes its context and posts a job. Two agents run automatically: one classifies the post into a normalized role (merging it with equivalent roles from other companies, even if the title is worded differently), and another extracts its discrete requirements, reconciling them against a shared catalog so "SQL" and "manejo de bases de datos relacionales" don't become two different entries. A profile enters resume text and skills. A third agent audits real fit against every relevant job post, producing a score and a roadmap — split into what's standard for the role across the market versus what's specific to this one company — with a concrete suggestion for anything missing. Visibility is staged. Before an invitation, the profile sees the job's fit score and roadmap without the company's name. The company sees the profile's first name, CV data, and score, while last name, email, and phone are withheld. A manual company invitation reveals the company name to the profile; only the profile's explicit acceptance reveals those protected profile fields to that company. Matching supports human decisions and does not make hiring decisions automatically.
How we built it
The core architectural decision is that there is no model-based coordinator. Two fixed workflows are orchestrated in plain Python: creating or editing a job post triggers role classification and requirement extraction; creating or updating a profile triggers embedding generation, two-level retrieval, and fit audits of candidate job posts. Routing is defined by application code. A model-based router would only add latency and cost here, not flexibility.
Three specialized agents (Role Classifier, Requirement Extractor, Fit Auditor) run real Gemini 3.5 reasoning through Google ADK on Vertex AI — no keyword-matching fallback. Retrieval is two levels: a Firestore native vector search (find_nearest()) narrows the field of possible roles down from the full catalog, then a plain filter narrows that to real job posts, and only that short list ever reaches the Fit Auditor.
The part we're most deliberate about is cost control as the catalog grows: instead of sending the entire role/requirement catalog to Gemini on every classification call, we added a pre-filter cascade — exact string match first (free), then an embedding-based shortlist (cheap), and only what's still ambiguous after those two steps reaches the model, batched into a single call. This limits the catalog context sent to the model and reduces unnecessary reasoning calls; we have not established a constant-cost or constant-latency guarantee at production scale.
Everything runs on Cloud Run (a single service serves both the FastAPI backend and the React frontend) and Firestore in native mode, with staged visibility enforced as a state machine (pendiente → notificado → confirmado/rechazado) rather than scattered checks.
Challenges we ran into
Cost vs. semantic quality. A naive design would send Gemini the entire role/requirement catalog on every job post. The three-tier pre-filter (string match → embedding → model) was the answer, but calibrating the similarity threshold meant actually running it against real data and measuring real embedding distances instead of guessing a number. Model availability. Gemini 3.5 turned out to only be served through Vertex AI's global endpoint for our project — the regional endpoint we'd been deploying to (us-central1) returned a flat 404 for that specific model. We only found this by testing directly against the API, not from documentation. Enforcing consent consistently. Staged visibility isn't a single check — it has to hold across every endpoint that returns profile or company data (the match view, the profile's own match list, the company's profile map), or a single missed filter leaks identifying info early. Keeping the frequency/roadmap data live, not frozen. An earlier version of the "is this requirement specific to this company or standard across the market" flag was computed once at indexing time and never revisited — so a requirement that started as one company's quirk stayed labeled that way even after it became the market standard. We caught this in an internal audit and moved it to be derived live from current frequency data instead.
Accomplishments that we're proud of
All three agents run real model reasoning end-to-end — not a scaffold, not a deterministic placeholder. The repository documents repeated end-to-end validation against the live Cloud Run deployment: register → post a job → register a profile → match → invite → accept → staged disclosure. Invitations and acceptance remain deliberate human actions. The embedding pre-filter cascade avoids passing the full catalog to every reasoning call, providing a practical basis for further cost and latency evaluation as the catalog grows.
What we learned
A deterministic, fixed-sequence pipeline can go a long way — reserving model reasoning for the three points where semantic judgment is genuinely needed kept the system fast, cheap, and easy to reason about, without losing any real capability. We also learned to distrust our own assumptions about cloud infrastructure until we'd tested them directly: model availability by region, quota behavior by tier, and documentation staying in sync with the actual deployed code all turned out to need active verification, not just a one-time setup.
What's next for Rumbo
Move job-post indexing from synchronous (inside the API request) to asynchronous via Pub/Sub, so publishing a job post doesn't block on Gemini latency. PDF resume parsing and an assisted Harvard-format CV generator, both scoped but not yet built. Recalibrate the retrieval similarity threshold with a larger, more diverse sample of real profiles and roles as usage grows.
Data sources
Rumbo uses company-provided context and job descriptions, user-entered CV text and skills, and the normalized role and requirement catalogs stored in Firestore. The repository includes synthetic seed profiles, companies, and job posts for reproducible demonstrations. Requirement frequency reflects job posts within Rumbo's catalog; it is not a measure of the entire labor market. No external job-board feed or scraped market dataset is required by the current implementation.
Testing and current scope
Open the hosted app at https://rumbo-dev-25592102293.us-central1.run.app/app/ and follow the README's "Try it live" section for seeded demo accounts and the complete company/profile walkthrough. For a fresh run, create a company and job post, then a profile with overlapping skills; inspect the fit score and roadmap, invite the profile, and accept from the profile dashboard to verify staged disclosure. Use synthetic information for testing.
The README includes local setup, Firestore vector-index creation, seed-data commands, and Cloud Run deployment instructions. Run python -m pytest tests/ -v for the automated suite using fake Firestore and Gemini; this suite does not prove live model or cloud availability. Configure GEMINI_MODEL_FLASH=gemini-3.5-flash explicitly when reproducing the hackathon deployment.
The current workflow is synchronous within API requests. Pub/Sub background execution, PDF resume parsing, and the assisted Harvard-format CV generator remain future work.