How the Interaction Graph Tracks User Interactions for Recommendation Feedback Loops
The Interaction Graph serves as Twitter's core user-interaction ledger, aggregating billions of signals into a time-weighted graph of users and directed interactions to power real-time recommendation feedback loops.
The Interaction Graph is the foundational data structure behind Twitter's recommendation pipelines, implemented in the twitter/the-algorithm repository. It continuously ingests low-level user signals—follows, retweets, likes, mentions, and dwell time—and compiles them into a dense, temporally decayed graph that drives ranking models across Home Timeline, Push notifications, and other surfaces.
Data Ingestion and Graph Construction
Raw event streams from client logs, address-book imports, and direct interactions feed into a family of Scio jobs located under src/scala/com/twitter/interaction_graph/scio/agg_*. Each job extracts edge-level features (e.g., num_retweets, num_follows) and vertex-level features (e.g., total_dwell_time) and persists them as Thrift structs defined in src/thrift/com/twitter/interaction_graph/interaction_graph.thrift.
The following Scala snippet demonstrates how daily edge features are read and combined from multiple sources:
// src/scala/com/twitter/interaction_graph/scio/agg_direct_interactions/InteractionGraphAggDirectInteractionsJob.scala
val directInteractionsEdgeFeatures = source.readDirectInteractionsFeatures(dateInterval)
val aggregatedEdge = FeatureGeneratorUtil.combineEdgeFeatures(
clientEventLogsEdgeFeatures ++ directInteractionsEdgeFeatures
)
Temporal Decay and Feature Engineering
To ensure the Interaction Graph remains responsive to recent behavior while retaining long-term context, the system applies exponential decay to historical aggregates. In src/scala/com/twitter/interaction_graph/scio/common/FeatureGeneratorUtil.scala, the methods combineEdgeFeaturesWithDecay and combineVertexFeaturesWithDecay merge yesterday’s graph with today’s snapshot using decay constants α and 1‑α.
This yields a time-series-aware representation where fresh interactions carry higher weight, preventing the feedback loop from stagnating on stale signals:
// src/scala/com/twitter/interaction_graph/scio/agg_all/InteractionGraphAggregationJob.scala
val aggregatedActivityEdge = FeatureGeneratorUtil
.combineEdgeFeaturesWithDecay(
prevAggEdgeValid, // yesterday's edges
aggregatedActivityEdgeDaily, // today’s edges
InteractionGraphScoringConfig.ONE_MINUS_ALPHA,
InteractionGraphScoringConfig.ALPHA
)
Exporting to Real-Graph Features
Once aggregated, the graph is pruned to the most influential connections. InteractionGraphAggregationTransform.getTopKTimelineFeatures in src/scala/com/twitter/interaction_graph/scio/agg_all/InteractionGraphAggregationTransform.scala selects the top‑K inbound and outbound edges per user, converts them to the RealGraphFeatures Thrift format, and writes them to a versioned dataset.
Utility functions such as GraphUtil.isFollow assist in filtering edge types during this extraction:
// src/scala/com/twitter/interaction_graph/scio/agg_all/InteractionGraphAggregationTransform.scala
val realGraphFeatures = getTopKTimelineFeatures(
aggregatedActivityScoredEdge,
pipelineOptions.getMaxDestinationIds
)
Powering the Recommendation Feedback Loop
The Interaction Graph closes the feedback loop by serving as the bridge between raw user behavior and model inference. Downstream services consume RealGraphFeatures through dedicated hydrators:
- Home Mixer uses
RealGraphViewerAuthorFeatureHydrator.scalato enrich tweet-ranking requests with interaction history between the viewer and candidate authors. - Push Service leverages
PushTargetUserBuilder.scalato incorporate real-graph signals into notification ranking.
The loop operates as follows:
- Signal → Graph: User actions (likes, follows, dwell) are ingested into the Interaction Graph during the next nightly aggregation.
- Graph → Models: Decayed, top‑K graph features hydrate ranking models (e.g., Home‑Mixer’s
ScoredTweetsPipeline). - Model → Surface: Ranked candidates are presented to users.
- Surface → Signal: New interactions are generated, re-entering the pipeline and closing the loop.
Because the graph applies temporal decay and filters low-signal edges, it balances responsiveness to recent trends with stability from long-term relationships—critical for preventing feedback loop collapse or echo chambers.
Summary
- The Interaction Graph is the centralized ledger that aggregates billions of user signals into a time-weighted graph structure.
- Scio jobs under
src/scala/com/twitter/interaction_graph/scio/handle daily ingestion of follows, retweets, likes, mentions, and dwell time. - Exponential decay in
FeatureGeneratorUtilensures recent interactions dominate while historical context persists. - Top‑K pruning converts dense graph data into
RealGraphFeaturesconsumed by Home Mixer, Push Service, and other ranking pipelines. - The architecture forms a closed feedback loop: user actions update the graph, graph features inform models, models generate recommendations, and recommendations drive new user actions.
Frequently Asked Questions
What types of interactions does the Interaction Graph track?
The Interaction Graph captures directed interactions including follows, retweets, likes, mentions, dwell time on tweets, address-book contacts, and flock activity. These are ingested from client-event logs and direct interaction streams, then aggregated into edge features such as num_retweets and vertex features such as total_dwell_time.
How does the Interaction Graph handle stale or outdated interactions?
The system applies exponential decay to historical aggregates using constants α and 1‑α in FeatureGeneratorUtil.combineEdgeFeaturesWithDecay. This weights recent interactions more heavily while preserving long-term signals, preventing the recommendation feedback loop from relying on outdated behavioral patterns.
What is the relationship between the Interaction Graph and RealGraph features?
The Interaction Graph is the underlying data structure; RealGraph features are the production-ready extraction of that data. InteractionGraphAggregationTransform.getTopKTimelineFeatures selects the top‑K inbound and outbound edges per user from the Interaction Graph and converts them into the RealGraphFeatures Thrift format used by downstream ranking services.
Which downstream services consume Interaction Graph data?
Multiple recommendation services ingest RealGraph features derived from the Interaction Graph, including Home Mixer (via RealGraphViewerAuthorFeatureHydrator.scala for tweet ranking), Push Service (via PushTargetUserBuilder.scala for notification ranking), and Tweet Mixer. These services hydrate ranking models with interaction history to personalize content surfaces.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →