← All N8N workflows
Automation & utilitiesCodeHTTP RequestManual TriggerMergeEdit Fields

[2/3] Set up medoids (2 types) for anomaly detection (crops dataset)

Configured with HTTP Request, Split Out. Review the connections and setup before adapting this template.

Workflow preview
TriggerWhen clicking ‘Testworkflow’Manual TriggerWeb / APITotal Points inCollectionHTTP RequestWeb / APICluster Distance MatrixHTTP RequestCodeScipy Sparse MatrixCodeWeb / APISet medoid idHTTP RequestWeb / APIGet Medoid VectorHTTP RequestTransformPrepare for SearchingThresholdEdit FieldsWeb / APISearching ScoreHTTP RequestTransformThreshold ScoreEdit FieldsWeb / APISet medoid thresholdscoreHTTP RequestTransformSplit Out1Split OutTransformMergeMergeTransformTextual (visual) cropdescriptionsEdit FieldsWeb / APIEmbed textHTTP RequestWeb / APIGet Medoid by TextHTTP RequestWeb / APISet text medoid idHTTP RequestTransformPrepare for SearchingThreshold1Edit FieldsTransformThreshold Score1Edit FieldsWeb / APISearching Text MedoidScoreHTTP RequestTransformMedoids VariablesEdit FieldsTransformText Medoids VariablesEdit FieldsTransformQdrant clustervariablesEdit FieldsTransformInfo About CropClustersEdit FieldsWeb / APICrop CountsHTTP RequestTransformSplit OutSplit OutWeb / APISet text medoidthreshold scoreHTTP Request

Node categories in this workflow

  • Trigger
  • Web / API
  • Code
  • Transform

Icons describe general node roles, inferred from node types. Inspect the parameters for the configured action.

Drag to pan; use + and − to zoom. Reconstructed from the exported node positions; nothing runs here. Dashed lines represent non-main connections. Full node names and notes appear below.

How it works

The exported workflow contains the following configured nodes. Their connections are shown above; inspect the JSON for the complete logic, expressions, and external actions.

Workflow node names, types, and operations
NodeCategoryTypeOperation
When clicking ‘Test workflow’TriggerManual TriggerSee node parameters
Total Points in CollectionWeb / APIHTTP RequestPOST
Cluster Distance MatrixWeb / APIHTTP RequestPOST
Scipy Sparse MatrixCodeCodeSee node parameters
Set medoid idWeb / APIHTTP RequestPOST
Get Medoid VectorWeb / APIHTTP RequestPOST
Prepare for Searching ThresholdTransformEdit FieldsSee node parameters
Searching ScoreWeb / APIHTTP RequestPOST
Threshold ScoreTransformEdit FieldsSee node parameters
Set medoid threshold scoreWeb / APIHTTP RequestPOST
Split Out1TransformSplit OutSee node parameters
MergeTransformMergeSee node parameters
Textual (visual) crop descriptionsTransformEdit FieldsSee node parameters
Embed textWeb / APIHTTP RequestPOST
Get Medoid by TextWeb / APIHTTP RequestPOST
Set text medoid idWeb / APIHTTP RequestPOST
Prepare for Searching Threshold1TransformEdit FieldsSee node parameters
Threshold Score1TransformEdit FieldsSee node parameters
Searching Text Medoid ScoreWeb / APIHTTP RequestPOST
Medoids VariablesTransformEdit FieldsSee node parameters
Text Medoids VariablesTransformEdit FieldsSee node parameters
Qdrant cluster variablesTransformEdit FieldsSee node parameters
Info About Crop ClustersTransformEdit FieldsSee node parameters
Crop CountsWeb / APIHTTP RequestPOST
Split OutTransformSplit OutSee node parameters
Set text medoid threshold scoreWeb / APIHTTP RequestPOST

Setup

  1. Download the JSON and import it into an isolated n8n instance. Leave the workflow inactive.
  2. Connect your own credentials through n8n’s credential store. Replace REPLACE_WITH_* values with your resource identifiers.
  3. Inspect API destinations, Code nodes, database queries, file paths, and outbound actions.
  4. Run with synthetic data and harmless destinations. Enable triggers only after checking the actual outputs.

Requirements

An n8n instance with the listed node types and compatible versions. Service-connected workflows may require accounts, credentials, or paid APIs.

Credential types declared in the source

  • httpHeaderAuth
  • qdrantApi

Workflow notes

Setting Up Medoids for Anomaly Detection

Setting Up Medoids for Anomaly Detection

Preparatory workflow to set cluster centres and cluster threshold scores, so anomalies can be detected based on these thresholds

Here, we're using two approaches to set up these centres: the upper branch is the "distance matrix approach", and the lower is the "multimodal embedding model approach".

Sticky Note1

Once again, variables for Qdrant: cluster URL and a collection we're working with

Sticky Note2

Which point in the cluster we're using to draw threshold on: the furthest one from center, or the 2nd, ... Xth furthest one;

Sticky Note3

Here we are getting facet counts: information which unique values are there behind "crop_name" payload and how many points have these values (for example, we have 31 "cucumber" and 29 "cotton")

Sticky Note4

Which point in the cluster we're using to draw threshold on: the furthest one from center, or the 2nd, ... Xth furthest one; <this is the 2nd approach>

Sticky Note5

We need to get the total amount of points in Qdrant collection to use it as a limit in the "Crop Counts" node, so we won't lose any information;

Sticky Note6

Here we're extracting and gathering all the information about crop clusters, so we can call Qdrant distance matrix API for each cluster. We're propagating the biggest cluster size (of labeled data, in our case all data is labeled; for real use cases don't call distance matrix API if your labeled data is more than a couple of hundreds), the number of unique crop values and unique crop values themselves. We will run the algorithm once per unique crop cluster (to find it's center and threshold).

Sticky Note8

Hardcoded descriptions on how each crop usually looks; They were generated with chatGPT, and that can be technically done directly in n8n based on the crop name or a crop picture (we need a good description of how the most normal specimen of a crop looks like)

Sticky Note9

Calling distance matrix API once per cluster.

sample - how many points we are sampling (here filtered by crop_name field, so we are sampling within each cluster, and since we are passing the biggest cluster size to sample, we will get all points from each cluster.

limit is the number of neighbours distance to which we will see calculated. Since we want all pairwise distances between the points within a cluster, here we're once again setting an upper limit equal to the biggest cluster size;

Sticky Note10

Splitting out into each unique crop cluster

Sticky Note11

Using distance matrix generated by Qdrant and coo_array from scipy, we're finding a representative for each cluster (point which is the most similar to all other points within a cluster, based on the Cosine distance)

Sticky Note12

To find a representative with this approach, we:

  1. Embed descriptions of crops with the same Voyage model we used for images (we can do so, since model is multimodal)
  2. For each (crop) cluster, find an image the closest by Cosine similarity metric to this embedded description. We will consider it a perfect representative of the cluster
Sticky Note13

Embedding descriptions with Voyage model [Note] mind input_type, it's "query"

Sticky Note14

Find the closest image to the description embeddings (done per cluster) [Note] Mind exact parameter [Note] limit is 1 because vector database always returns points sorted by distance from the most similar one to the least [Note] using parameter is here because our vectors uploaded in the previous pipeline are named "voyage".

Sticky Note15

Fetching vectors of centres by their IDs

Sticky Note16

Set in Qdrant "is_medoid" payloads for points which were defined as centres by "distance matrix approach"

Sticky Note17

Here, we don't have to fetch a vector by point id as in the "distance matrix approach", since an API call in the previous node is able to return vectors stored in Qdrant as a response, while the distance matrix API returns only points IDs.

Sticky Note18

Set in Qdrant "is_text_anchor_medoid" payloads for points which were defined as centres by "multimodal embedding model approach".

Sticky Note19

Starting from here, this and the three following nodes are analogous for both methods, with a difference only in variable names. The goal is to find a class (cluster) threshold score so we can use it for anomaly detection (for each class).

Sticky Note20

Finding the most dissimilar point to a centre vector (within each class) is equivalent to finding the most similar point to the opposite of a centre vector, aka the centre vector with all coordinates multiplied by -1. It is always true with Cosine vector similarity metric (that we're using).

Sticky Note21

So here, we found the most dissimilar point within the crop class to the class centre (or the Xth dissimilar point, depending on a variable set in the beginning of this pipeline). Our threshold score is the similarity score between this point and the class centre. Now we're saving it as meta information of each class centre point. All preparatory work for anomaly detection is done.

For anomaly detection

For anomaly detection

  1. The first pipeline is uploading (crops) dataset to Qdrant's collection.
  2. This is the second pipeline, to set up cluster (class) centres in this Qdrant collection & cluster (class) threshold scores.
  3. The third one is the anomaly detection tool, which takes any image as input and uses all preparatory work done with Qdrant (crops) collection.

To recreate it

You'll have to upload crops dataset from Kaggle to your own Google Storage bucket, and re-create APIs/connections to Qdrant Cloud (you can use Free Tier cluster), Voyage AI API & Google Cloud Storage

In general, pipelines are adaptable to any dataset of images

[2/3] Set up medoids (2 types) for anomaly detection (crops dataset)

[2/3] Set up medoids (2 types) for anomaly detection (crops dataset)

Purpose and structure

This folder contains the [2/3] Set up medoids (2 types) for anomaly detection (crops dataset) workflow. Its executable nodes use code, httpRequest, manualTrigger, merge, set, splitOut. The table and connection map below are derived from the JSON, not an inferred guarantee of behavior.

Setup

Import inactive. Configure your own credentials and replace REPLACE_WITH_* values. Review external destinations and Code nodes before running.

Entry points

When clicking ‘Test workflow’

Verification

Sanitized source candidate; not runtime-tested. This overview is generated from node metadata. Read the folder README for the connection map, credential types, and license.

Attribution

https://github.com/Zie619/n8n-workflows/blob/94007c1445d9258a7da116646b79473e7c7c3282/workflows/Splitout/1915_Splitout_Code_Automation_Webhook.json MIT; preserve source notices.

More in Automation & utilities