Skip to content

sisinflab/GraphAgenticCMR_PolibaT1

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

26 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

RecSys Challenge 2026 - Graph-Aware Agentic Framework for Conversational Music Recommendation

Official code repository for the paper "Beyond Single-Signal Retrieval: A Graph-Aware Agentic Framework for Conversational Music Recommendation".

RecSys Challenge 2026 - Team Poliba T1 b | Student members: Di Bitetto Bianca, De Palma Gianmichele, Como Berardino, Russo Roberta, Dicataldo Francesco Maria, Desiderato Francesco Salvatore, Falcone Francesco

Solution by team Poliba T1 b for the RecSys Challenge 2026, built on the TalkPlayData conversational music recommendation dataset. Given a multi-turn dialogue between a user, a music agent, and an assistant, the system must recommend a ranked list of K = 20 tracks at each conversational turn, while generating a grounded natural-language response justifying why each track fits the evolving intent.

RecSys Challenge 2026 Framework

The pipeline mirrors a modern retrieve-and-generate recommender, and is now driven end-to-end by a single entry point, run_pipeline.py, at the repository root:

  1. Graph Embeddings - a heterogeneous GCN jointly encodes users, sessions, and tracks via relation-aware message passing (a parameter-free LightGCN-style convolution on user↔session edges, plus a feedback-aware operator that Hadamard-modulates session↔track edges with a learnable feedback embedding), capturing long-term collaborative preferences. Cold-start users/tracks are handled via graph-based imputation (mean / multi-hop / personalized PageRank / heat-diffusion).
  2. Retrieval - a multi-agent retrieval system complements the graph signal with a sparse lexical retriever (BM25 over titles/artists/tags), a dense audio retriever (LAION-CLAP), and a dense lyrics retriever (Qwen3), each fed by an LLM query-rewriting agent; a learned WRRF router (an MLP predicting query-dependent weights) fuses the per-modality candidate lists via Weighted Reciprocal Rank Fusion.
  3. Reflective Reranking - a two-phase agent. LLM reranking: conditioned on the conversation, user profile, and item metadata, an LLM reranks the fused candidates and selects the top-K tracks with an initial rationale for each. Reflection: a feedback-aware self-critique pass over that list - the agent classifies every track as positive/negative, and rejected tracks are replaced by turning the rejection reason into a corrective query and running a secondary similarity search.
  4. Response Generation - a two-stage LLM pipeline (reasoning-driven content generation + persona-prompted stylistic refinement) produces the final grounded, explained recommendations, merged back into a predictions.json submission file.

run_pipeline.py chains all of the above automatically and, for stages 1 and 2, lets you choose between loading pretrained checkpoints (fast, deterministic - "inference mode") or training from scratch ("training mode"). A companion script, download_model_weights.py, fetches the pretrained checkpoints from the Hugging Face Hub for you.

πŸ“‘ Table of contents

πŸ‘₯ Team: Poliba T1 b

This project has been developed by:

Name Contact
Marco Valentini m.valentini7@phd.poliba.it
Bianca Di Bitetto b.dibitetto@studenti.poliba.it
Gianmichele De Palma g.depalma12@studenti.poliba.it
Berardino Como b.como@studenti.poliba.it
Roberta Russo r.russo14@studenti.poliba.it
Francesco Maria Dicataldo f.dicataldo1@studenti.poliba.it
Francesco Salvatore Desiderato f.desiderato1@studenti.poliba.it
Francesco Falcone f.falcone3@studenti.poliba.it
Chiara Mallamaci c.mallamaci@phd.poliba.it
Antonio Ferrara antonio.ferrara@poliba.it
Daniele Malitesta dmalitesta@luiss.it
Tommaso Di Noia tommaso.dinoia@poliba.it
Fedelucio Narducci fedelucio.narducci@poliba.it

πŸ—‚οΈ Repository structure

RecSysChallenge26/
β”œβ”€β”€ GraphEmbeddings/          # Heterogeneous GCN: user/session/track embeddings + FAISS indexes
β”‚   └── gcn/                  # Graph construction, model, training loop
β”œβ”€β”€ Retrieval/                # Multi-agent retrieval pipeline + learned router
β”‚   β”œβ”€β”€ agents/                #  LLM query-rewriting agents (sparse/audio/lyrics)
β”‚   β”œβ”€β”€ tools/                 #  Retriever implementations (sparse, audio, lyrics, cf, graph)
β”‚   β”œβ”€β”€ router/                #  WRRF query router (training, inference, evaluation)
β”‚   β”œβ”€β”€ data/                  #  Dataset/subset loading, states DB, track DB, vector DB
β”‚   └── config/                #  retrieval_config.yml
β”œβ”€β”€ Reranker/                  # Reflective reranking (LLM reranker + reflector) and response generation
β”‚   β”œβ”€β”€ LLM_reranker/           #  Candidate reranking via LLM (Gemini/Ollama)
β”‚   β”œβ”€β”€ Reflector/               #  Reflection/self-critique utilities
β”‚   β”œβ”€β”€ entry_points/            #  conversion_utils.py: parquet/json format conversions
β”‚   β”œβ”€β”€ conversation_llm/        #  Conversation description/augmentation
β”‚   β”œβ”€β”€ user_description/        #  User profile description generation
β”‚   └── item_query_similarity/   #  Item/query embedding similarity search
β”œβ”€β”€ ResponseGenerator/         # Two-stage response generation: content generation + stylistic refinement
β”‚   β”œβ”€β”€ baseline/               #  Response construction, Ollama client, similarity/metrics utils
β”‚   β”œβ”€β”€ LexicalEnricher/        #  Bigram/dictionary-based lexical diversity enrichment
β”‚   β”œβ”€β”€ scripts/                #  Blind dataset construction helpers
β”‚   └── sys_prompts/            #  System prompts (first-turn / follow-up / rewriter)
β”œβ”€β”€ artifacts/                 # Intermediate JSON artifacts produced during a run
β”‚   β”œβ”€β”€ retrieval_candidates.json
β”‚   β”œβ”€β”€ submission_ready.json
β”‚   └── generation_input.json
β”œβ”€β”€ cache/                     # Generated artifacts: indexes, models, predictions, states
β”œβ”€β”€ download_model_weights.py  # Downloads pretrained GCN + router checkpoints from HF Hub
β”œβ”€β”€ run_pipeline.py            # Single entry point: runs the full pipeline end-to-end
β”œβ”€β”€ keys.json                  # LLM API key (JSON or plain-text), shared by every LLM stage - see API keys
β”œβ”€β”€ reranker_output.json       # Generated by run_pipeline.py: LLM reranker output
β”œβ”€β”€ predictions.json           # Default final output of run_pipeline.py
β”œβ”€β”€ environment.yml            # Conda environment definition
β”œβ”€β”€ .gitignore                 # Git ignore rules
└── README.md                  # This file

βš™οΈ Installation

The project targets conda exclusively (no plain pip/venv workflow is supported).

Prerequisites

  • Miniconda or Anaconda
  • An NVIDIA GPU + CUDA drivers is strongly recommended (GCN training, faiss-gpu, torch are built against CUDA 12.1)
  • huggingface_hub (used by download_model_weights.py) - included in environment.yml
  • model weights can be downloaded here

Setup

git clone https://github.com/sisinflab/GraphAgenticCMR_PolibaT1.git
cd RecSysChallenge26

# Create the environment from the provided environment.yml
conda env create -f environment.yml

# Activate it
conda activate GACMR_T1

All commands below (run_pipeline.py, download_model_weights.py) are run from the repository root.

πŸ”‘ API keys

All LLM-backed stages - the retrieval query-rewriting agents, the reranker, the reflector, and the response generator - call an LLM (Gemini or a local Ollama server) and share a single credential file, passed everywhere via run_pipeline.py --key-path:

File Used by Notes
keys.json (repo root, default), or any plain-text file such as keys.txt passed via --key-path Every LLM-driven stage: the Retrieval query-rewriting agents (stage 2), the Reflective Reranking agent - reranker + reflector - (stage 3), and the Response Generator (stage 4) JSON (checked first - looks for an api_key/key/token/OPENAI_API_KEY field, in that order) or plain text containing only the key. Not committed to the repo - create it yourself before running.

If you prefer a local LLM for retrieval, set provider: "ollama" (and api_url) in Retrieval/config/retrieval_config.yml instead of providing a Gemini key; the reranker/reflector/response-generator configs likewise expose an OLLAMA_CONFIG field to point at a local Ollama model.

πŸ”„ Running the pipeline

run_pipeline.py runs everything end-to-end: graph embeddings β†’ retrieval + router fusion β†’ export candidates β†’ reflective reranking (LLM reranking + reflection) β†’ response generation β†’ predictions.json. Choose one of the two modes below depending on whether you want to reuse pretrained checkpoints or train from scratch.

Mode A - Inference (download pretrained weights)

Use this mode to reproduce results quickly using the team's pretrained checkpoints, without training anything.

# 1. Download the pretrained GCN checkpoint and the two router checkpoints
python download_model_weights.py

# 2. Run the full pipeline against the blind evaluation dataset
python run_pipeline.py

--graph-weights pretrained and --retriever-weights pretrained are the defaults, so the second command above is equivalent to:

python run_pipeline.py --graph-weights pretrained --retriever-weights pretrained

What download_model_weights.py does: it pulls three files from the giammidp/RecSysChallenge26_models Hugging Face repo and places them where run_pipeline.py expects them:

Remote file Local destination
music_hetero_gcn_mean_heat.pt GraphEmbeddings/<MODEL_DIR>/heat/ (GCN checkpoint)
query_router_30.pt cache/router/models/
query_router_500.pt cache/router/models/

⚠️ The downloaded GCN checkpoint corresponds to --aggr mean --imputation heat (the pipeline defaults). If you run run_pipeline.py with a different --aggr/--imputation, --graph-weights pretrained will fail with a FileNotFoundError since no matching pretrained checkpoint was downloaded - either keep the defaults or switch to --graph-weights scratch for that combination.

If either checkpoint is missing when you request pretrained weights, run_pipeline.py fails fast with a clear error telling you to run download_model_weights.py (or fall back to scratch).

Mode B - Training (train from scratch, no download needed)

Use this mode to train both the GCN and the retrieval router yourself - no call to download_model_weights.py is required.

python run_pipeline.py --graph-weights scratch --retriever-weights scratch

With --graph-weights scratch, run_pipeline.py:

  • deletes any cached checkpoint at cache/models/<imputation>/music_hetero_gcn_<aggr>_<imputation>.pt (if present), then
  • runs python -m gcn.main inside GraphEmbeddings/ with your --aggr/--imputation/--graph-* options, training a fresh heterogeneous GCN and exporting embeddings, FAISS indexes, and blind-B user embeddings.

With --retriever-weights scratch, it then trains the WRRF query router from scratch - no prerequisite step needed, the training data is computed automatically (if absent)

You can also mix modes - e.g. train the GCN from scratch but reuse the pretrained router, or vice versa:

python run_pipeline.py --graph-weights scratch --retriever-weights pretrained
python run_pipeline.py --graph-weights pretrained --retriever-weights scratch

(For the second case above, you'd still run python download_model_weights.py first to obtain the GCN checkpoint, since only the router is being retrained.)

What happens in between (both modes)

Once graph and retriever weights are ready (pretrained or freshly trained), run_pipeline.py continues identically in either mode:

  1. Mixed-mode retrieval - runs Retrieval.main --mode mixed against --dataset-name/--split (default: talkpl-ai/TalkPlayData-Challenge-Blind-B, split test), using the LLM query-rewriting agents (key: --key-path), producing a fused router_500 predictions parquet under cache/results/.
  2. Export candidates - converts the top --top-k-rerank candidates per query into artifacts/retrieval_candidates.json.
  3. Reflective reranking - runs the reranker (config: --reranker-config, key: --key-path) against the conversation and user profile, writing reranker_output.json (repo root); then runs the reflector (config: --reflector-config, key: --key-path) as a feedback-aware self-critique pass over that reranked list against the retrieval parquet.
  4. Response generation - converts the reranker output into submission and generation-ready formats (--submission-ready, --generation-input), then runs the response generator (config: --response-config, key: --key-path) to produce --response-output.
  5. Final predictions - merges the reranker output and generated responses into the final submission file at --output (default: predictions.json).

Full CLI reference

python run_pipeline.py [OPTIONS]

Dataset

Argument Default Description
--dataset-name talkpl-ai/TalkPlayData-Challenge-Blind-B HF dataset repo for the blind evaluation set
--split test Dataset split to run inference on

Credentials (shared across all LLM stages)

Argument Default Description
--key-path keys.json (repo root) JSON or plain-text file with the LLM API key, passed down to the Retrieval query-rewriting agents (stage 2), the Reflective Reranking agent - reranker + reflector - (stage 3), and the Response Generator (stage 4) alike - see API keys

Graph embeddings (stage 1)

Argument Choices Default Description
--graph-weights pretrained, scratch pretrained Load the cached GCN checkpoint, or retrain it from scratch. The graph retriever always runs off these embeddings either way.
--aggr mean, sum mean Session→track aggregation in the GCN
--imputation mean, neighmean, multihop, ppr, heat heat Cold-start user CF-embedding imputation strategy (selects model_dir/index_dir suffix)
--graph-top-n int 10 Top-N neighbors used when building graph-based imputation
--graph-hops int 3 Number of hops for multi-hop/PPR/heat imputation
--graph-alpha float 0.1 Alpha for personalized PageRank (PPR) imputation
--graph-b float 5.0 Diffusion parameter for heat-kernel imputation
--graph-interactions all, positive all Which session↔track edges to use when propagating

Retrieval + router (stage 2)

Argument Choices Default Description
--retriever-weights pretrained, scratch pretrained Load the cached WRRF router checkpoints, or retrain them from scratch before running mixed-mode retrieval
--top-k-retrieval int 500 Candidates kept per query at the retrieval/router stage
--top-k-rerank int 30 Candidates exported to retrieval_candidates.json for the LLM reranker

Reranker + reflector (stage 3)

Argument Default Description
--reranker-config Reranker/LLM_reranker/config.json Reranker config (paths, model, temperature); writes reranker_output.json (repo root)
--reflector-config Reranker/Reflector/config.json Reflector config; runs the feedback-aware self-critique pass over the reranker output

Response generation (stage 4)

Argument Default Description
--response-config ResponseGenerator/config_inf.json Response-generation config (paths, prompts, checkpointing)
--submission-ready artifacts/submission_ready.json Intermediate file: reranker output converted to submission format
--generation-input artifacts/generation_input.json Intermediate file produced by submission_to_generation, fed to the response generator
--response-output artifacts/responses.json File the response generator is expected to write to

Final output

Argument Default Description
--output predictions.json (repo root) Path the final predictions are written to - the reranker output merged with the generated responses

Partial runs / debug flags

Flag Effect
--skip-graph-pipeline Skip re-running GCN/main.py; assumes the graph, FAISS index, and blind-B embeddings are already up to date on disk
--skip-rerank Stop right after exporting retrieval candidates, and write them directly to --output (bypasses reflective reranking and response generation entirely)
--skip-response-gen Run the reflective reranking stage (reranker + reflector), then write predictions directly from the reranker output (bypasses response generation)

🧩 Configuration files at a glance

run_pipeline.py's flags only cover the parameters above; anything specific to a single module (prompt files, LLM provider/model, cache subpaths, router/GCN architecture details, ...) is controlled by that module's own config file below - edit the .yml/.json directly for that level of detail.

File Governs
GraphEmbeddings/config.yaml Embedding dimensions, GCN training hyperparameters, FAISS/index paths
Retrieval/config/retrieval_config.yml Dataset/split/subset, retriever top-k, router retriever order, LLM query-rewriting provider, cache paths
Reranker/LLM_reranker/config.json Reranking inputs, LLM model/provider, prompt paths
Reranker/Reflector/config.json Reflection/self-critique inputs and prompts
ResponseGenerator/config_inf.json Response-generation inputs, prompts, checkpointing

πŸ“š Reference

If you use this repository, please cite:

@inproceedings{TBD,
  title     = {TBD},
  author    = {TBD},
  booktitle = {RecSys Challenge 2026},
  year      = {2026}
}

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages