Compare commits
No commits in common. "6b9267af02647662cf1eeb017d6e35108436ec3c" and "ac3b2a27d523da269fdc0cdd3b6d615c89032350" have entirely different histories.
6b9267af02
...
ac3b2a27d5
@ -11,7 +11,6 @@ dependencies = [
|
|||||||
"langchain>=0.3.27",
|
"langchain>=0.3.27",
|
||||||
"langchain-openai>=0.3.35",
|
"langchain-openai>=0.3.35",
|
||||||
"langchain-qdrant>=1.1.0",
|
"langchain-qdrant>=1.1.0",
|
||||||
"langgraph-checkpoint-postgres>=3.0.0",
|
|
||||||
"psycopg[binary]>=3.2.12",
|
"psycopg[binary]>=3.2.12",
|
||||||
"pydantic>=2.0",
|
"pydantic>=2.0",
|
||||||
"pypdf>=6.1.2",
|
"pypdf>=6.1.2",
|
||||||
|
|||||||
@ -14,8 +14,6 @@ import os
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import sentry_sdk
|
import sentry_sdk
|
||||||
import clickhouse_connect
|
|
||||||
from qdrant_client import QdrantClient
|
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
||||||
@ -92,8 +90,6 @@ DATABASES = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
DB_URI = f"postgres://{DATABASES['default']['USER']}:{DATABASES['default']['PASSWORD']}@{DATABASES['default']['HOST']}:{DATABASES['default']['PORT']}/{DATABASES['default']['NAME']}?sslmode=disable"
|
|
||||||
|
|
||||||
|
|
||||||
# Password validation
|
# Password validation
|
||||||
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
|
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
|
||||||
@ -164,7 +160,3 @@ LOGGING = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
CLICKHOUSE_CLIENT = clickhouse_connect.create_client(host="127.0.0.1", port=18123)
|
|
||||||
|
|
||||||
QDARNT_CLIENT = QdrantClient(url="http://localhost:6333")
|
|
||||||
|
|||||||
@ -7,9 +7,8 @@ from pypdf import PdfReader
|
|||||||
from vacancies.main.models import Customer, CustomerCV
|
from vacancies.main.models import Customer, CustomerCV
|
||||||
from langchain.agents import create_agent
|
from langchain.agents import create_agent
|
||||||
from langchain_openai import ChatOpenAI
|
from langchain_openai import ChatOpenAI
|
||||||
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
|
from langgraph.checkpoint.memory import InMemorySaver
|
||||||
from vacancies.main.vector_store import add_vectors, extract_features
|
from vacancies.main.vector_store import add_vectors, extract_features
|
||||||
from vacancies.conf.settings import DB_URI
|
|
||||||
|
|
||||||
SYSTEM_PROMPT = """
|
SYSTEM_PROMPT = """
|
||||||
Ты — карьерный копилот для ИТ. Ты можешь отвечать на любые вопросы по тематике карьеры.
|
Ты — карьерный копилот для ИТ. Ты можешь отвечать на любые вопросы по тематике карьеры.
|
||||||
@ -26,6 +25,14 @@ async def get_user_resume(user_id: int):
|
|||||||
return customer_cv.content if customer_cv else ""
|
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):
|
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
await Customer.objects.aget_or_create(
|
await Customer.objects.aget_or_create(
|
||||||
telegram_id=update.effective_user.id,
|
telegram_id=update.effective_user.id,
|
||||||
@ -39,22 +46,14 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
|
|
||||||
|
|
||||||
async def prompt(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def prompt(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
async with AsyncPostgresSaver.from_conn_string(DB_URI) as checkpointer:
|
message = await context.bot.send_message(update.effective_chat.id, "📝 Обрабатываю твой запрос. Пожалуйста, подождите...")
|
||||||
agent = create_agent(
|
|
||||||
model=ChatOpenAI(model_name="gpt-5-mini", reasoning_effort="minimal"),
|
|
||||||
tools=[get_user_resume],
|
|
||||||
system_prompt=SYSTEM_PROMPT,
|
|
||||||
checkpointer=checkpointer,
|
|
||||||
)
|
|
||||||
|
|
||||||
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}},
|
||||||
|
)
|
||||||
|
|
||||||
response = await agent.ainvoke(
|
await context.bot.editMessageText(response['messages'][-1].content, update.effective_chat.id, message.id)
|
||||||
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:
|
async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
from django.core.management import BaseCommand
|
from django.core.management import BaseCommand
|
||||||
|
import clickhouse_connect
|
||||||
from vacancies.main.vector_store import add_vectors, extract_features, client as qdrant
|
from vacancies.main.vector_store import add_vectors, extract_features, client as qdrant
|
||||||
from vacancies.conf.settings import CLICKHOUSE_CLIENT
|
|
||||||
|
|
||||||
|
clickhouse_client = clickhouse_connect.create_client(host="127.0.0.1", port=18123)
|
||||||
|
|
||||||
query = """
|
query = """
|
||||||
SELECT id, chat_username, telegram_id, message, timestamp
|
SELECT id, chat_username, telegram_id, message, timestamp
|
||||||
@ -39,7 +40,7 @@ class Command(BaseCommand):
|
|||||||
next_page_offset = response[1]
|
next_page_offset = response[1]
|
||||||
exist_points_set = tuple(set(exist_points_ids))
|
exist_points_set = tuple(set(exist_points_ids))
|
||||||
|
|
||||||
result_rows = CLICKHOUSE_CLIENT.query(query, parameters={"exist_points": exist_points_set}).result_rows
|
result_rows = clickhouse_client.query(query, parameters={"exist_points": exist_points_set}).result_rows
|
||||||
result_rows_len = len(result_rows)
|
result_rows_len = len(result_rows)
|
||||||
for index, row in enumerate(result_rows):
|
for index, row in enumerate(result_rows):
|
||||||
(id, chat_username, telegram_id, message, timestamp) = row
|
(id, chat_username, telegram_id, message, timestamp) = row
|
||||||
|
|||||||
@ -1,19 +1,9 @@
|
|||||||
import sys
|
|
||||||
import asyncio
|
|
||||||
from django.core.management import BaseCommand
|
from django.core.management import BaseCommand
|
||||||
from vacancies.main.bot import application
|
from vacancies.main.bot import application
|
||||||
from langgraph.checkpoint.postgres import PostgresSaver
|
|
||||||
from vacancies.conf.settings import DB_URI
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = "Run bot"
|
help = "Run bot"
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
with PostgresSaver.from_conn_string(DB_URI) as checkpointer:
|
|
||||||
checkpointer.setup()
|
|
||||||
|
|
||||||
if sys.platform == "win32":
|
|
||||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
||||||
|
|
||||||
application.run_polling()
|
application.run_polling()
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
from qdrant_client import models
|
from qdrant_client import models
|
||||||
from langchain_openai import OpenAIEmbeddings
|
from langchain_openai import OpenAIEmbeddings
|
||||||
from langchain_openai import ChatOpenAI
|
from langchain_openai import ChatOpenAI
|
||||||
|
from qdrant_client import QdrantClient
|
||||||
from qdrant_client.models import Filter
|
from qdrant_client.models import Filter
|
||||||
from vacancies.main.models import VacancyFeatures
|
from vacancies.main.models import VacancyFeatures
|
||||||
from vacancies.conf.settings import QDARNT_CLIENT
|
|
||||||
|
|
||||||
|
client = QdrantClient(url="http://localhost:6333")
|
||||||
|
|
||||||
FEATURE_NAMES = [
|
FEATURE_NAMES = [
|
||||||
"job_title", "employment_type", "work_format", "experience", "position_level", "industry", "tech_stack",
|
"job_title", "employment_type", "work_format", "experience", "position_level", "industry", "tech_stack",
|
||||||
@ -31,13 +32,13 @@ vectors_config = {
|
|||||||
name: models.VectorParams(size=3072, distance=models.Distance.COSINE) for name in FEATURE_NAMES
|
name: models.VectorParams(size=3072, distance=models.Distance.COSINE) for name in FEATURE_NAMES
|
||||||
}
|
}
|
||||||
|
|
||||||
if not QDARNT_CLIENT.collection_exists("vacancies"):
|
if not client.collection_exists("vacancies"):
|
||||||
QDARNT_CLIENT.create_collection(
|
client.create_collection(
|
||||||
collection_name="vacancies",
|
collection_name="vacancies",
|
||||||
vectors_config=vectors_config
|
vectors_config=vectors_config
|
||||||
)
|
)
|
||||||
if not QDARNT_CLIENT.collection_exists("cvs"):
|
if not client.collection_exists("cvs"):
|
||||||
QDARNT_CLIENT.create_collection(
|
client.create_collection(
|
||||||
collection_name="cvs",
|
collection_name="cvs",
|
||||||
vectors_config=vectors_config
|
vectors_config=vectors_config
|
||||||
)
|
)
|
||||||
@ -70,7 +71,7 @@ def add_vectors(collection_name: str, _id: int, features: dict, payload: dict):
|
|||||||
max_similarities = {}
|
max_similarities = {}
|
||||||
for name, vec in vectors.items():
|
for name, vec in vectors.items():
|
||||||
if any(v != 0 for v in vec):
|
if any(v != 0 for v in vec):
|
||||||
results = QDARNT_CLIENT.query_points(
|
results = client.query_points(
|
||||||
collection_name="vacancies",
|
collection_name="vacancies",
|
||||||
query=vec,
|
query=vec,
|
||||||
using=name,
|
using=name,
|
||||||
@ -92,7 +93,7 @@ def add_vectors(collection_name: str, _id: int, features: dict, payload: dict):
|
|||||||
if scored and scored[0]["score"] > 33: # threshold
|
if scored and scored[0]["score"] > 33: # threshold
|
||||||
return
|
return
|
||||||
|
|
||||||
QDARNT_CLIENT.upsert(
|
client.upsert(
|
||||||
collection_name=collection_name,
|
collection_name=collection_name,
|
||||||
points=[
|
points=[
|
||||||
models.PointStruct(
|
models.PointStruct(
|
||||||
@ -105,7 +106,7 @@ def add_vectors(collection_name: str, _id: int, features: dict, payload: dict):
|
|||||||
|
|
||||||
|
|
||||||
def search_similarities(query_filter: Filter, cv_id: int):
|
def search_similarities(query_filter: Filter, cv_id: int):
|
||||||
cv = QDARNT_CLIENT.retrieve(
|
cv = client.retrieve(
|
||||||
collection_name="cvs",
|
collection_name="cvs",
|
||||||
ids=[cv_id],
|
ids=[cv_id],
|
||||||
with_vectors=True,
|
with_vectors=True,
|
||||||
@ -115,7 +116,7 @@ def search_similarities(query_filter: Filter, cv_id: int):
|
|||||||
vacancies_content = {}
|
vacancies_content = {}
|
||||||
for name, vec in cv.vector.items():
|
for name, vec in cv.vector.items():
|
||||||
if any(v != 0 for v in vec):
|
if any(v != 0 for v in vec):
|
||||||
results = QDARNT_CLIENT.query_points(
|
results = client.query_points(
|
||||||
collection_name="vacancies",
|
collection_name="vacancies",
|
||||||
query=vec,
|
query=vec,
|
||||||
using=name,
|
using=name,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user