ABAO77 commited on
Commit
f01124b
verified
1 Parent(s): e00e339

Upload 14 files

Browse files
Dockerfile ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
2
+ # you will also find guides on how best to write your Dockerfile
3
+
4
+ FROM python:3.11
5
+
6
+ RUN useradd -m -u 1000 user
7
+ USER user
8
+ ENV PATH="/home/user/.local/bin:$PATH"
9
+
10
+ WORKDIR /app
11
+
12
+ COPY --chown=user ./requirements.txt requirements.txt
13
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
14
+
15
+ COPY --chown=user . /app
16
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
app.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from pydantic import BaseModel, Field
3
+ from typing import Optional
4
+ from fastapi.middleware.cors import CORSMiddleware
5
+ from graph import app as workflow
6
+ from fastapi.responses import JSONResponse
7
+ from langchain_core.messages import HumanMessage, AIMessage
8
+
9
+ app = FastAPI(docs_url="/")
10
+
11
+
12
+ app.add_middleware(
13
+ CORSMiddleware,
14
+ allow_origins=["*"],
15
+ allow_credentials=True,
16
+ allow_methods=["*"],
17
+ allow_headers=["*"],
18
+ )
19
+
20
+
21
+ class Item(BaseModel):
22
+ messages: list = Field(..., description="List of messages")
23
+ language: Optional[str] = Field("vi", description="Language of the messages")
24
+
25
+ class Config:
26
+ json_schema_extra = {
27
+ "example": {
28
+ "messages": [
29
+ {"type": "human", "content": "Ch脿o b岷"},
30
+ {"type": "ai", "content": "B岷 mu峄憂 t矛m job g矛?"},
31
+ {
32
+ "type": "human",
33
+ "content": "T么i mu峄憂 t矛m job d岷 h峄峜 cho trung c岷",
34
+ },
35
+ ],
36
+ "language": "vi",
37
+ }
38
+ }
39
+
40
+
41
+ def convert_message(messages):
42
+ list_message = []
43
+ for message in messages:
44
+ if message["type"] == "human":
45
+ list_message.append(HumanMessage(content=message["content"]))
46
+ else:
47
+ list_message.append(AIMessage(content=message["content"]))
48
+ return list_message
49
+
50
+
51
+ @app.post("/chat")
52
+ async def Chat(item: Item):
53
+ messages = convert_message(item.messages)
54
+ history = messages[:-1] if len(messages) > 1 else []
55
+ try:
56
+ response = workflow.invoke(
57
+ {
58
+ "user_query": messages[-1],
59
+ "messages_history": history,
60
+ "language": item.language,
61
+ }
62
+ )["llm_response"]
63
+ return JSONResponse(content=response, status_code=200)
64
+ except Exception as e:
65
+ return JSONResponse(content={"error": str(e)}, status_code=500)
66
+
67
+
68
+ if __name__ == "__main__":
69
+ import uvicorn
70
+
71
+ uvicorn.run("app:app")
config/.DS_Store ADDED
Binary file (6.15 kB). View file
 
config/__pycache__/database.cpython-311.pyc ADDED
Binary file (542 Bytes). View file
 
config/__pycache__/llm.cpython-311.pyc ADDED
Binary file (808 Bytes). View file
 
config/database.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_pinecone import PineconeVectorStore
2
+ from .llm import embeddings
3
+ import os
4
+
5
+ API_PINCONE_KEY = os.getenv("PIPE_CONE")
6
+ index = "recruiment-new"
7
+ vector_store = PineconeVectorStore(
8
+ index_name=index, embedding=embeddings, pinecone_api_key=API_PINCONE_KEY
9
+ )
config/llm.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_google_genai import ChatGoogleGenerativeAI, GoogleGenerativeAIEmbeddings
2
+ import os
3
+ API_KEY_1 = os.getenv("API_KEY_1")
4
+ API_KEY_2 = os.getenv("API_KEY_2")
5
+ llm_1 = ChatGoogleGenerativeAI(
6
+ model="gemini-1.5-flash",
7
+ temperature=0,
8
+ max_tokens=None,
9
+ timeout=None,
10
+ max_retries=2,
11
+ api_key=API_KEY_1,
12
+ )
13
+ llm_2 = ChatGoogleGenerativeAI(
14
+ model="gemini-1.5-flash",
15
+ temperature=0,
16
+ max_tokens=None,
17
+ timeout=None,
18
+ max_retries=2,
19
+ api_key=API_KEY_2,
20
+ )
21
+ embeddings = GoogleGenerativeAIEmbeddings(
22
+ model="models/text-embedding-004", google_api_key=API_KEY_1
23
+ )
graph.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langgraph.graph import StateGraph, START, END
2
+ from graph_function import (
3
+ route_fn,
4
+ transform_query_fn,
5
+ retrieve_document_fn,
6
+ grade_document_fn,
7
+ gen_answer_normal_fn,
8
+ grade_hallucinations_fn,
9
+ generate_answer_rag_fn,
10
+ State,
11
+ )
12
+
13
+ workflow = StateGraph(State)
14
+ workflow.add_node("routing", route_fn)
15
+ workflow.add_node("transform_query", transform_query_fn)
16
+ workflow.add_node("retrieve_document", retrieve_document_fn)
17
+ workflow.add_node("grade_document", grade_document_fn)
18
+ workflow.add_node("generate_answer_rag", generate_answer_rag_fn)
19
+ workflow.add_node("grade_hallucinations", grade_hallucinations_fn)
20
+ workflow.add_node("generate_answer_normal", gen_answer_normal_fn)
21
+
22
+ workflow.add_edge(START, "routing")
23
+
24
+
25
+ def routing_after_route(state: State):
26
+ if state["route"] == "vectorstore":
27
+ return "transform_query"
28
+ else:
29
+ return "generate_answer_normal"
30
+
31
+
32
+ workflow.add_conditional_edges(
33
+ "routing",
34
+ routing_after_route,
35
+ {
36
+ "transform_query": "transform_query",
37
+ "generate_answer_normal": "generate_answer_normal",
38
+ },
39
+ )
40
+ workflow.add_edge("transform_query", "retrieve_document")
41
+
42
+
43
+ def routing_after_retrieve_document(state: State):
44
+ return "grade_document" if len(state["documents"]) != 0 else "generate_answer_normal"
45
+
46
+
47
+ workflow.add_conditional_edges(
48
+ "retrieve_document",
49
+ routing_after_retrieve_document,
50
+ {
51
+ "grade_document": "grade_document",
52
+ "generate_answer_normal": "generate_answer_normal",
53
+ },
54
+ )
55
+
56
+
57
+ def route_after_grade_document(state: State):
58
+ return (
59
+ "generate_answer_rag"
60
+ if len(state["documents"]) != 0
61
+ else "generate_answer_normal"
62
+ )
63
+
64
+
65
+ workflow.add_conditional_edges(
66
+ "grade_document",
67
+ route_after_grade_document,
68
+ {
69
+ "generate_answer_rag": "generate_answer_rag",
70
+ "generate_answer_normal": "generate_answer_normal",
71
+ },
72
+ )
73
+ workflow.add_edge("generate_answer_rag", "grade_hallucinations")
74
+
75
+
76
+ def routing_check_pass_grade_hallucinations(state: State):
77
+ return END if state["grade_response"] == "yes" else "generate_answer_normal"
78
+
79
+
80
+ workflow.add_conditional_edges(
81
+ "grade_hallucinations",
82
+ routing_check_pass_grade_hallucinations,
83
+ {
84
+ END: END,
85
+ "generate_answer_normal": "generate_answer_normal",
86
+ },
87
+ )
88
+ workflow.add_edge("generate_answer_normal", END)
89
+ app = workflow.compile()
graph_function.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dotenv import load_dotenv
2
+ load_dotenv()
3
+ from prompt import (
4
+ RouteQuery,
5
+ GradeDocuments,
6
+ GenerateAnswer,
7
+ GradeHallucinations,
8
+ ExtractFilter,
9
+ route_chain,
10
+ transform_query_chain,
11
+ grade_documents_chain,
12
+ gen_normal_answer_chain,
13
+ gen_answer_rag_chain,
14
+ grade_hallucinations_chain,
15
+ extract_filter_chain,
16
+ )
17
+ from config.database import vector_store
18
+ from langchain_core.documents import Document
19
+ from prompt import GradeDocuments
20
+ from helper import convert_list_context_source_to_str
21
+ from logger import logger
22
+ from langgraph.graph.message import AnyMessage, add_messages
23
+ from typing import TypedDict, Literal
24
+
25
+
26
+
27
+ class State(TypedDict):
28
+ user_query: AnyMessage
29
+ route: str
30
+ messages_history: list
31
+ documents: list[Document]
32
+ filter: dict
33
+ llm_response: AnyMessage
34
+ grade_response: Literal["yes", "no"]
35
+ language: str
36
+ document_id_selected: str
37
+
38
+
39
+ def route_fn(state: State):
40
+ question = state["user_query"].content
41
+ route_response: RouteQuery = route_chain.invoke({"question": question})
42
+ logger.info(f"Route response: {route_response}")
43
+ return {"route": route_response.datasource}
44
+
45
+
46
+ def transform_query_fn(state: State):
47
+ question = state["user_query"].content
48
+ chat_history = state["messages_history"]
49
+ transform_response = transform_query_chain.invoke(
50
+ {"question": question, "chat_history": chat_history}
51
+ )
52
+ logger.info(f"Transform response: {transform_response}")
53
+ return {"user_query": transform_response}
54
+
55
+
56
+ def retrieve_document_fn(state: State):
57
+ question = state["user_query"].content
58
+ history = state["messages_history"]
59
+ filter = state.get("filter", None)
60
+ if not filter:
61
+ filter_response: ExtractFilter = extract_filter_chain.invoke(
62
+ {"question": question, "history": history}
63
+ )
64
+ logger.info(f"Extract filter response: {filter_response}")
65
+ job_title = filter_response.job_title
66
+ job_level = filter_response.job_level
67
+ filter = {}
68
+ if job_title:
69
+ filter["title"] = job_title
70
+ if job_level:
71
+ filter["level"] = job_level
72
+ retriever = vector_store.as_retriever(
73
+ search_type="similarity_score_threshold",
74
+ search_kwargs={"k": 5, "score_threshold": 0.0},
75
+ )
76
+ documents = retriever.invoke(question, filter=filter)
77
+ logger.info(f"Retrieved documents: {documents}")
78
+ return {"documents": documents}
79
+
80
+
81
+ def grade_document_fn(state: State):
82
+ question = state["user_query"].content
83
+ documents = state["documents"]
84
+ inputs_bach = [
85
+ {"question": question, "document": doc.page_content} for doc in documents
86
+ ]
87
+ grade_response: list[GradeDocuments] = grade_documents_chain.batch(inputs_bach)
88
+ logger.info(f"Grade response: {grade_response}")
89
+ document_index = [
90
+ index for index, doc in enumerate(grade_response) if doc.binary_score == "yes"
91
+ ]
92
+ filtered_documents = [documents[i] for i in document_index]
93
+
94
+ return {"documents": filtered_documents}
95
+
96
+
97
+ def generate_answer_rag_fn(state: State):
98
+ question = state["user_query"].content
99
+ documents = state["documents"]
100
+ language = state["language"]
101
+ if documents:
102
+ context_str = convert_list_context_source_to_str(documents)
103
+
104
+ gen_answer_response: GenerateAnswer = gen_answer_rag_chain.invoke(
105
+ {"question": question, "context": context_str, "language": language}
106
+ )
107
+ logger.info(f"Generate answer response: {gen_answer_response}")
108
+ id_selected = None
109
+ if gen_answer_response.selected_document_index is not None:
110
+ id_selected = documents[gen_answer_response.selected_document_index].metadata[
111
+ "id"
112
+ ]
113
+ logger.info(f"Document id selected: {id_selected}")
114
+ return {
115
+ "llm_response": gen_answer_response.answer,
116
+ "document_id_selected": id_selected,
117
+ }
118
+
119
+
120
+ def grade_hallucinations_fn(state: State):
121
+ question = state["user_query"].content
122
+ llm_response = state["llm_response"]
123
+ grade_response: GradeHallucinations = grade_hallucinations_chain.invoke(
124
+ {"question": question, "generation": llm_response}
125
+ )
126
+ logger.info(f"Grade hallucinations response: {grade_response}")
127
+ return {"grade_response": grade_response.binary_score}
128
+
129
+
130
+ def gen_answer_normal_fn(state: State):
131
+ question = state["user_query"].content
132
+ history = state["messages_history"]
133
+ gen_answer_response = gen_normal_answer_chain.invoke(
134
+ {"question": question, "history": history}
135
+ )
136
+ logger.info(f"Generate answer response: {gen_answer_response}")
137
+ return {"llm_response": gen_answer_response.content}
helper.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_core.documents import Document
2
+
3
+
4
+ def convert_list_context_source_to_str(contexts: list[Document]):
5
+ formatted_str = ""
6
+ for i, context in enumerate(contexts):
7
+ formatted_str += f"Document index {i}:\nContent: {context.page_content}\n"
8
+ formatted_str += "----------------------------------------------\n\n"
9
+ return formatted_str
job_data.xlsx ADDED
Binary file (112 kB). View file
 
logger.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import os
3
+ from datetime import datetime
4
+ from pathlib import Path
5
+
6
+ import pytz
7
+
8
+
9
+ class CoreCFG:
10
+ PROJECT_NAME = "RECRUITMENT"
11
+ BOT_NAME = str("RECRUITMENT")
12
+
13
+
14
+ def get_date_time():
15
+ return datetime.now(pytz.timezone("Asia/Ho_Chi_Minh"))
16
+
17
+
18
+ DATE_TIME = get_date_time().date()
19
+ BASE_DIR = os.path.dirname(Path(__file__).parent.parent)
20
+ LOG_DIR = os.path.join(BASE_DIR, "logs")
21
+
22
+
23
+ class CustomFormatter(logging.Formatter):
24
+ green = "\x1b[0;32m"
25
+ grey = "\x1b[38;5;248m"
26
+ yellow = "\x1b[38;5;229m"
27
+ red = "\x1b[31;20m"
28
+ bold_red = "\x1b[31;1m"
29
+ blue = "\x1b[38;5;31m"
30
+ white = "\x1b[38;5;255m"
31
+ reset = "\x1b[38;5;15m"
32
+
33
+ base_format = f"{grey}%(asctime)s | %(name)s | %(threadName)s | {{level_color}}%(levelname)-8s{grey} | {blue}%(module)s:%(lineno)d{grey} - {white}%(message)s"
34
+
35
+ FORMATS = {
36
+ logging.INFO: base_format.format(level_color=green),
37
+ logging.WARNING: base_format.format(level_color=yellow),
38
+ logging.ERROR: base_format.format(level_color=red),
39
+ logging.CRITICAL: base_format.format(level_color=bold_red),
40
+ }
41
+
42
+ def format(self, record):
43
+ log_fmt = self.FORMATS.get(record.levelno)
44
+ formatter = logging.Formatter(log_fmt)
45
+ return formatter.format(record)
46
+
47
+
48
+ def custom_logger(app_name="APP"):
49
+ logger_r = logging.getLogger(name=app_name)
50
+ tz = pytz.timezone("Asia/Ho_Chi_Minh")
51
+
52
+ logging.Formatter.converter = lambda *args: datetime.now(tz).timetuple()
53
+
54
+ ch = logging.StreamHandler()
55
+ ch.setLevel(logging.INFO)
56
+ ch.setFormatter(CustomFormatter())
57
+
58
+ logger_r.setLevel(logging.INFO)
59
+ logger_r.addHandler(ch)
60
+
61
+ return logger_r
62
+
63
+
64
+ logger = custom_logger(app_name=CoreCFG.PROJECT_NAME)
prompt.py ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, Field
2
+ from langchain_core.prompts import ChatPromptTemplate
3
+ from typing import Literal
4
+ from config.llm import llm_1, llm_2
5
+ from typing import Optional
6
+
7
+
8
+ class RouteQuery(BaseModel):
9
+ """Route a user query to the most relevant datasource."""
10
+
11
+ datasource: Literal["vectorstore", "casual_convo"] = Field(
12
+ ...,
13
+ description="Given a user question choose to route it to casual_convo or a vectorstore.",
14
+ )
15
+
16
+
17
+ class ExtractFilter(BaseModel):
18
+ """Extract job level and job title from user question."""
19
+
20
+ job_level: str = Field(description="The level of the job the user is asking about.")
21
+ job_title: str = Field(description="The title of the job the user is asking about.")
22
+
23
+
24
+ class GradeDocuments(BaseModel):
25
+ """Binary score for relevance check on retrieved documents."""
26
+
27
+ binary_score: str = Field(
28
+ description="Documents are relevant to the question, 'yes' or 'no'"
29
+ )
30
+
31
+
32
+ class GenerateAnswer(BaseModel):
33
+ """Generate an answer based on the provided documents."""
34
+
35
+ answer: str = Field(description="Generated answer based on the provided documents.")
36
+ selected_document_index: Optional[int] = Field(
37
+ description="Index of the selected document."
38
+ )
39
+
40
+
41
+ class GradeHallucinations(BaseModel):
42
+ """Binary score for grounding of generation answer in provided facts."""
43
+
44
+ binary_score: Literal["yes", "no"] = Field(
45
+ description="Whether the answer is grounded in the provided facts. 'yes' if the answer is supported by facts, 'no' if the answer contains information not present or contradicting the given facts"
46
+ )
47
+
48
+
49
+ route_prompt = ChatPromptTemplate(
50
+ [
51
+ (
52
+ "system",
53
+ """You are an expert at routing the user's question to vectorstore or casual_convo.
54
+ choose vectorstore if the question is related to job recruitment and casual_convo otherwise. \n
55
+ vectorstore contains documents related to recruitment of human resources in all industries! Related to salary, recruitment position, job description for each job. Use vectorstore for questions about these topics that require some data and follow-up questions. Otherwise, if only need normal feedback and chat history, use casual_convo.
56
+
57
+ example:
58
+ user: Hi are you [this is a random question not related to recruitment so route to casual_convo] : casual_convo
59
+ user: Jobs for junior dev? [this question is related to CS324 so route to vectorstore] : vectorstore""",
60
+ ),
61
+ ("human", "{question}"),
62
+ ]
63
+ )
64
+ re_write_query_prompt = ChatPromptTemplate(
65
+ [
66
+ (
67
+ "system",
68
+ """You a question re-writer that converts an input question to a better version that is optimized
69
+ for vectorstore retrieval, and very concise. Look at the input and try to reason about the underlying semantic intent/meaning. The input can also be a
70
+ follow up question, look at the chat history to re-write the question to include necessary info from the chat history to a better version that is optimized
71
+ for vectorstore retrieval without any other info needed. [the topic of convo will be generally around recruitment topic. You need to re-write query base on history and include keyword related to this topic""",
72
+ ),
73
+ ("placeholder", "{history}"),
74
+ (
75
+ "human",
76
+ "{question}",
77
+ ),
78
+ ]
79
+ )
80
+
81
+ extract_filter_prompt = ChatPromptTemplate.from_messages(
82
+ [
83
+ (
84
+ "system",
85
+ """You are an expert at extracting metadata from the user's question about recruitment and using it to filter the retrieved documents.
86
+
87
+ Fields to extract:
88
+ - Job level: The level of the job the user is asking about. Possible values are:
89
+ + intern
90
+ + fresher
91
+ + junior
92
+ + middle
93
+ + senior
94
+ + expert
95
+ - Job title: The title of the job the user is asking about.
96
+ + developer
97
+ + engineer
98
+ + designer
99
+ + business
100
+ + translator
101
+ + other (if the job title is not in the list above)
102
+
103
+ Note:
104
+ Leave the field blank if the information is not present in the user's question.
105
+ If the user's question contains multiple job titles, choose the most relevant one.
106
+ If the user's question contains multiple job levels, choose the most relevant one.
107
+ If not sure job-title or job-level, leave it blank.
108
+ """,
109
+ ),
110
+ ("placeholder", "{history}"),
111
+ ("user", "{question}"),
112
+ ]
113
+ )
114
+
115
+ check_relevant_document_prompt = ChatPromptTemplate(
116
+ [
117
+ (
118
+ "system",
119
+ """
120
+ You are a grader assessing relevance of a retrieved document to a user question.
121
+ If the document contains keyword(s) or semantic meaning related to the user question, grade it as relevant.
122
+ It does not need to be a stringent test. The goal is to filter out erroneous retrievals.
123
+ Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question.
124
+ Then, give a score ranges from 0 to 1, with higher values indicating a stronger match and the more corresponding keywords.
125
+ """,
126
+ ),
127
+ (
128
+ "human",
129
+ "Retrieved document: \n\n {document} \nvs\n User question: {question}",
130
+ ),
131
+ ]
132
+ )
133
+
134
+ gen_answer_rag_prompt = ChatPromptTemplate(
135
+ [
136
+ (
137
+ "system",
138
+ """You are chat bot related to recruitment. You are asked to generate an answer based on the provided documents.
139
+ Your are given context related to job description of a job position. If the context not provided, you just sau 'kh么ng c贸 t脿i li峄噓 li锚n quan'
140
+ Answer in {language} language.
141
+
142
+ Context:
143
+ ```
144
+ {context}
145
+ ```
146
+
147
+ """,
148
+ ),
149
+ (
150
+ "human",
151
+ """
152
+ Question: {question}
153
+ """,
154
+ ),
155
+ ]
156
+ )
157
+
158
+
159
+ grade_answer_prompt = ChatPromptTemplate(
160
+ [
161
+ (
162
+ "system",
163
+ """You are a grader assessing whether an answer addresses / resolves a question \n
164
+ Give a binary score 'yes' or 'no'. Yes' means that the answer resolves the question.
165
+ If the LLM Generation is saying that it doesnt know or not sure or stating to keep the questions relevant to topic , grade it as 'yes'.""",
166
+ ),
167
+ (
168
+ "human",
169
+ "If the LLM Generation is saying that it doesnt know or not sure or stating to keep the questions relevant to topic , grade it as 'yes'. User question: \n\n {question} \n\n LLM generation: {generation}",
170
+ ),
171
+ ]
172
+ )
173
+ gen_normal_answer_prompt = ChatPromptTemplate(
174
+ [
175
+ (
176
+ "system",
177
+ """You are assitant related recruitment. If user ask about seeking job, you can ask more detail about job title, job level, etc. If user ask about another topic, you can say that you don't know. Not answer a job information.
178
+ Only answer related to hot trend in recruitment domain. Not give user a job hiring information. Can using history like a context to prov
179
+ """,
180
+ ),
181
+ ("placeholder", "{history}"),
182
+ ("human", "{question}"),
183
+ ]
184
+ )
185
+ gen_answer_history_prompt = ChatPromptTemplate(
186
+ [
187
+ (
188
+ "system",
189
+ "You are assitant related recruitment. Using your knowledge about recruitment domain. If you not sure about the answer, just say that you don't know.",
190
+ ),
191
+ ("placeholder", "{history}"),
192
+ ("user", "{question}"),
193
+ ]
194
+ )
195
+ route_chain = route_prompt | llm_1.with_structured_output(RouteQuery)
196
+ transform_query_chain = re_write_query_prompt | llm_1
197
+ extract_filter_chain = extract_filter_prompt | llm_1.with_structured_output(ExtractFilter)
198
+ grade_documents_chain = check_relevant_document_prompt | llm_2.with_structured_output(
199
+ GradeDocuments
200
+ )
201
+ gen_answer_rag_chain = gen_answer_rag_prompt | llm_2.with_structured_output(
202
+ GenerateAnswer
203
+ )
204
+ gen_normal_answer_chain = gen_normal_answer_prompt | llm_1
205
+ grade_hallucinations_chain = grade_answer_prompt | llm_1.with_structured_output(
206
+ GradeHallucinations
207
+ )
requirments.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ langchain-pinecone
2
+ langgraph
3
+ langchain-google-genai