AlfredHarun commited on
Commit
17dd328
·
verified ·
1 Parent(s): 13eceeb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +93 -34
app.py CHANGED
@@ -2,30 +2,40 @@ import os
2
  import json
3
  import random
4
  import logging
5
- from typing import List
6
  import streamlit as st
7
  from dotenv import load_dotenv
 
 
 
 
 
 
 
8
  import autogen
9
  from autogen import AssistantAgent, UserProxyAgent
10
 
 
11
  from langchain_community.embeddings import HuggingFaceEmbeddings
12
  from langchain_community.vectorstores import Chroma
13
- from langchain.docstore.document import Document
14
 
 
15
  load_dotenv()
16
 
 
17
  logging.basicConfig(level=logging.INFO)
18
  logger = logging.getLogger(__name__)
19
- st.set_page_config(page_title="IT Support System (RAG)", layout="centered")
20
 
21
- # Initialize session state for chat and logs
 
 
22
  if "chat_history" not in st.session_state:
23
  st.session_state.chat_history = []
24
 
25
  if "workflow_logs" not in st.session_state:
26
  st.session_state.workflow_logs = []
27
 
28
- # Knowledge Base Setup
29
  kb_path = os.path.join(os.path.dirname(__file__), 'kb.json')
30
  with open(kb_path, encoding='utf-8') as f:
31
  kb_entries = json.load(f)
@@ -34,10 +44,7 @@ docs: List[Document] = []
34
  for entry in kb_entries:
35
  docs.append(Document(
36
  page_content=entry['answer'],
37
- metadata={
38
- 'id': entry.get('id'),
39
- 'question': entry.get('question')
40
- }
41
  ))
42
 
43
  embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
@@ -64,6 +71,7 @@ def escalate_ticket(query: str, analysis: str = "") -> str:
64
  logger.info(f"Escalating issue with ticket {ticket_id}: {description}")
65
  return f"Escalated issue. Created ticket {ticket_id}. A support technician will contact you shortly."
66
 
 
67
  llm_config = {
68
  "config_list": [{
69
  "model": "llama3",
@@ -74,10 +82,8 @@ llm_config = {
74
  "temperature": 0.5,
75
  }
76
 
77
- master_agent = AssistantAgent(
78
- name="Master",
79
- llm_config=llm_config,
80
- system_message="""
81
  You are the Master Agent that orchestrates the IT support workflow:
82
  1. First determine if the query is IT-related. If not, provide a direct response explaining your limitations.
83
  2. For IT-related queries, pass to the Planning Agent for execution plan development
@@ -85,13 +91,9 @@ You are the Master Agent that orchestrates the IT support workflow:
85
  4. Provide a comprehensive yet concise final response to the user
86
 
87
  Only handle one query at a time through the complete workflow.
88
- """
89
- )
90
 
91
- planning_agent = AssistantAgent(
92
- name="Planning",
93
- llm_config=llm_config,
94
- system_message="""
95
  You are the Planning Agent responsible for:
96
  1. Validating if the user query is clear and complete
97
  2. Refining the query if needed for better processing
@@ -104,13 +106,9 @@ Provide your analysis as a structured output with sections for:
104
  - Execution Plan
105
 
106
  Always end your message with: "Forwarding to Analysis Agent"
107
- """
108
- )
109
 
110
- analysis_agent = AssistantAgent(
111
- name="Analysis",
112
- llm_config=llm_config,
113
- system_message="""
114
  You are the Analysis Agent responsible for:
115
  1. Identifying key entities in the user query (devices, software, errors, etc.)
116
  2. Determining severity level (Low, Medium, High, Critical)
@@ -124,8 +122,7 @@ Provide your analysis as structured output with sections for:
124
  - Analysis Summary
125
 
126
  Always end your message with: "Forwarding to Resolution Agent"
127
- """
128
- )
129
 
130
  resolution_agent = AssistantAgent(
131
  name="Resolution",
@@ -166,7 +163,59 @@ Always include the ticket ID and expected follow-up timeframe.
166
  function_map={"escalate_ticket": escalate_ticket}
167
  )
168
 
169
- def handle_it_query(query: str) -> (str, dict):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  query = query.strip()
171
  if not query:
172
  return "Please enter an IT question or issue.", {}
@@ -236,15 +285,25 @@ def handle_it_query(query: str) -> (str, dict):
236
  logger.info(f"Final Master Agent Response: {final_response}")
237
  workflow_logs["final_response"] = final_response
238
 
 
 
 
 
 
 
 
239
  return final_response, workflow_logs
240
 
241
  except Exception as e:
242
  logger.error(f"Error in workflow: {e}", exc_info=True)
243
- return f"An error occurred during processing: {str(e)}\n\nPlease try rephrasing your question.", {}
 
 
244
 
 
 
245
 
246
- st.title("AI Help Desk")
247
- st.write("Ask any IT support question and our multi-agent system will assist you.")
248
 
249
  with st.form(key="query_form", clear_on_submit=True):
250
  user_input = st.text_area("Describe your IT issue:", height=100)
@@ -258,19 +317,19 @@ if submitted:
258
  with st.spinner("Processing your request through our agent workflow..."):
259
  response, logs = handle_it_query(user_input)
260
 
261
- # Append to session history to maintain conversation flow
262
  st.session_state.chat_history.append({"user": user_input, "assistant": response})
263
  if show_logs:
264
  st.session_state.workflow_logs.append(logs)
265
 
266
- # Display conversation history
267
  st.markdown("## Conversation")
268
  for chat in st.session_state.chat_history:
269
  st.markdown(f"**User:** {chat['user']}")
270
  st.markdown(f"**Assistant:** {chat['assistant']}")
271
  st.markdown("---")
272
 
273
- # Display logs if requested
274
  if show_logs and st.session_state.workflow_logs:
275
  st.markdown("## Workflow Logs")
276
  for i, log in enumerate(st.session_state.workflow_logs):
 
2
  import json
3
  import random
4
  import logging
5
+ from typing import List, Dict, Any
6
  import streamlit as st
7
  from dotenv import load_dotenv
8
+
9
+ import torch
10
+ import faiss
11
+ import numpy as np
12
+ from sentence_transformers import SentenceTransformer
13
+ from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
14
+
15
  import autogen
16
  from autogen import AssistantAgent, UserProxyAgent
17
 
18
+ from langchain.docstore.document import Document
19
  from langchain_community.embeddings import HuggingFaceEmbeddings
20
  from langchain_community.vectorstores import Chroma
 
21
 
22
+ # --- Load environment ---
23
  load_dotenv()
24
 
25
+ # --- Logging ---
26
  logging.basicConfig(level=logging.INFO)
27
  logger = logging.getLogger(__name__)
 
28
 
29
+ st.set_page_config(page_title="AI Help Desk with Hybrid Open Source + Multi-Agent", layout="centered")
30
+
31
+ # --- Initialize session state ---
32
  if "chat_history" not in st.session_state:
33
  st.session_state.chat_history = []
34
 
35
  if "workflow_logs" not in st.session_state:
36
  st.session_state.workflow_logs = []
37
 
38
+ # --- Load Knowledge Base ---
39
  kb_path = os.path.join(os.path.dirname(__file__), 'kb.json')
40
  with open(kb_path, encoding='utf-8') as f:
41
  kb_entries = json.load(f)
 
44
  for entry in kb_entries:
45
  docs.append(Document(
46
  page_content=entry['answer'],
47
+ metadata={'id': entry.get('id'), 'question': entry.get('question')}
 
 
 
48
  ))
49
 
50
  embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
 
71
  logger.info(f"Escalating issue with ticket {ticket_id}: {description}")
72
  return f"Escalated issue. Created ticket {ticket_id}. A support technician will contact you shortly."
73
 
74
+ # --- OpenAI Multi-agent LLM Config ---
75
  llm_config = {
76
  "config_list": [{
77
  "model": "llama3",
 
82
  "temperature": 0.5,
83
  }
84
 
85
+ # --- Define Agents ---
86
+ master_agent = AssistantAgent(name="Master", llm_config=llm_config, system_message="""
 
 
87
  You are the Master Agent that orchestrates the IT support workflow:
88
  1. First determine if the query is IT-related. If not, provide a direct response explaining your limitations.
89
  2. For IT-related queries, pass to the Planning Agent for execution plan development
 
91
  4. Provide a comprehensive yet concise final response to the user
92
 
93
  Only handle one query at a time through the complete workflow.
94
+ """)
 
95
 
96
+ planning_agent = AssistantAgent(name="Planning", llm_config=llm_config, system_message="""
 
 
 
97
  You are the Planning Agent responsible for:
98
  1. Validating if the user query is clear and complete
99
  2. Refining the query if needed for better processing
 
106
  - Execution Plan
107
 
108
  Always end your message with: "Forwarding to Analysis Agent"
109
+ """)
 
110
 
111
+ analysis_agent = AssistantAgent(name="Analysis", llm_config=llm_config, system_message="""
 
 
 
112
  You are the Analysis Agent responsible for:
113
  1. Identifying key entities in the user query (devices, software, errors, etc.)
114
  2. Determining severity level (Low, Medium, High, Critical)
 
122
  - Analysis Summary
123
 
124
  Always end your message with: "Forwarding to Resolution Agent"
125
+ """)
 
126
 
127
  resolution_agent = AssistantAgent(
128
  name="Resolution",
 
163
  function_map={"escalate_ticket": escalate_ticket}
164
  )
165
 
166
+ # --- Initialize open-source fallback Mistral-7B model for AI fallback ---
167
+ quantization_config = BitsAndBytesConfig(
168
+ load_in_4bit=True,
169
+ bnb_4bit_compute_dtype=torch.float16,
170
+ bnb_4bit_use_double_quant=True,
171
+ bnb_4bit_quant_type="nf4"
172
+ )
173
+ model_name = "mistralai/Mistral-7B-Instruct-v0.1"
174
+ device = "cuda" if torch.cuda.is_available() else "cpu"
175
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
176
+ mistral_model = AutoModelForCausalLM.from_pretrained(
177
+ model_name,
178
+ quantization_config=quantization_config,
179
+ device_map="auto"
180
+ )
181
+
182
+ # --- Load FAQ Data for fallback ---
183
+ faq_data = [
184
+ # Populate with your actual FAQ data or import from file if needed
185
+ {"question": "What is Onfon Mobile?", "answer": "Onfon Mobile is a device financing company..."},
186
+ {"question": "How do I get a phone via Onfon Mobile?", "answer": "To get a smartphone, register with *797#..."},
187
+ # ... Add as needed ...
188
+ ]
189
+
190
+ embed_model = SentenceTransformer("BAAI/bge-large-en")
191
+ faq_embeddings = np.array([embed_model.encode(f["question"], convert_to_numpy=True) for f in faq_data])
192
+ faiss_index = faiss.IndexFlatL2(faq_embeddings.shape[1])
193
+ faiss_index.add(faq_embeddings)
194
+
195
+ def find_top_faqs(user_input: str, top_k=3):
196
+ emb = embed_model.encode(user_input, convert_to_numpy=True).reshape(1, -1)
197
+ _, idxs = faiss_index.search(emb, top_k)
198
+ return [faq_data[i] for i in idxs[0]]
199
+
200
+ def generate_ai_faq_answer(user_input: str):
201
+ top_faqs = find_top_faqs(user_input, 3)
202
+ system_prompt = (
203
+ "You are an Onfon Mobile customer support assistant. Be kind and helpful to onfon mobile customers. "
204
+ "From the following FAQs, provide the best possible answer briefly:\n\n"
205
+ )
206
+ faq_context = "\n".join([f"Q: {f['question']}\nA: {f['answer']}" for f in top_faqs])
207
+ prompt = f"{system_prompt}{faq_context}\n\nUser Question: {user_input}\nAnswer:"
208
+
209
+ inputs = tokenizer(prompt, return_tensors="pt", truncation=True).to(device)
210
+ with torch.no_grad():
211
+ outputs = mistral_model.generate(**inputs, max_new_tokens=150)
212
+ answer = tokenizer.decode(outputs[0], skip_special_tokens=True)
213
+ if "Answer:" in answer:
214
+ answer = answer.split("Answer:")[-1].strip()
215
+ return answer
216
+
217
+ # --- Main Query Handler ---
218
+ def handle_it_query(query: str) -> (str, Dict[str, Any]):
219
  query = query.strip()
220
  if not query:
221
  return "Please enter an IT question or issue.", {}
 
285
  logger.info(f"Final Master Agent Response: {final_response}")
286
  workflow_logs["final_response"] = final_response
287
 
288
+ # Fallback to open-source model if no valid response or error
289
+ if not final_response or final_response.lower().startswith("an error occurred"):
290
+ logger.warning("Using fallback open-source Mistral-7B FAQ model due to empty or error response")
291
+ fallback_response = generate_ai_faq_answer(query)
292
+ workflow_logs["fallback_response"] = fallback_response
293
+ final_response = fallback_response
294
+
295
  return final_response, workflow_logs
296
 
297
  except Exception as e:
298
  logger.error(f"Error in workflow: {e}", exc_info=True)
299
+ fallback_response = generate_ai_faq_answer(query)
300
+ workflow_logs["fallback_response"] = fallback_response
301
+ return f"An error occurred during processing, fallback answer provided.\n\n{fallback_response}", workflow_logs
302
 
303
+ # --- Streamlit UI ---
304
+ st.title("AI Help Desk (Hybrid Multi-Agent + Open Source)")
305
 
306
+ st.write("Ask any IT support question and our hybrid multi-agent + open-source system will assist you.")
 
307
 
308
  with st.form(key="query_form", clear_on_submit=True):
309
  user_input = st.text_area("Describe your IT issue:", height=100)
 
317
  with st.spinner("Processing your request through our agent workflow..."):
318
  response, logs = handle_it_query(user_input)
319
 
320
+ # Append conversation to history
321
  st.session_state.chat_history.append({"user": user_input, "assistant": response})
322
  if show_logs:
323
  st.session_state.workflow_logs.append(logs)
324
 
325
+ # --- Show conversation ---
326
  st.markdown("## Conversation")
327
  for chat in st.session_state.chat_history:
328
  st.markdown(f"**User:** {chat['user']}")
329
  st.markdown(f"**Assistant:** {chat['assistant']}")
330
  st.markdown("---")
331
 
332
+ # --- Show logs if requested ---
333
  if show_logs and st.session_state.workflow_logs:
334
  st.markdown("## Workflow Logs")
335
  for i, log in enumerate(st.session_state.workflow_logs):