Compare commits

..

No commits in common. "5e7b44f6111163df99111bb2dc1f7adcb6759efa" and "b4310bba559a2cf1916fd60f5fd817a39b75f589" have entirely different histories.

4 changed files with 38 additions and 73 deletions

View File

@ -7,3 +7,12 @@ class VacancyAdmin(admin.ModelAdmin):
list_filter = ("source", "created_at") list_filter = ("source", "created_at")
search_fields = ("title",) search_fields = ("title",)
list_display = ("title", "source", "link", "created_at") list_display = ("title", "source", "link", "created_at")
def has_add_permission(self, request):
return False
def has_change_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False

View File

@ -26,10 +26,17 @@ from telegram.ext import (
) )
from vacancies.conf.settings import DB_URI from vacancies.conf.settings import DB_URI
from vacancies.main import prompts
from vacancies.main.models import Customer, CustomerCV, JobTitle from vacancies.main.models import Customer, CustomerCV, JobTitle
from vacancies.main.recommendations import get_next_vacancy from vacancies.main.recommendations import get_next_vacancy
SYSTEM_PROMPT = """
Ты карьерный копилот для ИТ. Ты можешь отвечать на любые вопросы по тематике карьеры.
У тебя есть доступ к резюме пользователя при необходимости.
Пиши кратко (до 56 строк, буллеты приветствуются).
После полезного ответа предложи что-нибудь, чем ты можешь помочь еще.
Отвечай простым текстом, не используй форматирование markdown.
"""
async def get_user_resume(user_id: int): async def get_user_resume(user_id: int):
"""Получает резюме пользователя для подбора вакансий.""" """Получает резюме пользователя для подбора вакансий."""
@ -85,7 +92,7 @@ async def prompt(update: Update, context: ContextTypes.DEFAULT_TYPE):
agent = create_agent( agent = create_agent(
model=chat_model, model=chat_model,
tools=[get_user_resume], tools=[get_user_resume],
system_prompt=prompts.BOT_SYSTEM_PROMPT, system_prompt=SYSTEM_PROMPT,
checkpointer=checkpointer, checkpointer=checkpointer,
) )
@ -128,7 +135,14 @@ async def handle_document(update: Update, context: ContextTypes.DEFAULT_TYPE):
openai_client = ChatOpenAI(model_name="gpt-5-mini", temperature=0, seed=42, top_p=1) openai_client = ChatOpenAI(model_name="gpt-5-mini", temperature=0, seed=42, top_p=1)
structured_llm = openai_client.with_structured_output(Structure) structured_llm = openai_client.with_structured_output(Structure)
prompt = f'{prompts.STRUCTURED_OUTPUT_PROMPT} {resume}' prompt = f"""
Ты HR-классификатор. Ниже приведён список допустимых профессий.
Твоя задача выбрать наиболее подходящие по смыслу.
Качество классификации - самое важное.
Игнорируй орфографические и стилистические различия.
Резюме:
{resume}
"""
response = await structured_llm.ainvoke(prompt) response = await structured_llm.ainvoke(prompt)
customer = await Customer.objects.aget(telegram_id=update.effective_user.id) customer = await Customer.objects.aget(telegram_id=update.effective_user.id)

View File

@ -9,7 +9,6 @@ from django.utils import timezone
from langchain_openai import ChatOpenAI from langchain_openai import ChatOpenAI
from pydantic import BaseModel from pydantic import BaseModel
from vacancies.main import prompts
from vacancies.main.models import JobTitle, Vacancy from vacancies.main.models import JobTitle, Vacancy
query = """ query = """
@ -65,7 +64,18 @@ class Command(BaseCommand):
batches = list(batched(result_rows, settings.COLLECT_VACANCIES_BATCH_SIZE)) batches = list(batched(result_rows, settings.COLLECT_VACANCIES_BATCH_SIZE))
for index, rows in enumerate(batches): for index, rows in enumerate(batches):
prompts = [f"{prompts.STRUCTURED_OUTPUT_PROMPT} {row[3]}" for row in rows] prompts = [
f"""
Ты HR-классификатор. Ниже приведён список допустимых профессий.
Твоя задача выбрать наиболее подходящую по смыслу.
Качество классификации - самое важное.
Если не уверен, то лучше укажи "Другое", ошибки недопустимы.
Игнорируй орфографические и стилистические различия.
Вакансия:
{row[3]}
"""
for row in rows
]
responses = structured_llm.batch(prompts) responses = structured_llm.batch(prompts)
vacancies = [] vacancies = []
for row, response in zip(rows, responses): for row, response in zip(rows, responses):

View File

@ -1,68 +0,0 @@
BOT_SYSTEM_PROMPT = """
Ты IT Career Copilot, высококвалифицированный HR-советчик и эксперт по рынку труда в ИТ. Твоя главная задача предоставлять пользователю практические и стратегические рекомендации по любым вопросам, связанным с карьерой в технологической сфере (поиск работы, развитие навыков, переговоры по зарплате, адаптация, увольнение).
Твой стиль общения: Фокус на действии (Action-Oriented): Отвечай максимально конкретно и применимо, предлагая шаги, которые пользователь может предпринять немедленно.
Консультация: Используй инсайты и данные о текущих трендах ИТ-рынка.
Краткость: Пиши лаконично, не более 4-5 содержательных пунктов или предложений. Используй маркированные списки (буллеты) для структурирования информации.
Формат: Отвечай простым текстом, не используя форматирование Markdown (заголовки, жирный шрифт, курсив, кодблоки).
Использование данных: У тебя есть дополнительный доступ к резюме пользователя (или фрагментам его карьерной истории) для персонализации ответов.
Завершение: После ответа обязательно предложи один-два релевантных следующих шага, чтобы продолжить карьерное планирование.
"""
STRUCTURED_OUTPUT_PROMPT = """
You are an HR specialist. Your task is to review vacansies and independently select a suitable topic (e.g., DevSecOps, Java Developer, Information Security Specialist, etc.).
You also need to analyze vacansies and structure the information from them according to the scheme.
You don't need to change or invent anything in the job posting below. You only need to structure the information provided.
Example vacancy:
'Network Security Team lead - Infrastructure Security, Wildberries 💜
ЗП: до 500 000 рублей net
Уровень: Lead
Формат работы: удалёнка или гибрид
🚀 Вместе с масштабным развитием IT направления Wildberries и Russ развивает информационную безопасность. Мы решаем сложные и разнообразные задачи: от повышения защищенности каждого сервиса до развития безопасности в рамках всей нашей инфраструктуры.
Мы ищем эксперта, который вместе с командой займется разработкой сложных технических решений и внедрением практик безопасности для повышения защищенности нашей сетевой инфраструктуры.
📝 Основные задачи:
Составление проектных планов и управление командой
Взаимодействие с сетевыми командами для сбора потребностей и согласования технических решений
Построение плана развития безопасности сетевой инфраструктуры
Внедрение практик ИБ и контроль состояния защищенности внешнего и внутреннего сетевого периметра
Разработка и внедрение мер по повышению прозрачности и контролируемости сетевых доступов в компании
💫 Необходимый опыт и навыки:
Опыт в организации командной работы
Опыт в построении сложных кросс-командных процессов
Умение разрабатывать комплексные решения по безопасности для серверной инфраструктуры на базе Linux
Хорошее знание сетевых технологий
Опыт решения проблем ИБ в сетевой инфраструктуре
Знание сетевых атак и способов защиты от них
Опыт работы с NGFW
🔥 Что мы предлагаем:
Полная удаленка или свободное посещение офисов в Москве и Санкт-Петербурге
IT-ипотека и оформление в аккредитованную IT-компанию
Бесплатное питание в офисах, ДМС со стоматологией (после испытательного срока)
Оплачиваемые Day Off, корпоративное обучение и IT-мероприятия
💘 Контакты: @Alens_HR'
Structured output of the example vacansy:
{
job_title: "Network Security Team lead - Infrastructure Security",
company_name: "Wildberries",
min_salary_rub: None,
max_salary_rub: 500000,
requirements: "Опыт в организации командной работы. Опыт в построении сложных кросс-командных процессов. Умение разрабатывать комплексные решения по безопасности для серверной инфраструктуры на базе Linux, Хорошее знание сетевых технологий. Опыт решения проблем ИБ в сетевой инфраструктуре. Знание сетевых атак и способов защиты от них. Опыт работы с NGFW]
}
Vacancy:
"""