product-recommendation
Product discovery and recommendation. Trigger on the quick-reply button the widget ships — "What's recommended?" — and whenever a shopper is browsing, asks what you have, asks for ideas, names a category, or names a specific product. Read the full body before your first search — it defines when to ask a preference question versus search straight away, and the exact message sequence for presenting results.
name: product-recommendation description: Product discovery and recommendation. Trigger on the quick-reply button the widget ships — "What's recommended?" — and whenever a shopper is browsing, asks what you have, asks for ideas, names a category, or names a specific product. Read the full body before your first search — it defines when to ask a preference question versus search straight away, and the exact message sequence for presenting results. tags: [internal, concierge, requires-yepai-tools] license: MIT-0
Recommending products
Ported from the product-recommendation-journey the ADK agent ran. The two modes
below are the core of it: guessing wrong between them is the difference between
"helpful" and "interrogating".
Pick a mode from the shopper's first message
Mode 1 — Guided discovery. The request is vague: "what do you recommend?", "show me something", "what do you have?". Search first to see what the store actually carries, then ask ONE preference question grounded in those results.
Mode 2 — Direct search. The request is specific: it names a product, or names a category plus at least one preference. Search immediately. Do not ask anything.
A category alone ("t-shirts") is Mode 1 territory but only barely — search first, and only ask a preference question if the results genuinely split on something.
If the shopper says "just give me recommendations", "I don't know", "not sure", or declines to narrow down: stop asking and present what you have.
Guided discovery
SEARCH_CATALOG(query=<their words, or '*'>, context='discovering available products', limit=10)- If fewer than 3 products came back, do NOT ask a preference question — present them directly. Asking someone to narrow down three items is annoying.
- If the results share no meaningful differentiating attribute, present directly too.
- Otherwise ask ONE preference question via
suggestionQuestion, using values that actually appear in the results. Never ask about price or budget at this stage — not in the text, not in the options. Price refinement is offered only AFTER products have been shown (see "Refining by price"), keyed to the actual price spread of what they saw. A price question before any products reads as qualifying the shopper's wallet instead of helping them look. - That question ends the turn. After the shopper picks, search again with the accumulated preferences and present in that next turn.
Bestsellers / featured: when the shopper asks for bestsellers or featured
products, search with featured=true for the first presentation — the merchant
curates that list, and their analytics distinguish featured recommendations.
Presenting results — exact sequence
Three NOTIFY_MESSAGE calls, in this order, nothing in between — this whole sequence runs once per turn, and the third call ends the turn:
NOTIFY_MESSAGE(message_type='text')— a short, friendly lead-inNOTIFY_MESSAGE(message_type='product')— the products, ids only (see below)NOTIFY_MESSAGE(message_type='suggestionQuestion')— up to 2 options: one price/budget refinement, one other useful direction
The product payload
message_content carries nothing but the ids, in the order you want them shown:
{"productsId": ["gid://shopify/Product/123", "gid://shopify/Product/456"]}
That is the whole payload. The card — images, prices, variants, links — and the
merchant's analytics are both built downstream from the catalogue entry the
SEARCH_CATALOG you just ran returned, so there is nothing to copy through and nothing
to retype.
Never paste the SEARCH_CATALOG result into message_content, and never send it as a
quoted string — send the object above. A payload no ids can be read out of is dropped
before the shopper sees it: they get your lead-in sentence and then silence, which is
worse than not offering products at all.
Only ids that came back from a SEARCH_CATALOG in this turn belong in the payload.
The entry each card is built from is short-lived, and a miss is silent — the call reports
queued either way, so there is no failure for you to react to and no way for you to
tell how long ago something was searched. An id you are carrying over from an earlier
turn has to come back from a search in this one before you send it.
When results do not match what they asked for
State it plainly and pivot. Do not apologise, do not say "sorry".
"I couldn't find exactly that, but take a look at these."
Then the product cards, then suggestions. The bridge message goes first so the cards do not read as a wrong answer.
When there are no results at all
Again, no apology.
"I couldn't find that. Feel free to try different keywords."
Follow with suggestionQuestion offering two alternative directions drawn from
what you know about the store — not generic filler.
Refining by price
When the shopper wants to filter by price ("cheaper", "filter by price", "按价格筛选"):
- Look at the prices of the products already shown in this conversation.
- Build 2–4 brackets from that actual spread — if they run $20–$200, offer "Under $50", "$50–$100", "$100–$200", "Over $200". Do not invent generic bands.
- Ask via
text+suggestionQuestion— that ends the turn. After the shopper picks a bracket, search and present as above in that next turn.
When refining after products were already shown, merge the new results with the previously shown products that still fit — otherwise good matches silently vanish. To keep one, make this turn's search return it too: search its name alongside the refined query. An id that no search in this turn returned cannot be rendered, so sending it loses the very card you were trying to keep.
Download
ZIP package — ready to use
Skill Info
- Creator
- Yepai
- License
- MIT-0
- Downloads
- 0
- Published
- Oct 26, 2026
- Updated
- Oct 29, 2026