ES | EN

EL CONTEXTO COMO SUPERPODER: INYECCIÓN AUTOMÁTICA DE ESTADO EN CADA MENSAJE LLM - PARTE I

TAGS: LLMs / PRODUCTIVIDAD / PROMPT ENGINEERING / PYTHON READ_TIME: 12 MIN
El Contexto como Superpoder: Inyección automática de estado en cada mensaje LLM

La diferencia entre un LLM genérico y un asistente que realmente te conoce no está en el modelo. Está en el contexto que le das. Pero escribir el contexto manualmente en cada mensaje es lento, inconsistente y fácil de olvidar. FocOs resuelve esto inyectando automáticamente el contexto del proyecto activo en cada mensaje — sin que el usuario escriba nada extra, sin que se olvide, sin inconsistencias entre sesiones.

PROJECT_STATUS: STABLE

Stack: Python · JavaScript · cualquier LLM
Proyecto de referencia: FocOs — sistema de contexto
Objetivo: Transformar un LLM genérico en un asistente que sabe exactamente dónde estás y qué construyes

01. EL PROBLEMA QUE RESUELVE

Sin contexto automático, el LLM no sabe en qué proyecto estás, qué tarea estás haciendo, qué decisiones se han tomado, ni cuál es el estado actual del sistema. El usuario termina repitiendo el contexto en cada mensaje, lo que consume tokens, tiempo y atención. O peor — no lo hace, y el LLM genera respuestas genéricas que no se aplican al problema específico.

🎓 Explicación no técnica

Imagina que contratas a un consultor experto. Cada vez que lo llamas, tiene amnesia total — no recuerda nada de la última llamada. Tienes dos opciones: explicarle todo desde cero cada vez (agotador), o darle un brief de una página al inicio de cada llamada que lo pone al día en 30 segundos. FocOs genera ese brief automáticamente y lo mete en cada mensaje antes de que llegue al LLM.

02. LA STAMP — EL SELLO DE CONTEXTO

FocOs construye una línea comprimida llamada Stamp que se antepone a cada mensaje. En 80-120 caracteres comunica todo lo que el LLM necesita para responder con precisión.

# Formato de la Stamp:
[F|HH:MM TZ|Día|DX/1096|Proyecto|Tarea|PX|WS]

# Campos:
# F = Identificador del usuario (Frank)
# HH:MM TZ = Hora local con zona horaria
# Día = Día de la semana abreviado
# DX/1096 = Día del KayrOs (cuánto del horizonte ha pasado)
# Proyecto = Nombre del proyecto activo
# Tarea = ID de la tarea o célula activa
# PX = Número de pomodoro en la sesión actual
# WS = Workspace activo (WS1, WS2, WS3)

# Ejemplos reales:
[F|10:30 COT|Jue|D4/1096|TeliOs|TELIO-U003|P1|WS1]
[F|14:45 COT|Mar|D18/1096|FocOs|FOCOS-B012|P3|WS2]
[F|09:00 COT|Lun|D1/1096|Novela-Ciclos|CAP-07|P1|WS1]
[F|22:30 COT|Vie|D365/1096|InfoGraTech|POST-14|P2|WS3]
Campo Ejemplo Descripción
F F Identificador del usuario. Personaliza la identidad del asistente.
HH:MM TZ 10:30 COT Hora local con zona horaria. Permite respuestas sensibles al momento del día.
DX/1096 D4/1096 Día del horizonte KayrOs. Ubica al LLM en el ciclo de vida del proyecto.
Proyecto TeliOs Nombre del proyecto activo en el workspace. Define el dominio de respuesta.
PX P1 Número de pomodoro en la sesión. Indica nivel de fatiga cognitiva del usuario.

03. CONSTRUIR LA STAMP EN PYTHON

La función get_context() en FocOs construye la stamp y el system prompt completo que se inyecta en cada llamada al LLM. Consulta la base de datos local para obtener el estado real del workspace activo.

# main.py — FocOsAPI

def get_context(self) -> dict:
    '''
    Construye el contexto completo del proyecto activo.
    Se inyecta automáticamente en cada llamada al LLM.
    '''
    import datetime
    conn = sqlite3.connect(DB_PATH)

    # Datos del workspace activo
    ws = conn.execute(
        'SELECT project_id FROM workspaces WHERE ws_id=1'
    ).fetchone()
    project_id = ws[0] if ws else None

    # Datos del proyecto
    project = None
    if project_id:
        row = conn.execute(
            'SELECT name, status, meta FROM projects WHERE id=?',
            (project_id,)
        ).fetchone()
        if row:
            project = {
                'name': row[0],
                'status': row[1],
                'meta': json.loads(row[2] or '{}'),
            }

    # Tarea activa
    task = conn.execute(
        'SELECT title FROM tasks WHERE status="active" LIMIT 1'
    ).fetchone()
    conn.close()

    # Construir stamp
    now = datetime.datetime.now()
    hora = now.strftime('%H:%M')
    dia = now.strftime('%a')[:3].capitalize()
    proyecto = project['name'] if project else 'Sin proyecto'
    tarea = task[0][:20] if task else 'Sin tarea'
    day_num = self._get_kayros_day()

    stamp = f'[F|{hora} COT|{dia}|D{day_num}/1096|{proyecto}|{tarea}|P1|WS1]'

    # System prompt con contexto completo
    meta = project['meta'] if project else {}
    system = f'''Eres Chronos — asistente de desarrollo de Frank.
Contexto activo: {stamp}

PROYECTO: {proyecto}
ESTADO: {project["status"] if project else "desconocido"}
TAREA ACTIVA: {tarea}
TIPO: {meta.get("tipo", "general")}
STACK: {meta.get("stack", "no definido")}
FILOSOFÍA: {meta.get("filosofia", "")}

Responde siempre en el idioma del usuario.
Prioriza soluciones prácticas sobre teoría.
Si detectas un error, señálalo antes de responder la pregunta.
Máximo 3 opciones cuando hay alternativas.
'''

    return {
        'stamp': stamp,
        'system': system,
        'proyecto': proyecto,
        'tarea': tarea,
        'day_num': day_num,
    }

04. INYECTAR EL CONTEXTO EN CADA MENSAJE

send_to_llm() llama a get_context() automáticamente. El usuario nunca tiene que escribir el contexto manualmente — llega al LLM en cada mensaje sin intervención.

def send_to_llm(self, message: str, history: list = None) -> dict:
    cfg = self.get_llm_config()
    provider = cfg.get('provider', 'gemini')
    model = cfg.get('model', 'gemini-2.0-flash')

    # CONTEXTO AUTOMÁTICO — siempre presente
    ctx = self.get_context()
    system = ctx['system']
    stamp = ctx['stamp']

    # El mensaje que llega al LLM incluye la stamp
    # EJ: '[F|10:30 COT|Jue|D4/1096|TeliOs|TELIO-U003|P1|WS1]\n\ncómo implemento el bus pub/sub?'
    full_message = f'{stamp}\n\n{message}'

    messages = []
    if history:
        for h in (history if isinstance(history, list) else []):
            if isinstance(h, dict):
                messages.append({
                    'role': h.get('role', 'user'),
                    'content': h.get('content', '')
                })
    messages.append({'role': 'user', 'content': full_message})

    # Despachar al proveedor con system prompt contextualizado
    return self._dispatch_llm(provider, model, system, messages, cfg)

05. EL CONTEXTO PARA PROYECTOS NO TÉCNICOS

La stamp no es exclusiva del desarrollo de software. El mismo mecanismo se adapta a cualquier tipo de proyecto — el system prompt cambia según el dominio, pero la estructura comprimida permanece idéntica.

# ESCRITOR — trabajando en una novela
[F|09:15 COT|Mar|D22/1096|Novela-Ciclos|CAP-07|P1|WS1]

# System prompt adaptado:
# Proyecto: Novela — Ciclos
# Capítulo activo: 7 — El encuentro
# Personaje actual: Elena
# Tono: melancólico, introspectivo
# Última decisión: POV cambia a primera persona en este capítulo

# MÚSICO — produciendo un álbum
[F|23:00 COT|Vie|D45/1096|Album-Raices|TRACK-03|P2|WS1]

# System prompt adaptado:
# Proyecto: Álbum — Raíces
# Track activo: 03 — Sin título aún
# BPM: 92 | Tonalidad: Am | Sección: puente
# DAW: Ableton | Referencia: Nils Frahm

# INVESTIGADOR
[F|11:30 COT|Mié|D8/1096|Tesis-Ecosistemas|CAP-04|P3|WS2]

# System prompt adaptado:
# Proyecto: Tesis — Ecosistemas Digitales
# Capítulo: 4 — Marco metodológico
# Hipótesis activa: H3 — correlación datos/comportamiento
# Fuente pendiente: IEEE 2024 sobre redes complejas
Tipo de proyecto Stamp de ejemplo Contexto clave inyectado
Desarrollo de software D4/1096|TeliOs|TELIO-U003 Stack, arquitectura, estado de la tarea activa.
Escritura / novela D22/1096|Novela-Ciclos|CAP-07 Capítulo, personaje, tono, última decisión narrativa.
Producción musical D45/1096|Album-Raices|TRACK-03 BPM, tonalidad, sección activa, referencia sonora.
Investigación / tesis D8/1096|Tesis-Ecosistemas|CAP-04 Capítulo, hipótesis activa, fuentes pendientes.

06. MOSTRAR LA STAMP EN LA UI DE FOCOS

El bridge de JavaScript expone el contexto al panel LLM en tiempo real. El usuario puede ver en todo momento qué stamp está siendo inyectada y confirmar que el asistente tiene el estado correcto antes de enviar cualquier mensaje.

// bridge.js — Mostrar stamp en el panel LLM

async function llmInit() {
  try {
    const api = await FocOs.ready()
    const ctx = await api.get_context()

    // Mostrar stamp en el header del panel LLM
    const stampEl = document.getElementById('llm-stamp')
    if (stampEl && ctx.stamp) {
      stampEl.textContent = ctx.stamp
      stampEl.title = 'Contexto activo inyectado en cada mensaje'
    }

    // Mostrar proyecto y tarea en el header
    const projEl = document.getElementById('llm-project')
    if (projEl) projEl.textContent = ctx.proyecto

  } catch(e) {
    console.log('llmInit:', e.message)
  }
}

// Cuando el usuario envía un mensaje:
async function llmSend(message) {
  const history = llmGetHistory() // historial de la conversación

  // send_to_llm() inyecta el contexto automáticamente
  // El usuario no tiene que hacer nada
  const result = await FocOs.sendToLLM(message, history)

  if (result.ok) {
    llmAppendMessage('assistant', result.response)
    llmSaveHistory('user', message)
    llmSaveHistory('assistant', result.response)
  } else {
    llmShowError(result.error)
  }
}

CONCLUSIÓN

La inyección automática de contexto transforma un LLM genérico en un asistente que sabe exactamente dónde estás y qué construyes — sin que el usuario escriba nada extra. La stamp de 80 caracteres comunica en formato ultra-comprimido el estado completo de la sesión de trabajo. El mismo mecanismo funciona para cualquier tipo de proyecto: código, escritura, música, investigación. Y porque el contexto se construye desde la base de datos local de FocOs, siempre refleja el estado real — nunca la ficción.

> SYSTEM_READY > NODE_ONLINE

< session_end // node: exit >
> INFOGRATECH_CORE_SHELL X
$