Loss = 0.0001 by step 50.
I stared at my Google Colab screen, confused. Wasn’t lower loss supposed to be good? Isn’t that the whole point of training?
Then I ran inference. The model’s output:
トトトトトトトトトトトトトトトトトトトトトトトトトトトトトトト...Japanese characters. Repeating endlessly. My n8n workflow generator had become a broken karaoke machine.
That’s when I learned the hard way: a loss of zero doesn’t mean you won. It means your model memorized instead of learned.
This is Part 2 of my journey building an AI-powered n8n workflow generator. In Part 1, I scraped and analyzed 6,837 workflows from the n8n marketplace (the Reddit community loved it — 65K views!).
Now comes the fun part: teaching an LLM to generate workflows from natural language.
Spoiler: I failed spectacularly with Mistral 7B, debugged for days, and finally succeeded with Llama 3 8B.
Let’s talk about what went wrong, what went right, and what I learned about fine-tuning LLMs.
Why Fine-Tune an LLM for Workflows?
After analyzing 6,000+ workflows, I had a dataset goldmine. But I wanted more than just insights — I wanted to build something people could actually use.
The idea: Type “Build a Telegram chatbot that uses OpenAI” → Get a complete n8n workflow configuration.
No more starting from scratch. No more browsing templates. Just describe what you want, and the AI generates the workflow structure.
Why is this a good use case for LLMs?
- Structured output — Workflows have a consistent JSON structure
- Pattern recognition — Similar descriptions → similar node combinations
- Limited vocabulary — Only ~200 node types in n8n
- Abundant data — 6,837 public examples to learn from
Perfect for fine-tuning. Or so I thought.
From Workflows to Training Data
First challenge: Turn 6,837 workflows into something an LLM can learn from.
Each workflow has:
- A name (“AI Email Assistant”)
- Nodes (Gmail Trigger, OpenAI, Gmail)
- Categories (AI, Communication)
- Complexity (node count)
I needed to create instruction-output pairs:
{ "instruction": "Create an n8n workflow for: AI Email Assistant", "input": "", "output": { "name": "AI Email Assistant", "nodes": [ {"type": "Gmail Trigger"}, {"type": "OpenAI Chat Model"}, {"type": "Gmail"} ], "node_count": 3, "categories": ["AI", "Communication"] }}I generated these in three formats:
- Alpaca format (for Llama/Mistral)
- OpenAI format (for GPT)
- Simple format (for custom pipelines)
The full dataset is on HuggingFace if you want to use it.
The Dataset Decision
Here’s something important: I didn’t use all 6,837 workflows for training.
I used a curated subset — about 1,283 high-quality examples.
Why not use everything?
- Quality over quantity — Some workflows were too simple (2 nodes) or too complex (50+ nodes). I wanted the sweet spot: workflows that teach patterns without overwhelming the model.
- Computational efficiency — Training on 1,283 examples took 55 minutes. Scaling to 6,000+ would mean 4+ hours and higher costs. I wanted to validate my approach first.
- Overfitting prevention — More data doesn’t always mean better results, especially with smaller models. A focused dataset helps the model learn patterns, not memorize examples.
- Faster iteration — With 55-minute training cycles, I could experiment, fail, and try multiple times again in a day. That agility was crucial when things went wrong.
Think of it like learning to cook: You don’t need to taste every recipe in the world to become a good chef. You need the right variety of well-chosen examples.
The results proved this approach worked — more on that later.
The Setup: Google Colab Pro
I’m applying for jobs by day and building automation tools by night. I don’t have an expensive GPU sitting around.
Enter Google Colab Pro ($9.99/month):
- A100 GPU access (occasionally)
- Enough for serious fine-tuning
- Way cheaper than buying hardware
I used Unsloth — a library that makes fine-tuning ridiculously efficient:
- 2x faster training
- 80% less memory usage
- 4-bit quantization support
- Works perfectly with Colab
The stack:
- Python 3.12- Unsloth (for efficient training)- Transformers (Hugging Face)- W&B (Weights & Biases for tracking)- Google Colab Pro (A100 GPU)Everything ready. Time to train.
Attempt #1: The Mistral Disaster
I chose Mistral 7B for my first attempt. Why?
- Smaller than Llama (faster training)
- Great reputation in the community
- Good instruction-following abilities
- Proven track record for fine-tuning
I loaded the model:
python
model, tokenizer = FastLanguageModel.from_pretrained( model_name = "unsloth/mistral-7b-v0.3-bnb-4bit", max_seq_length = 2048, dtype = None, load_in_4bit = True,)Set up LoRA (Low-Rank Adaptation) for efficient fine-tuning:
python
model = FastLanguageModel.get_peft_model( model, r = 16, # LoRA rank target_modules = ["q_proj", "k_proj", "v_proj", "o_proj"], lora_alpha = 16, lora_dropout = 0, bias = "none",)Hit train. Watched the loss drop.
Step 10: Loss = 4.5 Step 20: Loss = 2.1 Step 30: Loss = 0.8 Step 40: Loss = 0.2 Step 50: Loss = 0.0001
“This is amazing!” I thought. “It’s learning so fast!”
Then I ran inference:
python
instruction = "Create an n8n workflow for: Email automation with Google Drive"# Generate...output = "トトトトトトトトトトトトトトトトトトトトトト..."Wait, what?
Tried again:
instruction = "Build a Telegram chatbot"output = "トトトトトトトトトトトトトト..."Same thing. Just Japanese characters repeating forever.
The model had broken.
Understanding What Went Wrong
I did what any confused developer does: Google it.
“loss zero bad fine tuning”
That’s when I discovered the truth: Loss approaching zero is a massive red flag.
Here’s what happened:
The Overfitting Trap
The model didn’t learn patterns. It memorized the training examples.
When I asked for a new workflow, it didn’t generate one. It tried to recall an exact match from training data. When it couldn’t find one, it just… glitched.
Healthy loss range for fine-tuning: 0.7–1.5 My loss: 0.0001 Translation: Complete overfitting
It’s like teaching someone to cook by making them memorize recipes word-for-word. Ask them to make something slightly different? They freeze.
The Debugging Marathon
I wasn’t ready to give up. I tried everything:
Attempt #2: Lower Learning Rate
learning_rate = 5e-5 # Instead of 2e-4Result: Loss still crashed to 0.001 by step 80. Still gibberish.
Attempt #3: Fewer Epochs
num_train_epochs = 1 # Instead of 2Result: Loss hit 0.01 by step 60. Less gibberish, but still broken.
Attempt #4: Add Regularization
lora_dropout = 0.1 # Add dropoutweight_decay = 0.01 # Add regularizationResult: Loss to 0.005. Model still memorizing.
Attempt #5: Smaller LoRA Rank
r = 8 # Reduce capacityResult: Slower overfitting, but same outcome. Loss to 0.01, gibberish output.
Five attempts. Five failures.
The pattern was clear: Mistral 7B wanted to memorize, not generalize.
The Research Dive
I spent a weekend reading fine-tuning guides, research papers, and Reddit threads.
Key insights I found:
- Base model selection matters more than hyperparameters — A stable base model will generalize better than an unstable one, regardless of how you tune it.
- Some models are more prone to overfitting — Especially on structured data like code or JSON.
- Community wisdom beats solo experimentation — Other people had already figured this out.
Then I found it: Llama 3 8B has a reputation for stable fine-tuning.
Time to try a different model.
Time to try a different model.
Attempt #6: The Llama Breakthrough
I switched to Llama 3 8B:
model, tokenizer = FastLanguageModel.from_pretrained( model_name = "unsloth/llama-3-8b-bnb-4bit", max_seq_length = 2048, dtype = None, load_in_4bit = True,)Same data. Same training setup. Different base model.
Hit train.
Get Mustaphaliaichi’s stories in your inbox
Join Medium for free to get updates from this writer.
This time, the loss curve looked completely different:
Looking at my Weights & Biases dashboard:
- Initial loss: 1.9
- Step 200: 1.5
- Step 400: 1.35
- Step 600: 1.28
- Step 800: 1.24
- Step 1000: 1.22
- Final (step 1283): 1.20
A healthy, gradual descent.
The learning rate decreased smoothly. The gradient norm stayed stable. No sudden crashes. No memorization.
After 55 minutes and 46 seconds, training completed.
Two epochs. 1,283 steps. Loss plateaued at 1.235900.
Perfect range.
Time to test.
The Moment of Truth
instruction = "Create an n8n workflow for: Build a Telegram chatbot that uses OpenAI to respond to messages"# Generate...I held my breath.
{ "name": "Telegram OpenAI Bot", "nodes": [ {"type": "Telegram Trigger", "displayName": "When message received"}, {"type": "OpenAI Chat Model", "displayName": "Generate response"}, {"type": "Telegram", "displayName": "Send reply"} ], "node_count": 3, "node_types": ["Telegram", "OpenAI", "Telegram"], "categories": ["AI", "Communication", "Chatbot"]}It worked.
Valid JSON. Correct structure. Reasonable node choices.
I tried another:
instruction = "Email automation that saves attachments to Google Drive"{ "name": "Email to Drive Automation", "nodes": [ {"type": "Gmail Trigger"}, {"type": "Extract Attachments"}, {"type": "Google Drive"} ], "node_count": 3, "categories": ["Productivity", "Automation"]}Perfect.
One more:
instruction = "Social media scheduler for Twitter"{ "name": "Twitter Scheduler", "nodes": [ {"type": "Schedule Trigger"}, {"type": "Read from Spreadsheet"}, {"type": "Twitter"} ], "node_count": 3}Nailed it.
I tested 15 different prompts. All produced sensible workflows.
Quality score: 15/15.
After five failures with Mistral, Llama 3 8B just… worked.
Why Llama Worked Where Mistral Failed
I’ve thought a lot about this. Here’s what I believe made the difference:
1. Base Model Stability
Llama 3 8B is trained on a more diverse dataset with better regularization. It’s harder to push into overfitting.
Mistral 7B, while excellent for many tasks, seemed more sensitive to structured data like JSON.
2. Instruction-Following Architecture
Llama 3’s architecture is specifically optimized for following instructions. That’s exactly what I needed.
3. Community Validation
Thousands of people have successfully fine-tuned Llama 3. The community's knowledge was right.
4. Size Sweet Spot
8B parameters were the right size for this task. Not too small (underfitting), not too large (overfitting on limited data).
The Technical Details
For those who want to replicate this:
Final Training Configuration
training_args = TrainingArguments( per_device_train_batch_size = 2, gradient_accumulation_steps = 4, warmup_steps = 5, num_train_epochs = 2, learning_rate = 2e-4, fp16 = not torch.cuda.is_bf16_supported(), bf16 = torch.cuda.is_bf16_supported(), logging_steps = 1, optim = "adamw_8bit", weight_decay = 0.01, lr_scheduler_type = "linear", seed = 3407, output_dir = "outputs",)LoRA Configuration
r = 16 # LoRA attention dimensionlora_alpha = 16 # Alpha parameterlora_dropout = 0 # No dropout needed with Llamabias = "none"target_modules = ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"]Training Metrics
- Total steps: 1,283
- Training time: 55 minutes 46 seconds
- GPU: A100 (via Colab Pro)
- Final loss: 1.235900
- Hardware cost: $9.99/month (Colab Pro)
The Loss Curve
If you look at the W&B charts (Weights & Biases tracking):
- train/loss: Smooth decrease from 1.9 to 1.2, then plateau
- train/learning_rate: Linear decay as scheduled
- train/grad_norm: Stable throughout (good sign!)
No sudden spikes. No crashes. Just healthy, gradual learning.
What I Learned
This journey taught me more than any tutorial could:
1. Loss = 0 is a Bug, Not a Feature
If your loss crashes to near-zero during fine-tuning, stop immediately. You’re memorizing, not learning.
Target range: 0.7–1.5 for most tasks.
2. Model Selection > Hyperparameter Tuning
I spent days tweaking learning rates, epochs, and dropout with Mistral. Wasted time.
Switching to Llama 3 8B with default settings solved everything instantly.
Choose a stable base model. Then tune.
3. Overfitting Looks Like Success (Until It Doesn’t)
Watching loss drop is exciting. It feels like progress.
But if it drops too fast, you’re heading for disaster.
4. Community Wisdom is Real
The ML community had already figured out that Llama 3 8B is great for fine-tuning. I should have trusted that from the start.
Don’t reinvent the wheel. Learn from others’ mistakes.
5. Document Failures, Not Just Successes
This article is better because I failed with Mistral. The failures taught me what success looks like.
Build in public. Share the messy parts.
6. Quality > Quantity (Dataset Edition)
1,283 curated examples > 6,000+ noisy examples.
I spent time selecting workflows that represented good patterns. That curation mattered.
7. Iteration Speed Matters
55-minute training cycles let me try multiple approaches in a weekend.
If each attempt took 4 hours, I might have given up after Mistral.
Try It Yourself
The model is live on Hugging Face: MustaphaL/n8n-workflow-generator
The training dataset: MustaphaL/n8n-workflow-training-data
the GitHub repo : https://github.com/MuLIAICHI/MuLIAICHI-n8n-what-the-hell-is-everyone-building
You can use it like this:
from unsloth import FastLanguageModelmodel, tokenizer = FastLanguageModel.from_pretrained( model_name = "MustaphaL/n8n-workflow-generator", max_seq_length = 2048, dtype = None, load_in_4bit = True,)FastLanguageModel.for_inference(model)# Generate workflowinputs = tokenizer( "Create an n8n workflow for: Your description here", return_tensors="pt").to("cuda")outputs = model.generate(**inputs, max_new_tokens=512)workflow = tokenizer.decode(outputs[0])What’s Next: Part 3
I have a working model. But it’s just sitting in a Colab notebook.
In Part 3, I’ll cover:
- Deploying to Hugging Face Spaces
- Building a web interface with Gradio
- Creating an API endpoint
- Real user testing
- Performance optimization
From notebook to production. From experiment to product.
The Real Lesson
Here’s what I want you to take away:
Building AI isn’t about having the perfect plan from the start.
I didn’t know Mistral would fail. I didn’t know Llama would succeed. I just tried things, failed, learned, and tried again.
That’s how most good things get built.
So if you’re thinking about fine-tuning an LLM, or building any AI project:
- Start with what you have
- Expect to fail (a lot)
- Learn from the failures
- Try different approaches
- Document the journey
- Share what you learn
Five failures with Mistral taught me more than one success with Llama ever could.
The training data is public. The model is public. The code is public. Everything is out there for you to use, learn from, or improve upon.
That’s the power of building in public.
Coming in Part 3: “Deploying the n8n Workflow Generator — From Colab Notebook to Production API”
Mustapha Liaichi is an AI Engineer based in Morocco, navigating the job market by day and building automation tools by night. Creator of n8nlearninghub.com. When not debugging overfitting models, he’s helping others learn n8n automation.
Read Part 1: What Are People Actually Building in n8n?
Try the model: HuggingFace Get the data: Training Dataset Join the community: n8nlearninghub.com / follow on reddit : here
