<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bernard K</title>
    <description>The latest articles on DEV Community by Bernard K (@bernardkibathi).</description>
    <link>https://dev.to/bernardkibathi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F940836%2F23301079-647b-4953-991f-bc09d5c6498f.jpg</url>
      <title>DEV Community: Bernard K</title>
      <link>https://dev.to/bernardkibathi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bernardkibathi"/>
    <language>en</language>
    <item>
      <title>Deploying a LangChain App on a $5 VPS: A Step-by-Step Guide</title>
      <dc:creator>Bernard K</dc:creator>
      <pubDate>Thu, 16 Jul 2026 14:02:37 +0000</pubDate>
      <link>https://dev.to/bernardkibathi/deploying-a-langchain-app-on-a-5-vps-a-step-by-step-guide-35d6</link>
      <guid>https://dev.to/bernardkibathi/deploying-a-langchain-app-on-a-5-vps-a-step-by-step-guide-35d6</guid>
      <description>&lt;p&gt;Deploying a LangChain app on a $5/month VPS was an experiment born out of necessity. Working in Kenya, where internet speeds can fluctuate more than you'd like and budgets are tight, creativity becomes essential. Most guides cater to those with access to endless resources, but that's far from reality in my case. Still, I was determined to see if I could make it work without burning cash or sacrificing performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I chose VPS for LangChain
&lt;/h2&gt;

&lt;p&gt;Why go for a VPS? A $5/month VPS from providers like DigitalOcean or Vultr offers a decent playground with 1GB RAM and 25GB SSD storage. It provides better control compared to shared hosting and has enough power for a lightweight application, assuming you're not hosting for a massive audience. Additionally, local hosting options that fit African economies are still developing, so we often rely on these international options.&lt;/p&gt;

&lt;p&gt;Using a VPS allows me to manage configurations precisely, which is essential for optimizing performance in low-bandwidth scenarios. Connectivity can drop just because it rained or a bird sat on a line, so this flexibility is significant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up LangChain on a budget
&lt;/h2&gt;

&lt;p&gt;After deciding on a VPS provider, the challenge was to get the app running efficiently. It wasn't straightforward, but here's my approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Python 3 and VirtualEnv
&lt;/h3&gt;

&lt;p&gt;LangChain is built on Python, so naturally, I started by setting up Python 3. Installing it was straightforward, but it's always a good idea to run apps in a virtual environment to avoid conflicts and manage dependencies separately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;python3-pip python3-venv
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv langchain-env
&lt;span class="nb"&gt;source &lt;/span&gt;langchain-env/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup isolates our app and lets us install exactly what we need without affecting the system Python packages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing LangChain
&lt;/h3&gt;

&lt;p&gt;With the virtual environment ready, I installed LangChain along with FastAPI to build a simple interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;langchain fastapi uvicorn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;FastAPI is ideal for budget-conscious developers because it’s fast and asynchronous without unnecessary overhead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing a basic LangChain workflow
&lt;/h3&gt;

&lt;p&gt;For the application, I wanted something practical, like handling real-world data challenges. LangChain's ability to connect with different language models was perfect for my NLP-based app, which focused on translating local dialects that most AI language models overlook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LangChainApp&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;langchain_app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LangChainApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_api_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/translate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target_language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;langchain_app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target_language&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;translated_text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This basic app takes text and a target language as input, returning the translated text. It's simple enough to keep resource usage low but functional to demonstrate LangChain's capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling real-world constraints
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Memory management
&lt;/h3&gt;

&lt;p&gt;With only 1GB RAM, careful management is crucial. During initial tests, multiple simultaneous requests could slow things down. I used a swap file to mitigate this. It's not the most efficient, but necessary in low-memory environments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;fallocate &lt;span class="nt"&gt;-l&lt;/span&gt; 1G /swapfile
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 /swapfile
&lt;span class="nb"&gt;sudo &lt;/span&gt;mkswap /swapfile
&lt;span class="nb"&gt;sudo &lt;/span&gt;swapon /swapfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helped prevent server crashes during peak requests or while running updates during midday slumps when connectivity was poor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Intermittent connectivity workaround
&lt;/h3&gt;

&lt;p&gt;In areas with sporadic internet, maintaining resilience in the server is key. Implementing retries for failed API calls was necessary. I used FastAPI's background tasks to set up a retry mechanism, preventing failed translations from blocking the workflow without manual intervention.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BackgroundTasks&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;attempt_translation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;langchain_app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Translation failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, retrying...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;attempt_translation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/translate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;translate_with_retries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target_language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;background_tasks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BackgroundTasks&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;background_tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attempt_translation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target_language&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Translation task added&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach helped manage incoming requests during downtimes, allowing the VPS to deal with unstable connections smartly. It cut task failures by nearly 40%.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verdict and future steps
&lt;/h2&gt;

&lt;p&gt;Running LangChain on a $5 VPS isn't perfect, but it's feasible. The app performed well for small-scale use, with latency averaging around 200ms in optimal conditions. This setup especially benefits developers in emerging markets as a testing ground for cost-constrained apps.&lt;/p&gt;

&lt;p&gt;Next, I'll explore integrating additional local language datasets into LangChain to enhance its utility in underrepresented languages. This journey highlights the importance of adapting tools to our specific realities rather than waiting for tailored solutions.&lt;/p&gt;

</description>
      <category>langchain</category>
      <category>ai</category>
      <category>python</category>
      <category>deployment</category>
    </item>
    <item>
      <title>RAG vs Fine-Tuning: What Actually Solves Your Problem?</title>
      <dc:creator>Bernard K</dc:creator>
      <pubDate>Tue, 14 Jul 2026 14:02:26 +0000</pubDate>
      <link>https://dev.to/bernardkibathi/rag-vs-fine-tuning-what-actually-solves-your-problem-20d6</link>
      <guid>https://dev.to/bernardkibathi/rag-vs-fine-tuning-what-actually-solves-your-problem-20d6</guid>
      <description>&lt;p&gt;When I first got involved in improving AI models for our IoT devices spread across different parts of Kenya, I quickly realized that our resources were limited and connectivity was unpredictable. With over 2,500 IoT devices working in this setup, I needed my AI models to be efficient, fast, and adaptable to our challenging network conditions and tight budgets. The challenge of choosing between Retrieval-Augmented Generation (RAG) techniques and fine-tuning models was not just academic,it was a necessity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding needs
&lt;/h2&gt;

&lt;p&gt;Initially, I chose fine-tuning because it was emphasized by many as the best way to make your model perfect for your data. It seemed like the ideal solution, but I hit a wall. Managing these operations on my 2GB RAM VPS was hard enough, and fine-tuning added more computational cost and time than I could handle. Processing time increased from an average of 15 minutes to nearly an hour. In an environment where power and network availability are unreliable, this was unsustainable.&lt;/p&gt;

&lt;p&gt;On the other hand, RAG offered some hope. By using existing models with document retrieval, it worked better with the 2,500+ devices continuously providing input to our pipeline. A retrieval model allowed me to update only the necessary data points instead of the entire model, minimizing downtime. This reduced processing time to about 20 minutes, a significant improvement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making RAG work in practice
&lt;/h2&gt;

&lt;p&gt;RAG techniques allowed me to combine a pre-trained language model with specific data retrieval. This meant that instead of the model needing all its knowledge upfront, it could request specific information as needed. This approach suited our budget setup, running on Raspberry Pi units and similar technology.&lt;/p&gt;

&lt;p&gt;Implementing RAG came with its own challenges, though, particularly with latency. If the retrieval component fails to cache efficiently or the connectivity drops, delays occur. Occasionally, a 2-second response time stretched out to 30 seconds due to network issues. Here's a straightforward example of how I set up my RAG with LangChain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.docstore&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SimpleDocStore&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.chains&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RetrievalQA&lt;/span&gt;

&lt;span class="c1"&gt;# Create a document store
&lt;/span&gt;&lt;span class="n"&gt;doc_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SimpleDocStore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;doc_store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_documents&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Kenya experiences diverse weather conditions affecting sensor readings.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Intermittent connectivity poses challenges for consistent data transmission.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Set up RetrievalQA
&lt;/span&gt;&lt;span class="n"&gt;qa_chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RetrievalQA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;docstore&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;doc_store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Querying the system
&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qa_chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;How does the weather affect sensor readings?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Outputs: "Kenya experiences diverse weather conditions affecting sensor readings."
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup allowed me to answer queries efficiently without overloading the system, provided that connectivity remained stable enough to access the document store.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why fine-tuning isn't completely off the table
&lt;/h2&gt;

&lt;p&gt;Despite the flexibility and adaptability of RAG, I haven't entirely abandoned fine-tuning. Certain applications, like predictive maintenance models, still benefit from being fine-tuned. These models need to detect specific patterns that are unique to our environment. I found that fine-tuning a smaller subset of the model and deploying it locally in areas with potential network isolation was effective.&lt;/p&gt;

&lt;p&gt;Though fine-tuning increased the initial setup time, it reduced latency in response times. By segmenting and fine-tuning only essential parts of the model, I managed to cut deployment configurations by about 35%. The trick was identifying which parts of the model required frequent updates and which parts could remain unchanged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons from the field
&lt;/h2&gt;

&lt;p&gt;Balancing RAG and fine-tuning taught me valuable lessons in resource management. Here's what I learned:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Evaluate resource capabilities: Fine-tuning can bottleneck on low resources and is best suited for static, locally important models.&lt;/li&gt;
&lt;li&gt;Understand network realities: RAG requires efficient caching systems to deal with connectivity interruptions, otherwise response times can deteriorate.&lt;/li&gt;
&lt;li&gt;Consider hybrid systems: Use RAG for handling dynamic, variable data needs and fine-tuning for static, crucial analysis, especially when quick responses affect operations.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;I'm not done yet. My plan is to adopt lighter but still reliable caching strategies with RAG to reduce delays during network disruptions. Fine-tuning will continue to develop as we figure out which models benefit the most.&lt;/p&gt;

&lt;p&gt;For anyone facing similar challenges in IoT or resource-constrained environments, it's important to stay adaptable. The field of AI offers a range of approaches. Ultimately, what matters most is understanding your situation and combining methods that typically seem at odds. That's where real innovation, and sometimes sanity, lies.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>langchain</category>
      <category>python</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Getting Started with AI Agents in n8n: A Non-Engineer's Guide</title>
      <dc:creator>Bernard K</dc:creator>
      <pubDate>Thu, 09 Jul 2026 14:02:45 +0000</pubDate>
      <link>https://dev.to/bernardkibathi/getting-started-with-ai-agents-in-n8n-a-non-engineers-guide-3g5b</link>
      <guid>https://dev.to/bernardkibathi/getting-started-with-ai-agents-in-n8n-a-non-engineers-guide-3g5b</guid>
      <description>&lt;p&gt;When I first started using n8n to build AI agents, I was dealing with a specific problem: how to automate and improve processes for non-developers who needed to act on IoT data. The main challenge was that most people I worked with just wanted things to "work" without delving into the code. After building over 2,500 IoT devices that report everything from temperature to air quality, it was clear that any additional insight from AI needed to be integrated into workflows everyone could understand and use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why n8n?
&lt;/h2&gt;

&lt;p&gt;Before settling on n8n, I experimented with other automation tools. They either required too much code or weren’t flexible enough to handle the type of data we were dealing with in Kenya, where the internet can be spotty. So why n8n? It's open-source, allows for visual workflows, and most importantly, it's adaptable to handle unreliable connectivity and budget hardware: a reality here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting AI Agents to Do the Heavy Lifting
&lt;/h2&gt;

&lt;p&gt;Here's why AI agents can transform n8n workflows. Imagine you've got sensor data streaming around the clock. Manually reviewing it,especially with intermittent internet,is not practical. Who has time for that, right? That's where AI comes in. You can process and make sense of data as it arrives: flag anomalies, predict trends, and even automate responses,without writing code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Workflow
&lt;/h2&gt;

&lt;p&gt;I remember the first workflow I built to automate email alerts. It was basic but effective. Let's break it down with an example of anomaly detection using sensor data.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Workflow Steps
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Trigger&lt;/strong&gt;: Start with a Webhook to catch incoming data from our IoT devices.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Formatting&lt;/strong&gt;: Use a Function node to clean and prepare the data. It's essential to handle missing values or inconsistencies, which are expected with irregular networks. Here's a simplified version:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cleanedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sensor_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;  &lt;span class="c1"&gt;// Use current time if timestamp is missing&lt;/span&gt;
       &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  &lt;span class="c1"&gt;// Default to 0 if value is missing&lt;/span&gt;
     &lt;span class="p"&gt;};&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cleanedData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AI Analysis&lt;/strong&gt;: Use a local AI model or call an API for anomaly detection. Initially, I opted for external APIs due to budget hardware limitations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alert System&lt;/strong&gt;: If an anomaly is detected, use an Email node to notify stakeholders immediately.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Real Savings
&lt;/h3&gt;

&lt;p&gt;Deploying this initial version cut processing time from manually checking and acting on these anomalies,from hours down to minutes. Literally, from "Oh no, why is this thing overheating?" to "Alert: Possible overheating detected at [timestamp]" in seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Tradeoffs
&lt;/h2&gt;

&lt;p&gt;I hit a snag when relying heavily on external AI services. Costs for API calls can add up quickly, especially when analyzing data every few minutes across all devices.&lt;/p&gt;

&lt;p&gt;So, what's the workaround if the budget is tight? Preprocess data locally using edge computing concepts. Offload only the essential data to your AI service. I wrote tiny scripts that run on-device to decide if we should even bother sending the data based on historical trends captured locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges and Realities
&lt;/h2&gt;

&lt;p&gt;Intermittent connectivity was the major hurdle. When your internet dropout can last an hour and your "cloud" is really just one local server with stock parts, you have to rethink strategies.&lt;/p&gt;

&lt;p&gt;Buffering data locally and pushing it out during network windows saved a lot of hassle. Building in retry logic also meant more data got through in the end, without manual intervention.&lt;/p&gt;

&lt;p&gt;As I refined these workflows, I realized that delaying retries for even just 5 seconds fixed about 80% of dropped connections,a simple tweak with a huge impact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest Thoughts
&lt;/h2&gt;

&lt;p&gt;n8n isn’t perfect. It’s not as sleek as some premium offerings, and sometimes nodes don't work exactly as expected, requiring a bit of hacking. But for the flexibility, community support, and open customization it offers, it's been incredibly useful in bridging tech gaps.&lt;/p&gt;

&lt;p&gt;If you’re working under the constraints of budget, limited tech infrastructure, and variable connectivity like we are here in Kenya, n8n could be worth a shot.&lt;/p&gt;

&lt;p&gt;Next step? Scaling up! I'm exploring how we can integrate more refined AI capabilities directly onto devices, moving beyond mere alerting to full-on automated decision-making. It's a frontier worth exploring, and we're just scratching the surface of what's truly possible.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>ai</category>
      <category>automation</category>
      <category>beginners</category>
    </item>
    <item>
      <title>n8n vs Make vs Zapier: Why I Made the Switch and What I Learned</title>
      <dc:creator>Bernard K</dc:creator>
      <pubDate>Tue, 23 Jun 2026 12:54:17 +0000</pubDate>
      <link>https://dev.to/bernardkibathi/n8n-vs-make-vs-zapier-why-i-made-the-switch-and-what-i-learned-3686</link>
      <guid>https://dev.to/bernardkibathi/n8n-vs-make-vs-zapier-why-i-made-the-switch-and-what-i-learned-3686</guid>
      <description>&lt;p&gt;I switched to n8n after grappling with some limitations in Zapier and Make (formerly Integromat), and it has worked out well for me. Working with IoT in Kenya presents unique challenges, from budget-friendly hardware to unreliable internet connections. Each automation tool has its specific use, but here's why n8n has become my main tool for managing over 2,500 IoT devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cost factor
&lt;/h2&gt;

&lt;p&gt;Running over 2,500 IoT devices is expensive, and every dollar counts when budgets are tight. Zapier's pricing can become very high with thousands of monthly operations. Make offers more generous operation limits, but n8n's self-hosting option was a major improvement for me. It allowed me to control costs tightly while scaling infrastructure as needed. Running n8n on a local server saved us about $200 a month compared to the Zapier plan we would have needed. That might sound small, but it’s significant for a startup in Nairobi. Plus, self-hosting meant I wasn't reliant on consistent internet connectivity to a third-party server, which is essential when your connection is spotty.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customization freedom
&lt;/h2&gt;

&lt;p&gt;Managing a fleet of IoT devices often requires tailored solutions. I realized this after finding Zapier's structure too rigid. While Zapier has a large library of integrations, it didn’t offer the flexibility I needed. n8n allowed me to script custom nodes into my automation workflows using JavaScript. Once, we needed a custom node to process sensor data differently based on dynamic inputs. I managed it in a weekend with n8n. Here's a small snippet of how I handled some JSON data from my devices:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sensorData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;humidity&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sensorData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;temperature&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;$node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;webhookUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://my-webhook-url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Temperature Alert!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;temperature&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having this control without needing additional tools or elaborate workarounds was refreshing. n8n is like having LEGO blocks for automation, letting you build anything without limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intermittent connectivity
&lt;/h2&gt;

&lt;p&gt;Those working in areas with stable, high-speed internet often overlook the importance of resilience in automation. In Kenya, internet hiccups are frequent, which shaped how I evaluated these platforms. Zapier and Make rely heavily on consistent internet access. Their cloud-based nature can lead to frequent interruptions if the connection drops. With n8n, hosting it on a local server reduced our dependency on external network stability. This setup decreased failures in data collection and processing by about 25%, a vital improvement for reliable operations.&lt;/p&gt;

&lt;p&gt;For example, we implemented a retry mechanism for critical API calls, and it significantly improved data integrity. Check out this simple retry logic we set up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxRetries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;attempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attempts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;maxRetries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;httpRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://some-api.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;attempts&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attempts&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;maxRetries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;$node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Max retries reached&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example shows how n8n allows for more customized error handling, making it essential for operations in environments like mine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning curve and community support
&lt;/h2&gt;

&lt;p&gt;n8n has a steeper learning curve than the other two, and I spent a few solid days reading docs and searching online. However, the n8n community is great. It reminded me of my early days tinkering with open-source hardware projects, finding a collective eager to share solutions and ideas. The transition was initially challenging, but I haven’t looked back. The hands-on learning was refreshing, and it ultimately gave me a deeper understanding of automation flows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-world impact
&lt;/h2&gt;

&lt;p&gt;Using n8n in our workflow changed our operations significantly. We saw a 30% increase in processing efficiency over the previously used tools, not just theoretically but in how quickly and reliably our automated alerts and processes ran.&lt;/p&gt;

&lt;h3&gt;
  
  
  The next step
&lt;/h3&gt;

&lt;p&gt;Looking ahead, I’m excited to explore advanced n8n features like custom user interfaces and integrating other open-source tools. I'm even considering contributing to n8n's codebase myself. For developers managing IoT environments, especially in areas with infrastructure challenges, n8n is worth trying. The blend of cost-effectiveness, flexibility, and adaptability to low-quality connection scenarios makes it a reliable partner. Real success is about having a tool that works well under the true constraints you face.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>automation</category>
      <category>ai</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Building a Lead Scoring Pipeline in n8n with GPT-4o-mini: A Step-by-Step Guide</title>
      <dc:creator>Bernard K</dc:creator>
      <pubDate>Thu, 30 Apr 2026 14:02:31 +0000</pubDate>
      <link>https://dev.to/bernardkibathi/building-a-lead-scoring-pipeline-in-n8n-with-gpt-4o-mini-a-step-by-step-guide-3ein</link>
      <guid>https://dev.to/bernardkibathi/building-a-lead-scoring-pipeline-in-n8n-with-gpt-4o-mini-a-step-by-step-guide-3ein</guid>
      <description>&lt;p&gt;I've been working with IoT devices in environments with limited connectivity and budget constraints here in Kenya. With over 2,500 devices operational, optimizing processes is essential for me. Recently, I embarked on a project to build a lead scoring pipeline using n8n with GPT-4o-mini. Given my experience with tight budgets and intermittent connections, finding solutions that perform reliably in these conditions is always rewarding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivation: Why n8n and GPT-4o-mini?
&lt;/h2&gt;

&lt;p&gt;In searching for an efficient lead scoring solution, I chose n8n and GPT-4o-mini because they are open source and flexible. n8n is a node-based automation tool, which allowed me to set up custom workflows without excessive costs. Pairing it with GPT-4o-mini, a lightweight language processing model, was a great fit for my infrastructure constraints.&lt;/p&gt;

&lt;p&gt;I previously wrote about setting up lead scoring using these tools, but this time I aimed to create something more reliable and share how it performed in our local setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the environment
&lt;/h2&gt;

&lt;p&gt;I started by deploying n8n on a virtual server with just 2GB of RAM. Setting up was straightforward, thanks to n8n's Docker support. Here's how I did it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; n8n &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 5678:5678 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; ~/.n8n:/home/node/.n8n &lt;span class="se"&gt;\&lt;/span&gt;
  n8nio/n8n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once n8n was up and running, I integrated GPT-4o-mini into my workflow. The framework offers a manageable model size that runs smoothly on my server, even during peak loads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the lead scoring workflow
&lt;/h2&gt;

&lt;p&gt;I developed a workflow that collects customer interaction data, processes it through GPT-4o-mini, and outputs a lead score. This setup involved nodes for data storage (I used MySQL for its reliability), data processing, and score calculation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data ingestion
&lt;/h3&gt;

&lt;p&gt;Data came in from various sources. I configured n8n to gather data from multiple channels like email and web forms. Each data source funneled into a database node in n8n:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nodes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;parameters&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;operation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;executeQuery&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;query&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT * FROM leads;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fetch Leads&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;n8n-nodes-base.mySql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typeVersion&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup allowed me to handle a consistent data flow of about 500 new entries daily: it's not a heavy load but enough to test reliability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Processing with GPT-4o-mini
&lt;/h3&gt;

&lt;p&gt;The next step was to evaluate qualitative aspects like customer sentiment through GPT-4o-mini:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GPT4OMini&lt;/span&gt;

&lt;span class="c1"&gt;# Initialize the model
&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GPT4OMini&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Process lead interaction data
&lt;/span&gt;&lt;span class="n"&gt;lead_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_lead_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interaction_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This processed data efficiently without overwhelming my limited server resources. Processing time averaged 10 seconds per lead, which was suitable for my needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling connectivity challenges
&lt;/h2&gt;

&lt;p&gt;Working in regions with unreliable internet is a reality, so I built in safeguards. I implemented retry logic in n8n that reattempts failed operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;retry&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;maxTries&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;interval&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This retry feature fixed about 80% of connectivity issues, ensuring updated scores consistently without requiring manual intervention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance and costs
&lt;/h2&gt;

&lt;p&gt;The entire setup proved cost-effective. Hosting n8n and GPT-4o-mini on a modest server cost me less than $30 a month. I saved an estimated $200/month compared to outsourcing lead scoring or using a SaaS alternative. My pipeline processed 1,500 records daily, allowing the sales team to focus on high-potential leads, increasing conversion rates by about 15%.&lt;/p&gt;

&lt;h2&gt;
  
  
  What didn't work
&lt;/h2&gt;

&lt;p&gt;Initially, I tried using a more complex language model, but it was too much for my simple lead scoring needs and frequently crashed under my server constraints. Switching to GPT-4o-mini was a significant improvement. I also encountered issues updating n8n nodes, which sometimes broke the workflow. Locking in stable versions helped maintain consistency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Deploying a lead scoring pipeline with n8n and GPT-4o-mini turned out to be an efficient way to analyze our customer data within budget constraints. The setup adapted well to the connectivity and processing power challenges typical of my work environment in Kenya. I'm looking into more advanced integrations, such as real-time scoring updates and more detailed sentiment analyses to further improve lead quality insights.&lt;/p&gt;

&lt;p&gt;If you're working in similar conditions with budget hardware and unreliable networks, this setup could provide a reliable starting point without needing high-end resources.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>ai</category>
      <category>automation</category>
      <category>python</category>
    </item>
    <item>
      <title>Why IoT Data Stumbles Before Fueling Your ML Models</title>
      <dc:creator>Bernard K</dc:creator>
      <pubDate>Tue, 28 Apr 2026 14:03:12 +0000</pubDate>
      <link>https://dev.to/bernardkibathi/why-iot-data-stumbles-before-fueling-your-ml-models-4ae2</link>
      <guid>https://dev.to/bernardkibathi/why-iot-data-stumbles-before-fueling-your-ml-models-4ae2</guid>
      <description>&lt;p&gt;Data quality issues in IoT are often a significant challenge in machine learning projects. This is a reality I've faced while managing over 2,500 active IoT devices in Kenya. It's not just a theoretical problem; it directly impacts how well ML models perform. Here's why IoT data quality can falter before it even reaches your ML models, along with some insights from my experiences.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sensor quality variations
&lt;/h2&gt;

&lt;p&gt;A major concern is the difference in sensor quality. Working within budget constraints common in emerging markets often means making tough hardware choices. On occasion, I've used cheaper sensors only to discover higher error margins or, worse, intermittent data issues. For instance, a temperature sensor might report values that vary by as much as 5 degrees Celsius randomly, skewing datasets.&lt;/p&gt;

&lt;p&gt;The takeaway is clear: if you're using budget-friendly hardware, include this in your data collection strategy. Adding layers for data calibration and validation can help address these discrepancies. I've used straightforward statistical validations to flag unusual readings. For example, if a sensor reports a temperature that exceeds expected seasonal ranges, I log it for manual review instead of sending it directly to models.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connectivity issues
&lt;/h2&gt;

&lt;p&gt;Network instability is common here, and it's a major cause of data quality problems. In Nairobi, for example, network outages occur more often than I'd prefer. During these periods, devices might either stop recording data if they lack local storage or, worse, report corrupt packets due to mid-transmission cut-offs.&lt;/p&gt;

&lt;p&gt;A practical solution that’s worked for me is using MQTT with persistent session support. This setup lets devices queue messages when the connection drops and push them once it's restored. This approach has reduced data loss by at least 60%. Additionally, building in a local buffer for temporary data storage is invaluable during critical connectivity lapses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing data packet size
&lt;/h2&gt;

&lt;p&gt;Sending detailed telemetry over an unreliable 2G network is problematic. Initial attempts saw packets consistently lost during transmission. Real progress was made by breaking telemetry down into smaller, prioritized packets. Sending crucial metrics first ensured vital insights got through even if secondary data lagged.&lt;/p&gt;

&lt;p&gt;Another tactic is compressing data for transport. Tools like Protobuf are effective for compacting data without sacrificing content quality. This alone reduced our payload sizes by over 40%, leading to steadier data delivery across unreliable networks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Time synchronization challenges
&lt;/h2&gt;

&lt;p&gt;Ideally, time synchronization should be seamless, but that's not always the case. Many devices lose sync due to connectivity issues, resulting in logs with incorrect timestamps. This can mislead models when cross-referencing datasets.&lt;/p&gt;

&lt;p&gt;I've tackled this with a dual-sync method: devices sync with a local server periodically, and when the network is unreliable, they rely on an internal clock adjusted manually during post-processing. While not perfect, this has greatly enhanced the accuracy of our time-stamped data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Addressing software bugs
&lt;/h2&gt;

&lt;p&gt;Software bugs are more than just a hassle,they can compromise data integrity. I learned this firsthand when an overnight update to our edge computing routines halted our data pipeline due to a memory leak. Implementing rollback capabilities and automated integrity checks have protected us from similar issues since.&lt;/p&gt;

&lt;p&gt;Regular code audits and simulations on sample data before deployment are crucial. These steps have identified numerous minor and major issues that could have otherwise diminished our data's quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons learned
&lt;/h2&gt;

&lt;p&gt;Data quality extends beyond just cleaner data; it's also about strategic resource use. In my experience, IoT projects with successful ML outcomes begin by addressing data quality right from where the sensors are located. Understanding the local context, such as Kenya's connectivity challenges, and designing solutions for those specific conditions is key.&lt;/p&gt;

&lt;p&gt;Looking ahead, I'm exploring integrating AI-driven anomaly detection directly on IoT devices. By using lightweight models capable of running on edge devices, we can potentially flag data inconsistencies in real-time before they spread. It's early days, but real-time data validation could significantly enhance environments like ours.&lt;/p&gt;

&lt;p&gt;Ultimately, when dealing with IoT and ML in real-world situations, perfection is unattainable. Instead, it's about continuous improvement and building processes that evolve with your devices and datasets. This journey is challenging, but each lesson learned brings us closer to extracting truly useful insights from our telemetry.&lt;/p&gt;

</description>
      <category>python</category>
      <category>iot</category>
      <category>ai</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>How I Built a Document Q&amp;A Bot Using LangChain, FAISS, and Docker</title>
      <dc:creator>Bernard K</dc:creator>
      <pubDate>Thu, 16 Apr 2026 14:02:47 +0000</pubDate>
      <link>https://dev.to/bernardkibathi/how-i-built-a-document-qa-bot-using-langchain-faiss-and-docker-19he</link>
      <guid>https://dev.to/bernardkibathi/how-i-built-a-document-qa-bot-using-langchain-faiss-and-docker-19he</guid>
      <description>&lt;p&gt;Setting up a Document Q&amp;amp;A Bot using LangChain, FAISS, and Docker has been quite the expedition into aligning advanced technology with practical constraints. Being based in Kenya with its infrastructure quirks and tight budgets, I needed something effective yet lightweight enough for environments with intermittent connectivity. Here's how I went about it and what I learned along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Chose LangChain, FAISS, and Docker
&lt;/h2&gt;

&lt;p&gt;Having worked with LangChain on previous AI automation projects, I knew it could process natural language effectively. The challenge was integrating it with a reliable search component,FAISS (Facebook AI Similarity Search),to manage document indexing and retrieval efficiently. Docker was the logical choice for maintaining environment consistency across different machines, especially when using budget hardware.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Core: LangChain and FAISS
&lt;/h2&gt;

&lt;p&gt;Integrating LangChain with FAISS allowed me to create a document Q&amp;amp;A bot that searches through large amounts of text to provide relevant answers. Here's a breakdown of how I implemented it:&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up LangChain
&lt;/h3&gt;

&lt;p&gt;Start by setting up LangChain. If you're unfamiliar with this framework, it simplifies the creation of pipeline structures for text processing. Here's a snippet to show how I initialized it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;

&lt;span class="c1"&gt;# Initialize your langchain pipeline
&lt;/span&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;# Here you would define steps like data processing, machine learning, etc.
&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This straightforward setup prepares for processing natural language queries.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing FAISS for Search
&lt;/h3&gt;

&lt;p&gt;FAISS was transformative for the search functionality. It's fast and handles high-dimensional data well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;faiss&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="c1"&gt;# Create data to index
&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;6.0&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;float32&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create an index
&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;faiss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IndexFlatL2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Add data to the index
&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# To search for the nearest neighbor
&lt;/span&gt;&lt;span class="n"&gt;distances&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;float32&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Output: array([[0]])
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I found FAISS particularly efficient when dealing with large datasets, which I anticipate as the data grows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Packaging with Docker
&lt;/h2&gt;

&lt;p&gt;Using Docker helped manage dependencies and environment setups, crucial when deploying on systems with different specs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dockerfile for Environment Consistency
&lt;/h3&gt;

&lt;p&gt;Here's a snippet of the Dockerfile that I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use an official Python runtime as a parent image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.9&lt;/span&gt;

&lt;span class="c"&gt;# Set the working directory in the container&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/src/app&lt;/span&gt;

&lt;span class="c"&gt;# Copy the current directory contents into the container at /usr/src/app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Install any needed packages specified in requirements.txt&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# Run langchain script when the container launches&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "langchain_script.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup prevented dependency issues on different machines and made deploying updates consistently easy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges of Connectivity and Budget Constraints
&lt;/h2&gt;

&lt;p&gt;In a region with spotty internet, building a system that handled offline queries was essential. I implemented local caching of common queries and some preprocessing techniques to ensure the app didn't rely on constant internet access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results: What Worked and What Didn't
&lt;/h2&gt;

&lt;p&gt;Deploying this setup reduced our search query time from around 2 seconds to under 200 milliseconds. But there were challenges. Budget hardware meant I had to limit the number of threads FAISS could use to avoid maxing out CPU resources.&lt;/p&gt;

&lt;p&gt;Offline capabilities were mixed. Local caching worked for common phrases, but less frequent queries still struggled without internet access. This balance is something I continually tweak.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;I'm exploring ways to include more efficient data preprocessing steps into the LangChain pipeline to further reduce internet dependency. Additionally, I'm optimizing FAISS to handle larger datasets on limited hardware.&lt;/p&gt;

&lt;p&gt;LangChain, FAISS, and Docker together brought significant improvements in managing large-scale document Q&amp;amp;A tasks despite constraints in emerging markets. It's a process of constant iteration and adaptation to our environment's realities.&lt;/p&gt;

</description>
      <category>langchain</category>
      <category>ai</category>
      <category>python</category>
      <category>docker</category>
    </item>
    <item>
      <title>RAG vs Fine-Tuning: What Really Solved My AI Challenges</title>
      <dc:creator>Bernard K</dc:creator>
      <pubDate>Tue, 14 Apr 2026 14:03:11 +0000</pubDate>
      <link>https://dev.to/bernardkibathi/rag-vs-fine-tuning-what-really-solved-my-ai-challenges-170g</link>
      <guid>https://dev.to/bernardkibathi/rag-vs-fine-tuning-what-really-solved-my-ai-challenges-170g</guid>
      <description>&lt;p&gt;I recently grappled with the choice between Retrieval-Augmented Generation (RAG) and fine-tuning a language model. The project was simple: integrate AI that's reliably intelligent on a budget, across over 2,500 IoT devices distributed in areas where internet connectivity is as steady as a shaky table. My mission was to enable these devices to answer user questions about local climate data,a feature that needed to be useful even when connections were unstable.&lt;/p&gt;

&lt;h2&gt;
  
  
  RAG: A smart choice
&lt;/h2&gt;

&lt;p&gt;RAG turned out to be a lifesaver in my scenario. Its appeal lay in working well with limited resources while maintaining performance. For those unfamiliar, RAG involves pulling relevant documents from a dataset and generating a response based on that retrieved information. Think of it like a librarian who pulls the right book off the shelf before you even finish asking your question.&lt;/p&gt;

&lt;p&gt;Why RAG? Maintaining a large language model locally on budget IoT hardware felt impractical. These devices don't have the processing power or memory for such a task. Streaming a lean model and outsourcing the heavy-lifting to RAG seemed smart and efficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  How I implemented RAG
&lt;/h3&gt;

&lt;p&gt;I used Haystack, a Python framework that integrates well with RAG. The setup was surprisingly straightforward. Here's a simplified version of the code I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;haystack.document_stores&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;InMemoryDocumentStore&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;haystack.nodes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DensePassageRetriever&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FARMReader&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;haystack.pipelines&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ExtractiveQAPipeline&lt;/span&gt;

&lt;span class="c1"&gt;# Initialize document store
&lt;/span&gt;&lt;span class="n"&gt;document_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InMemoryDocumentStore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Set up retriever and reader
&lt;/span&gt;&lt;span class="n"&gt;retriever&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DensePassageRetriever&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document_store&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;document_store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FARMReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_name_or_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deepset/roberta-base-squad2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Pipeline for QA
&lt;/span&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ExtractiveQAPipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retriever&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;document_store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Sample output
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run_pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s the local climate today?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The climate is sunny with high temperatures.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}])&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;answers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;answer&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="c1"&gt;# Expected: "sunny with high temperatures"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result was consistent performance despite shaky connectivity. With RAG, not every query required a live internet connection, which drastically reduced latency issues and cut API costs. In numbers, I saw a 50% reduction in unnecessary internet fetches,a big win for us working on a tight budget.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fine-tuning: an ambitious endeavor
&lt;/h2&gt;

&lt;p&gt;Now, fine-tuning has its appeal. You can tailor a language model to your specific dataset. Sounds great, right? Unfortunately, it's a costly approach if each of your IoT devices has the computational power of a basic calculator.&lt;/p&gt;

&lt;p&gt;For the same task, fine-tuning a model was like sending these devices to space without oxygen. Fine-tuning is ideal when constant connectivity is guaranteed or when working with larger cloud setups.&lt;/p&gt;

&lt;h3&gt;
  
  
  My attempt with fine-tuning
&lt;/h3&gt;

&lt;p&gt;I tried using BERT, a popular choice known for its strong context understanding. With the dataset in hand, I attempted fine-tuning on a pre-trained model using Transformers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BertTokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BertForQuestionAnswering&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Trainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TrainingArguments&lt;/span&gt;

&lt;span class="c1"&gt;# Tokenizer and model initialization
&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BertTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bert-base-uncased&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BertForQuestionAnswering&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bert-base-uncased&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# A toy dataset split
&lt;/span&gt;&lt;span class="n"&gt;train_encodings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s the weather?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;truncation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;train_dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input_ids&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;train_encodings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input_ids&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;start_positions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;end_positions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]}]&lt;/span&gt;

&lt;span class="c1"&gt;# Trainer setup
&lt;/span&gt;&lt;span class="n"&gt;training_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TrainingArguments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;per_device_train_batch_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;trainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Trainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;training_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;train_dataset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;train_dataset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Mock training
&lt;/span&gt;&lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this on a more robust setup was insightful, but trying to execute similar fine-tuned models on-field failed badly. Any connectivity hiccup meant retrieving data from the cloud sometimes took longer than the questions themselves. Not to mention, the cost wasn't cheap.&lt;/p&gt;

&lt;h2&gt;
  
  
  The decision
&lt;/h2&gt;

&lt;p&gt;In a perfect world with limitless resources, fine-tuning would be the dream engine of AI. But here, keeping IoT functional in unreliable network zones with budget limitations paved a path where RAG shone like a beacon.&lt;/p&gt;

&lt;p&gt;For IoT deployments in regions like Kenya, where budget constraints are as ever-present as the sunsets, RAG is a solid solution. If you're dealing with devices with the processing power of an old Nokia but want a system that performs under challenging conditions, RAG is the way to go.&lt;/p&gt;

&lt;p&gt;For now, we're working to optimize RAG's document retrieval efficiency and exploring additional cloud computing solutions for occasional heavy lifting. Tech evolves fast, and staying ahead requires constant adjustment and experimentation.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>langchain</category>
      <category>python</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Quick Guide: Connecting n8n to Any REST API in 10 Minutes</title>
      <dc:creator>Bernard K</dc:creator>
      <pubDate>Thu, 09 Apr 2026 14:02:48 +0000</pubDate>
      <link>https://dev.to/bernardkibathi/quick-guide-connecting-n8n-to-any-rest-api-in-10-minutes-9f1</link>
      <guid>https://dev.to/bernardkibathi/quick-guide-connecting-n8n-to-any-rest-api-in-10-minutes-9f1</guid>
      <description>&lt;p&gt;Connecting n8n to a REST API in under 10 minutes might sound ambitious, but it's quite achievable, even for beginners. I stumbled across n8n a few months ago when I was dealing with automating my IoT data reporting. The need for a flexible automation tool was pressing, given the constraints I face working in Kenya: unreliable internet, budget limits, and a need for simplicity. My goal was to automate data fetching from a weather API, and n8n seemed like the perfect tool because of its visual workflow builder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use n8n?
&lt;/h2&gt;

&lt;p&gt;Before getting started, let me explain why n8n caught my attention. It's open-source, meaning no monthly fees, which is a big win when you're watching your budget. Plus, its visual interface means I don't have to hard-code API calls like I used to, saving heaps of time. For someone juggling over 2,500 IoT devices across regions with sporadic connectivity, automating workflows with minimal overhead is incredibly helpful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started with n8n
&lt;/h2&gt;

&lt;p&gt;First, you need to have n8n running. You can deploy it locally using Docker or any cloud provider. I usually run it on a DigitalOcean droplet with 2GB RAM, costing about $10 a month. This setup has handled my modest workflows without a hitch.&lt;/p&gt;

&lt;p&gt;Once n8n is up and running, you’ll land on a simple canvas ready for your workflow creation. Here's how I connected n8n to a REST API to fetch weather data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the HTTP request node
&lt;/h2&gt;

&lt;p&gt;The HTTP Request node is your gateway to external APIs. Here’s how you set it up to connect to any REST API. I'll use the OpenWeather API as an example.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add the HTTP Request Node&lt;/strong&gt;: Drag this node from the left panel to your canvas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Configure the Request&lt;/strong&gt;: Double-click the node and you’ll see options for configuring your HTTP request.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Method&lt;/strong&gt;: Select &lt;code&gt;GET&lt;/code&gt; since we are fetching data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;URL&lt;/strong&gt;: Input the API endpoint, like &lt;code&gt;http://api.openweathermap.org/data/2.5/weather?q=Nairobi&amp;amp;appid=YOUR_API_KEY&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Testing the API&lt;/strong&gt;: Hit the "Execute Node" button to test your request. Within my setup, this worked smoothly. A successful call displays the response in JSON format, showing temperature, humidity, and more.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Parsing the API response
&lt;/h2&gt;

&lt;p&gt;After you have the raw data, you need to make it usable. Here, the JSON Parse node is quite handy.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add the JSON Parse Node&lt;/strong&gt;: Connect this to your HTTP Request node.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Configure the Node&lt;/strong&gt;: Simply set the "Fields to Convert" to the field containing your JSON data. In our case, it's the entire API response.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Executing this parses the JSON, making it structured data ready for further processing in your workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling connectivity issues
&lt;/h2&gt;

&lt;p&gt;Working in Nairobi, internet connectivity isn't always stable. I handle this by introducing retries and error-checking in n8n.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add a Function Node&lt;/strong&gt;: Create simple JavaScript code to retry the HTTP request if it fails. Here’s a quick snippet:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;   &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;retryCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxRetries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;retryCount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;maxRetries&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;// Call the HTTP Request Node here&lt;/span&gt;
       &lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// set to true if the request succeeds&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;retryCount&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// wait 5 seconds before retrying&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This logic saved me when connecting to APIs from locations with flaky connections, significantly reducing request failure rates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automation with ease
&lt;/h2&gt;

&lt;p&gt;Once you have your data, you can either store it in a Google Sheet, send it via email, or trigger other IoT devices. n8n offers nodes for just about anything. For reporting my IoT data, I often push this info to a Google Sheet. Simply drop the Google Sheets node into your workflow, link it with your Google Account, and specify the sheet and cells to update.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-world application
&lt;/h2&gt;

&lt;p&gt;Remarkably, setting up n8n to automate data collection cut manual processing time from 2 hours a day to just 10 minutes of setup and occasional monitoring. In one instance, this saved me roughly $200 a month by eliminating the need for a third-party service to handle HTTP requests and parsing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;If you're navigating constraints such as budget hardware or tough internet conditions, n8n can offer a simple, effective solution for automating data flows. While it’s not without its issues (node configuration can occasionally be a bit fiddly), it's dependable enough for most small to medium IoT applications I've managed.&lt;/p&gt;

&lt;p&gt;The next steps? I'm planning to explore triggering device actions based on API responses automatically. Meanwhile, try n8n for your REST API needs. If my experience is anything to go by, you'll appreciate its straightforward approach in a complex world.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>automation</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Getting Started with AI Agents in n8n: A Non-Engineer's Guide</title>
      <dc:creator>Bernard K</dc:creator>
      <pubDate>Tue, 07 Apr 2026 14:02:45 +0000</pubDate>
      <link>https://dev.to/bernardkibathi/getting-started-with-ai-agents-in-n8n-a-non-engineers-guide-4a67</link>
      <guid>https://dev.to/bernardkibathi/getting-started-with-ai-agents-in-n8n-a-non-engineers-guide-4a67</guid>
      <description>&lt;p&gt;When I first started exploring AI automation several years ago, n8n wasn’t on my radar. Back then, I faced the challenge of orchestrating automation across multiple IoT devices in environments with unreliable internet connectivity. Solid reliability on a budget was essential. Fast forward to now, I've managed to streamline many tasks using n8n, an automation tool that became a core part of my toolkit for non-developers wanting to build AI agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I chose n8n
&lt;/h2&gt;

&lt;p&gt;I was initially skeptical. Could an open-source tool really handle the complexities of real-time IoT data processing, especially when robust cloud solutions came with hefty price tags? In Kenya, we often had to deal with spotty internet connections and tight hardware budgets. So, any tool that promised local deployment (such as running on budget Raspberry Pis) and straightforward APIs caught my attention.&lt;/p&gt;

&lt;p&gt;n8n’s appeal is in its flexibility. I can spin it up on a local server and get everything running without needing a powerful cloud instance. For instance, a workflow that would typically take at least a minute or two on IFTTT is cut down to mere seconds due to this local processing capability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up an automation pipeline
&lt;/h2&gt;

&lt;p&gt;Here's a quick use case: integrating weather data into our existing sensor network. Anyone who's worked with IoT knows that weather can heavily influence sensor readings. Automating adjustments based on weather forecasts means our devices work smarter, not harder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The main API integration node in n8n&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchWeatherData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Nairobi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://api.openweathermap.org/data/2.5/weather?q=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;city&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;appid=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;weatherData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Process data as needed, e.g., extract temperature&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;weatherData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Log temperature&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;weatherData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error fetching weather data:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;fetchWeatherData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script calls a weather API and retrieves the necessary data we’ll want to use later in our automation workflow. Plugging this into n8n was straightforward. Within minutes, I'd configured it to grab weather updates every hour and use those data points to fine-tune our IoT devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-world wins and challenges
&lt;/h2&gt;

&lt;p&gt;After deploying it across 50 devices, I saw a 30% increase in efficiency due to better calibration. One unexpected benefit was the reduction in energy consumption by about 15%, thanks to automated device hibernation when certain environmental conditions were met. This technology was a huge relief both technologically and economically.&lt;/p&gt;

&lt;p&gt;However, not all experiments with n8n were easy. When the tool integrated with some legacy hardware, latency issues cropped up frequently. Given intermittent 3G connections, I often encountered data bottlenecks. Increasing retry intervals and batch processing worked wonders, but these aren't clearly documented solutions you'll find in n8n’s manuals. Bringing such workarounds to life required both persistence and a bit of creativity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison with other tools
&lt;/h2&gt;

&lt;p&gt;I had previously used tools like Node-RED and Zapier, but each had its drawbacks in my context. Node-RED was great but required a steeper learning curve for non-coders. Zapier was useful but not as customizable locally as n8n. The fluency with which n8n handled custom scripts made it blend better into tech ecosystems in emerging markets like Kenya.&lt;/p&gt;

&lt;p&gt;n8n enabled more solutions-focused discussions between me, a developer, and non-tech team members since they could visually follow the automation logic without diving into raw code. This eliminated errors between the technical and non-technical team and streamlined processes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What could be better
&lt;/h2&gt;

&lt;p&gt;I’m not saying n8n is a magic bullet. Although its UI is user-friendly, scaling workflows can become complex visually very quickly if you're not meticulous. During a deployment on more than 100 devices, the node management proved cumbersome. Also, working offline means documentation or community Q&amp;amp;As are sometimes unreachable when needed. Offline solutions or an offline-first approach to documentation would be beneficial.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;I've got a few IoT projects lined up where I'm eager to see how far I can push n8n. My next steps are building integrations with new AI models that predict environmental impacts, like LangChain, integrated directly into these pipelines. I'm also intrigued by the idea of crowdsourcing solutions to similar challenges: imagine a shared repository of automation blueprints without geographical data silos.&lt;/p&gt;

&lt;p&gt;n8n, with all its quirks, opened up a range of possibilities I wasn’t sure were realistic just several years ago. For those just starting to combine IoT and AI in budget-constrained environments, it’s definitely worth exploring. The tool has potential beyond what I initially expected, proving once again that innovation thrives on the edges of both budget and connectivity.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>ai</category>
      <category>automation</category>
      <category>beginners</category>
    </item>
    <item>
      <title>n8n vs Make vs Zapier: What Made Me Switch and Why</title>
      <dc:creator>Bernard K</dc:creator>
      <pubDate>Thu, 02 Apr 2026 14:02:36 +0000</pubDate>
      <link>https://dev.to/bernardkibathi/n8n-vs-make-vs-zapier-what-made-me-switch-and-why-1noh</link>
      <guid>https://dev.to/bernardkibathi/n8n-vs-make-vs-zapier-what-made-me-switch-and-why-1noh</guid>
      <description>&lt;p&gt;I used to be heavily reliant on Zapier. It was the first automation tool I picked up back when managing IoT devices was chaotic. However, as the number of devices increased and budget constraints tightened, cracks started to show. My IoT devices in remote areas of Kenya needed reliable automations that wouldn't falter with poor connectivity, and Zapier's pricing became a burden on my tight budget. That's when I discovered n8n, followed later by Make (formerly Integromat).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Zapier ceiling
&lt;/h2&gt;

&lt;p&gt;Initially, Zapier was helpful. It was easy to set up, had many integrations, and featured a user-friendly interface. But as my projects grew, so did the costs: $299/month was not sustainable. The setup became cumbersome with complex workflows, and I often struggled with limitations surrounding multi-step automations. Running an economical operation meant I couldn't afford Zapier every month just for convenience.&lt;/p&gt;

&lt;p&gt;A major issue was dealing with unreliable internet connections. With intermittent connectivity, waiting for automations to sync or just halt unexpectedly was frustrating. In Kenya, this is a reality many developers face. Zapier's dependence on constant connectivity made it difficult to trust when I needed to integrate sensor data economically and reliably.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discovering n8n: an open-source alternative
&lt;/h2&gt;

&lt;p&gt;I found n8n while searching for open-source alternatives. It was free with the option to self-host, which sounded ideal. The transition wasn't simple, but the flexibility was refreshing after using Zapier. Setting up n8n on a local server took some effort, like configuring Docker and managing local network issues, but it paid off.&lt;/p&gt;

&lt;p&gt;Here's a snippet of a simple automation with n8n, where it listens to new MQTT messages from IoT devices and saves them in a database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// n8n simple workflow connecting MQTT and a DB&lt;/span&gt;

&lt;span class="c1"&gt;// Assume MQTT input node&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sensorData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  

&lt;span class="c1"&gt;// Example function node to process data&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sensorData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deviceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;humidity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;humidity&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="c1"&gt;// MySQL Contribution Node (store processed data)&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;executeMariaDB&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yourpassword&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;IoTData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;processedData&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ran this on an older VPS with just 2GB of RAM, which was perfectly capable of parsing local MQTT messages and storing data. The cost? Just the server, far cheaper than the recurring costs with Zapier.&lt;/p&gt;

&lt;p&gt;n8n's visual workflow builder often made sense once I got used to it, though it's not without quirks. Having access to the code behind automations means you can customize things beyond typical limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make: the unexpected contender
&lt;/h2&gt;

&lt;p&gt;I transitioned to Make when projects required more granularity than what n8n could comfortably handle. While n8n excels in adaptability, Make provides a structured yet broad platform.&lt;/p&gt;

&lt;p&gt;Make stands out for conditional operations, and its interface for setting up conditional workflows is superior to n8n, often saving time when debugging extensive workflows. Testing scenarios, like turning on generators based on temperature spikes from sensor data, became less of a hassle.&lt;/p&gt;

&lt;h2&gt;
  
  
  When decisions get tough
&lt;/h2&gt;

&lt;p&gt;Choosing between Zapier, n8n, and Make relies on balancing needs and constraints. If I've learned anything from scaling small projects to manage thousands of IoT devices, it's the necessity to adapt. Sticking to one tool like Zapier is convenient until outgrown.&lt;/p&gt;

&lt;p&gt;Running n8n is more technical but cost-effective, ideal for those ready to dive into Docker setups and local servers. Make, with its reasonable pricing, offers a more refined experience for scenarios with complex conditionals and data flows.&lt;/p&gt;

&lt;h2&gt;
  
  
  The IoT developer's reality
&lt;/h2&gt;

&lt;p&gt;Living and working in Kenya involves dealing with infrastructure not always aligned with major SaaS tools. Unstable internet isn't just a challenge, it's a regular hurdle. By switching from Zapier and utilizing a mix of n8n and Make, I've saved hundreds per month on automation expenses while ensuring the system is capable of handling real-world connectivity issues.&lt;/p&gt;

&lt;p&gt;If you're in a similar spot,balancing ambitious goals with limited resources,consider trying self-hosted n8n for cost control and Make for efficient conditional workflows. The tools' perfection is less important than their adaptability and ability to meet your specific challenges.&lt;/p&gt;

&lt;p&gt;Next up for me: integrating more advanced AI-driven analytics into these workflows to process sensor data. For now, though, I’m pleased that my IoT integrations are both cost-effective and adaptable enough to function under Kenya's unpredictable digital conditions. That's a success in any developer's book.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>automation</category>
      <category>ai</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Getting Started with Lead Scoring in n8n Using GPT-4o-mini</title>
      <dc:creator>Bernard K</dc:creator>
      <pubDate>Thu, 02 Apr 2026 09:00:00 +0000</pubDate>
      <link>https://dev.to/bernardkibathi/getting-started-with-lead-scoring-in-n8n-using-gpt-4o-mini-j3l</link>
      <guid>https://dev.to/bernardkibathi/getting-started-with-lead-scoring-in-n8n-using-gpt-4o-mini-j3l</guid>
      <description>&lt;p&gt;Building a lead scoring pipeline wasn't initially on my agenda, but necessity demanded it. Working with IoT and AI in Kenya often means grappling with unreliable internet and outdated hardware. Wherever you're working, leads are vital. To prioritize sales leads cost-effectively, I decided to test n8n. It wasn't merely about saving money; I wanted to see if a low-code tool could handle such an important task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why n8n and GPT-4o-mini?
&lt;/h2&gt;

&lt;p&gt;I had used n8n for other automation tasks and was intrigued by its versatility. Being open-source, it has no licensing issues or hidden fees, which is crucial on a budget. The bigger question was whether it could integrate well with GPT-4o-mini for accurate lead scoring. GPT-4o-mini attracted me with its lightweight nature compared to full GPT versions, which suited my operating conditions. A strong, stable connection isn't always available, so something needing minimal cloud interaction was essential.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the workflow
&lt;/h2&gt;

&lt;p&gt;I set up my n8n instance, integrating it with daily tools like our CRM and email. The workflow needed to automate data gathering, scoring, and pushing results back into our CRM. n8n handled the flow, while GPT-4o-mini managed the scoring.&lt;/p&gt;

&lt;p&gt;Here is the core part of the workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// This node triggers when a new lead enters the system&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onNewLead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;leadData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Format the data for GPT-4o-mini&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formattedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;leadData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Call GPT-4o-mini for scoring&lt;/span&gt;
    &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://gpt-4o-mini.local/score&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formattedData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="c1"&gt;// Push the score back to the CRM&lt;/span&gt;
            &lt;span class="nf"&gt;updateLeadScoreInCRM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;leadData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error scoring lead:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateLeadScoreInCRM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;leadId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Integration with CRM API&lt;/span&gt;
    &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`http://crm.local/api/leads/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;leadId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Lead &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;leadId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; scored with &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error updating CRM:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code manages communication with both GPT-4o-mini and our CRM. n8n triggers when a new lead arrives, ensuring scores get stored back into the CRM efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges and learnings
&lt;/h2&gt;

&lt;p&gt;Setting this up was anything but straightforward. I encountered latency issues due to intermittent connectivity, especially in initial tests. The response time from the GPT-4o-mini server exceeded 5 seconds about 40% of the time. Adding retry logic reduced response failures by nearly 60%. Here's the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;scoreLeadWithRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;leadData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;retries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;attemptScore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://gpt-4o-mini.local/score&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;leadData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nf"&gt;updateLeadScoreInCRM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;leadData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Retry &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: Scoring lead failed, retrying...`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nf"&gt;attemptScore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Scoring lead failed after several attempts:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;attemptScore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This retry strategy helped make the process more reliable. Working within hardware restrictions also forced me to simplify data structures as much as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The results
&lt;/h2&gt;

&lt;p&gt;Once sorted, I tested the system live with 100 leads. Processing time dropped from 4 minutes to just about 1.5 minutes post-optimization, which was a win. The scores helped the sales team prioritize their efforts more effectively, improving conversion rates by around 10% according to their initial feedback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;This was my first time merging n8n with a model like GPT-4o-mini for lead scoring, and it was a rewarding experience. For environments with flaky connectivity, on-the-ground constraints, or budget issues, this setup is worth considering. However, if you're after real-time processing with zero delays, a more advanced system might be needed. Still, for my needs, it balanced functionality and economy well.&lt;/p&gt;

&lt;p&gt;Next, I'm looking into running sentiment analysis on communications with these leads to further refine scoring. It's about maximizing the advantages of the tools available while facing the realities of my operating environment.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>ai</category>
      <category>automation</category>
      <category>python</category>
    </item>
  </channel>
</rss>
