Vedat Erenoglu

n8n, LangChain, and RAG: A Developer's Guide

image

n8n, LangChain, and RAG: A Developer's Guide

Introduction

This guide provides a comprehensive understanding of three powerful technologies that, when combined, can create sophisticated AI-powered automation systems. Whether you're building intelligent chatbot, automated content pipelines, or knowledge management systems, understanding these tools and their relationships is crucial.

Why These Technologies Matter

  • n8n: Eliminates manual processes and connects disparate systems
  • LangChain: Makes LLM integration accessible and manageable
  • RAG: Ensures AI responses are accurate, relevant, and grounded in facts

n8n - Workflow Automation

What is n8n?

n8n (pronounced "nodemation") is an open-source workflow automation platform that enables you to connect anything to everything. Think of it as a visual programming environment for APIs and services.

Core Concepts

1. Nodes

Individual building blocks that perform specific actions:

<span class="hljs-comment">// Example: HTTP Request Node Configuration</span>
{
  <span class="hljs-string">&quot;method&quot;</span>: <span class="hljs-string">&quot;POST&quot;</span>,
  <span class="hljs-string">&quot;url&quot;</span>: <span class="hljs-string">&quot;https://api.example.com/data&quot;</span>,
  <span class="hljs-string">&quot;authentication&quot;</span>: <span class="hljs-string">&quot;bearer&quot;</span>,
  <span class="hljs-string">&quot;headers&quot;</span>: {
    <span class="hljs-string">&quot;Content-Type&quot;</span>: <span class="hljs-string">&quot;application/json&quot;</span>
  }
}

2. Workflows

Sequences of connected nodes that execute in order:

  • Trigger Nodes: Start workflows (webhooks, schedules, manual triggers)
  • Action Nodes: Perform operations (HTTP requests, database queries, transformations)
  • Logic Nodes: Control flow (IF conditions, loops, merge operations)

3. Expressions

Dynamic values using JavaScript-like syntax:

<span class="hljs-comment">// Access previous node&#x27;s data</span>
{{ $node[<span class="hljs-string">&quot;HTTP Request&quot;</span>].<span class="hljs-property">json</span>.<span class="hljs-property">userId</span> }}

<span class="hljs-comment">// Use built-in functions</span>
{{ $now.<span class="hljs-title function_">format</span>(<span class="hljs-string">&#x27;yyyy-MM-dd&#x27;</span>) }}

<span class="hljs-comment">// Conditional logic</span>
{{ $json.<span class="hljs-property">status</span> === <span class="hljs-string">&#x27;active&#x27;</span> ? <span class="hljs-string">&#x27;Process&#x27;</span> : <span class="hljs-string">&#x27;Skip&#x27;</span> }}

Real-World Example: E-commerce Order Processing

<span class="hljs-attr">Workflow:</span> <span class="hljs-string">Order</span> <span class="hljs-string">Processing</span> <span class="hljs-string">Automation</span>
<span class="hljs-attr">Triggers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">Webhook</span> <span class="hljs-string">from</span> <span class="hljs-string">Shopify</span> <span class="hljs-string">(new</span> <span class="hljs-string">order)</span>

<span class="hljs-attr">Steps:</span>
  <span class="hljs-number">1</span><span class="hljs-string">.</span> <span class="hljs-string">Validate</span> <span class="hljs-string">order</span> <span class="hljs-string">data</span>
  <span class="hljs-number">2</span><span class="hljs-string">.</span> <span class="hljs-string">Check</span> <span class="hljs-string">inventory</span> <span class="hljs-string">in</span> <span class="hljs-string">PostgreSQL</span>
  <span class="hljs-number">3</span><span class="hljs-string">.</span> <span class="hljs-string">Send</span> <span class="hljs-string">to</span> <span class="hljs-string">fulfillment</span> <span class="hljs-string">center</span> <span class="hljs-string">API</span>
  <span class="hljs-number">4</span><span class="hljs-string">.</span> <span class="hljs-string">Update</span> <span class="hljs-string">CRM</span> <span class="hljs-string">(Salesforce)</span>
  <span class="hljs-number">5</span><span class="hljs-string">.</span> <span class="hljs-string">Send</span> <span class="hljs-string">confirmation</span> <span class="hljs-string">email</span> <span class="hljs-string">(SendGrid)</span>
  <span class="hljs-number">6</span><span class="hljs-string">.</span> <span class="hljs-string">Post</span> <span class="hljs-string">to</span> <span class="hljs-string">Slack</span> <span class="hljs-string">channel</span>
  <span class="hljs-number">7</span><span class="hljs-string">.</span> <span class="hljs-string">Generate</span> <span class="hljs-string">invoice</span> <span class="hljs-string">PDF</span>
  <span class="hljs-number">8</span><span class="hljs-string">.</span> <span class="hljs-string">Upload</span> <span class="hljs-string">to</span> <span class="hljs-string">Google</span> <span class="hljs-string">Drive</span>

n8n Best Practices

  1. Error Handling
<span class="hljs-comment">// Always implement error workflows</span>
{
  <span class="hljs-string">&quot;errorWorkflow&quot;</span>: <span class="hljs-string">&quot;workflow_error_handler_id&quot;</span>,
  <span class="hljs-string">&quot;continueOnFail&quot;</span>: <span class="hljs-literal">false</span>,
  <span class="hljs-string">&quot;retry&quot;</span>: {
    <span class="hljs-string">&quot;maxRetries&quot;</span>: <span class="hljs-number">3</span>,
    <span class="hljs-string">&quot;waitBetweenRetries&quot;</span>: <span class="hljs-number">5000</span>
  }
}
  1. Use Environment Variables
<span class="hljs-comment"># .env file</span>
API_KEY=your-secure-key
DATABASE_URL=postgresql://...
  1. Modular Workflows
  • Break complex workflows into sub-workflows
  • Use the "Execute Workflow" node for reusability

LangChain - LLM Application Framework

What is LangChain?

LangChain is a framework designed to simplify the creation of applications using large language models. It provides abstractions for common patterns in LLM development.

Core Components

1. Models

Interface with various LLMs:

<span class="hljs-keyword">from</span> langchain.llms <span class="hljs-keyword">import</span> OpenAI, Anthropic, HuggingFace

<span class="hljs-comment"># OpenAI GPT</span>
llm = OpenAI(
    temperature=<span class="hljs-number">0.7</span>,
    model=<span class="hljs-string">&quot;gpt-4&quot;</span>,
    max_tokens=<span class="hljs-number">1000</span>
)

<span class="hljs-comment"># Anthropic Claude</span>
llm = Anthropic(
    model=<span class="hljs-string">&quot;claude-3-sonnet&quot;</span>,
    temperature=<span class="hljs-number">0.5</span>
)

2. Prompts

Structured prompt templates:

<span class="hljs-keyword">from</span> langchain.prompts <span class="hljs-keyword">import</span> PromptTemplate

template = <span class="hljs-string">&quot;&quot;&quot;
You are a {role} assistant for {company}.
Context: {context}
Question: {question}
Answer in {tone} tone:
&quot;&quot;&quot;</span>

prompt = PromptTemplate(
    input_variables=[<span class="hljs-string">&quot;role&quot;</span>, <span class="hljs-string">&quot;company&quot;</span>, <span class="hljs-string">&quot;context&quot;</span>, <span class="hljs-string">&quot;question&quot;</span>, <span class="hljs-string">&quot;tone&quot;</span>],
    template=template
)

3. Chains

Combine components into sequences:

<span class="hljs-keyword">from</span> langchain.chains <span class="hljs-keyword">import</span> LLMChain, SimpleSequentialChain

<span class="hljs-comment"># Single chain</span>
chain = LLMChain(llm=llm, prompt=prompt)

<span class="hljs-comment"># Sequential chains</span>
analysis_chain = LLMChain(llm=llm, prompt=analysis_prompt)
summary_chain = LLMChain(llm=llm, prompt=summary_prompt)

overall_chain = SimpleSequentialChain(
    chains=[analysis_chain, summary_chain]
)

4. Memory

Maintain conversation context:

<span class="hljs-keyword">from</span> langchain.memory <span class="hljs-keyword">import</span> ConversationBufferMemory

memory = ConversationBufferMemory(
    memory_key=<span class="hljs-string">&quot;chat_history&quot;</span>,
    return_messages=<span class="hljs-literal">True</span>
)

conversation = ConversationChain(
    llm=llm,
    memory=memory,
    verbose=<span class="hljs-literal">True</span>
)

5. Agents

Autonomous decision-making:

<span class="hljs-keyword">from</span> langchain.agents <span class="hljs-keyword">import</span> create_sql_agent
<span class="hljs-keyword">from</span> langchain.agents.agent_toolkits <span class="hljs-keyword">import</span> SQLDatabaseToolkit

toolkit = SQLDatabaseToolkit(db=db, llm=llm)
agent = create_sql_agent(
    llm=llm,
    toolkit=toolkit,
    verbose=<span class="hljs-literal">True</span>,
    agent_type=AgentType.ZERO_SHOT_REACT_DESCRIPTION
)

<span class="hljs-comment"># Agent can now answer questions about your database</span>
result = agent.run(<span class="hljs-string">&quot;What were the top 5 selling products last month?&quot;</span>)

Real-World Example: Customer Support Chatbot

<span class="hljs-comment"># Complete implementation</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">CustomerSupportBot</span>:
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self</span>):
        <span class="hljs-variable language_">self</span>.llm = OpenAI(temperature=<span class="hljs-number">0.3</span>)

        <span class="hljs-comment"># Knowledge base loader</span>
        <span class="hljs-variable language_">self</span>.loader = DirectoryLoader(
            <span class="hljs-string">&#x27;support_docs/&#x27;</span>,
            glob=<span class="hljs-string">&quot;**/*.md&quot;</span>
        )

        <span class="hljs-comment"># Vector store for similarity search</span>
        <span class="hljs-variable language_">self</span>.vectorstore = Chroma.from_documents(
            documents=<span class="hljs-variable language_">self</span>.loader.load(),
            embedding=OpenAIEmbeddings()
        )

        <span class="hljs-comment"># QA chain with retrieval</span>
        <span class="hljs-variable language_">self</span>.qa_chain = RetrievalQA.from_chain_type(
            llm=<span class="hljs-variable language_">self</span>.llm,
            retriever=<span class="hljs-variable language_">self</span>.vectorstore.as_retriever(),
            return_source_documents=<span class="hljs-literal">True</span>
        )

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">answer_question</span>(<span class="hljs-params">self, question</span>):
        result = <span class="hljs-variable language_">self</span>.qa_chain({<span class="hljs-string">&quot;query&quot;</span>: question})
        <span class="hljs-keyword">return</span> {
            <span class="hljs-string">&quot;answer&quot;</span>: result[<span class="hljs-string">&quot;result&quot;</span>],
            <span class="hljs-string">&quot;sources&quot;</span>: result[<span class="hljs-string">&quot;source_documents&quot;</span>]
        }

LangChain Best Practices

  1. Token Management
<span class="hljs-comment"># Monitor token usage</span>
<span class="hljs-keyword">from</span> langchain.callbacks <span class="hljs-keyword">import</span> get_openai_callback

<span class="hljs-keyword">with</span> get_openai_callback() <span class="hljs-keyword">as</span> cb:
    result = llm(<span class="hljs-string">&quot;What is the meaning of life?&quot;</span>)
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;Tokens used: <span class="hljs-subst">{cb.total_tokens}</span>&quot;</span>)
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;Cost: $<span class="hljs-subst">{cb.total_cost}</span>&quot;</span>)
  1. Caching Strategies
<span class="hljs-keyword">from</span> langchain.cache <span class="hljs-keyword">import</span> InMemoryCache
<span class="hljs-keyword">import</span> langchain

<span class="hljs-comment"># Enable caching</span>
langchain.llm_cache = InMemoryCache()
  1. Error Recovery
<span class="hljs-keyword">from</span> tenacity <span class="hljs-keyword">import</span> retry, stop_after_attempt, wait_exponential

<span class="hljs-meta">@retry(<span class="hljs-params">
    stop=stop_after_attempt(<span class="hljs-params"><span class="hljs-number">3</span></span>),
    wait=wait_exponential(<span class="hljs-params">multiplier=<span class="hljs-number">1</span>, <span class="hljs-built_in">min</span>=<span class="hljs-number">4</span>, <span class="hljs-built_in">max</span>=<span class="hljs-number">60</span></span>)
</span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">safe_llm_call</span>(<span class="hljs-params">prompt</span>):
    <span class="hljs-keyword">return</span> llm(prompt)

RAG - Retrieval-Augmented Generation

What is RAG?

RAG combines the power of retrieval systems with generative AI models. Instead of relying solely on the model's training data, RAG fetches relevant information from a knowledge base to generate more accurate, contextual responses.

How RAG Works

graph LR
    A[User Query] --&gt; B[Embedding Model]
    B --&gt; C[Vector Search]
    C --&gt; D[Knowledge Base]
    D --&gt; E[Relevant Documents]
    E --&gt; F[Context + Query]
    F --&gt; G[LLM]
    G --&gt; H[Generated Response]

Components of a RAG System

1. Document Ingestion

<span class="hljs-comment"># Load various document types</span>
<span class="hljs-keyword">from</span> langchain.document_loaders <span class="hljs-keyword">import</span> (
    PDFLoader,
    TextLoader,
    CSVLoader,
    UnstructuredMarkdownLoader,
    WebBaseLoader
)

<span class="hljs-comment"># Example: Loading multiple sources</span>
loaders = [
    PDFLoader(<span class="hljs-string">&quot;manual.pdf&quot;</span>),
    TextLoader(<span class="hljs-string">&quot;faq.txt&quot;</span>),
    WebBaseLoader(<span class="hljs-string">&quot;https://docs.example.com&quot;</span>),
    CSVLoader(<span class="hljs-string">&quot;products.csv&quot;</span>)
]

documents = []
<span class="hljs-keyword">for</span> loader <span class="hljs-keyword">in</span> loaders:
    documents.extend(loader.load())

2. Text Splitting

<span class="hljs-keyword">from</span> langchain.text_splitter <span class="hljs-keyword">import</span> RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=<span class="hljs-number">1000</span>,
    chunk_overlap=<span class="hljs-number">200</span>,
    length_function=<span class="hljs-built_in">len</span>,
    separators=[<span class="hljs-string">&quot;\n\n&quot;</span>, <span class="hljs-string">&quot;\n&quot;</span>, <span class="hljs-string">&quot; &quot;</span>, <span class="hljs-string">&quot;&quot;</span>]
)

chunks = text_splitter.split_documents(documents)

3. Embedding and Storage

<span class="hljs-keyword">from</span> langchain.embeddings <span class="hljs-keyword">import</span> OpenAIEmbeddings
<span class="hljs-keyword">from</span> langchain.vectorstores <span class="hljs-keyword">import</span> Pinecone, Chroma, Weaviate

<span class="hljs-comment"># Create embeddings</span>
embeddings = OpenAIEmbeddings()

<span class="hljs-comment"># Store in vector database</span>
vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    persist_directory=<span class="hljs-string">&quot;./chroma_db&quot;</span>
)

<span class="hljs-comment"># Or use Pinecone for production</span>
<span class="hljs-keyword">import</span> pinecone
pinecone.init(api_key=<span class="hljs-string">&quot;your-key&quot;</span>, environment=<span class="hljs-string">&quot;your-env&quot;</span>)
vectorstore = Pinecone.from_documents(
    chunks,
    embeddings,
    index_name=<span class="hljs-string">&quot;knowledge-base&quot;</span>
)

4. Retrieval Strategies

<span class="hljs-comment"># Similarity search</span>
retriever = vectorstore.as_retriever(
    search_type=<span class="hljs-string">&quot;similarity&quot;</span>,
    search_kwargs={<span class="hljs-string">&quot;k&quot;</span>: <span class="hljs-number">5</span>}
)

<span class="hljs-comment"># MMR (Maximum Marginal Relevance) for diversity</span>
retriever = vectorstore.as_retriever(
    search_type=<span class="hljs-string">&quot;mmr&quot;</span>,
    search_kwargs={<span class="hljs-string">&quot;k&quot;</span>: <span class="hljs-number">5</span>, <span class="hljs-string">&quot;lambda_mult&quot;</span>: <span class="hljs-number">0.5</span>}
)

<span class="hljs-comment"># Hybrid search (combining keyword and semantic)</span>
<span class="hljs-keyword">from</span> langchain.retrievers <span class="hljs-keyword">import</span> EnsembleRetriever
<span class="hljs-keyword">from</span> langchain.retrievers <span class="hljs-keyword">import</span> BM25Retriever

bm25_retriever = BM25Retriever.from_documents(chunks)
ensemble_retriever = EnsembleRetriever(
    retrievers=[bm25_retriever, retriever],
    weights=[<span class="hljs-number">0.3</span>, <span class="hljs-number">0.7</span>]
)

Real-World Example: Technical Documentation Assistant

<span class="hljs-keyword">class</span> <span class="hljs-title class_">TechnicalDocsRAG</span>:
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, docs_path</span>):
        <span class="hljs-comment"># 1. Load documentation</span>
        <span class="hljs-variable language_">self</span>.loader = DirectoryLoader(
            docs_path,
            glob=<span class="hljs-string">&quot;**/*.md&quot;</span>,
            loader_cls=UnstructuredMarkdownLoader
        )

        <span class="hljs-comment"># 2. Process documents</span>
        <span class="hljs-variable language_">self</span>.text_splitter = RecursiveCharacterTextSplitter(
            chunk_size=<span class="hljs-number">1500</span>,
            chunk_overlap=<span class="hljs-number">300</span>,
            separators=[<span class="hljs-string">&quot;\n## &quot;</span>, <span class="hljs-string">&quot;\n### &quot;</span>, <span class="hljs-string">&quot;\n\n&quot;</span>, <span class="hljs-string">&quot;\n&quot;</span>, <span class="hljs-string">&quot; &quot;</span>]
        )

        <span class="hljs-comment"># 3. Create vector store with metadata</span>
        docs = <span class="hljs-variable language_">self</span>.loader.load()
        chunks = <span class="hljs-variable language_">self</span>.text_splitter.split_documents(docs)

        <span class="hljs-comment"># Add metadata</span>
        <span class="hljs-keyword">for</span> chunk <span class="hljs-keyword">in</span> chunks:
            chunk.metadata[<span class="hljs-string">&quot;source_type&quot;</span>] = <span class="hljs-string">&quot;documentation&quot;</span>
            chunk.metadata[<span class="hljs-string">&quot;indexed_at&quot;</span>] = datetime.now()

        <span class="hljs-variable language_">self</span>.vectorstore = Chroma.from_documents(
            chunks,
            OpenAIEmbeddings(),
            persist_directory=<span class="hljs-string">&quot;./tech_docs_db&quot;</span>
        )

        <span class="hljs-comment"># 4. Setup QA chain</span>
        <span class="hljs-variable language_">self</span>.qa_chain = RetrievalQA.from_chain_type(
            llm=ChatOpenAI(temperature=<span class="hljs-number">0</span>, model=<span class="hljs-string">&quot;gpt-4&quot;</span>),
            retriever=<span class="hljs-variable language_">self</span>.vectorstore.as_retriever(
                search_kwargs={<span class="hljs-string">&quot;k&quot;</span>: <span class="hljs-number">4</span>}
            ),
            chain_type=<span class="hljs-string">&quot;stuff&quot;</span>,
            return_source_documents=<span class="hljs-literal">True</span>
        )

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">answer_technical_question</span>(<span class="hljs-params">self, question</span>):
        <span class="hljs-comment"># Add context to improve retrieval</span>
        enhanced_query = <span class="hljs-string">f&quot;&quot;&quot;
        Technical Question: <span class="hljs-subst">{question}</span>
        Please provide a detailed technical answer with code examples if applicable.
        &quot;&quot;&quot;</span>

        result = <span class="hljs-variable language_">self</span>.qa_chain({<span class="hljs-string">&quot;query&quot;</span>: enhanced_query})

        <span class="hljs-keyword">return</span> {
            <span class="hljs-string">&quot;answer&quot;</span>: result[<span class="hljs-string">&quot;result&quot;</span>],
            <span class="hljs-string">&quot;sources&quot;</span>: [doc.metadata[<span class="hljs-string">&quot;source&quot;</span>] <span class="hljs-keyword">for</span> doc <span class="hljs-keyword">in</span> result[<span class="hljs-string">&quot;source_documents&quot;</span>]],
            <span class="hljs-string">&quot;confidence&quot;</span>: <span class="hljs-variable language_">self</span>._calculate_confidence(result)
        }

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">_calculate_confidence</span>(<span class="hljs-params">self, result</span>):
        <span class="hljs-comment"># Simple confidence based on source relevance</span>
        <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(result[<span class="hljs-string">&quot;source_documents&quot;</span>]) &gt;= <span class="hljs-number">3</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;High&quot;</span>
        <span class="hljs-keyword">elif</span> <span class="hljs-built_in">len</span>(result[<span class="hljs-string">&quot;source_documents&quot;</span>]) &gt;= <span class="hljs-number">1</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;Medium&quot;</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;Low&quot;</span>

RAG Best Practices

  1. Chunking Strategy
<span class="hljs-comment"># Domain-specific chunking</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">smart_chunk_documents</span>(<span class="hljs-params">docs, doc_type</span>):
    <span class="hljs-keyword">if</span> doc_type == <span class="hljs-string">&quot;code&quot;</span>:
        <span class="hljs-keyword">return</span> CodeTextSplitter(
            language=<span class="hljs-string">&quot;python&quot;</span>,
            chunk_size=<span class="hljs-number">500</span>
        ).split_documents(docs)
    <span class="hljs-keyword">elif</span> doc_type == <span class="hljs-string">&quot;markdown&quot;</span>:
        <span class="hljs-keyword">return</span> MarkdownHeaderTextSplitter(
            headers_to_split_on=[
                (<span class="hljs-string">&quot;#&quot;</span>, <span class="hljs-string">&quot;Header 1&quot;</span>),
                (<span class="hljs-string">&quot;##&quot;</span>, <span class="hljs-string">&quot;Header 2&quot;</span>),
            ]
        ).split_documents(docs)
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> RecursiveCharacterTextSplitter(
            chunk_size=<span class="hljs-number">1000</span>
        ).split_documents(docs)
  1. Metadata Enrichment
<span class="hljs-comment"># Add rich metadata for better filtering</span>
<span class="hljs-keyword">for</span> doc <span class="hljs-keyword">in</span> documents:
    doc.metadata.update({
        <span class="hljs-string">&quot;date_processed&quot;</span>: datetime.now(),
        <span class="hljs-string">&quot;version&quot;</span>: <span class="hljs-string">&quot;1.0&quot;</span>,
        <span class="hljs-string">&quot;department&quot;</span>: <span class="hljs-string">&quot;engineering&quot;</span>,
        <span class="hljs-string">&quot;access_level&quot;</span>: <span class="hljs-string">&quot;public&quot;</span>,
        <span class="hljs-string">&quot;language&quot;</span>: <span class="hljs-string">&quot;en&quot;</span>
    })
  1. Hybrid Retrieval
<span class="hljs-comment"># Combine multiple retrieval methods</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">HybridRetriever</span>:
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, vectorstore, keyword_index</span>):
        <span class="hljs-variable language_">self</span>.vector_retriever = vectorstore.as_retriever()
        <span class="hljs-variable language_">self</span>.keyword_retriever = keyword_index.as_retriever()

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">retrieve</span>(<span class="hljs-params">self, query, k=<span class="hljs-number">5</span></span>):
        vector_results = <span class="hljs-variable language_">self</span>.vector_retriever.get_relevant_documents(query)
        keyword_results = <span class="hljs-variable language_">self</span>.keyword_retriever.get_relevant_documents(query)

        <span class="hljs-comment"># Merge and deduplicate</span>
        all_docs = vector_results + keyword_results
        unique_docs = {doc.page_content: doc <span class="hljs-keyword">for</span> doc <span class="hljs-keyword">in</span> all_docs}

        <span class="hljs-keyword">return</span> <span class="hljs-built_in">list</span>(unique_docs.values())[:k]

Integration Patterns

Pattern 1: n8n + LangChain

Use Case: Automated Content Generation Pipeline

<span class="hljs-attr">n8n Workflow:</span>
  <span class="hljs-number">1</span><span class="hljs-string">.</span> <span class="hljs-string">RSS</span> <span class="hljs-string">Feed</span> <span class="hljs-string">Trigger</span> <span class="hljs-string">(new</span> <span class="hljs-string">blog</span> <span class="hljs-string">post</span> <span class="hljs-string">detected)</span>
  <span class="hljs-number">2</span><span class="hljs-string">.</span> <span class="hljs-string">HTTP</span> <span class="hljs-string">Request</span> <span class="hljs-string">(fetch</span> <span class="hljs-string">full</span> <span class="hljs-string">article)</span>
  <span class="hljs-attr">3. LangChain Node:</span>
     <span class="hljs-bullet">-</span> <span class="hljs-string">Summarize</span> <span class="hljs-string">article</span>
     <span class="hljs-bullet">-</span> <span class="hljs-string">Generate</span> <span class="hljs-string">social</span> <span class="hljs-string">media</span> <span class="hljs-string">posts</span>
     <span class="hljs-bullet">-</span> <span class="hljs-string">Extract</span> <span class="hljs-string">key</span> <span class="hljs-string">points</span>
  <span class="hljs-number">4</span><span class="hljs-string">.</span> <span class="hljs-string">Twitter</span> <span class="hljs-string">API</span> <span class="hljs-string">(post</span> <span class="hljs-string">summary)</span>
  <span class="hljs-number">5</span><span class="hljs-string">.</span> <span class="hljs-string">LinkedIn</span> <span class="hljs-string">API</span> <span class="hljs-string">(post</span> <span class="hljs-string">professional</span> <span class="hljs-string">version)</span>
  <span class="hljs-number">6</span><span class="hljs-string">.</span> <span class="hljs-string">Slack</span> <span class="hljs-string">(notify</span> <span class="hljs-string">team)</span>

Implementation:

<span class="hljs-comment">// n8n Custom Node calling LangChain</span>
<span class="hljs-keyword">const</span> langchainEndpoint = <span class="hljs-string">&#x27;http://langchain-api:8000/generate&#x27;</span>;

<span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">helpers</span>.<span class="hljs-title function_">httpRequest</span>({
  <span class="hljs-attr">method</span>: <span class="hljs-string">&#x27;POST&#x27;</span>,
  <span class="hljs-attr">url</span>: langchainEndpoint,
  <span class="hljs-attr">body</span>: {
    <span class="hljs-attr">task</span>: <span class="hljs-string">&#x27;summarize&#x27;</span>,
    <span class="hljs-attr">content</span>: $node[<span class="hljs-string">&quot;RSS Feed&quot;</span>].<span class="hljs-property">json</span>.<span class="hljs-property">content</span>,
    <span class="hljs-attr">style</span>: <span class="hljs-string">&#x27;professional&#x27;</span>,
    <span class="hljs-attr">max_length</span>: <span class="hljs-number">280</span>
  }
});

<span class="hljs-keyword">return</span> [{
  <span class="hljs-attr">json</span>: {
    <span class="hljs-attr">summary</span>: response.<span class="hljs-property">summary</span>,
    <span class="hljs-attr">hashtags</span>: response.<span class="hljs-property">hashtags</span>,
    <span class="hljs-attr">sentiment</span>: response.<span class="hljs-property">sentiment</span>
  }
}];

Pattern 2: LangChain + RAG

Use Case: Intelligent Customer Support System

<span class="hljs-keyword">class</span> <span class="hljs-title class_">SupportSystem</span>:
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self</span>):
        <span class="hljs-comment"># Setup RAG</span>
        <span class="hljs-variable language_">self</span>.knowledge_base = <span class="hljs-variable language_">self</span>._load_knowledge_base()
        <span class="hljs-variable language_">self</span>.retriever = <span class="hljs-variable language_">self</span>.knowledge_base.as_retriever()

        <span class="hljs-comment"># Setup conversation chain with RAG</span>
        <span class="hljs-variable language_">self</span>.chain = ConversationalRetrievalChain.from_llm(
            llm=ChatOpenAI(temperature=<span class="hljs-number">0.3</span>),
            retriever=<span class="hljs-variable language_">self</span>.retriever,
            memory=ConversationBufferMemory(
                memory_key=<span class="hljs-string">&quot;chat_history&quot;</span>,
                return_messages=<span class="hljs-literal">True</span>
            )
        )

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">handle_support_ticket</span>(<span class="hljs-params">self, ticket</span>):
        <span class="hljs-comment"># 1. Classify intent</span>
        intent = <span class="hljs-variable language_">self</span>._classify_intent(ticket.message)

        <span class="hljs-comment"># 2. Retrieve relevant docs</span>
        context = <span class="hljs-variable language_">self</span>.retriever.get_relevant_documents(
            ticket.message
        )

        <span class="hljs-comment"># 3. Generate response</span>
        response = <span class="hljs-variable language_">self</span>.chain({
            <span class="hljs-string">&quot;question&quot;</span>: ticket.message,
            <span class="hljs-string">&quot;chat_history&quot;</span>: ticket.history
        })

        <span class="hljs-comment"># 4. Check if escalation needed</span>
        <span class="hljs-keyword">if</span> <span class="hljs-variable language_">self</span>._needs_human_review(response):
            <span class="hljs-keyword">return</span> <span class="hljs-variable language_">self</span>._escalate_to_human(ticket, response)

        <span class="hljs-keyword">return</span> response

Pattern 3: n8n + RAG + LangChain

Use Case: Automated Documentation Update System

graph TD
    A[GitHub Webhook] --&gt;|Code Change| B[n8n Workflow]
    B --&gt; C[Extract Changes]
    C --&gt; D[RAG System]
    D --&gt;|Find Related Docs| E[LangChain]
    E --&gt;|Update Documentation| F[Generate PR]
    F --&gt; G[Notify Team]

Implementation:

<span class="hljs-comment"># LangChain service called by n8n</span>
<span class="hljs-meta">@app.post(<span class="hljs-params"><span class="hljs-string">&quot;/update-docs&quot;</span></span>)</span>
<span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">update_documentation</span>(<span class="hljs-params">request: UpdateRequest</span>):
    <span class="hljs-comment"># 1. RAG: Find affected documentation</span>
    affected_docs = rag_system.find_related_docs(
        request.changed_files
    )

    <span class="hljs-comment"># 2. LangChain: Generate updates</span>
    updates = []
    <span class="hljs-keyword">for</span> doc <span class="hljs-keyword">in</span> affected_docs:
        update = <span class="hljs-keyword">await</span> generate_doc_update(
            original_doc=doc,
            code_changes=request.changes,
            style_guide=load_style_guide()
        )
        updates.append(update)

    <span class="hljs-comment"># 3. Create pull request</span>
    pr_url = create_github_pr(updates)

    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">&quot;status&quot;</span>: <span class="hljs-string">&quot;success&quot;</span>,
        <span class="hljs-string">&quot;pr_url&quot;</span>: pr_url,
        <span class="hljs-string">&quot;docs_updated&quot;</span>: <span class="hljs-built_in">len</span>(updates)
    }

Real-World Implementation Examples

Example 1: E-Learning Platform with AI Tutor

Architecture:

Student Question → n8n → RAG (Course Materials) → LangChain (Personalized Response) → Student

Implementation:

<span class="hljs-keyword">class</span> <span class="hljs-title class_">AITutor</span>:
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self</span>):
        <span class="hljs-comment"># Load course materials into RAG</span>
        <span class="hljs-variable language_">self</span>.course_vectorstore = <span class="hljs-variable language_">self</span>._load_course_materials()

        <span class="hljs-comment"># Setup personalization</span>
        <span class="hljs-variable language_">self</span>.student_profiles = {}

        <span class="hljs-comment"># LangChain with custom prompts</span>
        <span class="hljs-variable language_">self</span>.tutor_chain = <span class="hljs-variable language_">self</span>._setup_tutor_chain()

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">answer_student_question</span>(<span class="hljs-params">self, student_id, question</span>):
        <span class="hljs-comment"># 1. Get student profile</span>
        profile = <span class="hljs-variable language_">self</span>.student_profiles.get(student_id, {})

        <span class="hljs-comment"># 2. Retrieve relevant course materials</span>
        materials = <span class="hljs-variable language_">self</span>.course_vectorstore.similarity_search(
            question,
            k=<span class="hljs-number">3</span>,
            <span class="hljs-built_in">filter</span>={<span class="hljs-string">&quot;difficulty&quot;</span>: profile.get(<span class="hljs-string">&quot;level&quot;</span>, <span class="hljs-string">&quot;beginner&quot;</span>)}
        )

        <span class="hljs-comment"># 3. Generate personalized response</span>
        response = <span class="hljs-variable language_">self</span>.tutor_chain.run(
            question=question,
            materials=materials,
            learning_style=profile.get(<span class="hljs-string">&quot;learning_style&quot;</span>, <span class="hljs-string">&quot;visual&quot;</span>),
            previous_topics=profile.get(<span class="hljs-string">&quot;completed_topics&quot;</span>, [])
        )

        <span class="hljs-comment"># 4. Update student profile</span>
        <span class="hljs-variable language_">self</span>._update_student_progress(student_id, question, response)

        <span class="hljs-keyword">return</span> response

Example 2: Legal Document Analysis System

Components:

  • n8n: Orchestrates document intake and workflow
  • RAG: Searches legal precedents and regulations
  • LangChain: Analyzes and summarizes findings
<span class="hljs-keyword">class</span> <span class="hljs-title class_">LegalAnalyzer</span>:
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self</span>):
        <span class="hljs-comment"># Specialized legal embeddings</span>
        <span class="hljs-variable language_">self</span>.embeddings = LegalBERT()

        <span class="hljs-comment"># Multiple vector stores for different document types</span>
        <span class="hljs-variable language_">self</span>.precedents_store = Pinecone(
            index=<span class="hljs-string">&quot;legal-precedents&quot;</span>,
            embedding=<span class="hljs-variable language_">self</span>.embeddings
        )
        <span class="hljs-variable language_">self</span>.regulations_store = Pinecone(
            index=<span class="hljs-string">&quot;regulations&quot;</span>,
            embedding=<span class="hljs-variable language_">self</span>.embeddings
        )

        <span class="hljs-comment"># Chain for legal analysis</span>
        <span class="hljs-variable language_">self</span>.analysis_chain = <span class="hljs-variable language_">self</span>._create_legal_chain()

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">analyze_case</span>(<span class="hljs-params">self, case_details</span>):
        <span class="hljs-comment"># 1. Extract key legal issues</span>
        issues = <span class="hljs-variable language_">self</span>._extract_legal_issues(case_details)

        <span class="hljs-comment"># 2. Search precedents for each issue</span>
        relevant_cases = {}
        <span class="hljs-keyword">for</span> issue <span class="hljs-keyword">in</span> issues:
            relevant_cases[issue] = <span class="hljs-variable language_">self</span>.precedents_store.similarity_search(
                issue,
                k=<span class="hljs-number">10</span>,
                <span class="hljs-built_in">filter</span>={<span class="hljs-string">&quot;jurisdiction&quot;</span>: case_details[<span class="hljs-string">&quot;jurisdiction&quot;</span>]}
            )

        <span class="hljs-comment"># 3. Search applicable regulations</span>
        regulations = <span class="hljs-variable language_">self</span>.regulations_store.similarity_search(
            case_details[<span class="hljs-string">&quot;summary&quot;</span>],
            k=<span class="hljs-number">5</span>
        )

        <span class="hljs-comment"># 4. Generate comprehensive analysis</span>
        analysis = <span class="hljs-variable language_">self</span>.analysis_chain.run(
            <span class="hljs-keyword">case</span>=case_details,
            precedents=relevant_cases,
            regulations=regulations,
            output_format=<span class="hljs-string">&quot;legal_memo&quot;</span>
        )

        <span class="hljs-keyword">return</span> {
            <span class="hljs-string">&quot;analysis&quot;</span>: analysis,
            <span class="hljs-string">&quot;cited_cases&quot;</span>: <span class="hljs-variable language_">self</span>._extract_citations(relevant_cases),
            <span class="hljs-string">&quot;applicable_laws&quot;</span>: <span class="hljs-variable language_">self</span>._extract_law_references(regulations),
            <span class="hljs-string">&quot;confidence_score&quot;</span>: <span class="hljs-variable language_">self</span>._calculate_confidence(analysis)
        }

Example 3: Multi-Language Customer Service Bot

Features:

  • Automatic language detection
  • Cultural context awareness
  • Multi-lingual knowledge base
<span class="hljs-keyword">class</span> <span class="hljs-title class_">MultilingualSupportBot</span>:
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self</span>):
        <span class="hljs-comment"># Language-specific vector stores</span>
        <span class="hljs-variable language_">self</span>.vector_stores = {
            <span class="hljs-string">&quot;en&quot;</span>: Chroma(collection_name=<span class="hljs-string">&quot;support_en&quot;</span>),
            <span class="hljs-string">&quot;es&quot;</span>: Chroma(collection_name=<span class="hljs-string">&quot;support_es&quot;</span>),
            <span class="hljs-string">&quot;fr&quot;</span>: Chroma(collection_name=<span class="hljs-string">&quot;support_fr&quot;</span>),
            <span class="hljs-string">&quot;de&quot;</span>: Chroma(collection_name=<span class="hljs-string">&quot;support_de&quot;</span>)
        }

        <span class="hljs-comment"># Language models</span>
        <span class="hljs-variable language_">self</span>.llms = {
            <span class="hljs-string">&quot;en&quot;</span>: ChatOpenAI(model=<span class="hljs-string">&quot;gpt-4&quot;</span>),
            <span class="hljs-string">&quot;es&quot;</span>: ChatOpenAI(model=<span class="hljs-string">&quot;gpt-4&quot;</span>, temperature=<span class="hljs-number">0.4</span>),
            <span class="hljs-string">&quot;fr&quot;</span>: ChatOpenAI(model=<span class="hljs-string">&quot;gpt-4&quot;</span>, temperature=<span class="hljs-number">0.4</span>),
            <span class="hljs-string">&quot;de&quot;</span>: ChatOpenAI(model=<span class="hljs-string">&quot;gpt-4&quot;</span>, temperature=<span class="hljs-number">0.3</span>)
        }

    <span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">handle_customer_query</span>(<span class="hljs-params">self, query, metadata={}</span>):
        <span class="hljs-comment"># 1. Detect language</span>
        language = detect_language(query)

        <span class="hljs-comment"># 2. Get cultural context</span>
        cultural_context = <span class="hljs-variable language_">self</span>._get_cultural_context(
            language,
            metadata.get(<span class="hljs-string">&quot;country&quot;</span>)
        )

        <span class="hljs-comment"># 3. Retrieve from appropriate knowledge base</span>
        retriever = <span class="hljs-variable language_">self</span>.vector_stores[language].as_retriever()
        relevant_docs = retriever.get_relevant_documents(query)

        <span class="hljs-comment"># 4. Generate culturally appropriate response</span>
        chain = ConversationalRetrievalChain.from_llm(
            llm=<span class="hljs-variable language_">self</span>.llms[language],
            retriever=retriever,
            combine_docs_chain_kwargs={
                <span class="hljs-string">&quot;prompt&quot;</span>: <span class="hljs-variable language_">self</span>._get_localized_prompt(language, cultural_context)
            }
        )

        response = <span class="hljs-keyword">await</span> chain.ainvoke({
            <span class="hljs-string">&quot;question&quot;</span>: query,
            <span class="hljs-string">&quot;chat_history&quot;</span>: metadata.get(<span class="hljs-string">&quot;history&quot;</span>, [])
        })

        <span class="hljs-comment"># 5. Post-process for cultural appropriateness</span>
        final_response = <span class="hljs-variable language_">self</span>._apply_cultural_filters(
            response,
            language,
            cultural_context
        )

        <span class="hljs-keyword">return</span> {
            <span class="hljs-string">&quot;response&quot;</span>: final_response,
            <span class="hljs-string">&quot;language&quot;</span>: language,
            <span class="hljs-string">&quot;sources&quot;</span>: relevant_docs
        }

Best Practices

General Best Practices

  1. Monitoring and Observability
<span class="hljs-comment"># Use LangSmith for LangChain monitoring</span>
<span class="hljs-keyword">from</span> langsmith <span class="hljs-keyword">import</span> Client

client = Client()
client.create_project(<span class="hljs-string">&quot;production-rag-system&quot;</span>)

<span class="hljs-comment"># Add callbacks</span>
<span class="hljs-keyword">from</span> langchain.callbacks <span class="hljs-keyword">import</span> LangChainTracer

tracer = LangChainTracer(project_name=<span class="hljs-string">&quot;production-rag-system&quot;</span>)
chain.run(<span class="hljs-string">&quot;query&quot;</span>, callbacks=[tracer])
  1. Cost Optimization
<span class="hljs-comment"># Implement caching layers</span>
<span class="hljs-keyword">from</span> functools <span class="hljs-keyword">import</span> lru_cache
<span class="hljs-keyword">from</span> redis <span class="hljs-keyword">import</span> Redis

redis_client = Redis(host=<span class="hljs-string">&#x27;localhost&#x27;</span>, port=<span class="hljs-number">6379</span>)

<span class="hljs-meta">@lru_cache(<span class="hljs-params">maxsize=<span class="hljs-number">1000</span></span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">get_cached_embedding</span>(<span class="hljs-params">text</span>):
    <span class="hljs-comment"># Check Redis first</span>
    cached = redis_client.get(<span class="hljs-string">f&quot;emb:<span class="hljs-subst">{<span class="hljs-built_in">hash</span>(text)}</span>&quot;</span>)
    <span class="hljs-keyword">if</span> cached:
        <span class="hljs-keyword">return</span> json.loads(cached)

    <span class="hljs-comment"># Generate if not cached</span>
    embedding = embeddings.embed_query(text)
    redis_client.setex(
        <span class="hljs-string">f&quot;emb:<span class="hljs-subst">{<span class="hljs-built_in">hash</span>(text)}</span>&quot;</span>,
        <span class="hljs-number">86400</span>,  <span class="hljs-comment"># 24 hour TTL</span>
        json.dumps(embedding)
    )
    <span class="hljs-keyword">return</span> embedding
  1. Security Considerations
<span class="hljs-comment"># Input sanitization</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">sanitize_user_input</span>(<span class="hljs-params">input_text</span>):
    <span class="hljs-comment"># Remove potential injection attempts</span>
    cleaned = input_text.replace(<span class="hljs-string">&quot;${&quot;</span>, <span class="hljs-string">&quot;&quot;</span>).replace(<span class="hljs-string">&quot;{{&quot;</span>, <span class="hljs-string">&quot;&quot;</span>)

    <span class="hljs-comment"># Length limits</span>
    <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(cleaned) &gt; <span class="hljs-number">1000</span>:
        cleaned = cleaned[:<span class="hljs-number">1000</span>]

    <span class="hljs-comment"># Rate limiting</span>
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> rate_limiter.allow(user_id):
        <span class="hljs-keyword">raise</span> RateLimitException(<span class="hljs-string">&quot;Too many requests&quot;</span>)

    <span class="hljs-keyword">return</span> cleaned

n8n Specific Best Practices

  1. Workflow Testing
<span class="hljs-comment">// Create test workflows</span>
<span class="hljs-keyword">const</span> testWorkflow = {
  <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;TEST_OrderProcessing&quot;</span>,
  <span class="hljs-attr">nodes</span>: [...productionNodes],
  <span class="hljs-attr">settings</span>: {
    <span class="hljs-attr">executionTimeout</span>: <span class="hljs-number">60</span>,
    <span class="hljs-attr">saveDataSuccessExecution</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">saveDataErrorExecution</span>: <span class="hljs-literal">true</span>
  }
}
  1. Version Control
<span class="hljs-comment"># Export workflows for version control</span>
n8n <span class="hljs-built_in">export</span>:workflow --all --output=./workflows/
n8n <span class="hljs-built_in">export</span>:credentials --all --output=./credentials/

<span class="hljs-comment"># Import on deployment</span>
n8n import:workflow --input=./workflows/

LangChain Specific Best Practices

  1. Prompt Engineering
<span class="hljs-comment"># Use structured prompts with examples</span>
few_shot_prompt = FewShotPromptTemplate(
    examples=[
        {<span class="hljs-string">&quot;input&quot;</span>: <span class="hljs-string">&quot;What&#x27;s 2+2?&quot;</span>, <span class="hljs-string">&quot;output&quot;</span>: <span class="hljs-string">&quot;4&quot;</span>},
        {<span class="hljs-string">&quot;input&quot;</span>: <span class="hljs-string">&quot;What&#x27;s 10*5?&quot;</span>, <span class="hljs-string">&quot;output&quot;</span>: <span class="hljs-string">&quot;50&quot;</span>}
    ],
    example_prompt=PromptTemplate(
        input_variables=[<span class="hljs-string">&quot;input&quot;</span>, <span class="hljs-string">&quot;output&quot;</span>],
        template=<span class="hljs-string">&quot;Input: {input}\nOutput: {output}&quot;</span>
    ),
    prefix=<span class="hljs-string">&quot;You are a helpful math tutor.&quot;</span>,
    suffix=<span class="hljs-string">&quot;Input: {input}\nOutput:&quot;</span>
)
  1. Chain Debugging
<span class="hljs-comment"># Enable verbose mode for debugging</span>
chain = LLMChain(
    llm=llm,
    prompt=prompt,
    verbose=<span class="hljs-literal">True</span>,  <span class="hljs-comment"># Shows all intermediate steps</span>
    callbacks=[StdOutCallbackHandler()]
)

RAG Specific Best Practices

  1. Document Preprocessing
<span class="hljs-keyword">def</span> <span class="hljs-title function_">preprocess_documents</span>(<span class="hljs-params">docs</span>):
    processed = []
    <span class="hljs-keyword">for</span> doc <span class="hljs-keyword">in</span> docs:
        <span class="hljs-comment"># Clean text</span>
        text = clean_text(doc.page_content)

        <span class="hljs-comment"># Add section headers as metadata</span>
        doc.metadata[<span class="hljs-string">&quot;section&quot;</span>] = extract_section(text)

        <span class="hljs-comment"># Add semantic tags</span>
        doc.metadata[<span class="hljs-string">&quot;topics&quot;</span>] = extract_topics(text)

        processed.append(doc)

    <span class="hljs-keyword">return</span> processed
  1. Retrieval Optimization
<span class="hljs-comment"># Use metadata filtering for better results</span>
retriever = vectorstore.as_retriever(
    search_type=<span class="hljs-string">&quot;similarity&quot;</span>,
    search_kwargs={
        <span class="hljs-string">&quot;k&quot;</span>: <span class="hljs-number">5</span>,
        <span class="hljs-string">&quot;filter&quot;</span>: {
            <span class="hljs-string">&quot;source&quot;</span>: {<span class="hljs-string">&quot;$in&quot;</span>: [<span class="hljs-string">&quot;official_docs&quot;</span>, <span class="hljs-string">&quot;faq&quot;</span>]},
            <span class="hljs-string">&quot;last_updated&quot;</span>: {<span class="hljs-string">&quot;$gte&quot;</span>: <span class="hljs-string">&quot;2024-01-01&quot;</span>}
        }
    }
)
  1. Response Quality Validation
<span class="hljs-keyword">def</span> <span class="hljs-title function_">validate_rag_response</span>(<span class="hljs-params">query, response, sources</span>):
    <span class="hljs-comment"># Check relevance</span>
    relevance_score = calculate_relevance(query, response)
    <span class="hljs-keyword">if</span> relevance_score &lt; <span class="hljs-number">0.7</span>:
        <span class="hljs-keyword">return</span> regenerate_with_different_sources(query)

    <span class="hljs-comment"># Check factual grounding</span>
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> is_grounded_in_sources(response, sources):
        <span class="hljs-keyword">return</span> {
            <span class="hljs-string">&quot;response&quot;</span>: response,
            <span class="hljs-string">&quot;warning&quot;</span>: <span class="hljs-string">&quot;Low confidence - please verify&quot;</span>
        }

    <span class="hljs-keyword">return</span> {<span class="hljs-string">&quot;response&quot;</span>: response, <span class="hljs-string">&quot;confidence&quot;</span>: <span class="hljs-string">&quot;high&quot;</span>}

Common Pitfalls and Solutions

Pitfall 1: Token Limit Exceeded

Problem: LLM context window overflow with large documents

Solution:

<span class="hljs-comment"># Implement smart chunking with overlap</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">adaptive_chunk</span>(<span class="hljs-params">text, max_tokens=<span class="hljs-number">3000</span></span>):
    <span class="hljs-comment"># Estimate tokens (rough: 1 token ≈ 4 chars)</span>
    estimated_tokens = <span class="hljs-built_in">len</span>(text) / <span class="hljs-number">4</span>

    <span class="hljs-keyword">if</span> estimated_tokens &lt;= max_tokens:
        <span class="hljs-keyword">return</span> [text]

    <span class="hljs-comment"># Use sentence boundaries for clean splits</span>
    sentences = text.split(<span class="hljs-string">&#x27;. &#x27;</span>)
    chunks = []
    current_chunk = []
    current_size = <span class="hljs-number">0</span>

    <span class="hljs-keyword">for</span> sentence <span class="hljs-keyword">in</span> sentences:
        sentence_tokens = <span class="hljs-built_in">len</span>(sentence) / <span class="hljs-number">4</span>
        <span class="hljs-keyword">if</span> current_size + sentence_tokens &gt; max_tokens:
            chunks.append(<span class="hljs-string">&#x27;. &#x27;</span>.join(current_chunk) + <span class="hljs-string">&#x27;.&#x27;</span>)
            current_chunk = [sentence]
            current_size = sentence_tokens
        <span class="hljs-keyword">else</span>:
            current_chunk.append(sentence)
            current_size += sentence_tokens

    <span class="hljs-keyword">return</span> chunks

Pitfall 2: Hallucination in Responses

Problem: LLM generates information not present in sources

Solution:

<span class="hljs-keyword">class</span> <span class="hljs-title class_">HallucinationDetector</span>:
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self</span>):
        <span class="hljs-variable language_">self</span>.fact_checker = FactCheckChain()

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">verify_response</span>(<span class="hljs-params">self, response, sources</span>):
        <span class="hljs-comment"># Extract claims from response</span>
        claims = <span class="hljs-variable language_">self</span>.extract_claims(response)

        <span class="hljs-comment"># Verify each claim against sources</span>
        unverified = []
        <span class="hljs-keyword">for</span> claim <span class="hljs-keyword">in</span> claims:
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> <span class="hljs-variable language_">self</span>.is_supported_by_sources(claim, sources):
                unverified.append(claim)

        <span class="hljs-keyword">if</span> unverified:
            <span class="hljs-keyword">return</span> {
                <span class="hljs-string">&quot;valid&quot;</span>: <span class="hljs-literal">False</span>,
                <span class="hljs-string">&quot;unverified_claims&quot;</span>: unverified,
                <span class="hljs-string">&quot;suggestion&quot;</span>: <span class="hljs-string">&quot;Regenerate with stricter grounding&quot;</span>
            }

        <span class="hljs-keyword">return</span> {<span class="hljs-string">&quot;valid&quot;</span>: <span class="hljs-literal">True</span>}

Pitfall 3: Slow Retrieval Performance

Problem: Vector search taking too long

Solution:

<span class="hljs-comment"># Implement hierarchical indexing</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">HierarchicalRetriever</span>:
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self</span>):
        <span class="hljs-comment"># Coarse index for initial filtering</span>
        <span class="hljs-variable language_">self</span>.coarse_index = FAISSIndex(dimension=<span class="hljs-number">384</span>)

        <span class="hljs-comment"># Fine indices for detailed search</span>
        <span class="hljs-variable language_">self</span>.fine_indices = {}

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">retrieve</span>(<span class="hljs-params">self, query, k=<span class="hljs-number">5</span></span>):
        <span class="hljs-comment"># 1. Fast coarse search</span>
        coarse_results = <span class="hljs-variable language_">self</span>.coarse_index.search(query, k=<span class="hljs-number">20</span>)

        <span class="hljs-comment"># 2. Refined search in relevant clusters</span>
        fine_results = []
        <span class="hljs-keyword">for</span> cluster_id <span class="hljs-keyword">in</span> coarse_results:
            <span class="hljs-keyword">if</span> cluster_id <span class="hljs-keyword">in</span> <span class="hljs-variable language_">self</span>.fine_indices:
                cluster_results = <span class="hljs-variable language_">self</span>.fine_indices[cluster_id].search(
                    query, k=<span class="hljs-number">2</span>
                )
                fine_results.extend(cluster_results)

        <span class="hljs-comment"># 3. Re-rank and return top k</span>
        <span class="hljs-keyword">return</span> <span class="hljs-variable language_">self</span>.rerank(fine_results, query)[:k]

Pitfall 4: Inconsistent n8n Workflow Execution

Problem: Workflows failing silently or producing inconsistent results

Solution:

<span class="hljs-comment">// Implement comprehensive error handling</span>
{
  <span class="hljs-string">&quot;nodes&quot;</span>: [
    {
      <span class="hljs-string">&quot;type&quot;</span>: <span class="hljs-string">&quot;n8n-nodes-base.errorTrigger&quot;</span>,
      <span class="hljs-string">&quot;name&quot;</span>: <span class="hljs-string">&quot;Error Handler&quot;</span>,
      <span class="hljs-string">&quot;parameters&quot;</span>: {},
      <span class="hljs-string">&quot;position&quot;</span>: [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>]
    },
    {
      <span class="hljs-string">&quot;type&quot;</span>: <span class="hljs-string">&quot;n8n-nodes-base.function&quot;</span>,
      <span class="hljs-string">&quot;name&quot;</span>: <span class="hljs-string">&quot;Process Error&quot;</span>,
      <span class="hljs-string">&quot;parameters&quot;</span>: {
        <span class="hljs-string">&quot;code&quot;</span>: <span class="hljs-string">`
          const error = items[0].json;

          // Log to monitoring system
          await this.helpers.httpRequest({
            method: &#x27;POST&#x27;,
            url: process.env.MONITORING_URL,
            body: {
              workflow: error.workflow.name,
              node: error.node.name,
              error: error.message,
              timestamp: new Date().toISOString()
            }
          });

          // Retry logic
          if (error.retryCount &lt; 3) {
            return [{
              json: {
                retry: true,
                retryCount: error.retryCount + 1
              }
            }];
          }

          // Alert team after max retries
          return [{
            json: {
              alert: true,
              message: &quot;Manual intervention required&quot;
            }
          }];
        `</span>
      }
    }
  ]
}

Pitfall 5: Memory Issues with Large RAG Datasets

Problem: Running out of memory when loading large document sets

Solution:

<span class="hljs-comment"># Implement lazy loading and pagination</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">LazyDocumentLoader</span>:
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, directory, batch_size=<span class="hljs-number">100</span></span>):
        <span class="hljs-variable language_">self</span>.directory = directory
        <span class="hljs-variable language_">self</span>.batch_size = batch_size
        <span class="hljs-variable language_">self</span>.file_list = <span class="hljs-variable language_">self</span>._get_file_list()
        <span class="hljs-variable language_">self</span>.current_batch = <span class="hljs-number">0</span>

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__iter__</span>(<span class="hljs-params">self</span>):
        <span class="hljs-keyword">return</span> <span class="hljs-variable language_">self</span>

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__next__</span>(<span class="hljs-params">self</span>):
        start_idx = <span class="hljs-variable language_">self</span>.current_batch * <span class="hljs-variable language_">self</span>.batch_size
        end_idx = start_idx + <span class="hljs-variable language_">self</span>.batch_size

        <span class="hljs-keyword">if</span> start_idx &gt;= <span class="hljs-built_in">len</span>(<span class="hljs-variable language_">self</span>.file_list):
            <span class="hljs-keyword">raise</span> StopIteration

        batch_files = <span class="hljs-variable language_">self</span>.file_list[start_idx:end_idx]
        documents = []

        <span class="hljs-keyword">for</span> file_path <span class="hljs-keyword">in</span> batch_files:
            doc = <span class="hljs-variable language_">self</span>._load_document(file_path)
            documents.append(doc)

        <span class="hljs-variable language_">self</span>.current_batch += <span class="hljs-number">1</span>
        <span class="hljs-keyword">return</span> documents

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">process_all</span>(<span class="hljs-params">self, processor_fn</span>):
        <span class="hljs-keyword">for</span> batch <span class="hljs-keyword">in</span> <span class="hljs-variable language_">self</span>:
            processor_fn(batch)
            <span class="hljs-comment"># Clear memory after processing</span>
            gc.collect()

Conclusion

The combination of n8n, LangChain, and RAG provides a powerful toolkit for building sophisticated AI-powered automation systems. Key takeaways:

  1. n8n excels at orchestration and workflow automation
  2. LangChain simplifies LLM application development
  3. RAG ensures AI responses are grounded and accurate
  4. Integration of all three creates robust, production-ready systems

Getting Started Checklist

  • Set up n8n instance (local or cloud)
  • Install LangChain and dependencies
  • Choose vector database (Chroma for dev, Pinecone/Weaviate for production)
  • Implement basic RAG pipeline
  • Create test workflows in n8n
  • Set up monitoring and error handling
  • Implement caching strategies
  • Add security measures
  • Deploy with proper CI/CD pipeline
  • Monitor costs and optimize

Resources

Published: October 30, 2025

Share:

Stay updated

Get new articles delivered to your inbox.