Skip to main content
Intelliger
Agentic Commerce Architecture

Ecommerce Product Search at 100,000-Item Scale

A production ecommerce product search algorithm using typed filters, hybrid retrieval, compatibility, live inventory and outcome-aware reranking at scale.

Ecommerce Product Search at 100,000-Item Scale
Intelliger
13 minute read

An ecommerce product search algorithm for 100,000 items cannot be a single prompt. The catalog is a changing database with variants, regional prices, compatibility rules, inventory, promotions, warranties and delivery constraints.

Developers still reach for the context window first. Export the product feed, chunk it, embed it, retrieve a few passages, and ask the model to choose. That can produce a convincing demo. It also produces the wrong laptop charger, recommends an unavailable size, quotes yesterday's price and loses the distinction between a product family and a purchasable variant.

A bigger context window only lets you make the same category mistake at greater expense.

Commerce retrieval needs a query plan. The language model can interpret the customer's request and explain the result. It should not scan the full catalog, invent missing attributes or act as the authoritative source for volatile facts.

How ecommerce product search becomes an agent interface

This is no longer a hypothetical traffic source. OpenAI has extended its Agentic Commerce Protocol to product discovery, with merchants providing product feeds and promotions so catalogs can appear in ChatGPT. Its current shopping research flow can use merchant product data, public product information and other retail sources, then return a small set of top picks with tradeoffs. OpenAI's product discovery announcement and shopping research documentation make the architecture problem visible: agents need complete product coverage, but users need a short, relevant answer.

Those goals pull in opposite directions. Coverage belongs in retrieval infrastructure. Selection belongs in a bounded ranking pipeline. Only the final candidates belong in model context.

A product is not one document

Take an industrial pump sold in four voltage variants across six markets. The catalog record may include:

  • stable identity, brand, family and model
  • technical attributes and compatible fittings
  • product and compliance documents
  • market eligibility and customer-segment restrictions
  • variant-level SKU, voltage, dimensions and lead time
  • price lists, contract prices and active promotions
  • inventory by location
  • substitutes and superseded models

Flattening that into prose damages the information developers need most. A sentence such as "available in 230V and 400V from EUR 2,400" cannot tell the executor which SKU costs EUR 2,400, for which customer, in which country, or whether it is in stock.

Build a canonical product model that preserves identities and relationships:

type CatalogVariant = {
  productId: string
  variantId: string
  sku: string
  title: string
  taxonomy: string[]
  attributes: Record<string, string | number | boolean>
  compatibilityIds: string[]
  marketIds: string[]
  evidenceRefs: Array<{ type: string; digest: string; uri: string }>
  searchableText: string
  embeddingRef?: string
}

Price and inventory do not belong in this stable index unless you can tolerate their staleness. Even the same product can have contextual pricing by country or buyer segment. Shopify, for example, exposes product pricing in a specific country through its contextual pricing API rather than treating one catalog price as universal. Shopify's ProductContextualPricing reference is a useful reminder that "the price" is often a function call.

Turn the request into a typed intent

Suppose a facilities engineer asks:

Find a replacement pump for model XJ-40, 400V, food-safe seals, delivery to Hamburg by Friday, below EUR 3,500.

The model's first job is not to recommend a pump. It is to produce a typed query plan and expose uncertainty:

{
  "category": "industrial_pump",
  "must": {
    "voltage": "400V",
    "seal_certification": "food_safe",
    "compatible_with": "model:XJ-40",
    "delivery_region": "DE-HH",
    "latest_delivery_date": "2026-08-14",
    "currency": "EUR",
    "max_total": "3500.00"
  },
  "preferences": {
    "energy_efficiency": "higher_is_better",
    "warranty_months": "higher_is_better"
  },
  "unresolved": []
}

Validate this object against a schema. Map units and taxonomy terms. If "food-safe" could refer to the wetted parts, lubricant or entire assembly, ask a question before retrieval. Semantic similarity cannot repair an underspecified safety constraint.

Use a staged retrieval pipeline

A practical large-catalog path looks like this:

intent parsing
  -> hard structured filters
  -> lexical retrieval
  -> semantic retrieval
  -> compatibility expansion
  -> candidate fusion
  -> live price, inventory and delivery calls
  -> policy and eligibility checks
  -> learned or rules-based reranking
  -> 3 to 10 candidates for the model

Each stage has a different job.

Structured filters enforce facts that must be true: voltage, market, certification, size, buyer eligibility. Put these filters before expensive retrieval where possible.

Lexical search catches identifiers and exact language. SKU XJ-40-R2, part numbers and standards such as DIN 11864 often perform better with an inverted index than an embedding.

Semantic retrieval helps with descriptions that do not share exact terms. "Quiet pump for a small dairy line" may match products described through decibel levels and sanitary applications.

Compatibility is a graph problem. A replacement may require an adapter, a firmware floor or a prohibited combination. Do not reduce that to nearby vectors.

Fusion combines independently ranked candidate lists. Reciprocal rank fusion is one option, but the exact method matters less than preserving the individual retrieval signals for debugging.

Live enrichment calls authoritative systems for current price, promotion, stock and delivery promise. This happens after the candidate set is small enough to query efficiently.

Reranking scores candidates against user utility and transaction feasibility. It can include expected delivery success, return risk or historical compatibility outcomes, but never turn hard constraints into soft preferences.

Keep hard filters out of the scoring function

This error is subtle. A team gives compatibility a large positive weight and price a large negative weight. A cheap incompatible part can still win if the weights line up badly.

Represent non-negotiable requirements as eligibility predicates:

function eligible(candidate: EnrichedCandidate, intent: Intent): boolean {
  return candidate.marketEligible
    && candidate.compatibility.status === 'verified'
    && candidate.voltage === intent.must.voltage
    && candidate.delivery.latestDate <= intent.must.latestDeliveryDate
    && decimal(candidate.totalPrice).lte(decimal(intent.must.maxTotal))
}

function score(candidate: EnrichedCandidate): number {
  return 0.35 * candidate.semanticRelevance
    + 0.25 * candidate.deliveryConfidence
    + 0.20 * candidate.energyEfficiency
    + 0.10 * candidate.warrantyScore
    + 0.10 * candidate.outcomeQuality
}

The coefficients are illustrative. The separation is not. First decide whether a product can satisfy the request. Then rank eligible products.

Give the model compact evidence, not catalog prose

The final context should contain a handful of normalized candidate cards:

{
  "variant_id": "variant:pump-821:400v",
  "matched_constraints": [
    "400V",
    "food_safe_seals",
    "verified_replacement_for:XJ-40"
  ],
  "price": {
    "amount": "3275.00",
    "currency": "EUR",
    "observed_at": "2026-08-11T09:42:10Z"
  },
  "inventory": {
    "status": "available",
    "location": "DE-HAM-2",
    "observed_at": "2026-08-11T09:42:11Z"
  },
  "delivery": {
    "latest_date": "2026-08-14",
    "confidence": 0.93
  },
  "evidence": [
    { "type": "compatibility_matrix", "digest": "sha256:..." }
  ]
}

Now the model can explain why this candidate fits, compare tradeoffs and ask for confirmation. It does not need the other 99,997 products.

Treat discovery and transaction state differently

The Universal Commerce Protocol distinguishes exploration from purchase finalization. Its Cart capability supports pre-purchase item collection, while Checkout introduces payment handlers, status and order finalization. UCP also says checkout-time eligibility and policy enforcement must use binding transaction data rather than provisional context. UCP Cart and UCP Checkout support an important boundary: a discovery result is a candidate, not a promise.

Before reservation or checkout, refresh every volatile field and bind the accepted offer to a digest or version. If price changed, the agent should show the change or request approval according to policy. It should not quietly reuse the discovery snapshot.

Failure cases worth keeping in the evaluation set

Build cases that look plausible to a language model but fail commercially:

CaseExpected behavior
Exact SKU query loses to a semantically similar productlexical signal restores exact match
Product family matches but voltage variant does notvariant filter rejects it
Price is in budget but excludes required adaptertotal-system price fails budget
Indexed inventory says available, live system says zerolive state removes candidate
Compatible product is not certified for buyer's marketeligibility denies it
Semantic match lacks compatibility evidenceask or exclude, do not infer
Promotion expired between discovery and checkoutrefresh and reprice
Two merchants use the same manufacturer part numberpreserve merchant and offer identity
Reranker favors high-converting item over stated constrainthard filter blocks it
Product text contains prompt injectiontreat catalog content as untrusted data

Measure constraint recall before top-k relevance. Track exact-identifier recall, compatible-result recall, stale-state escapes, unsupported-claim rate, zero-result quality and the percentage of final candidates that survive checkout refresh. Click-through rate alone rewards attractive mistakes.

Cache policy deserves its own test plan. A product description may remain useful for hours, while an inventory promise may be unsafe after seconds. Set freshness by field and market rather than applying one time-to-live to the whole candidate. Carry observed_at, source and version through every retrieval stage. When a live dependency is unavailable, distinguish "out of stock" from "stock unknown." The first is a commercial fact. The second is a system condition. An agent that collapses both into the same friendly sentence will hide incidents and lose valid sales. For high-value or scarce inventory, a search result should trigger a reservation tool before the agent implies availability to the buyer.

What Intelliger has now, and what this architecture describes

The canonical Commerce Graph, catalog indexing service, Merchant Agent Runtime, compatibility graph, learned reranker and outcome-aware routing described here are target-state components in Intelliger's August 2026 agentic-commerce blueprint. They are not current production claims.

The implemented OATI developer preview covers a different layer: schemas, signed Passport and Mandate objects, canonical request binding, deterministic Commerce constraints, receipts, middleware and shared conformance vectors. The local sandbox demonstrates a signed paid-API transaction, and the hosted Commerce profile proves discovery and verification of the profile contract. It does not prove a live 100,000-product merchant deployment.

Implementation checklist

  • Normalize products, variants, offers and merchants into separate identities.
  • Keep stable descriptive facts apart from volatile transaction state.
  • Parse requests into typed constraints, preferences and unresolved questions.
  • Apply hard filters before ranking.
  • Preserve lexical retrieval for SKUs, standards and exact names.
  • Use semantic retrieval for descriptive intent, not authoritative facts.
  • Model compatibility with typed relationships and evidence.
  • Fetch price, stock, promotion and delivery from authoritative systems late.
  • Bind refreshed offer state before checkout.
  • Send only a small evidence-rich candidate set to the model.
  • Log retrieval-stage reasons so developers can reconstruct exclusions.
  • Evaluate plausible wrong answers, stale state and prompt injection.

The catalog belongs in indexes, graphs and authoritative APIs. The model needs the customer's intent and a small set of verified candidates. Giving it everything is not completeness. It is abandoning the query planner.

Use the AI shopping agent readiness guide to fix product identity and capability gaps around this pipeline. Then instrument it with the AI search optimization framework so retrieval failures become measurable rather than anecdotal.