Describe the problem/error/question
Hi everyone,
I’ve built an agentic customer service workflow in n8n that processes incoming customer messages from multiple platforms.
Setup
-
Each item represents one customer conversation (with many attributes like conversation_id, message text, metadata, etc.)
-
Some messages require a response, others don’t
Problem 1: Lineage break due to filtering
I filter out messages that don’t need a response early in the flow.
This causes:
-
Item count to change (N → M)
-
Loss of item lineage / mapping
-
Downstream nodes can no longer reliably reference the original item
Problem 2: Agent node destroys original data
The filtered items then pass through multiple Agent nodes:
However:
-
The Agent node output only contains { output: "..." }
-
All original fields (e.g. conversation_id, metadata) are lost
What I need
I want to enrich the original input item with:
-
intent
-
context
-
decisions
-
etc.
Current workaround (problematic)
I tried:
Problems:
-
LLM sometimes slightly alters the ID → merge fails
-
requires multiple Merge nodes → fragile
-
breaks easily with branching / async execution
-
not production-safe
Core challenge
After:
I no longer have a reliable way to map LLM output back to the original item
Question
What is the best practice in n8n to:
-
Preserve item identity through Agent nodes?
-
Enrich original items with LLM output reliably?
-
Handle cases where some items are filtered out (i.e. lineage is broken)?
Constraints
-
Agent node does not pass through input data
-
LLM output cannot be trusted for IDs
-
Workflow includes branching, MCP calls, multiple LLM steps
What I’m considering
-
Avoid filtering (tag instead?)
-
Wrapping original data before Agent nodes
-
Avoiding Merge completely
But I’d love to understand what the recommended pattern is for this kind of pipeline.
Goal
A production-safe pattern where:
Thanks a lot — this feels like a fundamental pattern issue with n8n + LLM workflows, so any guidance would be hugely appreciated 
What is the error message (if any)?
None, subsequent merges faisl silently if the llm does not pass the unique ID correctly.
Please share your workflow
(Select the nodes on your canvas and use the keyboard shortcuts CMD+C/CTRL+C and CMD+V/CTRL+V to copy and paste the workflow.)
Share the output returned by the last node
No output, fails silently.
Information on your n8n setup
- n8n version: 2.15.0
- Database (default: SQLite): SQLite
- n8n EXECUTIONS_PROCESS setting (default: own, main): own
- Running n8n via (Docker, npm, n8n cloud, desktop app): Docker
- Operating system: Linux
Hi @luxmediq you should be able to solve pretty much all the context leak and output matching problems just by upgrading your SYSTEM prompt and the AI model being used, but for the output structure consider using an Output Parser so that the AI agent knows exactly how to output and also in the output parser sample JSON give it kind of real world explanations of their own fields this works all the time, and if you have some kind of vector database where you store all the enrichment guidelines so consider using that instead of an MCP, also for the data you provide to the AI agent try to clearly classify it instead of actually dumping it down, basically curate your AI prompt and System prompt very precisely so that the AI agent knows what to follow, and things should be working fine after that.
Hey Benjamin, thank you for your response and great perspective! The core issue remains the “enrichment”, so bringing the tranformed information back to the originial input. The ref_id or any other value the LLM should return because subsequent logic (merge, lookup, etc..) builds upon it is not reliable enough for production use. Trigger for this thread is exactly this error happening, where the LLM has a “typo” in the ID and thus siltently fails the merge with the original message.
Looping (Split Items) would solve the “mapping issue” since there is only one item in the loop at a given time, but combined with a human in the loop step generate new challenges 
Hi Anschul, the prompt is super tight, output format is reliable, even defining the exact value to output, but the LLM still generates “typos” from time to time which is inacceptable in production automation.
@luxmediq can you show us the part i mean the flow where it causes errors, also the problem can be related to the AI model you are using so make sure you are using a capable model which could work with all that context.
I hope this visualizes the problem. The red circled node on the left is a Set Node that defines all field I need (incl. ID of the conversation with the customer). On the right the enhancement of the information from the set node with the additional fields from the Agent nodes via merge on Conversation ID.. The lineage is broken prior to the set node through a filter..
This is a really common pain point when building agentic workflows in production. I hit the same issue building a multi-platform customer service chatbot.
The pattern I use: “Context Envelope” via Set node before each Agent
Before each Agent node, use a Set node to wrap/preserve the original fields:
// Set node - add these fields:
original_conversation_id: {{ $json.conversation_id }}
original_metadata: {{ $json.metadata }}
original_platform: {{ $json.platform }}
// ... any fields you need downstream
Then after the Agent node output, use another Set node to merge:
conversation_id: {{ $('Set - Preserve Context').item.json.original_conversation_id }}
llm_output: {{ $json.output }}
metadata: {{ $('Set - Preserve Context').item.json.original_metadata }}
This works because $('NodeName').item still references the paired input item even through Agent nodes - as long as you’re processing one item at a time (which is usually the case with Loop Over Items).
For filtering (Problem 1): Instead of filtering items out, use an IF node but keep both branches alive. On the “no response needed” branch, just use a No-op (Set node that passes through), then merge both branches at the end. This preserves lineage without dropping items.
Key rule I follow: Never rely on the LLM to return IDs. Always store context before the Agent in a referenced node, then retrieve via $('nodeName').item.json.
“لا تعتمد أبداً على نموذج اللغة الكبير لإرجاع المعرّفات” هو الحدس الصحيح — من الجدير التعامل معه كقاعدة معمارية، لا كمشكلة في الأوامر. لا يوجد قدر من تحسين الأوامر يمكنه أن يجعل مولّد الرموز يصل إلى 100% في نسخ المعرّفات حرفياً، وفي الإنتاج، 99.9% لا يزال يعني فشل دمج صامت لكل ألف عنصر.
النمط الذي يحل هذه المشكلة نهائياً: المعرّفات تسير حول النموذج، وليس عبره. نهج @nguyenthieutoan للمغلفات السياقية هو بالضبط هذا ويعمل بشكل جيد في n8n النقي. النسخة العامة: لف كل عنصر كـ {passthrough: {...ids, ...metadata}, payload: {...fields for the LLM}}، أرسل فقط payload إلى الوكيل، وأعد إرفاق passthrough آلياً بعد ذلك. مفتاح الدمج لا يلمس النموذج أبداً، لذا لا يمكن أن يحتوي على خطأ إملائي.
إفصاح صغير: لقد واجهت هذا الفشل بالذات بشكل متكرر بحيث بنيته في منتج (Entity Enricher — أنا المؤسس). هناك، الحقول المحددة بـ preserve يتم استعادتها إلى قيم الإدخال الأصلية بعد التثري — يمكن للنموذج أن يكتب أي شيء يريده، ثم يتم إعادة تطبيق الحقيقة الأرضية آلياً، والنتائج تعود مع مفاتيح الإدخال الخاصة بك حرفياً حتى يكون الدمج مرة أخرى في نظامك مجرد ربط. نفس المبدأ الموجود في مغلف عقدة Set، فقط يتم فرضه بواسطة خط الأنابيب بدلاً من الانضباط.
بخصوص مشكلة التصفية والنسب: الوضع العلامات بدلاً من التصفية (IF مع كلا الفرعين نشطين) هو النمط الأنظف، كما اشتبهت — النسب يبقى على قيد الحياة، والفرع “لا توجد استجابة مطلوبة” ببساطة لا يفعل شيئاً.
واجهت هذه المشكلة بالذات أثناء بناء مصنف استقبال العملاء. كان إخراج عقدة LLM Chain يستبدل بيانات النموذج الأصلية بالكامل، لذا اختفت الحقول مثل الاسم والبريد الإلكتروني بحلول الوقت الذي وصلت فيه سير العمل إلى العقد الموجودة بعدها. حدث الشيء نفسه عندما أضفت عقدة Gmail لإرسال رسائل التأكيد.
كان الحل هو إضافة عقدة Set مباشرة بعد LLM (أو عقدة Gmail) لإعادة بناء كائن واحد نظيف وثابت. بهذه الطريقة، كل شيء بعد ذلك يقرأ من هيكل واحد يمكن التنبؤ به بدلاً من محاولة العودة للخلف للوصول إلى عقد مختلفة سابقة.
وجدت أيضاً أنه من المفيد فصل التسجيل بالكامل. نقلت عقدة تسجيل Google Sheets الخاصة بي في اتجاه المصدر، مباشرة بعد خطوة التصنيف، قبل أي فروع أو رسائل بريد إلكترونية. بما أن التسجيل لا يحتاج فعلياً إلى أي شيء ينتجه خطوة البريد الإلكتروني، فإن القيام به مبكراً يعني أن سير العمل لن ينقطع أو يفقد البيانات إذا فشلت عقدة بريد إلكتروني أو استبدلت العنصر.
ليس لديّ خبرة في إعدادات MCP متعددة الوكيل المعقدة أو قواعد البيانات الموجهة المتقدمة، لذا قد لا يكون هذا قابلاً للتوسع إلى فروع غير متزامنة معقدة. لكن بالنسبة لتدفق تصنيف وتوجيه قياسي، فإن الاحتفاظ ببيانات المرور في عقدة Set والتسجيل المبكر كان نمطاً موثوقاً بالنسبة لي.