Salta el contingut

3.3 Gestió del Context Window

El Repte

El context window és el recurs més limitat i car d'un agent. Gestionar-lo bé és la diferència entre un agent eficient i un que talla converses, oblida informació o genera factures enormes.


📏 Anatomia d'un Context Window

En cada crida a un LLM, el context window conté:

Component Tokens Típics Notes
System prompt 200-2000 Constant en cada crida
Historial de conversa Creix amb el temps El gran consumidor
Resultats d'eines 100-2000 per eina Pot ser molt gran
Context RAG 500-4000 Fragments recuperats
Missatge actual 10-500 La pregunta de l'usuari
TOTAL ~2000-10000 Per consulta típica

⚡ Estratègies de Compressió

1. Compressió per Resum

from langchain.memory import ConversationSummaryBufferMemory
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

# Combina: missatges recents íntegres + resum dels anteriors
memory = ConversationSummaryBufferMemory(
    llm=llm,
    max_token_limit=800,     # Quan supera 800 tokens → comença a resumir
    memory_key="history",
    return_messages=True
)

# El resum es genera automàticament:
# "L'usuari Anna pregunta sobre ASIX. Hem parlat de:
#  1. Conceptes de subnetting IPv4
#  2. Configuració de VLANs en Cisco
#  [Darrera conversa íntegra...]"

2. Prompt Caching (OpenAI i Anthropic)

# OpenAI suporta Prompt Caching automàticament per a prompts de >1024 tokens
# → Estalvia fins al 50% del cost en el prefix del prompt!

from openai import OpenAI
client = OpenAI()

# El system prompt llarg es pot cachear:
SYSTEM_LONG = """
[...2000 paraules de context constant...]
"""  # → Cacheada automàticament per OpenAI

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": SYSTEM_LONG},   # ← Cacheada!
        {"role": "user",   "content": "Pregunta nova"}
    ]
)

# Verificar si s'ha usat la caché
usage = response.usage
print(f"Tokens cacheats: {getattr(usage, 'prompt_tokens_details', {}).get('cached_tokens', 0)}")

3. Selecció Intel·ligent de Context

from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma

# En lloc de posar TOT l'historial al context,
# recuperar ÚNICAMENT els missatges rellevants per a la pregunta actual

embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

# Indexar l'historial de conversa
historial_vs = Chroma(
    collection_name="historial",
    embedding_function=embeddings
)

def context_rellevant(pregunta: str, k: int = 3) -> str:
    """Recupera els missatges de l'historial més rellevants per a la pregunta."""
    docs = historial_vs.similarity_search(pregunta, k=k)
    return "\n".join(d.page_content for d in docs)

# En lloc de:  history = TOTS_ELS_MISSATGES  (pot ser 50k tokens)
# Usem:        history = context_rellevant(pregunta_actual)  (~1k tokens)

💰 Estimació i Control de Costos

import tiktoken  # pip install tiktoken

def comptar_tokens(text: str, model: str = "gpt-4o") -> int:
    """Compta els tokens d'un text per a un model específic."""
    enc = tiktoken.encoding_for_model(model)
    return len(enc.encode(text))

def estimar_cost(tokens_input: int, tokens_output: int,
                 model: str = "gpt-4o-mini") -> float:
    """Estima el cost en USD d'una crida."""
    PREUS = {  # Per milió de tokens (USD) — actualitzar periòdicament!
        "gpt-4o":      {"input": 5.0,  "output": 15.0},
        "gpt-4o-mini": {"input": 0.15, "output": 0.60},
    }
    p = PREUS.get(model, PREUS["gpt-4o-mini"])
    return (tokens_input * p["input"] + tokens_output * p["output"]) / 1_000_000

# Exemple de càlcul
system_tokens  = comptar_tokens("Ets un assistent expert en ASIX...")
historial_tok  = comptar_tokens("Conversa de 20 missatges...")
pregunta_tok   = comptar_tokens("Com configuro una VLAN?")

total_input    = system_tokens + historial_tok + pregunta_tok
cost_estimat   = estimar_cost(total_input, tokens_output=500)

print(f"Cost estimat: ${cost_estimat:.6f}")
print(f"Cost 1000 consultes: ${cost_estimat * 1000:.4f}")

🔧 Bones Pràctiques de Context Window

✂️

Truncar els Resultats d'Eines

Limitar la sortida de cada eina a un màxim de tokens. Una cerca web no necessita retornar 10.000 paraules; 500-1000 és suficient.

🎯

System Prompt Concís

Cada paraula del system prompt costa diners en cada crida. Revisar periòdicament i eliminar instruccions redundants.

💾

Externalitzar el Coneixement

No posar el "manual d'instruccions" complet al context. Usar RAG per recuperar únicament les seccions rellevants per a cada consulta.

📊

Monitorar amb LangSmith

Instrumentar l'agent per veure exactament quants tokens consumeix cada component en cada consulta real.


✅ Activitats

Exercici 3.3.1 — Audit de Tokens

Per a un agent de la Pràctica 1 o 2, usa tiktoken per mesurar exactament quants tokens consumeix cada component (system prompt, historial, eines, resposta) en 10 consultes típiques. On es poden reduir tokens sense perdre qualitat?

Exercici 3.3.2 — Implementar Compressió

Modifica l'agent de la Pràctica 1 per usar ConversationSummaryBufferMemory amb max_token_limit=800. Verifica que: (a) les converses llargues no superen el limit, (b) el resum manté la informació important.

Exercici 3.3.3 — Calculadora de Costos

Crea una funció que, donats: model, nombre de consultes/dia, tokens promig per consulta, calculi el cost mensual. Compara GPT-4o vs GPT-4o-mini vs Ollama local per a 1000 consultes/dia.