Module 6.5 — Retrievers & Agents in LangChain

Advanced retrieval patterns and autonomous agents — the final piece of Phase 6


What This Module Covers

Advanced Retrievers → get better results from your vector store
LangChain Agents    → LLMs that think, plan, and take actions

These are the two most powerful concepts in LangChain. By the end you'll understand how production RAG systems go beyond simple similarity search — and how agents work differently from chains.


Part 1 — Advanced Retrievers

Why Basic Retrieval Isn't Always Enough

Your current RAG system does this:

User question → embed → find similar chunks → inject into prompt

This works well. But it has limitations:

Problem 1 — One query, one perspective
"What are the benefits of RAG?"
→ Only finds chunks that literally discuss "benefits"
→ Misses chunks that discuss advantages, pros, improvements

Problem 2 — Irrelevant sentences in relevant chunks
A chunk might be 80% relevant and 20% noise
→ That 20% noise confuses the LLM

Problem 3 — User question is vague or badly phrased
"Tell me about the thing we discussed earlier"
→ Terrible embedding → terrible retrieval → bad answer

LangChain has specific retrievers that solve each problem.


Create src/05_retrievers.js:


    import { OpenAIEmbeddings, ChatOpenAI } from "@langchain/openai";
    import { MemoryVectorStore } from "@langchain/classic/vectorstores/memory";
    import { PDFLoader } from "@langchain/community/document_loaders/fs/pdf";
    import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";
    import { MultiQueryRetriever } from "@langchain/classic/retrievers/multi_query";
    // MultiQueryRetriever = generates multiple versions of the question
    // searches with all versions → merges results
    // solves Problem 1

    import { ContextualCompressionRetriever } from "@langchain/classic/retrievers/contextual_compression";
    // ContextualCompressionRetriever = compresses retrieved chunks
    // removes irrelevant sentences from each chunk
    // solves Problem 2

    import { LLMChainExtractor } from "@langchain/classic/retrievers/document_compressors/chain_extract";
    // LLMChainExtractor = uses LLM to extract only relevant parts
    // from each retrieved chunk

    import { Document } from "@langchain/core/documents";
    // Document = LangChain's document class
    // { pageContent, metadata }

    import * as dotenv from "dotenv";
    dotenv.config();


    // ─────────────────────────────────────────
    // SETUP — Build vector store once
    // Reuse across all examples
    // ─────────────────────────────────────────

    async function buildVectorStore() {
        console.log("Setting up vector store...");

        const loader = new PDFLoader("./sample.pdf");
        const docs = await loader.load();

        const splitter = new RecursiveCharacterTextSplitter({
            chunkSize: 600,
            chunkOverlap: 100,
        });
        const chunks = await splitter.splitDocuments(docs);

        const vectorStore = await MemoryVectorStore.fromDocuments(
            chunks,
            new OpenAIEmbeddings({ model: "text-embedding-3-small" })
        );

        console.log(`✅ Vector store ready — ${chunks.length} chunks\n`);
        return vectorStore;
    }

    const llm = new ChatOpenAI({ model: "gpt-4o", temperature: 0 });


    // ─────────────────────────────────────────
    // RETRIEVER 1 — Basic retriever (baseline)
    // The simple retriever you already know
    // ─────────────────────────────────────────

    async function retriever1(vectorStore) {
        console.log("".repeat(55));
        console.log("RETRIEVER 1: Basic similarity search (baseline)");
        console.log("".repeat(55));

        const retriever = vectorStore.asRetriever({ k: 3 });
        // basic retriever — searches with the EXACT question as-is

        const question = "What are the main benefits?";
        console.log(`Question: "${question}"\n`);

        const docs = await retriever.invoke(question);
        // embeds question once → finds top 3 similar chunks

        console.log(`Retrieved: ${docs.length} chunks`);
        docs.forEach((doc, i) => {
            console.log(`  Chunk ${i + 1}: ${doc.pageContent.substring(0, 80)}...`);
        });

        console.log("\nLimitation: Only searched for this ONE version of the question");
        console.log("Might miss relevant content phrased differently.\n");
    }


    // ─────────────────────────────────────────
    // RETRIEVER 2 — MultiQueryRetriever
    // Generates multiple versions of the question
    // Searches with ALL versions — merges unique results
    // Solves: "missing relevant content" problem
    // ─────────────────────────────────────────

    async function retriever2(vectorStore) {
        console.log("".repeat(55));
        console.log("RETRIEVER 2: MultiQueryRetriever");
        console.log("".repeat(55));

        const baseRetriever = vectorStore.asRetriever({ k: 3 });

        const multiQueryRetriever = MultiQueryRetriever.fromLLM({
            llm: llm,
            // LLM used to generate alternative questions

            retriever: baseRetriever,
            // the underlying retriever to use for each question

            queryCount: 3,
            // generate 3 alternative versions of the question
            // then search with all 3 — merge unique results
        });

        const question = "What are the main benefits?";
        console.log(`Original question: "${question}"\n`);
        console.log("MultiQueryRetriever will generate ~3 alternative versions:");
        console.log('  e.g. "What advantages does this provide?"');
        console.log('  e.g. "What are the positive aspects?"');
        console.log('  e.g. "What value does this offer?"');
        console.log("Then search with ALL versions → merge unique results\n");

        const docs = await multiQueryRetriever.invoke(question);
        // internally generates multiple questions
        // searches with each → deduplicates results
        // returns combined unique documents

        console.log(`Retrieved: ${docs.length} unique chunks (vs 3 with basic retriever)`);
        docs.forEach((doc, i) => {
            console.log(`  Chunk ${i + 1}: ${doc.pageContent.substring(0, 80)}...`);
        });

        console.log("\nBenefit: Finds more relevant content by searching");
        console.log("from multiple angles.\n");
    }


    // ─────────────────────────────────────────
    // RETRIEVER 3 — ContextualCompressionRetriever
    // Compresses retrieved chunks — removes irrelevant parts
    // Solves: "noisy chunks" problem
    // ─────────────────────────────────────────

    async function retriever3(vectorStore) {
        console.log("".repeat(55));
        console.log("RETRIEVER 3: ContextualCompressionRetriever");
        console.log("".repeat(55));

        const baseRetriever = vectorStore.asRetriever({ k: 3 });

        // Create the compressor — uses LLM to extract relevant parts
        const compressor = LLMChainExtractor.fromLLM(llm);
        // LLMChainExtractor looks at each retrieved chunk
        // and extracts ONLY the sentences relevant to the question
        // discards irrelevant sentences from the chunk

        const compressionRetriever = new ContextualCompressionRetriever({
            baseCompressor: compressor,
            // the LLM-based compressor

            baseRetriever: baseRetriever,
            // retrieve chunks first with base retriever
            // then compress each chunk with the compressor
        });

        const question = "What are the key risks mentioned?";
        console.log(`Question: "${question}"\n`);

        console.log("Process:");
        console.log("1. Retrieve top 3 chunks (some may be partially relevant)");
        console.log("2. For each chunk — LLM extracts only relevant sentences");
        console.log("3. Return compressed, focused content\n");

        const compressedDocs = await compressionRetriever.invoke(question);
        // Step 1: basic retriever gets 3 chunks
        // Step 2: LLMChainExtractor compresses each chunk
        // Step 3: returns only the relevant parts

        console.log(`Compressed results: ${compressedDocs.length} chunks`);
        compressedDocs.forEach((doc, i) => {
            console.log(`\n  Compressed chunk ${i + 1}:`);
            console.log(`  "${doc.pageContent}"`);
            // much shorter than original — only relevant sentences kept
        });

        console.log("\nBenefit: Less noise in context → LLM gives more focused answers.\n");
        console.log("Tradeoff: Slower (extra LLM call per chunk) + costs more.\n");
    }


    // ─────────────────────────────────────────
    // RETRIEVER 4 — Ensemble (combine retrievers)
    // Use multiple retrievers and merge results
    // Good for: keyword AND semantic search combined
    // ─────────────────────────────────────────

    async function retriever4(vectorStore) {
        console.log("".repeat(55));
        console.log("RETRIEVER 4: Using retrievers with different settings");
        console.log("".repeat(55));

        // Retriever A — semantic similarity (good for meaning)
        const semanticRetriever = vectorStore.asRetriever({
            k: 3,
            searchType: "similarity",
        });

        // Retriever B — MMR (good for diversity)
        const diverseRetriever = vectorStore.asRetriever({
            k: 3,
            searchType: "mmr",
            searchKwargs: { fetchK: 10, lambda: 0.5 },
        });

        const question = "What are the main concepts?";
        console.log(`Question: "${question}"\n`);

        const semanticDocs = await semanticRetriever.invoke(question);
        const diverseDocs = await diverseRetriever.invoke(question);

        // Merge and deduplicate results
        const allContent = new Set();
        const merged = [];

        [...semanticDocs, ...diverseDocs].forEach(doc => {
            const key = doc.pageContent.substring(0, 50);
            // use first 50 chars as dedup key
            if (!allContent.has(key)) {
                allContent.add(key);
                merged.push(doc);
            }
        });

        console.log(`Semantic retriever: ${semanticDocs.length} chunks`);
        console.log(`Diverse retriever: ${diverseDocs.length} chunks`);
        console.log(`After merge+dedup: ${merged.length} unique chunks`);

        merged.forEach((doc, i) => {
            console.log(`  ${i + 1}. ${doc.pageContent.substring(0, 70)}...`);
        });

        console.log("\nBenefit: Combines relevance + diversity for richer context.\n");
    }


    // ─────────────────────────────────────────
    // CHOOSING THE RIGHT RETRIEVER
    // ─────────────────────────────────────────

    function retrieverGuide() {
        console.log("".repeat(55));
        console.log("GUIDE: Which retriever to use?");
        console.log("".repeat(55));

        console.log(`
    Basic Retriever
    → Use when: questions are clear and specific
    → Cost: cheapest (no extra LLM calls)
    → Speed: fastest

    MultiQueryRetriever
    → Use when: questions might be vague or have synonyms
    → Cost: moderate (generates N alternative questions)
    → Speed: moderate
    → Best for: general purpose RAG chatbots

    ContextualCompressionRetriever
    → Use when: chunks are long and might contain noise
    → Cost: expensive (one LLM call per chunk)
    → Speed: slow
    → Best for: high-accuracy requirements (legal, medical)

    MMR Retriever
    → Use when: answers need diverse information
    → Cost: same as basic (no extra LLM calls)
    → Speed: slightly slower than basic
    → Best for: summarization, broad questions

    Recommendation for most apps:
    → Start with MMR retriever
    → Add MultiQuery if retrieval quality is poor
    → Add Compression only if answer quality needs boost
    and you can afford the extra cost
    `);
    }


    async function main() {
        console.log("\n🔍 ADVANCED RETRIEVERS DEMO\n");

        const vectorStore = await buildVectorStore();

        await retriever1(vectorStore);
        await retriever2(vectorStore);
        await retriever3(vectorStore);
        await retriever4(vectorStore);
        retrieverGuide();

        console.log("✅ Retrievers demo complete!");
    }

    main().catch(console.error);

Run:

node src/05_retrievers.js

Part 2 — LangChain Agents

Chain vs Agent — The Key Difference

CHAIN:
Fixed sequence of steps
Input → Step 1 → Step 2 → Step 3 → Output
You decide the steps at build time
Same steps every time

AGENT:
LLM decides the steps at runtime
Input → LLM thinks → maybe tool → LLM thinks → maybe tool → Output
Steps change based on the question
Can handle questions you didn't plan for
Chain example — always does these steps:
embed question → search → build prompt → call LLM

Agent example — decides at runtime:
"This needs a calculator and a web search"
→ calls calculator
→ calls web search
→ combines results
→ generates answer

Different question → different tools → different steps

Create src/06_agents.js:


    import { ChatOpenAI } from "@langchain/openai";
    import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts";
    import { AgentExecutor, createToolCallingAgent } from "@langchain/classic/agents";
    import { tool } from "@langchain/core/tools";
    import { z } from "zod";
    import { RunnableWithMessageHistory } from "@langchain/core/runnables";
    import { InMemoryChatMessageHistory } from "@langchain/core/chat_history";
    import * as readline from "readline";
    import * as dotenv from "dotenv";
    dotenv.config();

    const llm = new ChatOpenAI({ model: "gpt-4o", temperature: 0 });


    // ─────────────────────────────────────────
    // DEFINE TOOLS
    // ─────────────────────────────────────────

    // Tool 1 — Calculator
    const calculator = tool(
        async ({ expression }) => {
            try {
                // Simple math evaluator
                // In production use a proper math library
                const sanitized = expression
                    .replace(/[^0-9+\-*/().% ]/g, "")
                    // remove anything that's not math
                    .trim();

                const result = Function(`"use strict"; return (${sanitized})`)();
                // evaluate the math expression
                // Function() = safer than eval() for simple math

                return `${expression} = ${result}`;
            } catch {
                return `Could not calculate: ${expression}`;
            }
        },
        {
            name: "calculator",
            description: `Evaluates math expressions.
            Use for any calculation — addition, subtraction, multiplication,
            division, percentages. Input should be a valid math expression.
            Examples: "245 * 18", "1500 / 12", "100 * 0.15"`,
            schema: z.object({
                expression: z.string()
                    .describe("math expression to evaluate, e.g. '245 * 18'"),
            }),
        }
    );


    // Tool 2 — Unit converter
    const unitConverter = tool(
        async ({ value, fromUnit, toUnit }) => {
            const conversions = {
                // length
                "km_to_miles": (v) => v * 0.621371,
                "miles_to_km": (v) => v * 1.60934,
                "m_to_feet": (v) => v * 3.28084,
                "feet_to_m": (v) => v * 0.3048,
                // weight
                "kg_to_lbs": (v) => v * 2.20462,
                "lbs_to_kg": (v) => v * 0.453592,
                // temperature
                "c_to_f": (v) => (v * 9 / 5) + 32,
                "f_to_c": (v) => (v - 32) * 5 / 9,
                // data
                "gb_to_mb": (v) => v * 1024,
                "mb_to_gb": (v) => v / 1024,
            };

            const key = `${fromUnit.toLowerCase()}_to_${toUnit.toLowerCase()}`;
            const fn = conversions[key];

            if (!fn) return `Conversion from ${fromUnit} to ${toUnit} not supported`;

            const result = fn(value).toFixed(4);
            return `${value} ${fromUnit} = ${result} ${toUnit}`;
        },
        {
            name: "unit_converter",
            description: `Converts between units.
            Supported: km/miles, m/feet, kg/lbs, c/f (celsius/fahrenheit), gb/mb.
            Use when asked to convert measurements.`,
            schema: z.object({
                value: z.number().describe("the number to convert"),
                fromUnit: z.string().describe("source unit e.g. 'km', 'kg', 'c'"),
                toUnit: z.string().describe("target unit e.g. 'miles', 'lbs', 'f'"),
            }),
        }
    );


    // Tool 3 — Dictionary / definition
    const dictionary = tool(
        async ({ word }) => {
            // Simulated dictionary — in production call a real dictionary API
            const definitions = {
                "RAG": "Retrieval Augmented Generation — a technique that combines LLMs with document retrieval to ground responses in real information",
                "embedding": "A numerical representation of text as a vector of floating point numbers, capturing semantic meaning",
                "hallucination": "When an AI model generates confident but factually incorrect information",
                "transformer": "A neural network architecture using self-attention mechanisms, the foundation of modern LLMs",
                "vector": "A list of numbers representing a point in multi-dimensional space",
                "token": "A unit of text used by LLMs — roughly 4 characters or 0.75 words in English",
                "fine-tuning": "Additional training of a pre-trained model on specific data to adapt its behavior",
                "inference": "Running a trained model to generate outputs — the process of actually using an LLM",
            };

            const def = definitions[word.toUpperCase()] || definitions[word.toLowerCase()];
            if (def) return `${word}: ${def}`;
            return `Definition not found for "${word}" in this dictionary`;
        },
        {
            name: "dictionary",
            description: `Looks up definitions of AI and tech terms.
            Use when asked to define or explain a technical term.
            Covers: RAG, embedding, hallucination, transformer, vector, token, fine-tuning, inference`,
            schema: z.object({
                word: z.string().describe("the word or term to define"),
            }),
        }
    );


    // Tool 4 — Text analyzer
    const textAnalyzer = tool(
        async ({ text, analysisType }) => {
            switch (analysisType) {
                case "word_count":
                    const words = text.trim().split(/\s+/).length;
                    const chars = text.length;
                    const sentences = text.split(/[.!?]+/).filter(s => s.trim()).length;
                    return `Word count: ${words} | Characters: ${chars} | Sentences: ${sentences}`;

                case "reading_time":
                    const wordCount = text.trim().split(/\s+/).length;
                    const minutes = Math.ceil(wordCount / 200);
                    // average reading speed = 200 words per minute
                    return `Estimated reading time: ${minutes} minute(s) (${wordCount} words at 200 wpm)`;

                case "sentiment":
                    const positiveWords = ["good", "great", "excellent", "amazing", "best", "love", "perfect"];
                    const negativeWords = ["bad", "terrible", "awful", "worst", "hate", "poor", "horrible"];
                    const lower = text.toLowerCase();
                    const posCount = positiveWords.filter(w => lower.includes(w)).length;
                    const negCount = negativeWords.filter(w => lower.includes(w)).length;

                    if (posCount > negCount) return `Sentiment: POSITIVE (${posCount} positive signals)`;
                    if (negCount > posCount) return `Sentiment: NEGATIVE (${negCount} negative signals)`;
                    return "Sentiment: NEUTRAL";

                default:
                    return "Unknown analysis type";
            }
        },
        {
            name: "text_analyzer",
            description: `Analyzes text for various properties.
            Use for: counting words, estimating reading time, detecting sentiment.
            Analysis types: 'word_count', 'reading_time', 'sentiment'`,
            schema: z.object({
                text: z.string().describe("the text to analyze"),
                analysisType: z.enum(["word_count", "reading_time", "sentiment"])
                    .describe("type of analysis to perform"),
            }),
        }
    );


    // ─────────────────────────────────────────
    // BUILD AGENT
    // ─────────────────────────────────────────

    const tools = [calculator, unitConverter, dictionary, textAnalyzer];

    const agentPrompt = ChatPromptTemplate.fromMessages([
        ["system", `You are a helpful AI assistant with access to tools.

            Guidelines:
            - Use tools whenever they would improve accuracy
            - Always use calculator for any math — never calculate in your head
            - Use unit_converter for any unit conversions
            - Use dictionary to define technical terms
            - Use text_analyzer for text statistics
            - If multiple tools are needed — use them all
            - After using tools — give a clear, natural response`],

        new MessagesPlaceholder("chat_history"),
        // conversation history for memory

        ["human", "{input}"],
        // current user message

        new MessagesPlaceholder("agent_scratchpad"),
        // LangChain uses this internally for tool call tracking
    ]);

    const agent = createToolCallingAgent({ llm, tools, prompt: agentPrompt });

    const agentExecutor = new AgentExecutor({
        agent,
        tools,
        verbose: false,
        // false = don't print internal thinking steps
        // set to true during development to see what agent is doing
        maxIterations: 5,
        // maximum tool calls per question
        // prevents infinite loops if agent gets confused
    });


    // ─────────────────────────────────────────
    // ADD MEMORY TO AGENT
    // ─────────────────────────────────────────

    const messageHistories = {};

    const agentWithMemory = new RunnableWithMessageHistory({
        runnable: agentExecutor,
        getMessageHistory: (sessionId) => {
            if (!messageHistories[sessionId]) {
                messageHistories[sessionId] = new InMemoryChatMessageHistory();
            }
            return messageHistories[sessionId];
        },
        inputMessagesKey: "input",
        historyMessagesKey: "chat_history",
        outputMessagesKey: "output",
    });


    // ─────────────────────────────────────────
    // DEMO 1 — Test agent with preset questions
    // ─────────────────────────────────────────

    async function runDemo() {
        console.log("\n" + "=".repeat(55));
        console.log("🤖 LANGCHAIN AGENT DEMO");
        console.log("=".repeat(55) + "\n");

        const testQuestions = [
            "What is 847 multiplied by 63, then divided by 7?",
            "Convert 5.5 km to miles and also 72 kg to pounds",
            "Define 'embedding' and 'hallucination'",
            "How many words are in this sentence: 'The quick brown fox jumps over the lazy dog'? Also what's the reading time?",
            "What is 15% of 24000? And is this sentence positive or negative: 'This is a terrible and awful experience'",
        ];

        for (const question of testQuestions) {
            console.log("".repeat(55));
            console.log(`Q: ${question}`);
            console.log("".repeat(55));

            try {
                const result = await agentWithMemory.invoke(
                    { input: question },
                    { configurable: { sessionId: "demo" } }
                );
                console.log(`A: ${result.output}\n`);
            } catch (error) {
                console.log(`Error: ${error.message}\n`);
            }
        }
    }


    // ─────────────────────────────────────────
    // DEMO 2 — Interactive agent chat
    // ─────────────────────────────────────────

    async function interactiveDemo() {
        const rl = readline.createInterface({
            input: process.stdin,
            output: process.stdout,
        });

        function prompt(q) {
            return new Promise(resolve => rl.question(q, resolve));
        }

        console.log("\n" + "=".repeat(55));
        console.log("💬 INTERACTIVE AGENT CHAT");
        console.log('   Type "quit" to exit');
        console.log("=".repeat(55) + "\n");

        const sessionId = `session_${Date.now()}`;
        // unique session ID for this conversation

        while (true) {
            const input = await prompt("You: ");
            const trimmed = input.trim();

            if (!trimmed) continue;

            if (trimmed.toLowerCase() === "quit") {
                console.log("\n👋 Goodbye!\n");
                rl.close();
                break;
            }

            try {
                const result = await agentWithMemory.invoke(
                    { input: trimmed },
                    { configurable: { sessionId } }
                );
                console.log(`\nAgent: ${result.output}\n`);
            } catch (error) {
                console.log(`\nError: ${error.message}\n`);
            }
        }
    }


    // ─────────────────────────────────────────
    // MAIN
    // ─────────────────────────────────────────

    async function main() {
        const mode = process.argv[2] || "demo";
        // node src/06_agents.js demo        → run preset questions
        // node src/06_agents.js interactive → chat with agent

        if (mode === "interactive") {
            await interactiveDemo();
        } else {
            await runDemo();
        }
    }

    main().catch(console.error);

Run demo mode:

node src/06_agents.js demo

Run interactive mode:

node src/06_agents.js interactive

Chain vs Agent — When to Use Which

Use a CHAIN when:
→ You know exactly what steps are needed
→ Same steps every time
→ Need predictable, fast execution
→ Building RAG — question → retrieve → answer
→ Building classifiers, extractors, summarizers
→ Production apps where reliability matters

Use an AGENT when:
→ Steps depend on the question
→ Need to use different tools based on input
→ Tasks require multi-step reasoning
→ Building assistants that handle varied requests
→ Combining multiple data sources dynamically
→ You need "I don't know which tool I'll need" flexibility

Real world examples:
Chain  → PDF chatbot, support ticket classifier, summarizer
Agent  → Personal assistant, research tool, code helper

The Complete LangChain Picture

LangChain Components You Now Know:

LOADING DATA:
  PDFLoader, TextLoader, WebLoader → Document objects

SPLITTING DATA:
  RecursiveCharacterTextSplitter → smaller Document chunks

EMBEDDING:
  OpenAIEmbeddings → vectors for each chunk

STORING:
  MemoryVectorStore → searchable vector storage

RETRIEVING:
  Basic retriever → simple similarity search
  MultiQueryRetriever → multiple question angles
  CompressionRetriever → noise removal from chunks
  MMR retriever → diverse results

GENERATING:
  ChatOpenAI → call GPT-4o
  ChatPromptTemplate → structured prompts
  StringOutputParser → clean string output
  StructuredOutputParser → typed JSON output

CHAINING:
  LCEL pipe operator → connect components
  RunnableSequence → multi-step pipelines
  RunnablePassthrough → pass data through

MEMORY:
  ChatMessageHistory → store conversations
  RunnableWithMessageHistory → wrap chain with memory

AGENTS:
  tool() → define callable functions
  createToolCallingAgent → LLM + tools
  AgentExecutor → run the agent loop

3-Line Summary

  1. Advanced retrievers solve specific problems — MultiQueryRetriever generates alternative phrasings of questions to find more relevant content, ContextualCompressionRetriever removes noise from retrieved chunks, and MMR retriever balances relevance with diversity.
  2. Agents differ from chains in that they decide at runtime which tools to call and in what order — while chains always follow the same fixed steps, agents reason about each question and dynamically choose their approach.
  3. Use chains when steps are predictable and reliability matters (RAG, classifiers, extractors), use agents when tasks are varied and require dynamic tool selection (personal assistants, research tools, multi-step reasoning).

Module 6.5 — Complete ✅

Phase 6 is done. 🎉

✅ Module 6.1 — What is LangChain & what problem it solves
✅ Module 6.2 — Models, Prompts & Chains
✅ Module 6.3 — Output Parsers, Memory & Tools
✅ Module 6.4 — Document Loaders, Text Splitters & Vector Stores
✅ Module 6.5 — Retrievers & Agents

Course Progress

✅ Phase 1 — AI Foundations
✅ Phase 2 — LLM Internals
✅ Phase 3 — Embeddings
✅ Phase 4 — Vector Databases
✅ Phase 5 — RAG (+ PDF Chatbot project)
✅ Phase 6 — LangChain

Coming up → Phase 7: Advanced LangChain
Coming up → Phase 8: AI Agents

Coming Up — Phase 7: Advanced LangChain

Module 7.1 — Production RAG System

We build a production-grade RAG system with LangChain — proper error handling, streaming responses, source citations, conversation history, and a clean API that you could actually deploy. This is portfolio-level work.

No comments:

Post a Comment

Module 6.5 — Retrievers & Agents in LangChain

Advanced retrieval patterns and autonomous agents — the final piece of Phase 6 What This Module Covers Advanced Retrievers → get better r...