Skip to content

Architecture

How the recommendation agent works

Nexora is a seven-node LangGraph state machine. It reads behavioural events, extracts the interests behind them, retrieves candidates with hybrid vector search, grades each one with an LLM judge, loops back to broaden the search when the result set is thin, and only then writes the recommendation. Every model call is routed through the Mesh API gateway.

The seven nodes

1

activity_analyzer

openai/gpt-4o

Behaviour → digest

Loads the last 60 events, renders them as a chronological log, and asks Mesh for a factual behavioural digest. Weights cart adds and long dwell times far above bare page views.

2

interest_extractor

openai/gpt-4o

Digest → signals + query + filters

Produces 3–5 interest signals with confidence scores and supporting evidence, a rich natural-language retrieval query, and the metadata filters (skill band, price ceiling) the behaviour actually supports.

3

retrieval_node

openai/text-embedding-3-small

Hybrid search over Qdrant

Embeds the query via Mesh, runs a filtered cosine ANN search in Qdrant, runs an Okapi BM25 keyword ranking over the SQL catalog, and fuses the two rankings with reciprocal rank fusion.

4

relevance_grader

openai/gpt-4o-mini

LLM-as-judge grading + re-ranking

Scores every candidate 0–1 with a one-line justification, then blends the judge score with the retrieval score (65/35) to produce the final ordering. Topical keyword overlap alone does not count as relevance.

5

retrieval_refiner

openai/gpt-4o

Broaden and retry

Entered only via the conditional edge when fewer than three relevant candidates survive. Moves the query up a level of abstraction and can drop the price or skill filters, then loops back into retrieval.

6

persuasion_writer

anthropic/claude-3-5-sonnet

Evidence-grounded narrative

Writes the headline, the narrative and a pitch per course — instructed to reflect the observed behaviour back to the learner, and forbidden from inventing courses, prices or urgency.

7

recommendation_storer

no model call

Persist and invalidate

Deactivates previous recommendations and inserts the new one in a single transaction, then invalidates the Redis result cache and clears the pending flag so the UI stops showing a skeleton.

Graph topology

One conditional edge, on relevance_grader: proceed to the writer when at least 3 relevant candidates survive; otherwise refine and retry, up to 2 times; if the budget runs out, write a recommendation from what we have rather than returning nothing.

---
config:
  flowchart:
    curve: linear
---
graph TD;
	__start__([<p>__start__</p>]):::first
	activity_analyzer(activity_analyzer)
	interest_extractor(interest_extractor)
	retrieval_node(retrieval_node)
	relevance_grader(relevance_grader)
	retrieval_refiner(retrieval_refiner)
	persuasion_writer(persuasion_writer)
	recommendation_storer(recommendation_storer)
	__end__([<p>__end__</p>]):::last
	__start__ --> activity_analyzer;
	activity_analyzer --> interest_extractor;
	interest_extractor --> retrieval_node;
	persuasion_writer --> recommendation_storer;
	relevance_grader -.-> persuasion_writer;
	relevance_grader -.-> retrieval_refiner;
	retrieval_node --> relevance_grader;
	retrieval_refiner --> retrieval_node;
	recommendation_storer --> __end__;
	classDef default fill:#f2f0ff,line-height:1.2
	classDef first fill-opacity:0
	classDef last fill:#bfb6fc

Mermaid definition, rendered live from the compiled graph. Machine-readable version: /api/agent/graph

When the agent runs

Running on every event would be wasteful; running once per session would go stale. Three rules, checked in priority order:

  1. first_time The user has events but no recommendation yet.
  2. event_threshold 10 new events have accumulated since the last recommendation.
  3. stale The last recommendation is older than 2h and at least 5 new events exist.

Dispatch is asynchronous: POST /api/events only evaluates two indexed COUNT queries, then hands the run to APScheduler. A Redis SET NX EX lock keyed by user id makes duplicate concurrent runs impossible.

Runtime status

mesh gateway
https://api.meshapi.ai/v1
reasoning model
openai/gpt-4o
writer model
anthropic/claude-3-5-sonnet
grader model
openai/gpt-4o-mini
embedding model
openai/text-embedding-3-small
langsmith
off
scheduler
running

Scheduled jobs

  • Release stale agent flags 2026-08-10T10:59
  • Daily proactive recommendation digest 2026-08-10T18:00

Failure behaviour

A recommendation engine that returns a 500 is worse than one that returns a slightly weaker answer. Every node catches its own exceptions, records them in the run trace, and falls back to a deterministic heuristic — behavioural weighting for interests, lexical overlap for grading, a templated narrative for copy. The run is flagged degraded rather than lost, which is also what makes the whole pipeline runnable in CI with no API keys. The same principle applies to infrastructure: an unreachable Qdrant falls back to an embedded in-process index, and an unreachable Redis falls back to a process-local cache with identical semantics.

about this build

Nexora, by AY Systum

Nexora was designed and built by AY Systum for the SmartReco Build Challenge 2026 — a hackathon brief to build a behavioural AI recommendation agent that watches, understands and persuades. Every LLM completion and every embedding in this product is routed through the Mesh API gateway; no provider SDK is called directly.

Built by
AY Systum
Built for
SmartReco Build Challenge 2026
Model gateway
Mesh API