""" Interactive Network Consultation Multi-turn conversation to gather complete requirements """ import json import logging from typing import Dict, List, Tuple, Optional from agent.llm_client import LLMClient, LLMMessage logger = logging.getLogger(__name__) CONSULTATION_SYSTEM_PROMPT = """You are an expert network consultant helping a client design their network infrastructure. Your job is to: 1. Understand their business needs and technical requirements 2. Ask clarifying questions to fill in gaps 3. Probe for important details they may have forgotten (security, compliance, scalability, budget) 4. Extract structured information: devices needed, VLANs, subnets, bandwidth, redundancy needs When you have enough information, respond with JSON in this format: ```json { "consultation_complete": true, "network_intent": { "description": "full description", "locations": [...], "business_requirements": [...], "constraints": [...], "timeline": "...", "budget": "...", "vendor_preference": "...", "compliance_requirements": [...], "bandwidth_requirements": {...}, "redundancy_requirements": {...} } } ``` If you need more information, respond with: ```json { "consultation_complete": false, "questions": ["question 1", "question 2", ...], "summary_so_far": "what we know so far" } ``` Be professional, thorough, and ask smart questions that a real network consultant would ask. """ class NetworkConsultant: """ Interactive consultation agent Gathers complete requirements through conversation """ def __init__(self): self.llm = LLMClient() self.conversation_history: List[LLMMessage] = [] self.intent_data: Optional[Dict] = None def start_consultation(self, initial_description: str) -> Tuple[bool, str, Optional[Dict]]: """ Start consultation process Returns: (is_complete, next_questions_or_summary, intent_dict) """ # Initialize conversation self.conversation_history = [ LLMMessage(role="system", content=CONSULTATION_SYSTEM_PROMPT), LLMMessage(role="user", content=f"Initial request: {initial_description}") ] # Get LLM response response = self.llm.chat(self.conversation_history, temperature=0.3) # Parse response try: # Extract JSON from response json_start = response.find('{') json_end = response.rfind('}') + 1 if json_start >= 0 and json_end > json_start: json_str = response[json_start:json_end] result = json.loads(json_str) if result.get("consultation_complete"): self.intent_data = result.get("network_intent") summary = self._format_intent_summary(self.intent_data) return True, summary, self.intent_data else: questions_text = self._format_questions(result.get("questions", [])) return False, questions_text, None else: # Fallback if no JSON return False, response, None except json.JSONDecodeError as e: logger.error(f"Failed to parse LLM response as JSON: {e}") return False, response, None def continue_consultation(self, user_response: str) -> Tuple[bool, str, Optional[Dict]]: """ Continue multi-turn consultation Returns: (is_complete, next_questions_or_summary, intent_dict) """ # Add user response to history self.conversation_history.append( LLMMessage(role="user", content=user_response) ) # Get LLM response response = self.llm.chat(self.conversation_history, temperature=0.3) self.conversation_history.append( LLMMessage(role="assistant", content=response) ) # Parse response try: json_start = response.find('{') json_end = response.rfind('}') + 1 if json_start >= 0 and json_end > json_start: json_str = response[json_start:json_end] result = json.loads(json_str) if result.get("consultation_complete"): self.intent_data = result.get("network_intent") summary = self._format_intent_summary(self.intent_data) return True, summary, self.intent_data else: questions_text = self._format_questions(result.get("questions", [])) summary = result.get("summary_so_far", "") output = f"**Progress Summary:**\n{summary}\n\n**Additional Questions:**\n{questions_text}" return False, output, None else: return False, response, None except json.JSONDecodeError as e: logger.error(f"Failed to parse LLM response: {e}") return False, response, None def _format_questions(self, questions: List[str]) -> str: """Format questions as numbered list""" return "\n".join(f"{i+1}. {q}" for i, q in enumerate(questions)) def _format_intent_summary(self, intent: Dict) -> str: """Format final intent as readable summary""" lines = ["## 📋 Consultation Complete!\n"] lines.append(f"**Description:** {intent.get('description', 'N/A')}\n") if intent.get('locations'): lines.append(f"**Locations:** {len(intent['locations'])} sites") for loc in intent['locations']: lines.append(f" - {loc}") lines.append("") if intent.get('business_requirements'): lines.append("**Business Requirements:**") for req in intent['business_requirements']: lines.append(f" - {req}") lines.append("") if intent.get('budget'): lines.append(f"**Budget:** {intent['budget']}\n") if intent.get('timeline'): lines.append(f"**Timeline:** {intent['timeline']}\n") if intent.get('vendor_preference'): lines.append(f"**Preferred Vendors:** {intent['vendor_preference']}\n") if intent.get('compliance_requirements'): lines.append("**Compliance:**") for req in intent['compliance_requirements']: lines.append(f" - {req}") lines.append("") return "\n".join(lines)