譯者 | 核子可樂
審校 | 重樓
在本文中,我們將共同了解如何使用DeepSeek R1大模型構建智能簡歷分析應用,節(jié)約運營成本。
DeepSeek開源大模型的發(fā)布已經(jīng)在技術社區(qū)引發(fā)巨大轟動。如今,開發(fā)者們無需接入Claude、ChatGPT等在線AI模型,即可輕松實現(xiàn)本地應用程序構建。換言之,開源大模型準入門檻的持續(xù)降低,為企業(yè)應用程序與生成式AI的集成打通了機會之橋。
在本文中,我們將學習如何在個人設備上本地運行開源模型,進而構建一款由React和NodeJS驅動的全棧應用程序。它能幫助大家快速分析簡歷內(nèi)容,并做出明智的招聘決策。在構建之前,我們先聊聊開源大模型的優(yōu)勢所在。
開源大語言模型的優(yōu)勢
與專有模型相比,開源大模型擁有以下幾個關鍵優(yōu)勢:
經(jīng)濟高效且無需許可證
開源大模型極具成本效益,無需特殊許可證。截至本文撰稿時,OpenAI o1的每百萬輸出token成本為60美元,而開源DeepSeek R1的成本僅為2.19美元。
可定制、可微調(diào)
開源大模型可輕松進行微調(diào)以滿足獨特的業(yè)務需求,在更多特定領域支撐業(yè)務用例。
增強數(shù)據(jù)安全性與隱私性
寶貴的個人數(shù)據(jù)無需被上傳至第三方服務器,而僅保留在本地設備或企業(yè)網(wǎng)絡之內(nèi),因此應用程序更加安全。此外,開源模型還可進行微調(diào)以消除數(shù)據(jù)偏差。
社區(qū)驅動,擺脫供應商鎖定
開源模型享有廣泛的社區(qū)支持,受益于快速功能開發(fā),也避免了專有模型受供應商鎖定、且高度依賴供應商提供功能更新的弊端。
在了解上述背景信息后,下面直奔主題,了解如何使用DeepSeek R1開源模型、Node.js和React構建應用程序。
項目與架構概述
我們的目標是構建一款簡歷分析器應用,快速把握上傳簡歷中的基本信息與申請者的優(yōu)缺點。DeepSeek R1大模型負責分析上傳的簡歷并提供反饋。大家可通過下圖了解這款應用的基本架構:
架構示意圖
用戶界面由React開發(fā),通過REST API與基于NodeJS的后端進行通信。之后,NodeJS后端將用戶請求發(fā)送至由Ollama托管的DeepSeek R1。整個技術??稍趩我辉O備上運行,亦可跨多個容器托管以支持更復雜的用例。
前期準備
- 要運行此項目,你需要一臺具備一定性能的設備,最好搭載英偉達獨立顯卡。此項目已在配備英偉達4090RTX的Windows設備和M2 MacBook Pro上通過開發(fā)測試。
- 你還需要在設備上安裝NodeJS。本項目使用NodeJS 22.3.0版本,你可使用node -v 命令驗證NodeJS的安裝情況。
- 你還需要選定一款編輯器以處理代碼。本示例中使用Visual Studio Code,推薦你使用同款工具。
在本地設置并運行DeepSeek
要在本地運行DeepSeek R1,請遵循以下步驟:
- 從官方網(wǎng)站處(https://ollama.com/)安裝Ollama。
- 安裝完成后,即可使用設備終端上的 ollama run 命令運行模型。
- 運行你選定的DeepSeek模型。本教程中使用DeepSeek R1 8B參數(shù)模型。你可使用 ollama run deepseek-r1:8b命令運行該模型。
- 如果你的硬件規(guī)格低于前期準備部分的最低配置,亦可嘗試運行7B及1.5B參數(shù)模型,但輸出質量可能受到影響。
- 模型首次運行可能須耗費一定時間,特別是下載過程。模型運行之后,你可直接在終端中向其提問并獲取輸出。你可參考下圖查看DeepSeek R1 8B模型的實際運行情況。
Ollama DeepSeek R1
- DeepSeek R1是一款推理模型,因此會先進行思考、而后開始生成答案。如上圖所示,它在給出答案前會列出思考過程,具體細節(jié)請參閱 <think> </think>標簽。
克隆并運行NodeJS后端
Ollama服務也可通過API訪問。你可使用此API并構建基于NodeJS的后端層,由該層獲取用戶上傳的PDF文件并從中提取文本內(nèi)容。提取完成后,后端將通過Ollama API將文本導入DeepSeek R1模型以獲取響應。最終響應結果將發(fā)送至客戶端并展示給用戶。
- 使用此URL從GitHub克隆后端項目。這里推薦大家分叉該項目,而后克隆出自己的本地副本。
- 克隆后,要運行此項目,請使用cd deepseek-ollama-backend轉至項目根目錄。
- 在根目錄內(nèi),使用npm install命令安裝依賴項。安裝完成后,即可使用npm start 命令運行本項目。項目核心為app.js文件,其代碼內(nèi)容如下所示:
const express = require('express');
const multer = require('multer');
const pdfParse = require('pdf-parse');
const axios = require('axios');
const fs = require('fs');
const cors = require('cors');
const app = express();
app.use(cors());
app.use(express.json());
const upload = multer({
dest: 'uploads/',
fileFilter: (req, file, cb) => {
file.mimetype === 'application/pdf' ? cb(null, true) : cb(new Error('Only PDF files are allowed!'));
}
}).single('pdfFile');
app.post('/analyze-pdf', (req, res) => {
upload(req, res, async function(err) {
if (err) {
return res.status(400).json({ error: 'Upload error', details: err.message });
}
try {
if (!req.file) {
return res.status(400).json({ error: 'No PDF file uploaded' });
}
const dataBuffer = fs.readFileSync(req.file.path);
const data = await pdfParse(dataBuffer);
const pdfText = data.text;
fs.unlinkSync(req.file.path);
const response = await axios.post('http://127.0.0.1:11434/api/generate', {
model: "deepseek-r1:8b",
prompt: `Analyze this resume. Resume text is between two --- given ahead: ---${pdfText}---`,
stream: false
});
res.json({ success: true, message: 'Successfully connected to Ollama', ollamaResponse: response.data });
} catch (error) {
if (req.file && fs.existsSync(req.file.path)) {
fs.unlinkSync(req.file.path);
}
res.status(500).json({ error: 'Error processing PDF', details: error.message });
}
});
});
if (!fs.existsSync('uploads')) {
fs.mkdirSync('uploads');
}
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
- 客戶端通過調(diào)用/analyze-pdfAPI端點與后端交互,此端點為POST類型??蛻舳藢⒂脩羯蟼鞯膒df文件作為有效載荷發(fā)送至該API。
- 服務器將此文件臨時存儲在uploads目錄中,并提取文件中的文本。
- 服務器隨后使用Oolama的hocalhost API端點向DeepSeek R1傳遞提示詞。
- DeepSeek R1分析簡歷內(nèi)容并提供反饋,而后由服務器使用 res.json().將分析結果響應給客戶端。
克隆并運行React用戶界面
通過項目的用戶界面上傳簡歷,即可將內(nèi)容發(fā)送至后端,而后向用戶展示DeepSeek R1的分析結果。其中還將包含DeepSeek R1的思考過程,即內(nèi)部思維鏈。
- 首先,通過此GitHub URL分叉并克隆本項目。如果各位沒有定制需求,也可直接克隆。
- 克隆完成后,使用命令 cd deepseek-ollama-frontend轉至項目根目錄。
- 在根目錄下,使用npm install 命令安裝必要依賴項。安裝完成后,使用npm run dev命令啟動本項目。
- 示例React應用的main組件為ResumeAnalyzer,你可在選定的編輯器中打開并進行分析。
import './ResumeAnalyzer.css';
import React, { useState } from 'react';
import { Upload, Loader2 } from 'lucide-react';
import AnalysisSection from './AnalysisSection';
const ResumeAnalyzer = () => {
const [file, setFile] = useState(null);
const [loading, setLoading] = useState(false);
const [feedback, setFeedback] = useState(null);
const [error, setError] = useState(null);
const handleFileChange = (event) => {
const selectedFile = event.target.files?.[0];
if (selectedFile && selectedFile.type === 'application/pdf') {
setFile(selectedFile);
setError(null);
} else {
setError('Please upload a PDF file');
setFile(null);
}
};
const analyzePDF = async () => {
if (!file) return;
setLoading(true);
setError(null);
try {
const formData = new FormData();
formData.append('pdfFile', file);
const response = await fetch('http://localhost:3000/analyze-pdf', {
method: 'POST',
body: formData,
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.details || 'Failed to analyze PDF');
}
const data = await response.json();
setFeedback(data);
} catch (err) {
setError(err.message || 'An error occurred');
} finally {
setLoading(false);
}
};
return (
<div className="max-w-4xl mx-auto p-4">
<div className="bg-gray-50 rounded-lg shadow-lg p-6">
<h1 className="text-3xl font-bold mb-6 text-gray-800">Resume Analyzer</h1>
<div className="bg-white rounded-lg shadow-sm p-8">
<div className="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center">
<Upload className="w-12 h-12 text-gray-400 mx-auto mb-4" />
<input
type="file"
accept=".pdf"
onChange={handleFileChange}
className="hidden"
id="file-upload"
/>
<label
htmlFor="file-upload"
className="cursor-pointer text-blue-600 hover:text-blue-800 font-medium"
>
Upload Resume (PDF)
</label>
{file && (
<p className="mt-2 text-sm text-gray-600">
Selected: {file.name}
</p>
)}
</div>
</div>
{error && (
<div className="mt-4 p-4 bg-red-50 text-red-700 rounded-lg border border-red-200">
{error}
</div>
)}
<button
onClick={analyzePDF}
disabled={!file || loading}
className="mt-6 w-full bg-blue-600 text-white py-3 px-4 rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center font-medium transition-colors"
>
{loading ? (
<>
<Loader2 className="mr-2 h-5 w-5 animate-spin" />
Analyzing Resume...
</>
) : (
'Analyze Resume'
)}
</button>
{feedback && !loading && (
<div className="mt-8">
<h2 className="text-2xl font-bold mb-6 text-gray-800">Analysis Results</h2>
{feedback.ollamaResponse &&
<AnalysisSection ollamaResponse={feedback.ollamaResponse} />
}
</div>
)}
</div>
</div>
);
};
export default ResumeAnalyzer;
- 此組件負責為用戶提供輸入字段以上傳文件。
- 使用API端點將上傳的文件發(fā)送至服務器。
- 服務器的響應結果分為兩部分——模型的內(nèi)部思維鏈與模型的實際響應內(nèi)容。
- 其中 AnalysisSection 組件用于顯示DeepSeek R1的實際響應結果,ExpandableSection組件則用于顯示模型的內(nèi)部思維過程。
在瀏覽器中導航至URL以加載示例應用。之后即可上傳任意簡歷并觀察DeepSeek R1生成的分析結果。
Resume Analyzer截屏
總結
DeepSeek R1讓我們擁有了以純本地方式構建生成式AI驅動應用的能力,并可根據(jù)實際需求進行靈活自定義。
在本文中,我們使用DeepSeek R1、Node.js與React構建起一款具備實用價值的應用程序,可完全離線使用并依托AI實現(xiàn)簡歷分析。歡迎大家按照本文分享的流程探索更多用例和設計思路。
原文標題:Building a Full-Stack Resume Screening Application With AI,作者:Anujkumarsinh Donvir