Skip to content

Phase 6: RAG-Powered Semantic Intelligence

Objective

Add Retrieval-Augmented Generation capabilities to provide semantic understanding, institutional memory, and contextual recommendations based on operational documentation, historical patterns, and organizational knowledge.

Strategic Vision

Transform the system from a network data interface into an organizational infrastructure advisor that combines structured NetBox data with unstructured operational knowledge to provide expert-level guidance and recommendations.

graph TD
    A[User Query] --> B[Intent Analysis]
    B --> C{Query Type}

    C -->|Factual| D[Graph/API Query]
    C -->|Contextual| E[RAG Enhancement]
    C -->|Advisory| F[Full RAG Pipeline]

    E --> G[Document Retrieval]
    F --> G
    G --> H[Context Assembly]
    H --> I[LLM Reasoning]
    I --> J[Contextual Response]

    K[Document Corpus] --> L[Vector Database]
    L --> G

    M[Operational History] --> K
    N[Best Practices] --> K
    O[Incident Reports] --> K
    P[Configuration Templates] --> K

    style J fill:#e8f5e8
    style L fill:#fff3e0

Document Corpus Development

Operational Knowledge Ingestion

Target Document Types:

class OperationalDocumentTypes:
    RUNBOOKS = {
        'examples': ['Network Maintenance Procedures', 'Emergency Response Plans'],
        'value': 'Step-by-step operational guidance',
        'update_frequency': 'quarterly'
    }

    INCIDENT_REPORTS = {
        'examples': ['Post-mortem analyses', 'RCA documentation'],
        'value': 'Historical problem resolution patterns',
        'update_frequency': 'continuous'
    }

    CONFIGURATION_STANDARDS = {
        'examples': ['Device templates', 'Security policies'],
        'value': 'Compliance and best practice enforcement',
        'update_frequency': 'monthly'
    }

    VENDOR_DOCUMENTATION = {
        'examples': ['Device manuals', 'Troubleshooting guides'],
        'value': 'Technical reference and procedures',
        'update_frequency': 'as_released'
    }

    CHANGE_RECORDS = {
        'examples': ['Change requests', 'Implementation logs'],
        'value': 'Change impact analysis and rollback procedures',
        'update_frequency': 'continuous'
    }

Document Processing Pipeline

import asyncio
from pathlib import Path
from langchain.document_loaders import (
    PDFLoader, MarkdownLoader, ConfluenceLoader, SharePointLoader
)
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma

class DocumentProcessingPipeline:
    def __init__(self, vector_store_path: str):
        self.embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
        self.vector_store = Chroma(
            persist_directory=vector_store_path,
            embedding_function=self.embeddings
        )

        # Specialized text splitters for different document types
        self.splitters = {
            'runbook': RecursiveCharacterTextSplitter(
                chunk_size=1000,
                chunk_overlap=200,
                separators=["\n## ", "\n### ", "\n\n", "\n", " "]
            ),
            'incident': RecursiveCharacterTextSplitter(
                chunk_size=800,
                chunk_overlap=150,
                separators=["Problem:", "Solution:", "Root Cause:", "\n\n"]
            ),
            'configuration': RecursiveCharacterTextSplitter(
                chunk_size=600,
                chunk_overlap=100,
                separators=["interface ", "vlan ", "router ", "\n"]
            )
        }

    async def process_document_batch(self, document_paths: List[Path],
                                   document_type: str) -> int:
        """
        Process multiple documents concurrently with type-specific handling
        """
        processing_tasks = [
            self.process_single_document(doc_path, document_type)
            for doc_path in document_paths
        ]

        processed_chunks = await asyncio.gather(*processing_tasks)
        total_chunks = sum(len(chunks) for chunks in processed_chunks if chunks)

        print(f"Processed {len(document_paths)} {document_type} documents -> {total_chunks} chunks")
        return total_chunks

    async def process_single_document(self, doc_path: Path, 
                                    document_type: str) -> List[Document]:
        """
        Process individual document with metadata enrichment
        """
        # Load document with appropriate loader
        loader_map = {
            '.pdf': PDFLoader,
            '.md': MarkdownLoader,
            '.txt': TextLoader
        }

        loader_class = loader_map.get(doc_path.suffix, TextLoader)
        loader = loader_class(str(doc_path))

        try:
            documents = loader.load()
        except Exception as e:
            print(f"Failed to load {doc_path}: {e}")
            return []

        # Apply type-specific text splitting
        splitter = self.splitters.get(document_type, self.splitters['runbook'])
        chunks = splitter.split_documents(documents)

        # Enrich metadata
        for chunk in chunks:
            chunk.metadata.update({
                'document_type': document_type,
                'source_file': str(doc_path),
                'processed_date': datetime.utcnow().isoformat(),
                'chunk_index': chunks.index(chunk),
                'estimated_tokens': len(chunk.page_content) // 4
            })

            # Add domain-specific metadata
            if document_type == 'incident':
                chunk.metadata.update(
                    self.extract_incident_metadata(chunk.page_content)
                )
            elif document_type == 'configuration':
                chunk.metadata.update(
                    self.extract_config_metadata(chunk.page_content)
                )

        # Add to vector store
        self.vector_store.add_documents(chunks)

        return chunks

    def extract_incident_metadata(self, content: str) -> Dict[str, Any]:
        """
        Extract incident-specific metadata for better retrieval
        """
        metadata = {}

        # Extract severity if mentioned
        severity_patterns = {
            r'severity[:\s]+critical': 'critical',
            r'severity[:\s]+high': 'high', 
            r'severity[:\s]+medium': 'medium',
            r'severity[:\s]+low': 'low'
        }

        for pattern, level in severity_patterns.items():
            if re.search(pattern, content, re.IGNORECASE):
                metadata['incident_severity'] = level
                break

        # Extract affected systems
        system_patterns = [
            r'affected\s+system[s]?[:\s]+([^\n]+)',
            r'impacted\s+device[s]?[:\s]+([^\n]+)',
            r'failed\s+component[s]?[:\s]+([^\n]+)'
        ]

        for pattern in system_patterns:
            match = re.search(pattern, content, re.IGNORECASE)
            if match:
                metadata['affected_systems'] = match.group(1).strip()
                break

        return metadata

Semantic Search Integration

from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor

class NetworkKnowledgeRetriever:
    def __init__(self, vector_store, llm):
        self.vector_store = vector_store
        self.llm = llm

        # Base retriever with semantic similarity
        self.base_retriever = vector_store.as_retriever(
            search_type="mmr",  # Maximal Marginal Relevance for diversity
            search_kwargs={
                'k': 10,  # Retrieve more candidates initially
                'fetch_k': 20,
                'lambda_mult': 0.7  # Balance relevance vs diversity
            }
        )

        # Contextual compression to filter irrelevant chunks
        compressor = LLMChainExtractor.from_llm(llm)
        self.compressed_retriever = ContextualCompressionRetriever(
            base_compressor=compressor,
            base_retriever=self.base_retriever
        )

    async def retrieve_relevant_context(self, query: str, 
                                      query_type: str,
                                      max_contexts: int = 5) -> List[Document]:
        """
        Retrieve and rank relevant documents based on query context
        """
        # Expand query with domain-specific terms
        expanded_query = self.expand_query_with_context(query, query_type)

        # Retrieve initial candidates
        candidates = await self.compressed_retriever.aretrieve(expanded_query)

        # Apply domain-specific filtering and ranking
        filtered_candidates = self.apply_domain_filters(candidates, query_type)

        # Re-rank based on multiple factors
        ranked_results = self.rerank_by_relevance(filtered_candidates, query)

        return ranked_results[:max_contexts]

    def expand_query_with_context(self, query: str, query_type: str) -> str:
        """
        Add contextual terms to improve retrieval accuracy
        """
        context_expansions = {
            'troubleshooting': ['problem', 'issue', 'failure', 'resolution', 'fix'],
            'configuration': ['config', 'setup', 'template', 'standard', 'policy'],
            'maintenance': ['procedure', 'steps', 'checklist', 'schedule'],
            'planning': ['capacity', 'growth', 'upgrade', 'expansion']
        }

        expansion_terms = context_expansions.get(query_type, [])
        if expansion_terms:
            expanded = f"{query} {' '.join(expansion_terms[:3])}"
            return expanded

        return query

    def rerank_by_relevance(self, documents: List[Document], 
                          original_query: str) -> List[Document]:
        """
        Re-rank documents using multiple relevance signals
        """
        scored_docs = []

        for doc in documents:
            score = 0

            # Base similarity score (from vector search)
            score += doc.metadata.get('relevance_score', 0) * 0.4

            # Document type relevance
            doc_type = doc.metadata.get('document_type', '')
            if 'incident' in doc_type and 'problem' in original_query.lower():
                score += 0.2
            if 'runbook' in doc_type and ('how' in original_query.lower() or 
                                         'procedure' in original_query.lower()):
                score += 0.2

            # Freshness score (recent documents slightly preferred)
            processed_date = doc.metadata.get('processed_date', '')
            if processed_date:
                age_days = (datetime.utcnow() - 
                           datetime.fromisoformat(processed_date)).days
                freshness_score = max(0, (365 - age_days) / 365 * 0.1)
                score += freshness_score

            # Content quality indicators
            content_length = len(doc.page_content)
            if 200 <= content_length <= 2000:  # Optimal chunk size
                score += 0.1

            scored_docs.append((doc, score))

        # Sort by score descending
        scored_docs.sort(key=lambda x: x[1], reverse=True)

        return [doc for doc, score in scored_docs]

Contextual Intelligence Development

Operational Pattern Recognition

class OperationalPatternAnalyzer:
    def __init__(self, vector_store, graph_db):
        self.vector_store = vector_store
        self.graph_db = graph_db
        self.pattern_cache = {}

    async def analyze_query_for_patterns(self, user_query: str,
                                       current_context: Dict[str, Any]) -> Dict[str, Any]:
        """
        Identify patterns in current query based on historical data
        """
        # Search for similar historical queries/incidents
        similar_cases = await self.find_similar_historical_cases(user_query)

        # Analyze current network state for known pattern indicators
        network_state = await self.assess_current_network_state(current_context)

        # Generate predictive insights
        predictions = await self.generate_predictive_insights(
            similar_cases, network_state, user_query
        )

        return {
            'similar_cases': similar_cases,
            'current_patterns': network_state,
            'predictions': predictions,
            'recommended_actions': self.generate_action_recommendations(predictions)
        }

    async def find_similar_historical_cases(self, query: str) -> List[Dict[str, Any]]:
        """
        Find similar incidents/situations from historical data
        """
        # Search incident reports for similar problems
        incident_query = f"similar issue problem: {query}"
        incident_docs = await self.vector_store.asimilarity_search(
            incident_query,
            filter={'document_type': 'incident'},
            k=5
        )

        cases = []
        for doc in incident_docs:
            case = {
                'title': doc.metadata.get('title', 'Historical Incident'),
                'severity': doc.metadata.get('incident_severity', 'unknown'),
                'affected_systems': doc.metadata.get('affected_systems', ''),
                'resolution': self.extract_resolution_summary(doc.page_content),
                'relevance_score': doc.metadata.get('relevance_score', 0)
            }
            cases.append(case)

        return cases

    def generate_action_recommendations(self, predictions: Dict[str, Any]) -> List[str]:
        """
        Generate actionable recommendations based on pattern analysis
        """
        recommendations = []

        if predictions.get('risk_level', 'low') == 'high':
            recommendations.extend([
                "Consider scheduling maintenance window for affected systems",
                "Review recent changes that might have contributed to this pattern",
                "Verify backup systems are operational"
            ])

        if predictions.get('capacity_concern', False):
            recommendations.extend([
                "Plan capacity expansion for identified bottlenecks",
                "Review utilization trends over past 30 days",
                "Consider load balancing adjustments"
            ])

        if predictions.get('configuration_drift', False):
            recommendations.extend([
                "Audit current configurations against approved standards",
                "Schedule configuration backup and verification",
                "Review change approval processes"
            ])

        return recommendations

Institutional Memory Integration

class InstitutionalMemoryEngine:
    def __init__(self, vector_store, graph_db, llm):
        self.vector_store = vector_store
        self.graph_db = graph_db
        self.llm = llm
        self.expertise_mapping = {}

    async def enhance_query_with_institutional_knowledge(self,
                                                       query: str,
                                                       context: Dict[str, Any]) -> str:
        """
        Add organizational context and historical knowledge to queries
        """
        # Retrieve organizational policies and standards
        policy_context = await self.get_relevant_policies(query)

        # Find historical decisions and their rationale
        decision_history = await self.get_decision_history(query, context)

        # Identify subject matter experts for this area
        experts = await self.identify_relevant_experts(query)

        # Build enriched context
        enhanced_context = f"""
        Original Query: {query}

        Organizational Context:
        {policy_context}

        Historical Decisions:
        {decision_history}

        Subject Matter Expertise:
        {experts}

        Please provide a response that considers our organizational standards,
        historical context, and available expertise.
        """

        return enhanced_context

    async def get_relevant_policies(self, query: str) -> str:
        """
        Retrieve applicable organizational policies and standards
        """
        policy_docs = await self.vector_store.asimilarity_search(
            query,
            filter={'document_type': 'policy'},
            k=3
        )

        if not policy_docs:
            return "No specific organizational policies found for this area."

        policy_summary = []
        for doc in policy_docs:
            policy_summary.append(f"- {doc.metadata.get('title', 'Policy')}: {doc.page_content[:200]}...")

        return "\n".join(policy_summary)

    async def get_decision_history(self, query: str, 
                                 context: Dict[str, Any]) -> str:
        """
        Find historical decisions related to current query context
        """
        # Search change records and decision documents
        decision_query = f"decision rationale choice: {query}"
        decision_docs = await self.vector_store.asimilarity_search(
            decision_query,
            filter={'document_type': 'change_record'},
            k=3
        )

        if not decision_docs:
            return "No relevant historical decisions found."

        decisions = []
        for doc in decision_docs:
            decision = {
                'date': doc.metadata.get('date', 'Unknown'),
                'summary': doc.page_content[:300],
                'outcome': self.extract_decision_outcome(doc.page_content)
            }
            decisions.append(f"- {decision['date']}: {decision['outcome']}")

        return "\n".join(decisions) if decisions else "No relevant decisions found."

Advanced RAG Capabilities

Multi-Modal Knowledge Integration

class MultiModalKnowledgeIntegrator:
    def __init__(self, vector_store, graph_db, netbox_client, llm):
        self.vector_store = vector_store
        self.graph_db = graph_db
        self.netbox = netbox_client
        self.llm = llm

    async def create_comprehensive_response(self, query: str) -> Dict[str, Any]:
        """
        Integrate structured data, graph relationships, and document knowledge
        """
        # Parallel data gathering
        structured_data_task = self.get_structured_data(query)
        graph_analysis_task = self.get_graph_insights(query)
        document_context_task = self.get_document_context(query)

        structured_data, graph_insights, doc_context = await asyncio.gather(
            structured_data_task,
            graph_analysis_task, 
            document_context_task
        )

        # Synthesize comprehensive response
        synthesis_prompt = f"""
        Based on the following information sources, provide a comprehensive answer to: {query}

        STRUCTURED DATA:
        {json.dumps(structured_data, indent=2)}

        NETWORK RELATIONSHIPS:
        {graph_insights}

        OPERATIONAL KNOWLEDGE:
        {doc_context}

        Please provide:
        1. Direct answer to the question
        2. Relevant context and implications
        3. Recommended next steps
        4. Potential risks or considerations
        5. Related best practices from our documentation
        """

        response = await self.llm.agenerate([synthesis_prompt])

        return {
            'answer': response.generations[0][0].text,
            'data_sources': {
                'structured': bool(structured_data),
                'relationships': bool(graph_insights),
                'documentation': bool(doc_context)
            },
            'confidence_level': self.calculate_confidence_level(
                structured_data, graph_insights, doc_context
            )
        }

    def calculate_confidence_level(self, structured_data, graph_insights, doc_context) -> str:
        """
        Assess confidence in response based on data availability
        """
        data_sources = sum([
            1 if structured_data else 0,
            1 if graph_insights else 0,
            1 if doc_context else 0
        ])

        if data_sources >= 3:
            return "high"
        elif data_sources >= 2:
            return "medium"
        elif data_sources >= 1:
            return "low"
        else:
            return "insufficient_data"

Conversational Context Enhancement

class ConversationalContextManager:
    def __init__(self, session_store):
        self.session_store = session_store
        self.context_window_size = 10  # Remember last 10 interactions

    async def enhance_query_with_conversation_history(self,
                                                    query: str,
                                                    session_id: str) -> str:
        """
        Add conversational context to improve query understanding
        """
        # Retrieve conversation history
        history = await self.session_store.get_conversation_history(
            session_id, 
            limit=self.context_window_size
        )

        if not history:
            return query  # No context to add

        # Analyze conversation for ongoing topics
        ongoing_topics = self.identify_ongoing_topics(history)
        previous_entities = self.extract_mentioned_entities(history)

        # Build contextual query enhancement
        context_enhancement = []

        if ongoing_topics:
            context_enhancement.append(f"Continuing discussion about: {', '.join(ongoing_topics)}")

        if previous_entities:
            context_enhancement.append(f"Previously mentioned: {', '.join(previous_entities)}")

        if context_enhancement:
            enhanced_query = f"""
            Context from conversation: {' | '.join(context_enhancement)}

            Current query: {query}

            Please consider the conversation context when responding.
            """
            return enhanced_query

        return query

    def identify_ongoing_topics(self, history: List[Dict[str, Any]]) -> List[str]:
        """
        Identify persistent topics in conversation history
        """
        topics = []
        topic_keywords = {
            'troubleshooting': ['problem', 'issue', 'error', 'failed', 'down'],
            'configuration': ['config', 'setup', 'configure', 'change'],
            'planning': ['plan', 'capacity', 'upgrade', 'expand'],
            'monitoring': ['status', 'health', 'utilization', 'performance']
        }

        recent_messages = [msg['content'] for msg in history[-5:]]  # Last 5 messages
        combined_text = ' '.join(recent_messages).lower()

        for topic, keywords in topic_keywords.items():
            if any(keyword in combined_text for keyword in keywords):
                topics.append(topic)

        return topics

Production Integration & Optimization

Performance Optimization Strategy

class RAGPerformanceOptimizer:
    def __init__(self):
        self.query_cache = {}
        self.embedding_cache = {}
        self.performance_metrics = {}

    async def optimize_retrieval_performance(self, query: str) -> Dict[str, Any]:
        """
        Optimize RAG pipeline performance through intelligent caching and batching
        """
        # Check for cached embeddings
        query_embedding = await self.get_cached_embedding(query)

        if not query_embedding:
            query_embedding = await self.generate_and_cache_embedding(query)

        # Parallel retrieval from multiple sources
        retrieval_tasks = [
            self.retrieve_from_vector_store(query_embedding),
            self.retrieve_from_graph_context(query),
            self.retrieve_from_structured_data(query)
        ]

        retrieval_results = await asyncio.gather(*retrieval_tasks)

        # Intelligent result merging and deduplication
        merged_results = self.merge_and_deduplicate_results(retrieval_results)

        return merged_results

    async def get_cached_embedding(self, query: str) -> Optional[List[float]]:
        """
        Retrieve cached query embedding if available
        """
        # Use semantic similarity to find cached embeddings for similar queries
        query_hash = hashlib.md5(query.encode()).hexdigest()

        if query_hash in self.embedding_cache:
            return self.embedding_cache[query_hash]

        # Check for semantically similar cached queries
        for cached_query, cached_embedding in self.embedding_cache.items():
            similarity = self.calculate_query_similarity(query, cached_query)
            if similarity > 0.95:  # Very high similarity threshold
                return cached_embedding

        return None

Success Metrics

Technical Performance

  • Query enhancement accuracy: 90%+ of queries benefit from RAG context
  • Response time: RAG-enhanced queries complete within 5 seconds
  • Document retrieval precision: 85%+ of retrieved documents rated as relevant
  • Context integration quality: 95%+ of responses demonstrate proper context usage

Business Value

  • Knowledge accessibility: 80% reduction in time to find operational information
  • Decision quality: Measurable improvement in infrastructure decisions through historical context
  • Training efficiency: 60% reduction in time for new team members to become productive
  • Incident resolution: 40% faster resolution through similar case pattern matching

User Experience

  • Response relevance: 90%+ of RAG-enhanced responses rated as more helpful than basic responses
  • Confidence indicators: Clear indication of response confidence level based on available sources
  • Source transparency: Users can trace recommendations back to source documents
  • Conversation continuity: Maintains context across multi-turn conversations

This RAG integration phase transforms the system from a technical interface into a knowledgeable infrastructure advisor that combines the precision of structured data with the wisdom of organizational experience.