Generación Automatizada de Propuestas de Trabajo para Upwork con CrewAI y Streamlit

En este artículo, quiero compartir un logro reciente en el desarrollo de una herramienta automatizada para generar cartas de presentación personalizadas para propuestas en Upwork. El objetivo principal fue crear una solución rápida y eficiente que analice automáticamente a través de agentes de CrewAI una oferta de trabajo y un perfil de LinkedIn, para generar un texto atractivo que resalte las habilidades más relevantes del candidato. Un ejercicio similar al que hice en «AI Agents: Encuentra trabajo con este sistema de agentes» pero en aquel se trata de generar un Currículum y en el que desarrollaremos ahora de hacer una propuesta personalizada y adaptada para cualquier oferta de trabajo en Upwork.

Desafío Inicial

El problema que buscaba resolver era simple pero crucial por la pereza que me da: generar cartas de presentación que se adapten perfectamente a las necesidades de cada oferta de trabajo en Upwork. Muchas veces, los freelancers se ven en la necesidad de crear propuestas repetitivas y poco personalizadas, lo que reduce las posibilidades de captar la atención del cliente. En este caso el resultado es una propuesta adpatada a tus skills y a los requerimientos de la oferta en concreto.

Enfoque y Solución

Opté por una solución basada en Streamlit, una librería de Python que facilita la creación de interfaces web interactivas de manera rápida. Utilizando un conjunto de herramientas y agentes inteligentes de CrewAI diseñados para interactuar con los perfiles de LinkedIn y las ofertas de Upwork. Con esto logramos automatizar la generación de cartas de presentación a partir de dos datos clave que introduce el usuario: la URL del perfil de LinkedIn y la URL de la oferta de trabajo en Upwork.

Estructura del Proyecto

El flujo de la aplicación sigue tres pasos clave:

  1. Análisis de la oferta de trabajo: Utilizamos herramientas de scraping propias del framework de CrewAI para extraer la descripción de la oferta y analizar los requisitos clave, tales como habilidades, experiencia y competencias necesarias.
  2. Perfilado del candidato: A partir del perfil de LinkedIn del usuario, recopilamos información relevante que pueda alinearse con los requisitos de la oferta. Esto incluye experiencia, educación y habilidades destacadas.
  3. Generación de la carta de presentación: Una vez recopilada toda la información, un “agente” especializado en estrategias de cartas de presentación crea una carta adaptada al contexto del trabajo y las competencias del candidato.

IMPORTANTE: es necesario abrir la url de la oferta de Upwork en incógnito para asegurarse de que sea accesible por el agente y no únicamente si tienes la sesión iniciada.

Código

Para este desarrollo vamos a necesitar un setup inicial que implica:

  • Instalación de las dependencias necesarias
  • Definir claves API en un archivo .env de Openai para la IA generativa y SerperAPI para el research en la web.
!pip install crewai==0.28.8 crewai_tools==0.1.6 langchain_community==0.0.29 streamlit 
import streamlit as st
from crewai import Agent, Task, Crew
from utils import get_openai_api_key, get_serper_api_key
from crewai_tools import ScrapeWebsiteTool, SerperDevTool
import time
import os
# Set up API keys
openai_api_key = get_openai_api_key()
serper_api_key = get_serper_api_key()

Streamlit UI Set up

# Set up Streamlit UI
st.title('Upwork Cover Letter Generator')
job_posting_url = st.text_input('Enter the Upwork Job Posting URL')
linkedin_profile_url = st.text_input('Enter your LinkedIn Profile URL')

CrewAI Agents Set up

  • Creamos los agentes, definimos sus roles, tools y tareas según el framework de CrewAI.
  • Definimos condicionales para que no falten los datos necesarios
  • Definimos un tiempo para que el script no quede en loop sin generar una respuesta concreta
# Add a button to trigger the script
if st.button('Generate Cover Letter'):
    if job_posting_url and linkedin_profile_url:
        try:
            # Start timer
            start_time = time.time()

            st.write("Initializing tools...")
            # Initialize tools
            search_tool = SerperDevTool()
            scrape_tool = ScrapeWebsiteTool()

            st.write("Creating agents...")
            # Create agents
            researcher = Agent(
                role="Upwork Job Posting Researcher",
                goal="Analyze job postings and extract key requirements,  skills, and 		experiences "
                    "that are necessary for the job.",
                tools=[scrape_tool, search_tool],
                verbose=True,
                backstory=(
                    "As a Job Researcher, your expertise lies in analyzing job 				postings and identifying "
                    "the most important qualifications and skills. This analysis will 			support creating a "
                    "tailored and effective cover letter."
                )
            )

            profiler = Agent(
                role="Personal Profiler for IT experts applying to Upwork jobs",
                goal="Create a detailed personal and professional profile for the 		candidate, "
                    "with a focus on tailoring it for job proposals on Upwork.",
                tools=[scrape_tool, search_tool],
                verbose=True,
                backstory=(
                    "You specialize in creating profiles that emphasize key skills, 		achievements, "
                    "and experiences. Your focus is to craft a professional narrative 		that supports "
                    "the Upwork proposal using the LinkedIn profile."
                )
            )

            resume_strategist = Agent(
                role="Cover Letter Strategist for Upwork proposals",
                goal="Create a personalized and persuasive cover letter for a job 		proposal "
                    "that highlights the candidate's most relevant skills and 			experiences. "
                    "Ensure the cover letter is aligned with the job description and 		focuses on "
                    "why the candidate is the ideal fit for the job.",
                tools=[scrape_tool, search_tool],
                verbose=True,
                backstory=(
                    "With expertise in crafting tailored cover letters, you excel at 		emphasizing "
                    "key skills and experiences that match job requirements. Your 		focus is on creating "
                    "a brief, compelling narrative that positions the candidate as the 		best fit for "
                    "the Upwork job posting."
                )
            )

            st.write("Defining tasks...")
            # Create tasks
            research_task = Task(
                description=(
                    "Analyze the job posting url provided ({job_posting_url}) "
                    "to extract key skills, experiences, and qualifications required. "
                    "The goal is to create a list of key points that can be used to 		tailor the cover letter."),
                expected_output=(
                    "A structured list of job requirements, including necessary 		skills, qualifications, "
                    "and experiences, that will help tailor the cover letter."),
                agent=researcher,
                async_execution=True
            )

            profile_task = Task(
                description=(
                    "Compile a detailed personal and professional profile using the 		candidate’s LinkedIn profile ({linkedin_profile_url}) "
                    "Focus on identifying key skills and experiences that align with 		the Upwork "
                    "job description."),
                expected_output=(
                    "A professional profile that can be used to enhance and tailor the 		Upwork cover letter."),
                agent=profiler,
                async_execution=True
            )

            resume_strategy_task = Task(
                description=(
                    "Using the profile and Upwork job requirements obtained from 		previous tasks, "
                    "craft a concise and personalized cover letter. Highlight the 		candidate’s key "
                    "skills, experiences, and qualifications that match the job 		description. "
                    "Make sure the cover letter is professional and persuasive without 		fabricating any information."
                ),
                expected_output=(
                    "A cover letter that highlights the candidate's most relevant 		skills and "
                    "experiences, effectively presenting them as the best fit for the 		Upwork job."
                ),
                output_file="tailored_cover_letter.md",
                context=[research_task, profile_task],
                agent=resume_strategist
            )

            # Create and run the crew
            st.write("Running crew...")
            job_application_crew = Crew(
                agents=[researcher, profiler, resume_strategist],
                tasks=[research_task, profile_task, resume_strategy_task],
                verbose=True
            )

            job_application_inputs = {
                'job_posting_url': job_posting_url,
                'linkedin_profile_url': linkedin_profile_url
            }

            result = job_application_crew.kickoff(inputs=job_application_inputs)

            st.write("Cover letter generated successfully!")

            # Display result
            st.write("Loading cover letter...")
            with open('tailored_cover_letter.md', 'r') as f:
                cover_letter = f.read()
                st.markdown(cover_letter)

            # End timer and display execution time
            end_time = time.time()
            execution_time = end_time - start_time
            st.write(f"Execution completed in {execution_time:.2f} seconds.")

        except Exception as e:
            st.error(f"An error occurred: {e}")
    else:
        st.error('Please enter both the job posting URL and your LinkedIn profile URL.')

Ejecución en tu servidor

Una vez que tienes el código y todas las instalaciones necesarias pones a correr el servidor de Streamlit:

streamlit run your-file-name.py (asegurate de poner el nombre exacto de tu archivo)

y voilá:

Upwork cover letter generator with CrewAI and Streamlit

Resultados Obtenidos

El resultado es una herramienta que genera cartas de presentación en cuestión de segundos, completamente alineadas con las necesidades de cada oferta de trabajo. Al reducir la necesidad de escribir manualmente cada propuesta, se ahorra tiempo y se asegura una mayor disponibilidad en las aplicaciones enviadas.

Proximamente iré mejorando esta herramienta para que responda preguntas adicionales y encuentre de manera automática algunos trabajos interesantes. ¿Alguna idea para sumar?


Este proyecto es un gran ejemplo de cómo se puede aplicar el framework de CrewAI y el trabajo de agentes para automatizar problemas cotidianos de una manera eficiente y escalable. Si tienes alguna mejora que proponer es bienvenida y si quieres que hablemos de algun desarrollo IA solo tienes que decir hola! =)

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio