Enrich Functions
Add context from knowledge bases using semantic search
Enrich functions perform semantic search against collections to add contextual information to your data. They're useful for looking up reference data, adding context from knowledge bases, or matching against existing records.
When to Use
Use an Enrich function when you need to:
- Look up vendor information from a master list
- Match extracted data against existing records
- Add context from a knowledge base or documentation
- Find similar items in a collection
- Validate data against reference sources
Prerequisites
Before creating an Enrich function, you need:
- A Collection with your reference data
- Items added to the collection with searchable content
See the Collections API for creating and populating collections.
Configuration Fields
Required Fields
| Field | Type | Description |
|---|---|---|
functionName | string | Unique identifier for the function |
type | string | Must be "enrich" |
config | object | Enrichment configuration |
Optional Fields
| Field | Type | Default | Description |
|---|---|---|---|
displayName | string | - | Human-readable display name |
tags | string[] | - | Tags for organization |
Enrich Config
The config object holds a steps array. Each step reads a value from the input, searches a collection, and writes the result back onto the data.
| Field | Type | Description |
|---|---|---|
steps | object[] | One or more enrichment steps (required) |
Each entry in steps carries:
| Field | Type | Default | Description |
|---|---|---|---|
sourceField | string | - | JMESPath to the value to search with (required) |
collectionName | string | - | Name of the collection to search (required) |
targetField | string | - | JMESPath where the match is written (required) |
topK | number | - | Number of matches to return per value (0–100) |
searchMode | string | semantic | One of semantic, exact, hybrid — see Search modes |
scoreThreshold | number | 0.6 | Max distance to keep — semantic & hybrid (0–2, lower is closer) |
includeScore | boolean | false | Include the match score in the output |
includeSubcollections | boolean | false | Search child collections under collectionName |
When sourceField uses array notation (e.g. lineItems[*].description), targetField must use the same notation (e.g. lineItems[*].matchedProduct) so each item carries its own match.
Search modes
searchMode selects how candidates are retrieved from the collection before results are returned. Every mode returns distinct items (duplicates are collapsed), best match first.
| Mode | How it retrieves | Best for |
|---|---|---|
semantic (default) | Dense-vector cosine similarity — matches on meaning, not wording | Natural-language and conceptual matches (e.g. "red sports car" → "crimson convertible") |
hybrid | Fuses the semantic and keyword rankings with weighted Reciprocal Rank Fusion | Cases where meaning and exact tokens matter — tags, categories, partial identifiers |
exact | Case-insensitive substring match on the item text | Literal identifiers — SKUs, routing numbers, account IDs |
Notes:
scoreThresholdapplies tosemanticandhybrid. It's a maximum dissimilarity on a 0–2 scale (lower keeps closer matches; default0.6). Forhybrid, the fusion score is mapped onto that same scale, so one threshold covers both modes.exactdoes keyword matching and ignores it. Note0.6is calibrated for cosine distance and is relatively strict forhybrid, so consider tuning it on hybrid steps.- With
includeScore, each match carries ascoreand ascoreType:cosineDistanceforsemanticorhybridScoreforhybrid. Both are 0–2 dissimilarities where lower is better (hybrid's fusion score is mapped onto cosine distance's scale;0.0= top of both the semantic and keyword rankings). semanticandhybridresults are additionally re-ranked by an LLM;exactis not.
Identifying a match
Every match carries an id. What it holds depends on where the match came from, and the
prefix tells you which:
| Source | id | Example |
|---|---|---|
| Collection | The collection item the match came from | clitm_2xK9… |
| Endpoint | A content hash of the match's data | h_a5fef997ef9f8992 |
Because a step has one source, every match within a given enriched field carries the same
kind of id.
Use id to reference a match back to bem — notably when submitting a ground-truth
re-ranking to POST /v3/events/{eventID}/enrich-feedback,
which resolves candidates by id and rejects anything else. For collection matches the
same value joins directly against your collection.
Two things worth knowing:
- A collection
idis a durable handle: editing the item's data does not change it, so ground truth recorded against it keeps resolving. A content hash does not have this property — editing the underlying data changes theid, and labels against the old value stop resolving. - Matches are de-duplicated by payload, so where the same data occupies several rows (the uniqueness constraint is per collection and embedding model, and a search can span collections) the oldest row is the representative.
- An endpoint step with no
matchInstructionsreturns the raw fetched values, which carry noidat all and therefore cannot be re-ranked.
Example
Vendor Lookup
Enrich invoice data with vendor information from a master vendor list:
{
"functionName": "vendor-enricher",
"type": "enrich",
"displayName": "Vendor Information Lookup",
"config": {
"steps": [
{
"sourceField": "vendorName",
"collectionName": "vendor_master_list",
"targetField": "vendor",
"topK": 1,
"searchMode": "semantic"
}
]
},
"tags": ["vendor", "lookup"]
}Product Matching
Match extracted product names against a product catalog:
{
"functionName": "product-matcher",
"type": "enrich",
"displayName": "Product Catalog Matcher",
"config": {
"steps": [
{
"sourceField": "lineItems[*].description",
"collectionName": "product_catalog",
"targetField": "lineItems[*].matchedProduct",
"topK": 1,
"searchMode": "semantic"
}
]
},
"tags": ["products", "matching"]
}Workflow Pattern
Enrich functions typically follow Transform functions:
Document
│
▼
┌───────────┐
│ Transform │ Extract raw data
└───────────┘
│
▼
┌───────────┐
│ Enrich │ Add context from collection
└───────────┘
│
▼
Enriched OutputExample Flow
- Transform extracts vendor name "Acme Corp" from invoice
- Enrich searches vendor collection for "Acme Corp"
- Output includes matched vendor ID, address, payment terms
Setting Up Collections
Before using Enrich functions, create a collection and add items to it. Each item carries a data field that can be either a string or an arbitrary JSON object — the data is embedded asynchronously, and POST /v3/collections/items returns immediately with status: "pending" and an eventID you can correlate with webhook notifications once embedding completes.
# Create the collection
curl -X POST https://api.bem.ai/v3/collections \
-H "Content-Type: application/json" \
-H "x-api-key: $BEM_API_KEY" \
-d '{
"collectionName": "vendor_master_list"
}'
# Add items (mix of string and object payloads)
curl -X POST https://api.bem.ai/v3/collections/items \
-H "Content-Type: application/json" \
-H "x-api-key: $BEM_API_KEY" \
-d '{
"collectionName": "vendor_master_list",
"items": [
{ "data": "Acme Corporation - primary supplier for office supplies" },
{
"data": {
"vendorID": "V001",
"name": "Acme Corporation",
"paymentTerms": "Net 30"
}
}
]
}'Collection names must contain only letters, digits, underscores, and dots — each segment must start with a letter or underscore — and dot notation is meaningful: customers.premium.vip is a child of customers.premium. Texts above the embedding model's 8,192-token limit are rejected; check sizes with POST /v3/collections/token-count before bulk uploads.