Ameer1606/PostgresPro-Support
0
1from langchain_groq import ChatGroq2from langchain_core.messages import SystemMessage, HumanMessage, AIMessage3from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder4from src.config import LLM_MODEL, COMPANY_NAME, COMPANY_PHONE, COMPANY_EMAIL5from src.retriever import PostgresRetriever6from src.tools import create_database_ticket7 8class SupportAgent:9 def __init__(self):10 self.retriever = PostgresRetriever()11 12 # Initialize the Chat Model with Tools13 self.llm = ChatGroq(model=LLM_MODEL, temperature=0.2)14 self.tools = [create_database_ticket]15 self.llm_with_tools = self.llm.bind_tools(self.tools)16 17 # System Prompt definition18 system_instruction = f"""You are a Customer Support AI for {COMPANY_NAME}.19Contact Info: Phone: {COMPANY_PHONE}, Email: {COMPANY_EMAIL}.20 21Your PRIMARY job is to answer PostgreSQL questions using the documentation context provided below.22 23## STEP 1 — ALWAYS TRY TO ANSWER FIRST24Read the context carefully. If the context contains information relevant to the user's question, answer it fully with citations.25Do NOT call any tool if you can answer from context.26 27## CITATION RULES28Every answer drawn from the context MUST cite the source.29Format: "According to the documentation (Source: <filename>, Page: <page_number>)..."30 31## STEP 2 — ONLY WHEN CONTEXT CANNOT ANSWER32If and only if the context truly cannot answer the question, tell the user that, then offer to create a support ticket.33 34## TICKET CREATION RULES — READ CAREFULLY35- NEVER call `create_database_ticket` unless the user has EXPLICITLY provided all four pieces of info in the conversation: their real name, real email address, a ticket title, and a description.36- NEVER invent, guess, or use placeholder values (like "your_email", "User", "N/A", "unknown") for any field.37- If any of those four fields are missing, ask the user for them first. Do NOT call the tool yet.38- Only call `create_database_ticket` after the user has confirmed their actual details.39 40Context to answer from:41{{context}}42"""43 self.prompt = ChatPromptTemplate.from_messages([44 ("system", system_instruction),45 MessagesPlaceholder(variable_name="chat_history"),46 ("user", "{input}"),47 MessagesPlaceholder(variable_name="agent_scratchpad"),48 ])49 50 def run(self, user_input: str, chat_history: list) -> str:51 """Process user input with the current chat history."""52 # Windowed history: keep only last 6 messages (3 interactions)53 windowed_history = chat_history[-6:] if len(chat_history) > 6 else chat_history54 55 # 1. Retrieve Context56 context = self.retriever.get_relevant_context(user_input)57 58 # 2. Format Messages59 messages = self.prompt.format_messages(60 context=context,61 chat_history=windowed_history,62 input=user_input,63 agent_scratchpad=[]64 )65 66 # 3. Call LLM67 response = self.llm_with_tools.invoke(messages)68 69 # 4. Handle Tool Calls70 if response.tool_calls:71 tool_call = response.tool_calls[0]72 if tool_call["name"] == "create_database_ticket":73 args = tool_call["args"]74 75 # Placeholder values that indicate the LLM hallucinated the fields76 PLACEHOLDERS = {77 "your_email", "user", "n/a", "unknown", "none", "example",78 "your_name", "your name", "your email", "placeholder",79 "test@example.com", "user@example.com",80 }81 82 # Required fields check — also catches placeholders83 missing = []84 for field in ("name", "email", "title", "description"):85 val = args.get(field, "").strip().lower()86 if not val or val in PLACEHOLDERS:87 missing.append(field)88 89 if missing:90 return (91 f"To create a support ticket I'll need a few details from you. "92 f"Could you please provide your **{', '.join(missing)}**?"93 )94 95 # Execute tool96 tool_result = create_database_ticket.invoke(args)97 return f"I've attempted to create the ticket.\n\n{tool_result}"98 99 # 5. Return standard text response100 return response.content101 102 