A simplified single-agent implementation inspired by ARAG: Agentic Retrieval Augmented Generation for Personalized Recommendation, applied to the MovieLens 1M dataset.
The ARAG paper proposes a 4-agent pipeline (User Understanding, NLI filtering, Context Summary, Item Ranker)
for personalized recommendation. This project distills that into a single pydantic-ai agent that performs
all reasoning stages within one LLM call, augmented with tool access for retrieval.
The agent receives text (demographics/preferences) and history (past interactions, may be empty for
cold-start users) and works in three stages:
- Context understanding β analyzes the text field and fetches interacted item texts from LanceDB (if history exists) to build a preference summary
- Candidate retrieval β issues 2-4 search queries (vector, FTS, or hybrid) derived from the text field, preference summary, and retrieved item texts; interacted items are excluded
- Ranking β deduplicates candidates, excludes previously interacted items, ranks by relevance and diversity, and attaches a per-item explanation
The system is served via a FastAPI REST endpoint.
Request (text, history: [{item_id, event_datetime, event_name, event_value}], limit)
β
ββ [Tool 1] get_item_texts(item_ids) β {item_id: item_text} (skipped if cold-start)
ββ LLM: context understanding β preference summary
ββ [Tool 2] search_items(query, exclude_ids) β candidates (called 2-4Γ)
ββ LLM: rank + explain β ItemRecommended list
POST /recommend β { items: [{ id, text, explanation }] }
POST /recommend/item β { items: [{ id, text, explanation }] }
- Python 3.12+
uvfor environment and task management β seepyproject.toml- An LLM API key matching the configured model
uv syncDownloads, extracts, and converts MovieLens 1M into Parquet files under data/:
uv run ml_1mIf you already have ml-1m.zip, place it under data/ before running the command.
Encodes items and users with sentence-transformers and writes to LanceDB:
uv run index
uv run index --parquet_path data/ml-1m/users.parquet --table_name usersexport AGENTIC_REC_LLM_MODEL="cerebras:gpt-oss-120b" # any pydantic-ai model string
export CEREBRAS_API_KEY="..."Supported model strings: cerebras:gpt-oss-120b, anthropic:claude-haiku-4-5, ollama:llama3, and any other
pydantic-ai provider.
uv run fastapi run agentic_rec.main:appRequest example:
curl -X POST http://localhost:3000/recommend \
-H "Content-Type: application/json" \
-d '{
"text": "25-year-old male, software engineer, enjoys sci-fi and thriller films",
"history": [
{"item_id": "1193", "event_datetime": "2000-12-31T22:12:40", "event_name": "rating", "event_value": 5},
{"item_id": "661", "event_datetime": "2000-12-31T22:35:09", "event_name": "rating", "event_value": 3}
],
"limit": 10
}'| File | Responsibility |
|---|---|
agentic_rec/settings.py |
pydantic-settings Settings class: paths, model names, table names |
agentic_rec/ml_1m.py |
MovieLens download, Parquet conversion, train/val/test split |
agentic_rec/index.py |
LanceDB index: embedding, hybrid search, reranking (items & users) |
agentic_rec/models.py |
Pydantic models: request/response types |
agentic_rec/agent.py |
pydantic-ai Agent singleton with tools |
agentic_rec/main.py |
FastAPI service entry point |
agentic_rec/repositories/ |
Repository layer (data access) |
agentic_rec/services/ |
Service layer (business logic) |
agentic_rec/routers/ |
API layer (routers) |
agentic_rec/dependencies.py |
Dependency injection |
| Route | Method | Description |
|---|---|---|
/healthz |
GET | Service health and model configuration |
/recommend |
POST | User-based recommendations (alias: /recommend/user) |
/recommend/item |
POST | Item-based similar-item recommendations |
/users/{user_id} |
GET | Look up user by ID (text + history) |
/users/{user_id}/recommend |
POST | Recommend for an existing user by ID |
/items/{item_id} |
GET | Look up item by ID (text) |
/items/{item_id}/recommend |
POST | Similar-item recommendations for an item by ID |
The /users/{user_id}/recommend and /items/{item_id}/recommend convenience routes look up the entity and
then delegate to the corresponding /recommend or /recommend/item endpoint.
# lint and format
uv run ruff check --fix .
uv run ruff format .
# tests
uv run pytest