Week 2 of 4

🤖 Agent Fundamentals

What Agents Are, How They Think, and What They Can Do

📅 Day 6 – Day 10
⏱️ ~45 min per session
🎯 Use Case: Customer Support Agent
🛠️ Stack: Python + LangChain + Ollama
DAY 6
What is an Agent — Chatbot vs Agent
⏱️ 45 min 📖 Theory + Demos 🎯 Core agent concept

🎯 Learning Objectives

💬 Chatbot vs Agent — The Key Distinction

Most developers confuse chatbots with agents. They are fundamentally different:

💬 Chatbot (Traditional)

  • Takes input → returns text output
  • Single turn or multi-turn conversation
  • No actions — only talks
  • No tool access — can't check databases, call APIs
  • No autonomy — only responds to what's asked
  • Stateless (unless manually managed)
# Chatbot: text in → text out
user:  "What's my order status?"
bot:   "I'd be happy to help! Could you 
        provide your order number?"
# ⚠️ Cannot actually look up the order

🤖 Agent (Agentic AI)

  • Takes input → reasonstakes actions → returns result
  • Multi-step, autonomous execution
  • Uses tools — calls APIs, queries databases, sends emails
  • Has access to external systems
  • Plans and decides what to do next
  • Maintains memory across interactions
# Agent: think → act → observe → respond
user:  "What's my order status?"
agent: [THINK] Need order ID → check DB
       [ACT]  query_orders(user_id=42)
       [OBS]  Order #7845: Shipped, ETA Apr 29
       [REPLY] "Your order #7845 shipped and 
                arrives by April 29th!"
🎯
One-line definition: An AI Agent is an LLM that can reason about a task, decide what tools to use, execute actions autonomously, and iterate until the goal is achieved.

🔄 The Agent Loop

Every agent — whether it's a customer support bot, a coding assistant, or an autonomous researcher — follows the same fundamental loop:

The Agentic Loop — Perceive → Reason → Act → Observe
👁️ PERCEIVE
Read user input,
tool outputs, context
🧠 REASON
Think about what
to do next (LLM)
ACT
Call a tool, API,
or generate response
👀 OBSERVE
Check tool result,
decide if done
🔄
Repeat until
goal is met

Agent Loop — Step by Step

StepWhat HappensCustomer Support Example
1. PerceiveAgent receives input (user message, system event)Customer says: "Where is my order #7845?"
2. ReasonLLM thinks: what do I need to do? Which tool?"I need to look up order #7845 in the database"
3. ActAgent calls a tool or takes an actionCalls lookup_order(order_id="7845")
4. ObserveAgent reads the result; decides: done or continue?Result: {status: "shipped", eta: "Apr 29"} → Done!
5. RespondAgent formulates final answer for user"Your order #7845 has shipped and arrives April 29th!"
🔄
Multi-step agents can loop multiple times. Example: Agent looks up order → finds it's delayed → checks shipping partner API → finds new ETA → checks refund policy → offers compensation → all in one conversation turn.

🌍 Real-World Agent Examples

🎧
Customer Support
Resolves tickets by querying databases, checking policies, drafting replies, escalating when needed
💻
Coding Assistant
GitHub Copilot Agent: reads code, runs tests, fixes bugs, creates PRs autonomously
🔬
Research Agent
Searches papers, summarizes findings, compares results, writes reports
📊
Data Analyst
Writes SQL queries, generates charts, interprets results, creates dashboards
🛒
Shopping Agent
Compares prices, checks reviews, applies coupons, places orders
🏥
Healthcare Triage
Collects symptoms, checks protocols, prioritizes urgency, schedules appointments

📊 Levels of Autonomy

Not all agents are fully autonomous. There's a spectrum:

LevelNameDescriptionExample
L0No AIPure rule-based, hardcodedIVR phone menus
L1ChatbotLLM generates text, no toolsFAQ bot with fixed answers
L2CopilotLLM suggests, human decides & actsCopilot suggests code, you accept
L3AgentLLM reasons, acts, uses tools — human approvesAgent drafts reply, human sends
L4Autonomous AgentFull autonomy, acts without human in loopAgent auto-resolves & closes ticket
L5Multi-Agent SystemMultiple agents collaboratingTriage agent → specialist agent → QA agent
🎯
Our course builds from L1 → L4. By Week 4, we'll have an autonomous Customer Support Agent that classifies tickets, retrieves knowledge, generates replies, and takes actions — with human oversight for escalations.
🎧 Use Case Connection

Customer Support — Agent vs Chatbot

What Our Agent Will Do (that a chatbot can't)
📨 Customer: "My order #7845 is late, I want a refund"
↓ Agent Thinks
🧠 Intent: refund request + order complaint → Need order data + refund policy
↓ Agent Acts (Step 1)
🔧 Tool Call: lookup_order("7845") → Shipped Apr 22, ETA Apr 28
↓ Agent Acts (Step 2)
🔧 Tool Call: get_refund_policy("shipping_delay") → ₹200 credit if >2 days late
↓ Agent Acts (Step 3)
🧠 Reason: Order is 2+ days late → eligible for ₹200 credit. Not eligible for full refund yet.
↓ Agent Responds
💬 "Hi! I checked order #7845 — it shipped Apr 22 and is expected Apr 28.
Since it's delayed, I've applied a ₹200 credit to your account.
If it doesn't arrive by Apr 29, we'll process a full refund."

✅ Key Takeaways

  • Chatbot = text in, text out. Agent = perceive, reason, act, observe, respond
  • Agents use tools to interact with the real world (APIs, databases, emails)
  • The Agent Loop repeats until the goal is achieved
  • Autonomy is a spectrum: L0 (rules) → L5 (multi-agent systems)
  • Enterprise value: agents reduce manual work, handle complexity, scale infinitely

❓ Quick Check

  1. What's the fundamental difference between a chatbot and an agent?
  2. Name the 4 steps of the Agent Loop.
  3. At what autonomy level does the agent start using tools?
  4. Why can't a chatbot resolve "Where is my order #7845?"
DAY 7
Agent Components — The 5 Building Blocks
⏱️ 45 min 📖 Architecture Deep Dive 🎯 Agent anatomy

🎯 Learning Objectives

🧩 The 5 Building Blocks

Agent Architecture — The 5 Pillars
🧠
1. LLM (Brain)
Reasoning engine that thinks, plans, and generates
🔧
2. Tools
Functions the agent can call to take actions
💾
3. Memory
Short-term and long-term information storage
📋
4. Planning
Strategy to break tasks into steps
📝
5. Prompt / Instructions
System prompt defining persona and rules

🧠 1. The LLM (Brain)

The LLM is the central reasoning engine. It receives all inputs (user message, tool results, memory) and decides what to do next. Think of it as the brain — everything else (tools, memory) are its arms and notebooks.

ResponsibilityDescription
UnderstandingParse user intent from natural language
ReasoningDecide which tool to use and what arguments to pass
GenerationCreate the final human-readable response
OrchestrationManage multi-step workflows (call tool A → use result in tool B)
Python — LLM as agent brain
from langchain_ollama import ChatOllama

# The "brain" of our agent
llm = ChatOllama(
    model="qwen2.5:3b",
    temperature=0.2,        # Low for consistent support replies
    base_url="http://localhost:11434"
)

# The LLM decides what to do
response = llm.invoke("Customer says: 'My order is late.' What tool should I use?")
# Output: "I should use the lookup_order tool to check the order status."

🔧 2. Tools (Hands)

Tools are functions the agent can call to interact with the outside world. Without tools, the LLM is just a text generator. With tools, it becomes an agent that can take action.

Tool Categories
🗄️
Database
Query orders, customers, products via SQL
🌐
API Calls
Shipping tracker, payment gateway, CRM
🔍
Search
Knowledge base, FAQs, policy documents
📧
Communication
Send emails, Slack messages, SMS
💻
Code Execution
Run calculations, generate reports
📁
File System
Read/write files, create tickets, log actions
Python — Defining a Tool
from langchain.tools import tool

@tool
def lookup_order(order_id: str) -> dict:
    """Look up an order by its ID and return status, items, and ETA."""
    # In production: query SQL Server database
    # For demo: hardcoded data
    orders = {
        "7845": {"status": "shipped", "eta": "2026-04-29", "items": ["MacBook Air M3"], "total": 89999},
        "7846": {"status": "delivered", "eta": "2026-04-25", "items": ["iPhone 16"], "total": 79999},
    }
    return orders.get(order_id, {"error": f"Order {order_id} not found"})

@tool
def get_refund_policy(issue_type: str) -> str:
    """Retrieve the refund policy for a given issue type."""
    policies = {
        "shipping_delay": "Shipping delay >2 days: ₹200 credit. >7 days: full refund.",
        "wrong_item": "Wrong item: free return pickup + full refund within 48 hours.",
        "damaged": "Damaged product: photo required. Full refund + ₹500 inconvenience credit.",
    }
    return policies.get(issue_type, "Policy not found. Please escalate to supervisor.")

@tool
def send_email(to: str, subject: str, body: str) -> str:
    """Send an email to the customer."""
    # In production: use SMTP/SendGrid
    print(f"📧 Sending to {to}: {subject}")
    return f"Email sent successfully to {to}"

# The agent has access to these tools:
tools = [lookup_order, get_refund_policy, send_email]

💾 3. Memory (Notebook)

Memory lets the agent remember information across conversation turns and even across sessions.

Memory TypeScopeUse Case
Short-term (Buffer)Current conversationRemember customer said their name is Rajesh 3 messages ago
Long-term (Persistent)Across sessionsRemember this customer had a bad experience last month
EpisodicPast interactionsRecall how a similar ticket was resolved before
Semantic (Vector)Knowledge baseSearch FAQs and policies by meaning, not keywords

📋 4. Planning (Strategy)

Planning is how the agent breaks a complex task into steps. Without planning, the agent might try to do everything at once and fail.

Planning Example — Customer Refund Request
📨 "I received a broken laptop. Order #7845. I want my money back."
↓ Agent plans
📋 Plan:
  Step 1: Look up order #7845 to verify
  Step 2: Check refund policy for "damaged" items
  Step 3: Initiate refund if eligible
  Step 4: Send confirmation email
  Step 5: Reply to customer with summary
↓ Execute steps
✅ All steps completed → Customer gets refund + confirmation

📝 5. Prompt / Instructions (Personality)

The system prompt defines the agent's persona, rules, and boundaries. This is what you built in Day 5.

System Prompt
SYSTEM_PROMPT = """
You are a customer support agent for ShopEasy, an Indian e-commerce platform.

You have access to these tools:
- lookup_order: Check order status by order ID
- get_refund_policy: Get refund/return policy for issue types
- send_email: Send email to customer

Rules:
1. Always look up the order before making any claims about it
2. Always check the refund policy before offering refunds
3. Be empathetic, professional, and concise
4. If you cannot resolve, say: "Let me connect you to a specialist"
5. Never make up information — only use tool results
6. Reference specific order numbers and amounts in your reply
"""

🏗️ How It All Fits Together

Agent Architecture — Complete Picture
👤 User Input: "My order #7845 is broken, I want a refund"
📝 System Prompt (persona + rules) prepended to message
🧠 LLM (Brain) — Reads message + prompt + memory → Decides: "Call lookup_order"
🔧 Tool: lookup_order("7845") → {status: "delivered", items: ["MacBook Air"]}
↓ result fed back to LLM
🧠 LLM — Now decides: "Call get_refund_policy('damaged')"
🔧 Tool: get_refund_policy("damaged") → "Photo required. Full refund + ₹500 credit"
↓ result fed back to LLM
🧠 LLM — Has all info → Generates final response
💾 Memory — Stores this conversation for future reference
💬 Response: "I've verified order #7845 (MacBook Air). For damaged items, please share a photo and we'll process a full refund + ₹500 credit within 48 hours."

⚡ Agent Frameworks Preview

You don't build agents from scratch. Frameworks wire the 5 components together:

FrameworkLanguageBest ForWe'll Use
LangChainPythonGeneral-purpose agents, RAG, chains✅ Week 2-3
LangGraphPythonComplex multi-step workflows as graphs✅ Week 3
Semantic KernelC# / PythonEnterprise .NET integration✅ Week 4
CrewAIPythonMulti-agent collaboration✅ Week 4
AutoGenPythonMicrosoft's multi-agent conversations📖 Overview
🎧 Use Case Connection

Customer Support Agent — Component Map

ComponentOur Implementation
🧠 LLMQwen 2.5:3B via Ollama (local, free)
🔧 Toolslookup_order, get_refund_policy, search_faq, send_email, escalate_ticket
💾 MemoryConversationBufferMemory (short-term) + ChromaDB vector store (long-term)
📋 PlanningReAct pattern (Reason + Act) — decides step-by-step
📝 PromptSystem prompt with persona, rules, guardrails, output format

✅ Key Takeaways

  • Every agent has 5 components: LLM, Tools, Memory, Planning, Prompt
  • The LLM is the brain — it orchestrates everything else
  • Tools give the agent "hands" to interact with the real world
  • Memory provides context across turns and sessions
  • Frameworks (LangChain, Semantic Kernel) wire these components together

❓ Quick Check

  1. Name the 5 building blocks of an AI agent.
  2. What happens if an agent has no tools?
  3. Why is memory important for customer support?
  4. Which framework will we use for .NET integration?
DAY 8
Tools — APIs, Search, Code Execution
⏱️ 45 min 💻 Heavy Hands-On 🎯 Build agent tools

🎯 Learning Objectives

📞 How Function Calling Works

When an LLM has tools available, it doesn't just generate text — it can output a structured function call instead. The framework then executes the function and feeds the result back to the LLM.

Function Calling Flow
🧠 LLM receives: User message + list of available tools (with descriptions)
↓ LLM decides
🤔 "I need to look up order #7845" → Output: {"tool": "lookup_order", "args": {"order_id": "7845"}}
↓ Framework intercepts
⚡ Framework calls: lookup_order(order_id="7845") → Returns result
↓ Result fed back to LLM
🧠 LLM sees tool result → Decides: need another tool? or generate response?
💡
Key insight: The LLM never executes code itself. It outputs a JSON instruction saying "call this function with these args." The framework (LangChain) does the actual execution. This is a critical safety boundary.

🛠️ Building Tools with LangChain

Method 1: @tool Decorator (Simple)

Python
from langchain.tools import tool

@tool
def lookup_order(order_id: str) -> str:
    """Look up an order by its ID. Returns order status, items, total, and ETA.
    Use this when a customer asks about their order status or delivery."""
    
    # Simulate database lookup (production: query SQL Server)
    import json
    orders_db = {
        "7845": {"order_id": "7845", "status": "shipped", "eta": "2026-04-29",
                 "items": ["MacBook Air M3"], "total": 89999, "customer": "Rajesh Kumar"},
        "7846": {"order_id": "7846", "status": "delivered", "eta": "2026-04-25",
                 "items": ["iPhone 16", "AirPods Pro"], "total": 99998, "customer": "Priya Singh"},
    }
    order = orders_db.get(order_id)
    if order:
        return json.dumps(order, indent=2)
    return f"Error: Order #{order_id} not found in the system."

# Check the auto-generated schema
print(lookup_order.name)        # "lookup_order"
print(lookup_order.description) # "Look up an order by its ID..."
print(lookup_order.args_schema.model_json_schema())
# {'properties': {'order_id': {'type': 'string'}}, 'required': ['order_id']}
⚠️
Tool docstrings are critical! The LLM reads the docstring to decide when to use the tool. Vague descriptions = wrong tool calls. Always include: what the tool does, when to use it, and what it returns.

Method 2: StructuredTool (More Control)

Python
from langchain.tools import StructuredTool
from pydantic import BaseModel, Field

class RefundPolicyInput(BaseModel):
    """Input for refund policy lookup."""
    issue_type: str = Field(
        description="Type of issue: 'shipping_delay', 'wrong_item', 'damaged', 'defective', 'changed_mind'"
    )
    days_since_delivery: int = Field(
        default=0,
        description="Number of days since the item was delivered (0 if not delivered)"
    )

def get_refund_policy(issue_type: str, days_since_delivery: int = 0) -> str:
    """Get the applicable refund/return policy based on issue type and delivery date."""
    policies = {
        "shipping_delay": {
            "policy": "If delayed >2 days: ₹200 store credit. If >7 days: full refund.",
            "auto_approve": True
        },
        "wrong_item": {
            "policy": "Free return pickup scheduled within 24 hours. Full refund processed in 3-5 days.",
            "auto_approve": True
        },
        "damaged": {
            "policy": "Customer must upload photo of damage. Full refund + ₹500 inconvenience credit.",
            "auto_approve": False,
            "requires": "photo_upload"
        },
        "defective": {
            "policy": "Eligible for replacement or refund within 15 days of delivery.",
            "auto_approve": days_since_delivery <= 15
        },
        "changed_mind": {
            "policy": "Return within 7 days if unused and in original packaging. Customer pays return shipping.",
            "auto_approve": days_since_delivery <= 7
        }
    }
    import json
    result = policies.get(issue_type, {"policy": "Unknown issue. Escalate to supervisor.", "auto_approve": False})
    return json.dumps(result, indent=2)

refund_policy_tool = StructuredTool.from_function(
    func=get_refund_policy,
    name="get_refund_policy",
    description="Get refund/return policy for a specific issue type. Use when customer requests refund or return.",
    args_schema=RefundPolicyInput
)

Method 3: API-Calling Tool

Python
import requests
from langchain.tools import tool

@tool
def check_shipping_status(tracking_id: str) -> str:
    """Check real-time shipping status using tracking ID.
    Use when customer asks about delivery status or tracking."""
    
    try:
        # Example: call shipping partner API
        # In production: use actual shipping API (Delhivery, BlueDart, etc.)
        response = requests.get(
            f"http://localhost:8000/api/tracking/{tracking_id}",
            timeout=10
        )
        response.raise_for_status()
        return response.json()
    except requests.exceptions.Timeout:
        return "Error: Shipping service is temporarily slow. Please try again."
    except requests.exceptions.RequestException as e:
        return f"Error checking shipping status: Unable to reach shipping service."

@tool
def search_knowledge_base(query: str) -> str:
    """Search the FAQ and policy knowledge base for relevant information.
    Use when you need to find answers about company policies, product info, or procedures."""
    
    # In production: vector search with ChromaDB (Week 3)
    # For now: simple keyword matching
    kb = {
        "return window": "Standard return window is 7 days. Electronics: 15 days.",
        "payment methods": "We accept UPI, credit cards, debit cards, and net banking.",
        "warranty": "All electronics come with 1-year manufacturer warranty.",
        "cancel order": "Orders can be cancelled before shipping. Go to My Orders → Cancel."
    }
    results = []
    for key, value in kb.items():
        if any(word in query.lower() for word in key.split()):
            results.append(f"- {value}")
    return "\n".join(results) if results else "No relevant information found."

🔗 Wiring Tools into an Agent

Python — Complete Agent Setup
from langchain_ollama import ChatOllama
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

# 1. LLM
llm = ChatOllama(model="qwen2.5:3b", temperature=0.2)

# 2. Tools
tools = [lookup_order, refund_policy_tool, check_shipping_status, search_knowledge_base]

# 3. Prompt
prompt = ChatPromptTemplate.from_messages([
    ("system", """You are a customer support agent for ShopEasy.
    
Available tools: lookup_order, get_refund_policy, check_shipping_status, search_knowledge_base

Rules:
- Always verify order details before making claims
- Always check policy before offering refunds
- Be empathetic and professional
- If stuck, say: "Let me connect you to a specialist"
"""),
    MessagesPlaceholder("chat_history", optional=True),
    ("human", "{input}"),
    MessagesPlaceholder("agent_scratchpad"),
])

# 4. Create Agent
agent = create_tool_calling_agent(llm, tools, prompt)

# 5. Agent Executor (runs the loop)
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,     # See the reasoning!
    max_iterations=5, # Safety: max 5 tool calls per turn
    handle_parsing_errors=True
)

# 6. Run it!
result = agent_executor.invoke({
    "input": "Hi, my order #7845 hasn't arrived yet. Can you check?"
})
print(result["output"])

What verbose=True Shows You

Agent Execution Trace
> Entering new AgentExecutor chain...

Thought: The customer wants to know about order #7845. I should look it up.
Action: lookup_order
Action Input: {"order_id": "7845"}
Observation: {"order_id": "7845", "status": "shipped", "eta": "2026-04-29", 
              "items": ["MacBook Air M3"], "total": 89999, "customer": "Rajesh Kumar"}

Thought: The order is shipped with ETA April 29. Let me give the customer this info.
Final Answer: Hi Rajesh! I checked your order #7845 — your MacBook Air M3 has been shipped 
and is expected to arrive by April 29th. You can track it in the "My Orders" section. 
Let me know if you need anything else! 😊

> Finished chain.

⚠️ Tool Error Handling

Python — Robust Tool Design
@tool
def lookup_order(order_id: str) -> str:
    """Look up an order by ID. Returns order details or error message."""
    
    # Validate input
    if not order_id or not order_id.strip():
        return "Error: No order ID provided. Ask the customer for their order number."
    
    # Sanitize input (prevent injection)
    order_id = order_id.strip().replace("'", "").replace(";", "")[:20]
    
    try:
        # Database lookup (simulated)
        order = db_lookup(order_id)  # Your actual DB call
        if order:
            return json.dumps(order)
        return f"Order #{order_id} not found. Please verify the order number with the customer."
    except Exception:
        return "Error: Unable to access order system. Please try again or escalate to support team."
🎧 Use Case Connection

Customer Support — Complete Tool Inventory

ToolWhen Agent Uses ItReturns
lookup_orderCustomer asks about order statusOrder details JSON
get_refund_policyCustomer wants refund/returnPolicy + auto-approve flag
check_shipping_statusCustomer asks "where is my package?"Live tracking info
search_knowledge_baseGeneral questions about policiesRelevant FAQ entries
send_emailAfter resolving — confirmation emailSuccess/failure status
escalate_ticketCannot resolve — hand off to humanTicket ID + assigned agent

✅ Key Takeaways

  • Tools are Python functions with clear docstrings — LLM decides when to call them
  • LLM outputs structured JSON (function call), framework executes it
  • Three ways to define tools: @tool decorator, StructuredTool, or custom classes
  • Tool docstrings are critical — the LLM reads them to choose the right tool
  • Always handle errors in tools — return helpful error messages, never crash

🔨 Hands-On Tasks

  1. Create a cancel_order tool that checks if order is cancellable (only before shipping)
  2. Create a calculate_refund tool that computes refund amount based on policy
  3. Wire 4+ tools into an agent and test with 5 different customer messages
  4. Set verbose=True and trace the agent's reasoning for each scenario
DAY 9
Memory — Short-term, Long-term, Vector Stores
⏱️ 45 min 💻 Theory + Code 🎯 Agent memory systems

🎯 Learning Objectives

🧠 Why Memory Matters

Without memory, every LLM call is stateless — the model doesn't remember anything from previous messages. This creates terrible user experiences:

❌ Without Memory

User: My name is Rajesh, order #7845
Bot:  Let me check order #7845 for you.

User: What's the status?
Bot:  Could you provide your order number?
      ← FORGOT everything!

✅ With Memory

User: My name is Rajesh, order #7845
Bot:  Let me check order #7845 for you.

User: What's the status?
Bot:  Hi Rajesh! Your order #7845 is 
      shipped and arrives April 29th.
      ← REMEMBERS context!

📦 Memory Types

1. ConversationBufferMemory (Full History)

Stores the entire conversation as-is. Simple but grows fast.

Python
from langchain.memory import ConversationBufferMemory

memory = ConversationBufferMemory(
    memory_key="chat_history",
    return_messages=True
)

# Stores every message
memory.save_context(
    {"input": "My name is Rajesh, order #7845"},
    {"output": "Hi Rajesh! Let me check order #7845 for you."}
)
memory.save_context(
    {"input": "What's the status?"},
    {"output": "Your order #7845 is shipped, arriving April 29th."}
)

# Load everything
print(memory.load_memory_variables({}))
# Returns ALL messages — both user and agent
⚠️
Problem: After 50 messages, the buffer is huge and eats up the context window. A 3B model with 4K context can only hold ~20-30 messages before it starts losing earlier context.

2. ConversationBufferWindowMemory (Sliding Window)

Keeps only the last K messages. Older messages are discarded.

Python
from langchain.memory import ConversationBufferWindowMemory

memory = ConversationBufferWindowMemory(
    memory_key="chat_history",
    return_messages=True,
    k=5  # Keep last 5 exchange pairs (10 messages)
)

# After 20 messages, only the last 10 are kept
# Good for: real-time support where recent context matters most

3. ConversationSummaryMemory (Summarized)

Uses the LLM to summarize older messages. Keeps a running summary instead of raw messages.

Python
from langchain.memory import ConversationSummaryMemory
from langchain_ollama import ChatOllama

llm = ChatOllama(model="qwen2.5:3b")

memory = ConversationSummaryMemory(
    llm=llm,
    memory_key="chat_history",
    return_messages=True
)

# After many messages, the summary might be:
# "Customer Rajesh Kumar contacted about order #7845 (MacBook Air). 
#  Order is shipped, ETA April 29. Customer expressed frustration about 
#  the delay. Agent offered ₹200 credit for the delay. Customer accepted."

# ✅ Compact — captures key facts
# ✅ Fits in small context windows
# ⚠️ Uses extra LLM calls to summarize

4. Vector Store Memory (Semantic Search)

Stores conversation chunks as embeddings and retrieves the most relevant ones based on the current query. Perfect for long conversations or knowledge bases.

Python
# pip install chromadb langchain-chroma
from langchain.memory import VectorStoreRetrieverMemory
from langchain_chroma import Chroma
from langchain_ollama import OllamaEmbeddings

# Setup vector store
embeddings = OllamaEmbeddings(model="nomic-embed-text")
vectorstore = Chroma(
    collection_name="support_memory",
    embedding_function=embeddings,
    persist_directory="./memory_db"
)

# Create memory with retriever
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
memory = VectorStoreRetrieverMemory(retriever=retriever)

# Save conversations as embeddings
memory.save_context(
    {"input": "I need to return my laptop, order #7845"},
    {"output": "I'll help with the return. Your MacBook Air is within the 15-day window."}
)

# Later, when customer asks about returns:
# The memory searches by MEANING and retrieves the relevant conversation
# Even if the customer says "send it back" instead of "return"

📊 Memory Type Comparison

TypeHow it WorksContext UsageBest For
BufferStores everything🔴 High — grows linearlyShort conversations (<10 turns)
WindowLast K messages only🟢 Fixed — always same sizeReal-time support chats
SummaryLLM summarizes older messages🟡 Medium — compact summariesLong conversations, complex issues
Vector StoreEmbed & search by meaning🟢 Fixed — retrieves top-KKnowledge base, cross-session memory

🧠 Combining Memory Types

Production agents often use multiple memory types together:

Hybrid Memory Architecture
💬
Short-term
Window Memory (last 5 turns)
Current conversation context
📝
Medium-term
Summary Memory
Compressed earlier conversation
🗄️
Long-term
Vector Store (ChromaDB)
Past sessions, knowledge base
Python — Combined Memory
from langchain.memory import CombinedMemory

# Short-term: last 3 exchanges
window_memory = ConversationBufferWindowMemory(
    memory_key="recent_chat",
    return_messages=True,
    k=3
)

# Long-term: vector search
vector_memory = VectorStoreRetrieverMemory(
    retriever=vectorstore.as_retriever(search_kwargs={"k": 2}),
    memory_key="relevant_history"
)

# Combine them
memory = CombinedMemory(memories=[window_memory, vector_memory])

# The agent now gets:
# 1. Last 3 messages (immediate context)
# 2. Most relevant past conversations (semantic search)
# → Best of both worlds!
🎧 Use Case Connection

Customer Support — Memory in Action

📨 Session 1 (Jan 15): Rajesh complained about damaged laptop. Refund processed.
↓ Stored in Vector Memory
📨 Session 2 (Apr 27 — today): Rajesh calls again: "I want to buy another laptop"
↓ Agent retrieves from memory
💾 Retrieved: "Customer Rajesh had damaged laptop experience in Jan. Received full refund + ₹500 credit."
↓ Agent uses context
💬 "Welcome back Rajesh! I see you had an issue with a laptop earlier — I'm sorry about that. Great news: you still have a ₹500 credit on your account. Would you like to apply it to your new purchase?"

This is the difference between a forgettable chatbot and a personalized agent.

✅ Key Takeaways

  • Without memory, every LLM call is stateless — terrible UX
  • Buffer = full history; Window = last K; Summary = compressed; Vector = semantic search
  • Production agents combine multiple memory types for best results
  • Vector stores (ChromaDB) enable cross-session and knowledge-base memory
  • Memory is what makes agents feel "intelligent" and personalized

🔨 Hands-On Tasks

  1. Implement ConversationBufferWindowMemory with k=3 and test with a 10-message conversation
  2. Implement ConversationSummaryMemory and observe what gets summarized
  3. Set up ChromaDB locally and store 5 FAQ entries as embeddings
  4. Query the vector store with variations of the same question and check results
DAY 10
Planning & Reasoning — ReAct, CoT, ReWOO
⏱️ 45 min 📖 Deep Theory + Code 🎯 How agents think

🎯 Learning Objectives

🧠 How Do Agents "Think"?

The reasoning pattern determines how the agent decides what to do. Different patterns have different strengths:

1️⃣ ReAct (Reason + Act) — The Most Common Pattern

ReAct interleaves reasoning (thinking out loud) with acting (calling tools). The agent thinks about what to do, does it, observes the result, then thinks again.

ReAct Pattern — Interleaved Reasoning & Action
User: "I received the wrong item in order #7845. I ordered AirPods but got a phone case."
💭 Thought 1: Customer received wrong item. I need to verify order #7845 to confirm what they ordered.
Action 1: lookup_order(order_id="7845")
👀 Observation 1: {items: ["AirPods Pro"], status: "delivered", total: 24999}
💭 Thought 2: Confirmed — they ordered AirPods Pro but received a phone case. This is a "wrong_item" issue. Let me check the refund policy.
Action 2: get_refund_policy(issue_type="wrong_item")
👀 Observation 2: {policy: "Free return pickup within 24hr. Full refund in 3-5 days.", auto_approve: true}
💭 Thought 3: Policy allows auto-approve. I can resolve this fully. I should offer the return pickup and refund.
💬 Final Answer: "I'm really sorry about this mix-up! I've confirmed your order #7845 was for AirPods Pro (₹24,999). We'll schedule a free pickup for the wrong item within 24 hours, and your full refund will be processed in 3-5 business days. We'll also ensure the correct AirPods Pro are shipped to you right away!"
Python — ReAct Agent with LangChain
from langchain_ollama import ChatOllama
from langchain.agents import AgentExecutor, create_react_agent
from langchain_core.prompts import PromptTemplate

llm = ChatOllama(model="qwen2.5:3b", temperature=0.2)

# ReAct-specific prompt template
react_prompt = PromptTemplate.from_template("""
You are a customer support agent. Answer the customer's question using the available tools.

You have access to these tools:
{tools}

Tool names: {tool_names}

Use this EXACT format:

Question: the customer's message
Thought: think about what to do
Action: the tool to use (must be one of [{tool_names}])
Action Input: the input to the tool
Observation: the result from the tool
... (repeat Thought/Action/Observation as needed)
Thought: I now have enough information to answer
Final Answer: your response to the customer

Begin!

Question: {input}
{agent_scratchpad}
""")

# Create ReAct agent
agent = create_react_agent(llm, tools, react_prompt)
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,
    max_iterations=5,
    handle_parsing_errors=True
)

2️⃣ Chain-of-Thought (CoT) — Think Before Acting

CoT forces the agent to reason through the entire problem before taking any action. Best for complex, multi-step reasoning.

🔄 ReAct (Interleaved)

Think → Act → Observe →
Think → Act → Observe →
Think → Final Answer

Good for: dynamic situations where each step depends on the previous result.

🧠 Chain-of-Thought (Plan First)

Think → Think → Think → 
(Full plan ready) →
Act → Act → Act →
Final Answer

Good for: problems where you can plan upfront and execute sequentially.

Python — CoT Prompting for Planning
cot_planning_prompt = """
You are a customer support agent. Before taking any action, think through 
your complete plan step by step.

Customer message: "{message}"

Think step by step:
1. What is the customer's intent?
2. What information do I need?
3. Which tools should I call, in what order?
4. What are the possible outcomes?
5. What's my response plan for each outcome?

Plan:
"""

# After planning, execute the plan
execution_prompt = """
Based on this plan:
{plan}

And these tool results:
{tool_results}

Generate the final response to the customer.
"""

3️⃣ ReWOO (Reasoning Without Observation) — Plan All, Execute All

ReWOO creates the entire plan upfront, executes all tool calls in parallel, then reasons about the combined results. Much faster than ReAct for independent tool calls.

ReWOO — Plan → Execute All → Reason
📋 Planner
Create full plan
with all tool calls
Worker
Execute ALL
tools in parallel
🧠 Solver
Reason about
all results together
💬 Response
Final answer
to customer
ReWOO — Conceptual Example
# Step 1: Planner (1 LLM call)
Plan:
  #E1 = lookup_order("7845")           ← get order details
  #E2 = get_refund_policy("wrong_item") ← get policy
  #E3 = search_knowledge_base("return process")  ← get FAQ

# Step 2: Worker (all 3 tool calls run IN PARALLEL)
  #E1 result = {status: "delivered", items: ["AirPods Pro"]}
  #E2 result = {policy: "Free return pickup..."}
  #E3 result = "Return process: schedule pickup → refund in 3-5 days"

# Step 3: Solver (1 LLM call with ALL results)
  Given: #E1, #E2, #E3 → Generate final response

# Total LLM calls: 2 (plan + solve) instead of ReAct's 4-6
# Total time: much faster because tools run in parallel!

📊 Reasoning Pattern Comparison

PatternLLM CallsTool ExecutionSpeedBest For
ReActMany (per step)Sequential🐢 SlowestDynamic, dependent steps
CoT + Act2-3 (plan + act)Sequential🐇 MediumComplex reasoning needed
ReWOO2 (plan + solve)Parallel🚀 FastestIndependent tool calls

⚠️ Planning Failure Modes

Agents don't always reason correctly. Common failures:

FailureDescriptionMitigation
Infinite LoopAgent keeps calling the same toolSet max_iterations=5
Wrong ToolAgent picks the wrong toolBetter tool descriptions / fewer tools
Hallucinated ToolAgent tries to call a tool that doesn't existhandle_parsing_errors=True
Missing InfoAgent responds without checking dataPrompt: "ALWAYS use tools before answering"
Over-PlanningAgent plans 10 steps for a 2-step problemPrompt: "Use minimum necessary steps"
Python — Safety Guards
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,
    max_iterations=5,           # Stop after 5 tool calls
    max_execution_time=30,      # Timeout after 30 seconds
    handle_parsing_errors=True, # Recover from malformed output
    early_stopping_method="generate",  # Generate answer if stuck
)
🎧 Use Case Connection

Customer Support — Choosing the Right Pattern

ScenarioBest PatternWhy
"Where is my order?"ReActSimple: 1 tool call, 1 answer
"Wrong item, want refund + replacement"ReActEach step depends on previous (verify order → check policy → decide)
"Compare return vs refund options"ReWOOFetch both policies in parallel, then compare
"Complex complaint — multiple issues"CoT + ActNeed full plan upfront to handle all issues systematically

✅ Key Takeaways

  • ReAct = think-act-observe loop (most common, good for dependent steps)
  • CoT = plan everything first, then execute (good for complex reasoning)
  • ReWOO = plan once, execute in parallel, solve once (fastest for independent calls)
  • Always set safety guards: max_iterations, timeout, error handling
  • The right pattern depends on whether tool calls are dependent or independent

🔨 Hands-On Tasks

  1. Build a ReAct agent with 3 tools and trace the Thought/Action/Observation loop
  2. Implement the same scenario with a CoT-then-Act approach — compare the reasoning
  3. Create a scenario with 3 independent tool calls and implement ReWOO (plan → parallel execute → solve)
  4. Trigger a failure (wrong tool, infinite loop) and verify your safety guards catch it
WEEKEND
Weekend: Build Your First Agent
⏱️ 3-4 hours 💻 100% Hands-On 🎯 End-to-end agent

🎯 Assignment: Customer Support Agent v1

Build a complete, working Customer Support Agent using everything from Week 2. This will be the foundation we enhance in Weeks 3-4.

Requirements

ComponentImplementation
🧠 LLMQwen 2.5:3B via Ollama
🔧 Tools (min 4)lookup_order, get_refund_policy, search_knowledge_base, send_email
💾 MemoryConversationBufferWindowMemory (k=5)
📋 PlanningReAct pattern
📝 PromptSystem prompt with persona + rules + guardrails
🖥️ InterfaceCLI (input loop) or Streamlit chat

Test Scenarios

Test these conversations
# Scenario 1: Order Status
User: "Where is my order #7845?"
→ Agent should call lookup_order and give status

# Scenario 2: Refund Request  
User: "My laptop arrived damaged. Order #7845. I want a refund."
→ Agent should: lookup_order → get_refund_policy("damaged") → offer refund

# Scenario 3: Multi-turn with Memory
User: "My name is Rajesh"
User: "Check order #7845"
User: "What's the refund policy for damaged items?"
User: "Ok process the refund"
→ Agent should remember Rajesh's name throughout

# Scenario 4: Unknown Issue (Escalation)
User: "I want to sue your company for negligence"
→ Agent should NOT attempt to handle legal issues; should escalate

# Scenario 5: General Knowledge
User: "What payment methods do you accept?"
→ Agent should search knowledge base

Bonus Challenges

  1. Add a cancel_order tool that checks if the order is cancellable
  2. Add an escalate_ticket tool that creates a ticket for human review
  3. Implement both CLI and Streamlit interfaces
  4. Add verbose=True logging to a file for debugging
🎯
Success criteria: Your agent should handle all 5 scenarios correctly, use tools appropriately, maintain context across turns, and never hallucinate information it didn't get from tools.
✅ Week 2 Complete — Agent Fundamentals Mastered!

Next week: Week 3 — Building Real Agents → RAG, LangGraph Workflows, Semantic Kernel, Multi-Agent Systems