File size: 5,984 Bytes
4afc8ce
 
 
 
 
 
 
 
 
9d5041f
 
4afc8ce
 
 
 
 
 
 
 
 
 
 
 
9d5041f
 
4afc8ce
9d5041f
4afc8ce
 
 
 
 
9d5041f
892a464
 
 
 
 
 
 
9d5041f
 
4afc8ce
 
 
 
 
 
 
 
9d5041f
4afc8ce
 
9d5041f
4afc8ce
 
 
 
 
 
9d5041f
 
4afc8ce
 
 
 
 
9d5041f
 
4afc8ce
 
 
 
9d5041f
4afc8ce
 
 
 
 
 
9d5041f
4afc8ce
9d5041f
4afc8ce
 
 
 
 
 
9d5041f
4afc8ce
9d5041f
 
 
4afc8ce
 
 
 
 
 
9d5041f
4afc8ce
 
 
 
 
 
9d5041f
4afc8ce
 
 
 
 
 
 
 
 
9d5041f
 
 
4afc8ce
 
 
 
 
 
 
 
 
 
9d5041f
 
 
 
 
 
 
 
 
4afc8ce
 
 
9d5041f
 
4afc8ce
 
9d5041f
4afc8ce
9d5041f
4afc8ce
 
9d5041f
4afc8ce
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
"""Main agent implementation"""

from langchain_openai import ChatOpenAI
from langchain.agents import create_agent

from .visualizer import ContextVisualizer
from .memory import ConversationMemory
from .knowledge import KnowledgeBase
from .tools import calculate_metric, get_current_time
from config.settings import Settings
from config import logger_agent, logger_memory, logger_knowledge


class ContextEngineeringAgent:
    """
    Agent that demonstrates context engineering principles:
    - Relevance: Only includes needed context
    - Structure: Clear separation of context layers
    - Timing: Retrieves information when needed
    - Consistency: Stable system instructions
    """
    
    def __init__(self):
        logger_agent.info("Initializing ContextEngineeringAgent")
        
        # Initialize components
        logger_agent.debug(f"Initializing LLM with model: {Settings.MODEL_NAME}")
        self.llm = ChatOpenAI(
            model=Settings.MODEL_NAME,
            temperature=Settings.TEMPERATURE
        )
        
        logger_agent.info("Loading knowledge base from FAISS index")
        self.knowledge_base = KnowledgeBase(
            pdf_path=Settings.PDF_PATH,
            index_path=Settings.FAISS_INDEX_PATH,
            embedding_model=Settings.EMBEDDING_MODEL,
            top_k=Settings.RAG_TOP_K,
            recreate_index=False  # Load existing index by default
        )
        
        logger_agent.debug(f"Initializing conversation memory (max_messages={Settings.MAX_CONVERSATION_MESSAGES})")
        self.memory = ConversationMemory(max_messages=Settings.MAX_CONVERSATION_MESSAGES)
        self.visualizer = ContextVisualizer()
        
        # System instructions
        self.system_prompt = Settings.SYSTEM_PROMPT
        
        # Create tools
        self.tools = [calculate_metric, get_current_time]
        logger_agent.debug(f"Created {len(self.tools)} tools: {[t.name for t in self.tools]}")
        
        # Create agent
        logger_agent.info("Creating LangChain agent with system prompt and tools")
        self.agent = create_agent(
            model=self.llm,
            tools=self.tools,
            system_prompt=self.system_prompt
        )
        
        logger_agent.info("ContextEngineeringAgent initialized successfully")
        
    def process_query(self, user_query: str) -> tuple[str, ContextVisualizer]:
        """
        Process a user query with full context engineering
        Returns: (response, visualizer)
        """
        logger_agent.info(f"Processing query: {user_query[:50]}..." if len(user_query) > 50 else f"Processing query: {user_query}")
        
        # Reset visualizer for new query
        self.visualizer = ContextVisualizer()
        
        # Layer 1: System Instructions
        logger_agent.debug("Adding layer: System Instructions")
        self.visualizer.add_layer(
            "System Instructions",
            self.system_prompt,
        )
        
        # Layer 2: Conversation History
        logger_memory.debug("Retrieving conversation history")
        history_text = self.memory.get_history_text()
        logger_agent.debug(f"Conversation history length: {len(history_text)} characters")
        self.visualizer.add_layer(
            "Conversation History",
            history_text if history_text != "No previous conversation" else "No previous conversation",
        )
        
        # Layer 3: Retrieved Knowledge (RAG)
        logger_knowledge.info(f"Retrieving relevant documents for query")
        retrieved_context = self.knowledge_base.retrieve_relevant(user_query)
        doc_count = len([c for c in retrieved_context.split("--- Chunk") if c.strip()])
        logger_knowledge.info(f"Retrieved {doc_count} document chunks")
        logger_agent.debug(f"Retrieved context length: {len(retrieved_context)} characters")
        self.visualizer.add_layer(
            "Retrieved Knowledge (RAG)",
            retrieved_context,
        )
        
        # Layer 4: Current User Query
        logger_agent.debug("Adding layer: User Query")
        self.visualizer.add_layer(
            "User Query",
            user_query,
        )
        
        # Layer 5: Available Tools
        logger_agent.debug("Adding layer: Available Tools")
        tools_context = "\n".join([
            f"- {tool.name}: {tool.description}" for tool in self.tools
        ])
        self.visualizer.add_layer(
            "Available Tools",
            tools_context,
        )
        
        # Build the context structure
        total_tokens = sum(self.visualizer.token_counts.values())
        logger_agent.info(f"Total context size: {total_tokens} tokens across {len(self.visualizer.context_layers)} layers")
        
        context_message = f"""Context from Knowledge Base:
{retrieved_context}

Previous Conversation:
{history_text}

Current Question:
{user_query}"""
        
        # Invoke agent
        logger_agent.info("Invoking LangChain agent with assembled context")
        try:
            result = self.agent.invoke({
                "messages": [{"role": "user", "content": context_message}]
            })
            logger_agent.info("Agent invocation successful")
        except Exception as e:
            logger_agent.error(f"Agent invocation failed: {str(e)}")
            raise
        
        # Extract response
        response = result["messages"][-1].content
        response_preview = response[:100] + "..." if len(response) > 100 else response
        logger_agent.info(f"Generated response: {response_preview}")
        
        # Update conversation memory
        logger_memory.info("Adding user message to conversation memory")
        self.memory.add_user_message(user_query)
        logger_memory.info("Adding AI response to conversation memory")
        self.memory.add_ai_message(response)
        
        logger_agent.info("Query processing completed successfully")
        return response, self.visualizer