TL;DR — The manager reduces token count by up to 60% using attention‑based importance scores, keeping semantic fidelity while cutting memory footprint; the implementation is a self‑contained Python package that runs on a single GPU and can be dropped into any LLM pipeline.
The ability to handle long inputs is a bottleneck for many production LLM applications. Naively feeding every token to a transformer quickly exhausts GPU memory and inflates latency. A context window manager that intelligently prunes low‑importance tokens solves this problem, and building one is a concrete way to demonstrate systems engineering, algorithmic thinking, and deep learning expertise—all skills that hiring managers look for.
Why This Project Stands Out on a CV
- End‑to‑end system design – You will ship a reusable library that wraps any Hugging Face model, exposing a clean
ContextManagerAPI. - Attention‑driven pruning – Instead of heuristics, you compute real attention scores from the model’s own forward pass, showing you understand transformer internals.
- Memory efficiency – By pruning tokens before the expensive self‑attention block, you cut peak memory usage dramatically, a key concern for production LLM services.
- Production‑ready patterns – The code includes configurable thresholds, logging, and a simple CLI, mirroring real‑world tooling.
- Scalability hints – The architecture is designed to be extended with batching, caching, and distributed execution, signaling awareness of scale.
These points map directly to roles such as ML Engineer, Applied Scientist, or Platform Engineer in teams building LLM‑powered products.
Architecture Overview
The system consists of four components:
- Tokenizer wrapper – Uses
tiktokenor Hugging Face’s tokenizer to convert raw text into token IDs and embed them. - Attention scorer – Runs a single forward pass on the full sequence to extract per‑token attention weights (averaged across heads).
- Pruner – Ranks tokens by their attention score, discards those below a configurable percentile, and returns a reduced token list.
- Context manager – Ties the pieces together, exposing
compress(tokens, keep_ratio)anddecompress(original_tokens, pruned_tokens)methods.
A high‑level flow:
Raw text → Tokenizer → Full token sequence → Attention scorer → Importance scores
↓
Pruner (percentile filter)
↓
Pruned token sequence
↓
Feeding to LLM (e.g., GPT‑NeoX)
The design keeps the original token IDs intact, so the model never sees a mismatch between tokenization and embedding.
Building It Step by Step
Step 1 – Set up the project skeleton
Create a directory ctx_window_manager with the following files:
ctx_window_manager/
├── __init__.py
├── manager.py
├── scorer.py
├── pruner.py
└── utils.py
Add a pyproject.toml to declare dependencies:
[project]
name = "ctx-window-manager"
version = "0.1.0"
dependencies = [
"torch>=2.0",
"transformers>=4.30",
"tiktoken>=0.5",
]
Install with pip install -e ..
Step 2 – Tokenizer wrapper (utils.py)
import tiktoken
from transformers import AutoTokenizer
class TokenizerWrapper:
def __init__(self, model_name: str = "gpt2", use_tiktoken: bool = True):
if use_tiktoken:
self.enc = tiktoken.get_encoding("cl100k_base")
self.decode = self.enc.decode
self.encode = self.enc.encode
else:
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
self.encode = self.tokenizer.encode
self.decode = self.tokenizer.decode
def tokenize(self, text: str) -> list[int]:
return self.encode(text)
def detokenize(self, token_ids: list[int]) -> str:
return self.decode(token_ids)
Step 3 – Attention scorer (scorer.py)
We use a small model (e.g., sshleifer/tiny-gpt2) to keep memory low while still producing meaningful attention patterns.
import torch
from transformers import AutoModelForCausalLM, AutoConfig
class AttentionScorer:
def __init__(self, model_name: str = "sshleifer/tiny-gpt2"):
self.model = AutoModelForCausalLM.from_pretrained(model_name)
self.model.eval()
self.config = AutoConfig.from_pretrained(model_name)
@torch.no_grad()
def get_attention_scores(self, token_ids: list[int]) -> torch.Tensor:
# shape: (batch=1, seq_len)
inputs = torch.tensor([token_ids], dtype=torch.long, device=self.model.device)
outputs = self.model(
inputs,
output_attentions=True,
return_dict=True,
)
# outputs.attentions is a tuple of length num_layers,
# each element shape (batch, num_heads, seq_len, seq_len)
attentions = outputs.attentions
# Average over layers and heads → (seq_len,)
stacked = torch.stack(attentions) # (layers, batch, heads, seq, seq)
avg_heads = stacked.mean(dim=(1, 2)) # (layers, seq, seq)
# Take the attention weight of each token to itself as a proxy for importance
# Or average over the row to get a single score per token
token_scores = avg_heads.mean(dim=-1) # (layers, seq)
final_scores = token_scores.mean(dim=0) # (seq,)
return final_scores.cpu()
Step 4 – Pruner (pruner.py)
import numpy as np
class TokenPruner:
def __init__(self, keep_ratio: float = 0.5):
"""
keep_ratio: fraction of tokens to retain (0.0–1.0)
"""
self.keep_ratio = keep_ratio
def prune(self, token_ids: list[int], scores: torch.Tensor) -> list[int]:
# scores: (seq_len,)
seq_len = len(token_ids)
k = max(1, int(seq_len * self.keep_ratio))
# Indices of top‑k scores
_, topk_idx = torch.topk(scores, k=k, largest=True)
# Preserve original order
topk_idx = torch.sort(topk_idx).values
pruned = [token_ids[i.item()] for i in topk_idx]
return pruned
Step 5 – Context manager (manager.py)
from .utils import TokenizerWrapper
from .scorer import AttentionScorer
from .pruner import TokenPruner
class ContextManager:
def __init__(self, model_name: str = "sshleifer/tiny-gpt2",
keep_ratio: float = 0.5):
self.tokenizer = TokenizerWrapper()
self.scorer = AttentionScorer(model_name)
self.pruner = TokenPruner(keep_ratio)
def compress(self, text: str) -> list[int]:
tokens = self.tokenizer.tokenize(text)
scores = self.scorer.get_attention_scores(tokens)
pruned = self.pruner.prune(tokens, scores)
return pruned
def decompress(self, original: list[int], pruned: list[int]) -> str:
# Simple reconstruction: join pruned tokens
return self.tokenizer.detokenize(pruned)
Step 6 – CLI entry point (__main__.py)
import argparse
from ctx_window_manager.manager import ContextManager
def main():
parser = argparse.ArgumentParser(description="Context Window Manager")
parser.add_argument("--text", type=str, required=True, help="Input text")
parser.add_argument("--keep-ratio", type=float, default=0.5,
help="Fraction of tokens to retain")
args = parser.parse_args()
mgr = ContextManager(keep_ratio=args.keep_ratio)
compressed = mgr.compress(args.text)
print("Original token count:", len(mgr.tokenizer.tokenize(args.text)))
print("Compressed token count:", len(compressed))
print("Compressed text:", mgr.tokenizer.detokenize(compressed))
if __name__ == "__main__":
main()
Install the package and run:
python -m ctx_window_manager --text "Your long document here..." --keep-ratio 0.4
Running and Testing It
Unit tests (tests/)
Create tests/test_manager.py:
import pytest
from ctx_window_manager.manager import ContextManager
def test_compress_reduces_tokens():
text = "The quick brown fox jumps over the lazy dog. " * 20
mgr = ContextManager(keep_ratio=0.3)
original = mgr.tokenizer.tokenize(text)
compressed = mgr.compress(text)
assert len(compressed) < len(original)
assert len(compressed) <= max(1, int(len(original) * 0.3))
def test_decompress_preserves_meaning():
text = "Artificial intelligence is transforming industries."
mgr = ContextManager(keep_ratio=0.5)
compressed = mgr.compress(text)
decoded = mgr.tokenizer.detokenize(compressed)
# Simple check: at least one keyword remains
assert "intelligence" in decoded or "AI" in decoded
Run with pytest -q.
End‑to‑end benchmark
import time
from ctx_window_manager.manager import ContextManager
text = open("sample.txt").read()
mgr = ContextManager(keep_ratio=0.4)
start = time.time()
compressed = mgr.compress(text)
elapsed = time.time() - start
print(f"Original tokens: {len(mgr.tokenizer.tokenize(text))}")
print(f"Compressed tokens: {len(compressed)}")
print(f"Compression time: {elapsed:.3f}s")
This script demonstrates both speed and memory reduction; you can extend it to measure GPU memory usage with torch.cuda.memory_allocated().
Extending It: Your Roadmap to Senior‑Level
- Batched inference – Process multiple documents in parallel using a
DataLoaderand a single model forward pass, reducing latency for high‑throughput services. - Caching of attention scores – Store precomputed scores for frequently used prompts (e.g., system messages) to avoid recomputation, improving response time in chatbots.
- Distributed pruning – Split large contexts across GPUs, compute local attention, then aggregate scores with
torch.distributed.all_reduce, enabling scaling to million‑token inputs. - Adaptive keep‑ratio – Train a small regressor on downstream task metrics (e.g., BLEU, ROUGE) to dynamically adjust
keep_ratioper request, balancing quality and cost. - Observability – Export metrics (token count, compression ratio, latency) to Prometheus and visualize them in Grafana, making the system production‑grade.
- Fault tolerance – Wrap the model forward pass in a retry loop with exponential backoff and fallback to a CPU‑only path if GPU OOM occurs, ensuring reliability under load.
Each upgrade addresses a real production concern: batching improves throughput, caching reduces latency, distributed execution handles scale, adaptive ratio optimizes cost‑quality trade‑off, observability enables SLO monitoring, and fault tolerance prevents outages.
Key Takeaways
- Build a reusable Python library that compresses LLM context windows using model‑derived attention scores.
- Demonstrate end‑to‑end system design, transformer internals, and memory‑efficiency techniques.
- Include production‑ready features: CLI, unit tests, and extensibility points for batching, caching, and monitoring.
- The project signals readiness for senior ML/platform roles by addressing scalability, observability, and reliability.
Further Reading
- Attention Is All You Need – The original transformer paper that introduced the attention mechanism: arXiv:1706.03762
- Hugging Face Transformers Documentation – Detailed guidance on extracting attention weights and model configuration: huggingface.co/docs/transformers
- TikToken – Fast, memory‑efficient tokenizer used in many LLM pipelines: github.com/openai/tiktoken
- Sparse Attention for Long Sequences – Survey of techniques that complement token pruning: arXiv:1904.10509
- PyTorch Distributed Communication – Reference for building distributed aggregation: pytorch.org/docs/stable/distributed.html