vision-career/vacancies/main/bot.py

97 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import io
import traceback
from telegram import Update
from telegram.ext import filters, ApplicationBuilder, MessageHandler, CommandHandler, ContextTypes
from pypdf import PdfReader
from vacancies.main.models import Customer, CustomerCV
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langgraph.checkpoint.memory import InMemorySaver
from vacancies.main.vector_store import add_vectors, extract_features
SYSTEM_PROMPT = """
Ты — карьерный копилот для ИТ. Ты можешь отвечать на любые вопросы по тематике карьеры.
У тебя есть доступ к резюме пользователя при необходимости.
Пиши кратко (до 56 строк, буллеты приветствуются).
После полезного ответа предложи что-нибудь, чем ты можешь помочь еще.
Отвечай простым текстом, не используй форматирование markdown.
"""
async def get_user_resume(user_id: int):
"""Получает резюме пользователя для подбора вакансий."""
customer_cv = await CustomerCV.objects.filter(customer__telegram_id=user_id).afirst()
return customer_cv.content if customer_cv else ""
agent = create_agent(
model=ChatOpenAI(model_name="gpt-5-mini", reasoning_effort="minimal"),
tools=[get_user_resume],
system_prompt=SYSTEM_PROMPT,
checkpointer=InMemorySaver(),
)
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
await Customer.objects.aget_or_create(
telegram_id=update.effective_user.id,
defaults=dict(
username=update.effective_user.username,
chat_id=update.effective_chat.id,
),
)
text = "Привет! Я карьерный копилот: помогу с работой, интервью и расскажу новости по рынку, специально для тебя. С чего начнем?"
await context.bot.send_message(chat_id=update.effective_chat.id, text=text)
async def prompt(update: Update, context: ContextTypes.DEFAULT_TYPE):
message = await context.bot.send_message(update.effective_chat.id, "📝 Обрабатываю твой запрос. Пожалуйста, подождите...")
response = await agent.ainvoke(
input={"messages": [{"role": "user", "content": f'user_id = {update.effective_user.id}\n{update.message.text}'}]},
config={"configurable": {"thread_id": update.effective_user.id}},
)
await context.bot.editMessageText(response['messages'][-1].content, update.effective_chat.id, message.id)
async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> None:
traceback.print_exception(context.error)
await context.bot.send_message(chat_id=update.effective_chat.id, text="Произошла ошибка. Повтоите попытку позже.")
async def handle_document(update: Update, context: ContextTypes.DEFAULT_TYPE):
message = await context.bot.send_message(update.effective_chat.id, "📝 Обрабатываю твой запрос. Пожалуйста, подождите...")
if not update.message.document:
await context.bot.send_message(chat_id=update.effective_chat.id, text="Не удалось прочитать информацию из файла! Попробуйте другой формат.")
return
buffer = io.BytesIO()
file = await update.message.document.get_file()
await file.download_to_memory(buffer)
reader = PdfReader(buffer)
resume = "\n".join(page.extract_text() for page in reader.pages)
customer = await Customer.objects.aget(telegram_id=update.effective_user.id)
customer_cv, _ = await CustomerCV.objects.aupdate_or_create(customer=customer, defaults=dict(
content=resume,
))
features = extract_features(customer_cv.content)
add_vectors(
"cvs",
customer_cv.id,
features.model_dump(),
{'content': customer_cv.content, 'features_json': features.model_dump()},
)
await context.bot.editMessageText("Отлично! Запомнил Ваше резюме.", update.effective_chat.id, message.id)
application = ApplicationBuilder().token(os.environ["BOT_TOKEN"]).build()
application.add_handler(CommandHandler('start', start))
application.add_handler(MessageHandler(filters.TEXT & (~filters.COMMAND), prompt))
application.add_handler(MessageHandler((filters.Document.ALL | filters.PHOTO) & (~filters.COMMAND), handle_document))
application.add_error_handler(error_handler)