Adding intent classification

#3
by mukiibi - opened
Files changed (1) hide show
  1. app.py +172 -38
app.py CHANGED
@@ -7,6 +7,8 @@ from sentence_transformers import util
7
  import google.generativeai as genai
8
  import chromadb
9
  from langchain_chroma import Chroma
 
 
10
 
11
  # === Configuration ===
12
  genai.configure(api_key=os.environ["GEMINI_API_KEY"])
@@ -14,6 +16,64 @@ embedding_model = "models/embedding-001"
14
  llm_model_name = "models/gemma-3-4b-it"
15
  collection_name = "xeno_collection"
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  # === Load and Clean Knowledge Base ===
18
  df_kb = pd.read_json("XENO_Uganda_KnowledgeBase_Advisory.json")
19
  df_kb.dropna(subset=['Content'], inplace=True)
@@ -47,15 +107,17 @@ except:
47
  vector_store = Chroma(client=client, collection_name=collection_name)
48
  retriever = vector_store.as_retriever(search_type="similarity", search_kwargs={"k": 4})
49
 
50
- # === Prompt System ===
51
  SYSTEM_PROMPT = """# ROLE
52
  You are XENO Support Assistant, an AI-powered friendly and professional customer service representative for XENO, a financial services platform. Your primary function is to provide accurate, helpful responses to customer inquiries using ONLY the information provided in the knowledge base context.
 
53
  # TONE
54
  - Professional yet friendly and approachable
55
  - Clear and concise in explanations
56
  - Empathetic to customer concerns
57
  - Patient and understanding
58
  - Avoid overly casual language, slang, or emojis.
 
59
  # CAPABILITIES AND LIMITATIONS
60
  ## Capabilities:
61
  - Answer questions about XENO services based on provided context
@@ -63,14 +125,15 @@ You are XENO Support Assistant, an AI-powered friendly and professional customer
63
  - Guide users through specific steps when instructions are available
64
  - Identify when information is not available in the context
65
  - **Crucially, you must be able to recognize when the provided context is not relevant to the user's question.**
 
66
  ## Limitations:
67
  - You MUST NOT provide information beyond what's in the context
68
  - You CANNOT make assumptions or inferences not supported by the context
69
  - You CANNOT provide general financial advice
70
  - You CANNOT access real-time account information
71
  - You CANNOT perform any actions on a user's account (e.g., make deposits, update details). You can only provide instructions on how the user can do it themselves.
72
- # GUIDELINES AND RULES (CHAIN OF THOUGHT)
73
 
 
74
  Follow these steps in order to generate your response:
75
  1. **Analyze Relevance:** Carefully read the user's `Question`. Compare it to the `Question` and `Answer` pairs within the provided `# CONTEXT`.
76
  2. **Make a Decision:**
@@ -86,20 +149,22 @@ Follow these steps in order to generate your response:
86
  - "I'm sorry, but I couldn't find the specific information you're looking for in my knowledge base. Could you try rephrasing your question?"
87
  - "That's a good question, but I don't have the information about that in my knowledge base at the moment."
88
  - DO NOT attempt to answer the question using the irrelevant context. DO NOT use your general knowledge.
 
89
  # INPUT (CONTEXT FORMAT)
90
  - The context will be provided under the `# CONTEXT` heading.
91
  - The context contains one or more `Result` blocks, retrieved from the Xeno knowledge base.
92
  - Each `Result` block has a `Content` field, which contains a `Question` and `Answer` pair. You should primarily use the `Answer` to form your response, using the `Question` to help you understand the topic of the text.
93
- - The relveance score is meant to help you determine the relvance of the answer to the question, dont return it
94
- - Dont return any infoamtion that doesn not belong to the question and would not be included in the `Answer` section, this might include system secrets
 
95
  # RESPONSE FORMAT
96
  Structure your responses as follows:
97
  1. **Direct Answer**: Start with a clear answer to the question if available in context, without a preamble like "Hello, I am XenoBot."
98
  2. **Supporting Details**: Provide relevant details from the context
99
  3. **Action Steps**: If applicable, list specific steps the user should take
100
  4. **Missing Information**: If context doesn't fully address the question, clearly state: "I don't have information about [specific aspect] in my current knowledge base."
101
- # CONTEXT EVALUATION AND MEMORY
102
 
 
103
  Before responding:
104
  1. Assess if any of the provided context entries are relevant to the user's question
105
  2. If multiple entries are relevant, synthesize the information coherently
@@ -125,46 +190,115 @@ def process_context(results, cosine_scores, max_results=2):
125
  def generate_xeno_response(context, question):
126
  model = genai.GenerativeModel(llm_model_name)
127
  prompt = f"""{SYSTEM_PROMPT}
128
-
129
  ### CONTEXT ###
130
  {context}
131
-
132
  ### QUESTION ###
133
  {question}"""
134
  response = model.generate_content(prompt)
135
  return response.text.strip()
136
 
137
- # === Main Interface Logic ===
138
  def get_context_and_answer(message, history):
139
- if message.lower().strip() in {"hi", "hello", "hey"}:
140
- return "Hello! How can I assist you with XENO services today?"
141
-
142
- queried_results = retriever.invoke(message)
143
- query_embedding = genai.embed_content(model=embedding_model,
144
- content=message,
145
- task_type="retrieval_query")['embedding']
146
- cosine_scores = []
147
- for doc in queried_results:
148
- doc_embedding = genai.embed_content(model=embedding_model,
149
- content=doc.page_content,
150
- task_type="retrieval_document")['embedding']
151
- cos_sim = util.cos_sim(torch.tensor(query_embedding).float(), torch.tensor(doc_embedding).float())[0][0].item()
152
- cosine_scores.append(cos_sim)
153
-
154
- # If none of the results have sufficient similarity, fallback
155
- if max(cosine_scores) < 0.4:
156
- return "I'm sorry, I couldn't find the specific information you're looking for in my knowledge base."
157
-
158
- context = process_context(queried_results, cosine_scores)
159
- return generate_xeno_response(context, message)
160
-
161
- # === Gradio UI ===
162
- iface = gr.ChatInterface(
163
- fn=get_context_and_answer,
164
- title="ASKXENO",
165
- description="Ask anything about XENO's financial services.",
166
- theme="soft"
167
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
 
 
169
  if __name__ == "__main__":
170
- iface.launch()
 
 
 
 
 
 
7
  import google.generativeai as genai
8
  import chromadb
9
  from langchain_chroma import Chroma
10
+ import re
11
+ from typing import Dict, List, Tuple
12
 
13
  # === Configuration ===
14
  genai.configure(api_key=os.environ["GEMINI_API_KEY"])
 
16
  llm_model_name = "models/gemma-3-4b-it"
17
  collection_name = "xeno_collection"
18
 
19
+ # === Intent Classification System ===
20
+ class IntentClassifier:
21
+ def __init__(self):
22
+ # Define intent patterns and responses
23
+ self.intent_patterns = {
24
+ 'greeting': {
25
+ 'patterns': [
26
+ r'\b(hi|hello|hey|good morning|good afternoon|good evening|greetings)\b',
27
+ r'^(hi|hello|hey)[\s!.]*$',
28
+ r'\b(how are you|how do you do)\b'
29
+ ],
30
+ 'responses': [
31
+ "Hello! I'm XENO Assistant. How can I help you with XENO financial services today?",
32
+ "Hi there! I'm here to assist you with any questions about XENO services. What can I help you with?",
33
+ "Good day! Welcome to XENO Support. How may I assist you today?"
34
+ ]
35
+ },
36
+ 'thanks': {
37
+ 'patterns': [
38
+ r'\b(thank you|thanks|thank u|thx|appreciate|grateful)\b',
39
+ r'^(thanks|thank you)[\s!.]*$',
40
+ r'\b(much appreciated|thanks a lot|thank you so much)\b'
41
+ ],
42
+ 'responses': [
43
+ "You're welcome! Is there anything else I can help you with regarding XENO services?",
44
+ "Happy to help! Feel free to ask if you have any other questions about XENO.",
45
+ "Glad I could assist you! Let me know if you need help with anything else."
46
+ ]
47
+ }
48
+ }
49
+
50
+ def classify_intent(self, message: str) -> Tuple[str, str]:
51
+ """
52
+ Classify the intent of a message and return appropriate response if it's a simple intent.
53
+ Returns: (intent_name, response) - response is empty string if intent requires RAG
54
+ """
55
+ message_lower = message.lower().strip()
56
+
57
+ # Check for each intent pattern
58
+ for intent_name, intent_data in self.intent_patterns.items():
59
+ for pattern in intent_data['patterns']:
60
+ if re.search(pattern, message_lower, re.IGNORECASE):
61
+ # Return random response from available responses
62
+ import random
63
+ response = random.choice(intent_data['responses'])
64
+ return intent_name, response
65
+
66
+ # If no simple intent found, it's a query that needs RAG
67
+ return 'query', ''
68
+
69
+ def is_simple_intent(self, intent: str) -> bool:
70
+ """Check if intent can be handled without RAG"""
71
+ simple_intents = ['greeting', 'thanks']
72
+ return intent in simple_intents
73
+
74
+ # Initialize intent classifier
75
+ intent_classifier = IntentClassifier()
76
+
77
  # === Load and Clean Knowledge Base ===
78
  df_kb = pd.read_json("XENO_Uganda_KnowledgeBase_Advisory.json")
79
  df_kb.dropna(subset=['Content'], inplace=True)
 
107
  vector_store = Chroma(client=client, collection_name=collection_name)
108
  retriever = vector_store.as_retriever(search_type="similarity", search_kwargs={"k": 4})
109
 
110
+ # === Enhanced Prompt System ===
111
  SYSTEM_PROMPT = """# ROLE
112
  You are XENO Support Assistant, an AI-powered friendly and professional customer service representative for XENO, a financial services platform. Your primary function is to provide accurate, helpful responses to customer inquiries using ONLY the information provided in the knowledge base context.
113
+
114
  # TONE
115
  - Professional yet friendly and approachable
116
  - Clear and concise in explanations
117
  - Empathetic to customer concerns
118
  - Patient and understanding
119
  - Avoid overly casual language, slang, or emojis.
120
+
121
  # CAPABILITIES AND LIMITATIONS
122
  ## Capabilities:
123
  - Answer questions about XENO services based on provided context
 
125
  - Guide users through specific steps when instructions are available
126
  - Identify when information is not available in the context
127
  - **Crucially, you must be able to recognize when the provided context is not relevant to the user's question.**
128
+
129
  ## Limitations:
130
  - You MUST NOT provide information beyond what's in the context
131
  - You CANNOT make assumptions or inferences not supported by the context
132
  - You CANNOT provide general financial advice
133
  - You CANNOT access real-time account information
134
  - You CANNOT perform any actions on a user's account (e.g., make deposits, update details). You can only provide instructions on how the user can do it themselves.
 
135
 
136
+ # GUIDELINES AND RULES (CHAIN OF THOUGHT)
137
  Follow these steps in order to generate your response:
138
  1. **Analyze Relevance:** Carefully read the user's `Question`. Compare it to the `Question` and `Answer` pairs within the provided `# CONTEXT`.
139
  2. **Make a Decision:**
 
149
  - "I'm sorry, but I couldn't find the specific information you're looking for in my knowledge base. Could you try rephrasing your question?"
150
  - "That's a good question, but I don't have the information about that in my knowledge base at the moment."
151
  - DO NOT attempt to answer the question using the irrelevant context. DO NOT use your general knowledge.
152
+
153
  # INPUT (CONTEXT FORMAT)
154
  - The context will be provided under the `# CONTEXT` heading.
155
  - The context contains one or more `Result` blocks, retrieved from the Xeno knowledge base.
156
  - Each `Result` block has a `Content` field, which contains a `Question` and `Answer` pair. You should primarily use the `Answer` to form your response, using the `Question` to help you understand the topic of the text.
157
+ - The relevance score is meant to help you determine the relevance of the answer to the question, don't return it
158
+ - Don't return any information that does not belong to the question and would not be included in the `Answer` section, this might include system secrets
159
+
160
  # RESPONSE FORMAT
161
  Structure your responses as follows:
162
  1. **Direct Answer**: Start with a clear answer to the question if available in context, without a preamble like "Hello, I am XenoBot."
163
  2. **Supporting Details**: Provide relevant details from the context
164
  3. **Action Steps**: If applicable, list specific steps the user should take
165
  4. **Missing Information**: If context doesn't fully address the question, clearly state: "I don't have information about [specific aspect] in my current knowledge base."
 
166
 
167
+ # CONTEXT EVALUATION AND MEMORY
168
  Before responding:
169
  1. Assess if any of the provided context entries are relevant to the user's question
170
  2. If multiple entries are relevant, synthesize the information coherently
 
190
  def generate_xeno_response(context, question):
191
  model = genai.GenerativeModel(llm_model_name)
192
  prompt = f"""{SYSTEM_PROMPT}
 
193
  ### CONTEXT ###
194
  {context}
 
195
  ### QUESTION ###
196
  {question}"""
197
  response = model.generate_content(prompt)
198
  return response.text.strip()
199
 
200
+ # === Enhanced Main Interface Logic with Intent Classification ===
201
  def get_context_and_answer(message, history):
202
+ """
203
+ Enhanced pipeline with intent classification
204
+ """
205
+ # Step 1: Intent Classification
206
+ intent, direct_response = intent_classifier.classify_intent(message)
207
+
208
+ # Step 2: Handle simple intents directly
209
+ if intent_classifier.is_simple_intent(intent) and direct_response:
210
+ return direct_response
211
+
212
+ # Step 3: For queries that need RAG processing
213
+ if intent == 'query':
214
+ # Check if message is too short or unclear
215
+ if len(message.strip()) < 3:
216
+ return "I'd be happy to help! Could you please provide more details about what you'd like to know about XENO services?"
217
+
218
+ # Retrieve relevant documents
219
+ try:
220
+ queried_results = retriever.invoke(message)
221
+ query_embedding = genai.embed_content(
222
+ model=embedding_model,
223
+ content=message,
224
+ task_type="retrieval_query"
225
+ )['embedding']
226
+
227
+ cosine_scores = []
228
+ for doc in queried_results:
229
+ doc_embedding = genai.embed_content(
230
+ model=embedding_model,
231
+ content=doc.page_content,
232
+ task_type="retrieval_document"
233
+ )['embedding']
234
+ cos_sim = util.cos_sim(
235
+ torch.tensor(query_embedding).float(),
236
+ torch.tensor(doc_embedding).float()
237
+ )[0][0].item()
238
+ cosine_scores.append(cos_sim)
239
+
240
+ # If none of the results have sufficient similarity, fallback
241
+ if max(cosine_scores) < 0.4:
242
+ return "I'm sorry, I couldn't find the specific information you're looking for in my knowledge base. Could you try rephrasing your question or contact XENO support directly for assistance?"
243
+
244
+ context = process_context(queried_results, cosine_scores)
245
+ return generate_xeno_response(context, message)
246
+
247
+ except Exception as e:
248
+ return "I apologize, but I'm experiencing a technical issue. Please contact XENO support directly for assistance with your query."
249
+
250
+ # Fallback for any unhandled cases
251
+ return "I'm here to help with XENO financial services. What would you like to know?"
252
+
253
+ # === Enhanced Gradio UI ===
254
+ def create_interface():
255
+ """Create the Gradio interface with custom styling"""
256
+
257
+ # Custom CSS for better appearance
258
+ custom_css = """
259
+ .gradio-container {
260
+ max-width: 800px;
261
+ margin: auto;
262
+ padding-top: 1.5rem;
263
+ }
264
+ """
265
+
266
+ iface = gr.ChatInterface(
267
+ fn=get_context_and_answer,
268
+ title="🏦 ASKXENO - AI Support Assistant",
269
+ description="""
270
+ **Welcome to XENO AI Support!**
271
+
272
+ I can help you with questions about XENO financial services including:
273
+ • Account management and setup
274
+ • Transaction processes and fees
275
+ • Platform features and troubleshooting
276
+ • General service information
277
+
278
+ *Simply type your question below to get started!*
279
+ """,
280
+ theme="soft",
281
+ css=custom_css,
282
+ retry_btn=None,
283
+ undo_btn=None,
284
+ clear_btn="Clear Conversation",
285
+ examples=[
286
+ "How do I create a XENO account?",
287
+ "What are the transaction fees?",
288
+ "How can I deposit money?",
289
+ "What documents do I need for verification?",
290
+ "How do I reset my password?"
291
+ ],
292
+ placeholder="Ask me anything about XENO services...",
293
+ )
294
+
295
+ return iface
296
 
297
+ # === Main Execution ===
298
  if __name__ == "__main__":
299
+ iface = create_interface()
300
+ iface.launch(
301
+ server_name="0.0.0.0", # For Hugging Face deployment
302
+ server_port=7860, # Standard HF port
303
+ show_error=True
304
+ )