A Reality Check for Indie Developers and Small Teams: Resume-to-JD Matching Is the Most Underrated AI Use Case in Job Platforms
It looks like just "computing similarity between two pieces of text," but if you actually build it that way, odds are you'll tear it down and start over within three months. Below, I'll first walk through the three pitfalls we hit, then share the architecture that's now running stably.
1. Three Failure Cases
Case one: one embedding to rule them all. In our first version, we simply embedded the full JD and the full resume, computed cosine similarity, and returned scores like 85% or 72%. Two weeks after launch, user feedback clustered around two points: all match scores were squeezed into the 75%–90% range with no discriminating power; and the scores were unexplainable—when candidates asked "why was this job recommended to me," ops had no answer. Embeddings are almost completely insensitive to degree adverbs like "proficient in Java" vs. "familiar with Java," and to numeric comparisons like "3 years of experience required vs. 5 years of experience."
Case two: letting the LLM read full text and freestyle. In the second version, we switched to stuffing the JD and resume into a prompt and letting a large model output a match score with reasoning. The result: inconsistent scoring every time—the same resume rerun the next day could differ by 15 points. Worse, batch-processing hundreds of resumes sent token costs completely out of control, and long resumes frequently exceeded the context window and got truncated—leaving the model to pass judgment after seeing only the candidate's education background.
Case three: rules for structured fields, models for descriptive text, each in its own silo. The third version recognized the problem: hard fields like salary, city, and years of experience were filtered by rules, while job descriptions were handed to the model. The approach was right, but in implementation, each field type was wired to a different model and vendor—one embedding service, two LLM APIs, and a local small model—with API keys scattered throughout the codebase. When one vendor rate-limited us, the entire matching pipeline went down, and it took two hours of debugging to discover the account had run out of credit.
2. The Right Approach: A Three-Layer Matching Architecture
After learning these lessons the hard way, we settled on a "rules layer → structured extraction layer → semantic matching layer" design, where each layer has a single responsibility and can be tested independently:
Layer one: hard rule filtering. City, salary range, education, years of experience—these binary judgments don't need AI at all. A rules engine handles them: fast, free, and 100% explainable. This layer eliminates over 60% of invalid matching requests.
Layer two: structured extraction. Use an LLM to extract both the JD and the resume into JSON following a unified schema: skill lists (with proficiency levels), industry tags, and responsibility highlights. Extraction is a far more stable task than matching—with output format constraints and a low temperature parameter, consistency is excellent. This step also solves the long-text problem along the way: a resume is extracted once and stored, so there's no need to read the full text on every match.
Layer three: dimension-level semantic matching. Matching no longer produces a single total score. Instead, it scores three dimensions separately—"skill match, experience relevance, industry fit"—then combines them with weighting. Each dimension has its own sub-score explanation, so users can see "why," and ops can tune the weights for different job types.
The core workflow is as follows:
1. JD入库 → 规则字段解析 + LLM结构化抽取 → 存入JD索引
2. 简历上传/更新 → 同样抽取为结构化JSON → 存入简历库
3. 匹配触发(用户搜索 / 岗位推荐):
a. 规则层:城市/薪资/年限硬过滤 → 剩余候选集
b. 召回层:embedding做粗排(注意:只用于召回排序,不产出对用户可见的分数)
c. 精排层:LLM维度级打分(技能/经验/行业)+ 简短理由
d. 加权合成总分 + 降级策略:LLM超时则只返回召回排序结果并标注"初步排序"
4. 结果落库,记录每次打分的模型版本,便于后续回归验证Key implementation points, mapped to the three pitfalls above:
- Embeddings for recall only, never for scoring. Every score users see comes from the dimension-level scoring in layer three—discriminating and explainable.
- Separate extraction from matching; extract each resume only once. At match time, the model reads a few-hundred-token structured JSON instead of a several-thousand-token original, cutting costs by an order of magnitude. Only with this does batch matching become commercially viable.
- Route everything through a unified AI API gateway. This was the most expensive lesson from case three.
3. Why a Unified AI Gateway Dramatically Cuts Maintenance Costs
The thing small teams overlook most easily: the bulk of AI application maintenance cost isn't the model calls themselves—it's "multi-vendor management." Without a unified gateway, the typical state is: three or four vendors' SDKs upgrading on their own schedules, API keys hardcoded across different services, billing scattered across several dashboards, and vendor rate limits discovered only after users file complaints.
After adopting a unified gateway:
- One set of keys, one way of calling. Cheap models for extraction, flagship models for fine-ranking—switching is just changing a model name in the request. No vendor-specific adapter code needed.
- Failover without code changes. If a model gets rate-limited or goes down, configure a fallback route at the gateway layer and the matching pipeline automatically switches to the backup model—no midnight code pushes and deployments.
- Unified usage observability. How many extractions per day, how many fine-ranking calls, how much spent, what the error rate is—all visible in one dashboard. For a product billed per match, being able to account for costs is a prerequisite for survival.
- Isolation from vendor changes. If a vendor raises prices or shuts down service today, switching vendors means changing one line of configuration, not rewriting a module.
By rough calculation, after adopting the gateway, time spent on "maintaining AI calls" dropped from two or three days a month to nearly zero—for a three-person team, that difference determines whether you have time for real product iteration.
4. Rollout Recommendations
If you're about to build something similar, the recommended order of progression is: launch with the rules layer only first, to validate whether users actually need AI matching (often, rules already solve 80% of the problem); then add structured extraction; finally add dimension-level fine-ranking. Every step is reversible and measurable. Whatever you do, don't be like my first version—jumping straight to the "intelligent matching" endgame.
When it comes to matching, what users want has never been a confident-looking percentage, but an explainable answer to "why this job is right for me." Get that straight, and the architecture becomes clear on its own.
If you're ready to get started, try this unified AI API gateway—register and go:
https://api.thistoken.ai/register
---
Tired of juggling provider integrations? Register at https://api.thistoken.ai/register and call every model through one base_url.
Хотите попробовать Token.AI?
Создайте API Key уровня проекта, включите каналы в консоли и настройте маршрутизацию, бюджеты и журналы аудита.
注册 ThisToken.AI 并获取 API Key