|
import json |
|
import os |
|
from concurrent.futures import ThreadPoolExecutor |
|
|
|
import gradio as gr |
|
import requests |
|
from langchain.chat_models import ChatOpenAI |
|
|
|
import utils |
|
from prompts import preprocess, recruiting_assistant, matches, intro |
|
|
|
llm = ChatOpenAI(temperature=0.0, openai_api_key=os.environ["OPENAI"]) |
|
|
|
|
|
def preprocess_resume(llm, resume) -> str: |
|
result = preprocess.preprocess_resume(llm, resume) |
|
resume_preprocess = result["resume_preprocess"] |
|
return resume_preprocess |
|
|
|
|
|
def call_endpoint(resume_preprocessed): |
|
url = f"https://3jxjznzonb.execute-api.eu-west-1.amazonaws.com/dev/prediction" |
|
headers = { |
|
"Content-Type": "application/json", |
|
"x-api-key": os.environ["API_KEY"], |
|
} |
|
response = requests.post( |
|
url, |
|
headers=headers, |
|
data=json.dumps({"text": resume_preprocessed, "limit": 10}), |
|
) |
|
response_data = response.json() |
|
return response_data |
|
|
|
|
|
def postprocess_vancy(vacancies, resume): |
|
if "prediction" in vacancies: |
|
prediction = vacancies["prediction"] |
|
if isinstance(prediction, list): |
|
|
|
html_table = "<table>" |
|
|
|
html_table += "<tr><th>Vacancy</th><th>Match</th></tr>" |
|
|
|
|
|
futures = [] |
|
matches_score_tuples = [] |
|
|
|
with ThreadPoolExecutor() as executor: |
|
for i, vacancy in enumerate(prediction): |
|
|
|
future = executor.submit( |
|
matches.get_skills_match, llm, vacancy, resume |
|
) |
|
futures.append((i, vacancy, future)) |
|
|
|
|
|
for i, vacancy, future in futures: |
|
skills_match = future.result() |
|
skills_match_predicted = utils.get_json_list_from_result( |
|
skills_match, "skills_match_predicted" |
|
) |
|
print("getting matched skills ...") |
|
matched_skills = [ |
|
element["content"] |
|
for element in skills_match_predicted |
|
if element["resume_index"] > 0 and element["vacancy_index"] > 0 |
|
] |
|
for element in matched_skills: |
|
vacancy = vacancy.lower().replace( |
|
element.lower(), |
|
f'<span style="background-color: yellow;">{element}</span>', |
|
) |
|
vacancy_formatted = vacancy.replace(".,", "<br/>") |
|
vacancy_formatted = f"VACATURE {i + 1}:<br/>{vacancy_formatted}" |
|
matches_html = "<br/> - ".join(matched_skills) |
|
resume_matched_formatted = ( |
|
f"Score: {len(matched_skills)}<br/>-{matches_html}" |
|
) |
|
matches_score_tuples.append((vacancy_formatted, resume_matched_formatted, len(matched_skills))) |
|
print("sorting matches based on the score.") |
|
matches_score_tuples.sort(key=lambda x: x[-1], reverse=True) |
|
print("constructing html table.") |
|
for element in matches_score_tuples: |
|
html_table += f"<tr><td>{element[0]}</td><td>{element[1]}</td></tr>" |
|
html_table += "</table>" |
|
return html_table |
|
|
|
return "niets teruggevonden, probeer nogmaals ..." |
|
|
|
|
|
def search(resume): |
|
original_resume = resume |
|
resume_preprocessed = preprocess_resume(llm, resume) |
|
vacancies = call_endpoint(resume_preprocessed) |
|
vacancies_formatted = postprocess_vancy(vacancies, original_resume) |
|
return vacancies_formatted |
|
|
|
def get_intro_jobseeker(vacancy, resume): |
|
intro_ = intro.get_intro_jobseeker(llm, vacancy, resume) |
|
return intro_["intro"] |
|
|
|
def get_intro_jobissuer(vacancy, resume): |
|
intro_ = intro.get_intro_jobissuer(llm, vacancy, resume) |
|
return intro_["intro"] |
|
|
|
def get_intro(vacancy, resume): |
|
jobseeker = get_intro_jobseeker(vacancy, resume) |
|
jobissuer = get_intro_jobissuer(vacancy, resume) |
|
return f""" |
|
EMAIL KANDIDAAT: |
|
================= |
|
|
|
{jobseeker} |
|
|
|
EMAIL BEDRIJF: |
|
=============== |
|
|
|
{jobissuer} |
|
|
|
""" |
|
|
|
|
|
examples = [ |
|
""" |
|
Jan De Vries |
|
Magazijnier |
|
|
|
Adres: Hoofdstraat 123, 1000 Brussel |
|
Telefoon: +32 123 456 789 |
|
E-mail: jan.devries@email.com |
|
|
|
Ervaren magazijnier met meer dan 7 jaar ervaring in het beheer van auto-onderdelen in grootschalige distributiecentra. Ik zoek een positie waar ik mijn expertise in voorraadbeheer, orderverwerking en teammanagement kan toepassen om de efficiëntie en productiviteit van het magazijn te verbeteren. |
|
|
|
Werkervaring |
|
|
|
Magazijnier, AutoParts Warehouse, Gent |
|
Januari 2017 - Heden |
|
|
|
Verantwoordelijk voor de ontvangst en verwerking van inkomende leveringen. |
|
Coördinatie van de dagelijkse pick- en pack-activiteiten. |
|
Beheer van een team van 5 medewerkers om te zorgen voor tijdige leveringen. |
|
Implementatie van een nieuw voorraadsysteem wat resulteerde in een vermindering van 15% in overstock. |
|
Assistent Magazijnier, CarParts Distributie, Antwerpen |
|
Juni 2014 - December 2016 |
|
|
|
Geholpen bij het organiseren van het magazijn voor optimale opslag. |
|
Bijgehouden van voorraadniveaus en tijdig bestellingen geplaatst. |
|
Geholpen bij het trainen van nieuwe medewerkers. |
|
Opleiding |
|
|
|
Diploma Secundair Onderwijs, Technisch Onderwijs, VTI Brussel |
|
2010 - 2013 |
|
|
|
Vaardigheden |
|
|
|
Grondige kennis van auto-onderdelen. |
|
Ervaren in het gebruik van voorraadbeheersystemen. |
|
Uitstekende organisatorische en multitasking-vaardigheden. |
|
Sterke communicatieve vaardigheden en teamspeler. |
|
Bekwaam in het gebruik van heftrucks en andere magazijnapparatuur. |
|
Talen |
|
|
|
Nederlands (Moedertaal) |
|
Engels (Vloeiend) |
|
Frans (Basis) |
|
Certificaten |
|
|
|
Heftruckcertificaat, VDAB, 2014 |
|
""", |
|
""" |
|
Johannes van der Meer |
|
Chef-kok |
|
|
|
Adres: Klaverstraat 45, 3000 Leiden |
|
Telefoon: +31 6 1234 5678 |
|
E-mail: johannesvdm@email.nl |
|
|
|
Gepassioneerde en ervaren chef-kok met meer dan 12 jaar ervaring in zowel traditionele Nederlandse als internationale keukens. Bekend om het creëren van innovatieve en heerlijke gerechten met een focus op verse en lokale ingrediënten. Sterke leiderschapsvaardigheden en een bewezen vermogen om keukenteams te leiden en te trainen. |
|
|
|
WERKERVARING |
|
|
|
Executive Chef – Luxe Restaurant De Zon, Amsterdam |
|
Januari 2019 - Heden |
|
|
|
Verantwoordelijk voor het dagelijks beheer van de keuken. |
|
Ontwikkeling en implementatie van nieuwe menu's volgens seizoensgebonden beschikbaarheid. |
|
Training en begeleiding van een team van 15 koks en keukenpersoneel. |
|
Sous Chef – Brasserie Lente, Utrecht |
|
Juni 2013 - December 2018 |
|
|
|
Assisteerde de hoofdchef bij het plannen van menu's en het organiseren van speciale evenementen. |
|
Beheerde voedselvoorraden en budgetten. |
|
Onderhield relaties met leveranciers en zorgde voor de hoogste kwaliteit ingrediënten. |
|
Chef de Partie – Restaurant De Oude Molen, Den Haag |
|
Augustus 2008 - Mei 2013 |
|
|
|
Gespecialiseerd in sauzen en was verantwoordelijk voor de sauzen- en soepensectie. |
|
Assisteerde bij de voorbereiding van dagelijkse specials. |
|
Hielp bij het opleiden van junior koks. |
|
OPLEIDING |
|
|
|
Diploma Culinaire Kunst, Culinaire School van Amsterdam |
|
2005 - 2007 |
|
|
|
Vakdiploma Kok, ROC Leiden |
|
2003 - 2005 |
|
|
|
VAARDIGHEDEN |
|
|
|
Uitstekende kooktechnieken |
|
Menuontwikkeling |
|
Teamleiderschap |
|
Voedselveiligheid en hygiëne |
|
Voorraadbeheer |
|
Budgetbeheer |
|
TALEN |
|
|
|
Nederlands (Moedertaal) |
|
Engels (Vloeiend) |
|
Frans (Basis) |
|
|
|
""", |
|
""" |
|
Naam: John Doe |
|
Koeltechnieker |
|
|
|
Adres: Parkstraat 123, 1000 Stadsveld |
|
Telefoon: +31 6 1234 5678 |
|
E-mail: john.doe@email.com |
|
Geboortedatum: 15 juli 1985 |
|
Nationaliteit: Nederlandse |
|
|
|
Een toegewijde en bekwame koeltechnieker met 8 jaar ervaring in het ontwerpen, installeren, onderhouden en repareren van koelsystemen. Technisch onderlegd en bekend met verschillende koeltechnieken. Een probleemoplosser die snel storingen kan diagnosticeren en efficiënte oplossingen kan implementeren. Goed in teamverband en klantgericht. |
|
|
|
Werkervaring: |
|
|
|
Service Technicus bij KoelTech B.V. - januari 2018 tot heden |
|
|
|
Verantwoordelijk voor het installeren en onderhouden van commerciële en industriële koelsystemen. |
|
Diagnose stellen en repareren van storingen in koelapparatuur. |
|
Uitvoeren van preventief onderhoud om de prestaties van koelinstallaties te optimaliseren. |
|
Klantgerichte aanpak om vragen en problemen van klanten op te lossen. |
|
Assistent Koeltechnieker bij CoolAir Installaties - maart 2014 tot december 2017 |
|
|
|
Betrokken bij het ontwerp en de installatie van nieuwe koelsystemen in residentiële gebouwen. |
|
Uitvoeren van druk- en temperatuurmetingen om de juiste werking van de systemen te waarborgen. |
|
Oplossen van technische problemen en het vervangen van defecte onderdelen. |
|
Het trainen van klanten in het juiste gebruik en onderhoud van koelapparatuur. |
|
Opleiding: |
|
|
|
MBO Koeltechniek - Technische School Stadsveld - 2014 |
|
Certificaat Veilig werken met koelinstallaties - Koelacademie Nederland - 2014 |
|
Vaardigheden: |
|
|
|
Uitgebreide kennis van verschillende koeltechnieken en -systemen. |
|
Sterk technisch inzicht en probleemoplossend vermogen. |
|
Bekend met veiligheidsvoorschriften en -procedures in de koeltechniek. |
|
Goede communicatieve vaardigheden om effectief te kunnen samenwerken met collega's en klanten. |
|
Bekwaamheid in het lezen van technische tekeningen en schema's. |
|
Talen: |
|
|
|
Nederlands: Moedertaal |
|
Engels: Goed |
|
Referenties: |
|
Beschikbaar op verzoek. |
|
|
|
Opmerking: Dit CV bevat fictieve gegevens en dient alleen ter illustratie. Gebruik het als een sjabloon en vervang de gegevens door je eigen informatie bij het maken van een CV. |
|
""", |
|
] |
|
|
|
demo = gr.Blocks(theme=gr.themes.Soft()) |
|
|
|
with demo: |
|
with gr.Group(): |
|
with gr.Box(): |
|
with gr.Row(elem_id="prompt-container").style( |
|
mobile_collapse=False, equal_height=True |
|
): |
|
with gr.Column(): |
|
text_cv = gr.Textbox( |
|
lines=7, |
|
label="1. Voer een CV in en krijg relevante vacatures terug.", |
|
) |
|
b1 = gr.Button("Zoek Vacatures").style( |
|
margin=False, |
|
rounded=(False, True, True, False), |
|
full_width=False, |
|
) |
|
html_search_result = gr.HTML( |
|
label="Top vacatures gevonden in de database", |
|
) |
|
b1.click(search, inputs=text_cv, outputs=html_search_result) |
|
|
|
text_vacature = gr.Textbox( |
|
label="2. Selecteer een geschikte vacature voor deze CV, plak deze in het tekstveld hieronder en krijg een relevante intro.", |
|
) |
|
b2 = gr.Button("Schrijf een relevante intro").style( |
|
margin=False, |
|
rounded=(False, True, True, False), |
|
full_width=False, |
|
) |
|
text_intro = gr.Textbox(label="3. Introductie E-mail") |
|
b2.click( |
|
get_intro, |
|
inputs=[text_vacature, text_cv], |
|
outputs=[text_intro], |
|
) |
|
|
|
gr.Examples( |
|
examples=examples, |
|
fn=search, |
|
inputs=text_cv, |
|
outputs=html_search_result, |
|
cache_examples=False, |
|
) |
|
|
|
demo.launch() |
|
|