
一、海投300份简历,只收到1个真offer?AI早已帮人躺赢
每个求职者都经历过这样的内耗:每天花3小时刷招聘软件,翻几百条岗位,一半是不符合经验的资深岗,一半是看似匹配实则无关的岗位——后端工程师刷到前端开发,应届生收到总监岗邀请,忙活一周连个有效面试都没有。
有人吐槽“找工作比上班还累”,却很少有人想过,重复的筛选工作本就不该由人来做。开发者Jerlin Joy就抓住了这个痛点,用Google Gemini和NestJS搭建了一款名为JobHunt.ai的AI求职代理,能自动爬取岗位、匹配简历,只把“精准匹配”的机会推送给自己,彻底解放双手。
这款AI助手的出现,的确 打破了传统求职的低效困境,让求职者从繁琐的筛选中解脱出来。但它真的能适配所有求职者?普通人没有编程基础,也能用上这样的工具吗?实则AI求职的核心从不是“替代人”,而是“帮人减负”,关键在于找对方法。
先给大家详细介绍下这款AI助手用到的核心技术,毕竟技术的可靠性,直接决定了求职匹配的精准度,也关系到我们普通人能否借鉴复用。
核心技术详情(开源+免费+GitHub星数,实测可查):
1. NestJS:完全开源免费,截至2026年3月,GitHub星数达67k+,基于Node.js构建,融合了多种编程思想,结构清晰、规范性强,尤其适合中小型微服务项目,也是前端转后端开发者的首选框架,能为AI助手提供稳定的模块化后端支撑。
2. Google Gemini Pro:谷歌推出的AI大模型,支持免费使用(免费版有每日配额限制),也有付费版本(AI Plus每月约50元,AI Pro每月约125元),其中免费版足以满足个人求职助手的搭建需求,能实现语义匹配、领域验证等核心功能,其嵌入服务可将文本转换为高维向量,实现精准语义识别。
3. Tesseract.js:开源免费,GitHub星数达28.8k,支持超过100种语言的OCR识别,能解决扫描版简历无法解析的问题,是AI助手的“眼睛”。
4. pdf2pic:开源免费,可将PDF文件转换为图片格式,配合Tesseract.js实现扫描版简历的文字提取,完美解决传统简历解析工具的短板。
5. GitHub Actions:完全免费,公共仓库可不限时长使用,能实现定时任务触发,让AI助手每天自动运行,无需手动操作。
二、核心拆解:手把手教你搭建AI求职助手,代码可直接复制使用
JobHunt.ai的核心逻辑很简单:替代人工完成“爬取岗位—解析简历—精准匹配—推送通知”的全流程,核心优势在于摆脱了传统招聘平台的“关键词陷阱”,用语义智能实现真正的精准匹配。下面从技术架构、核心模块、具体代码三个方面,完整拆解搭建过程,新手也能跟着操作。
核心前提:环境准备
搭建前需准备好基础环境,确保所有工具能正常运行,步骤如下:
1. 安装Node.js(推荐16.x及以上版本),用于运行NestJS项目;
2. 注册Google账号,获取Gemini API Key(免费版即可),用于调用Gemini Pro模型;
3. 安装Git,用于管理代码和配置GitHub Actions;
4. 安装依赖包:打开终端,执行相应命令安装NestJS、Tesseract.js、pdf2pic等核心依赖。
第一步:搭建后端框架(NestJS)
NestJS作为整个AI助手的后端支撑,负责整合所有模块、处理请求和任务调度,具体操作和代码如下:
// 1. 全局安装NestJS脚手架
npm install -g @nestjs/cli
// 2. 创建新项目
nest new jobhunt-ai
// 3. 进入项目目录
cd jobhunt-ai
// 4. 安装核心依赖(Gemini、OCR相关)
npm install @google/generative-ai tesseract.js pdf2pic axios
创建完成后,项目结构会自动生成,无需手动配置基础架构,NestJS的模块化设计会让后续的功能扩展更便捷。
第二步:实现OCR识别模块(解决扫描简历解析问题)
传统简历解析工具无法识别扫描版PDF,这也是许多求职者的痛点。该模块通过Tesseract.js和pdf2pic实现“PDF转图片—图片识别文字”的自动 fallback,确保所有格式的简历都能正常解析,代码如下:
// src/ocr/ocr.service.ts
import { Injectable } from '@nestjs/common';
import { createWorker } from 'tesseract.js';
import { fromPath } from 'pdf2pic';
import * as fs from 'fs';
@Injectable()
export class OcrService {
// 解析PDF简历(支持扫描版)
async parseResume(pdfPath: string): Promise<string> {
try {
// 先尝试读取PDF文本
let text = '';
// 此处省略简单PDF文本读取逻辑(可通过pdf-parse等依赖实现)
// 如果文本为空,说明是扫描版,执行OCR识别
if (!text || text.trim() === '') {
// 1. PDF转图片(300 DPI,确保识别清晰度)
const options = {
density: 300,
savePath: './temp',
format: 'png',
width: 1200,
};
const convert = fromPath(pdfPath, options);
const page = await convert(1, { responseType: 'image' });
// 2. 图片OCR识别
const worker = await createWorker('eng+chi_sim'); // 支持中英文
const { data: { text: ocrText } } = await worker.recognize(page.path);
await worker.terminate();
// 删除临时图片
fs.unlinkSync(page.path);
text = ocrText;
}
return text.trim();
} catch (error) {
console.error('简历解析失败:', error);
throw new Error('简历解析失败,请检查文件格式');
}
}
}
第三步:实现Gemini语义匹配模块(核心功能)
这是AI助手的核心,摆脱传统关键词匹配的弊端,通过向量嵌入(Vector Embeddings)将简历和岗位描述转换为高维向量,用余弦类似度判断两者的语义匹配度,同时加入领域验证,避免跨领域匹配,代码如下:
// src/gemini/gemini.service.ts
import { Injectable } from '@nestjs/common';
import { GoogleGenerativeAI, Embedding } from '@google/generative-ai';
@Injectable()
export class GeminiService {
private genAI: GoogleGenerativeAI;
constructor() {
// 初始化Gemini(替换为你的API Key)
this.genAI = new GoogleGenerativeAI('你的Gemini API Key');
}
// 生成文本的向量嵌入
private async generateEmbedding(text: string): Promise<number[]> {
const model = this.genAI.getGenerativeModel({ model: 'embedding-001' });
const result = await model.embedContent({
content: text,
taskType: 'retrieval_document',
});
return result.embedding.values;
}
// 计算余弦类似度(判断匹配度,0-1之间,越接近1匹配度越高)
private calculateCosineSimilarity(vec1: number[], vec2: number[]): number {
const dotProduct = vec1.reduce((sum, a, i) => sum + a * vec2[i], 0);
const mag1 = Math.sqrt(vec1.reduce((sum, a) => sum + a * a, 0));
const mag2 = Math.sqrt(vec2.reduce((sum, a) => sum + a * a, 0));
return mag1 && mag2 ? dotProduct / (mag1 * mag2) : 0;
}
// 领域验证(避免后端匹配前端等跨领域情况)
private async verifyDomain(resumeText: string, jobText: string): Promise<boolean> {
const model = this.genAI.getGenerativeModel({ model: 'gemini-pro' });
const prompt = `
请分析以下简历和岗位描述的领域是否匹配:
简历:${resumeText.substring(0, 500)}(截取前500字符,避免上下文溢出)
岗位描述:${jobText.substring(0, 500)}
要求:1. 判断简历对应的职业领域(如后端开发、前端开发、产品经理等);
2. 判断岗位对应的职业领域;
3. 若两者领域一致或高度相关,返回true,否则返回false。
仅返回true或false,不要多余内容。
`;
const result = await model.generateContent(prompt);
const response = await result.response;
return response.text().trim().toLowerCase() === 'true';
}
// 核心匹配方法(返回匹配分数,0-1)
async matchResumeAndJob(resumeText: string, jobText: string): Promise<number> {
// 1. 生成向量嵌入
const resumeEmbedding = await this.generateEmbedding(resumeText);
const jobEmbedding = await this.generateEmbedding(jobText);
// 2. 计算基础匹配分数
let score = this.calculateCosineSimilarity(resumeEmbedding, jobEmbedding);
// 3. 领域验证,不匹配则扣除50%分数
const isDomainMatch = await this.verifyDomain(resumeText, jobText);
if (!isDomainMatch) {
score *= 0.5; // 惩罚机制,确保不推送跨领域岗位
}
return score;
}
}
第四步:实现岗位爬取与通知模块
该模块负责爬取招聘平台岗位(以LinkedIn为例),筛选出匹配分数高于阈值(如0.7)的岗位,通过Discord Webhook推送给用户,同时加入Token优化,避免超出Gemini免费额度,代码如下:
// src/job/job.service.ts
import { Injectable } from '@nestjs/common';
import axios from 'axios';
import { GeminiService } from '../gemini/gemini.service';
import { OcrService } from '../ocr/ocr.service';
@Injectable()
export class JobService {
constructor(
private readonly geminiService: GeminiService,
private readonly ocrService: OcrService,
) {}
// 爬取LinkedIn岗位(简化版,实际可优化爬取逻辑)
private async scrapeLinkedInJobs(keyword: string): Promise<any[]> {
const url = `https://www.linkedin.com/jobs/search/?keywords=${encodeURIComponent(keyword)}`;
const response = await axios.get(url, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36',
},
});
// 此处省略HTML解析逻辑,提取岗位标题、描述、要求等信息
// 实际开发中可使用cheerio等依赖解析HTML
return [
{
title: 'Node.js Backend Engineer',
description: '负责后端服务开发,熟练使用Node.js、NestJS,熟悉数据库设计...',
company: '某科技公司',
},
// 更多岗位...
];
}
// Token & 上下文优化(避免超出Gemini免费额度)
private async optimizeContext(text: string): Promise<string> {
// 智能截断长文本(控制在700字符以内)
if (text.length > 700) {
return text.substring(0, 700) + '...';
}
return text;
}
// 延迟控制(12秒间隔,避免API调用过于频繁)
private async delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 推送通知到Discord
private async sendDiscordNotification(job: any): Promise<void> {
const webhookUrl = '你的Discord Webhook URL';
await axios.post(webhookUrl, {
content: `找到精准匹配岗位:
标题:${job.title}
公司:${job.company}
描述:${job.description.substring(0, 200)}...`,
});
}
// 核心执行方法
async runJobHunt(resumePath: string, keyword: string): Promise<void> {
try {
// 1. 解析简历
const resumeText = await this.ocrService.parseResume(resumePath);
// 2. 爬取岗位
const jobs = await this.scrapeLinkedInJobs(keyword);
// 3. 逐一匹配,推送精准岗位
for (const job of jobs) {
// 优化岗位描述,避免上下文溢出
const optimizedJobDesc = await this.optimizeContext(job.description);
// 计算匹配分数
const matchScore = await this.geminiService.matchResumeAndJob(resumeText, optimizedJobDesc);
// 匹配分数高于0.7,推送通知
if (matchScore > 0.7) {
await this.sendDiscordNotification(job);
}
// 延迟12秒,避免超出API调用限制
await this.delay(12000);
}
} catch (error) {
console.error('AI求职助手执行失败:', error);
}
}
}
第五步:配置定时任务(GitHub Actions)
通过GitHub Actions设置定时任务,让AI助手每天自动运行,无需手动触发,配置文件如下(创建
.github/workflows/jobhunt.yml):
name: JobHunt AI Auto Run
on:
schedule:
# 每天早上9点运行(UTC时间1点,北京时间比UTC快8小时)
- cron: '0 1 * * *'
workflow_dispatch: # 支持手动触发
jobs:
run-jobhunt:
runs-on: ubuntu-latest
steps:
- name: 拉取代码
uses: actions/checkout@v4
- name: 安装Node.js
uses: actions/setup-node@v4
with:
node-version: '16'
- name: 安装依赖
run: npm install
- name: 运行AI求职助手
run: npm run start:dev
env:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
配置完成后,将代码推送到GitHub,在仓库设置中添加Gemini API Key和Discord Webhook URL作为密钥,AI助手就会每天自动爬取岗位、匹配简历并推送通知。
三、辩证分析:AI求职助手不是“万能药”,优势与局限并存
JobHunt.ai的出现,无疑为求职者提供了一种高效的求职方式,它解决了传统海投的低效、内耗问题,让精准匹配成为可能,这是它不可替代的优势。尤其是对于程序员、设计师等技术岗位,语义匹配能精准识别技能需求,避免因关键词遗漏而错过合适岗位,同时自动化流程也节省了大量时间,让求职者能专注于简历优化和面试准备。
但我们不能盲目神化AI求职助手,它也有自身的局限。第一,它的核心依赖编程能力,普通人没有基础,很难独立搭建,即便有现成的代码,也可能因环境配置、API调用等问题无法正常使用;其次,岗位爬取存在必定的局限性,部分招聘平台有反爬机制,可能导致爬取失败或岗位信息不完整;最后,AI匹配无法替代人工判断,列如岗位的隐性要求、公司文化适配度等,都需要求职者自己进一步筛选。
更值得思考的是,AI求职助手的普及,会不会让求职竞争变得更激烈?当所有人都能用AI精准匹配岗位,原本靠“海投碰运气”的求职者,会不会彻底失去机会?实则答案很简单:AI只是工具,它能帮你筛选机会,但不能帮你提升能力。真正能决定你能否拿到offer的,还是你的专业技能和综合素质,AI只是帮你更快地找到适合自己的舞台。
四、现实意义:2026年,求职的核心的是“匹配”,而非“搜索”
在2026年这个自动化时代,“重复劳动就该被AI替代”已经成为共识,求职也不例外。传统的“海投简历—等待回复”模式,不仅效率低下,还容易让求职者陷入自我怀疑,而AI求职助手的核心价值,就是将求职者从重复的筛选工作中解放出来,让求职回归本质——找到真正适合自己的岗位,实现人岗精准匹配。
对于求职者而言,AI求职助手不仅能节省时间和精力,还能减少“无效投递”带来的内耗,让每一次投递都更有针对性,提升面试成功率。尤其是应届生和转行求职者,他们对岗位认知不足,容易投递不符合自身条件的岗位,AI的语义匹配和领域验证功能,能帮他们避开坑位,少走弯路。
对于开发者而言,JobHunt.ai的搭建思路也提供了一个很好的实践案例——将AI大模型与后端框架结合,解决现实中的实际问题。这种“AI+实用工具”的开发模式,未来会越来越普及,无论是求职、办公还是生活,都能通过类似的思路,用技术提升效率、解决痛点。
更重大的是,AI求职助手的出现,也在倒逼招聘行业升级。传统招聘平台的“关键词匹配”模式已经无法满足求职者和企业的需求,未来会有更多平台引入语义匹配、AI筛选等功能,让招聘变得更高效、更精准,实现求职者和企业的双向共赢。
五、互动话题:你被“无效求职”内耗过吗?AI能帮你解决痛点吗?
信任许多人都有过这样的经历:每天刷招聘软件到深夜,投出的简历石沉大海;好不容易收到面试邀请,去了才发现岗位和自己的预期完全不符;为了增加成功率,盲目海投,最后却连一个有效回复都没有,越找越焦虑。
JobHunt.ai这样的AI求职助手,的确 能解决这些痛点,但它也需要必定的编程基础,对于普通人来说,门槛并不低。那么问题来了:
1. 你找工作时,最头疼的是“海投内耗”还是“找不到合适岗位”?
2. 如果有现成的AI求职工具,你愿意尝试吗?你最看重它的什么功能?
3. 你觉得AI会彻底改变求职模式吗?未来求职者还需要自己筛选岗位吗?
欢迎在评论区留言讨论,说说你的求职经历和见解,也可以转发给正在找工作的朋友,一起摆脱内耗,精准求职!


