Esta guía es una contribución de Hubeet.com, impulsada por el equipo de Solúnika, como parte de nuestra misión de acercar inteligencia artificial aplicada a equipos humanos de verdad.

1.1 ¿Qué vamos a construir?

En este manual vas a desarrollar un agente inteligente desde cero, capaz de percibir detallesrecordarlos como memoriaentender su estado actual y anticipar lo que podría pasar a continuación. Este agente tendrá las siguientes capacidades:

  • Multimodal: podrá procesar tanto texto como imágenes.
  • Memoria secuencial: recordará eventos pasados y los usará para tomar mejores decisiones.
  • Contextual: comprenderá su “presente” como una síntesis inteligente del pasado reciente.
  • Predictivo: podrá proyectar el futuro cercano y actuar en consecuencia.

Todo esto lo vas a implementar en Node.js, con una arquitectura modularsimple y extensible.


1.2 ¿Para qué sirve esto?

Esta clase de agentes puede aplicarse en sistemas como:

  • Asistentes personales introspectivos.
  • Bots que aprenden del usuario en tiempo real.
  • Sistemas de monitoreo con autoevaluación.
  • Juegos con NPCs con memoria y personalidad real.
  • Herramientas de productividad que se anticipan a tu próxima acción.

Este proyecto no es una prueba de concepto: es una base real para aplicaciones potentes. El código será didáctico pero profesional, optimizado para escalar a producción si lo deseás.


1.3 ¿Cómo lo vamos a encarar?

Dividiremos el desarrollo en cuatro módulos clave:

  1. Memoria: cómo representar y almacenar los eventos (detalles) que el agente percibe.
  2. Inferencia contextual: cómo entender el estado actual a partir del pasado reciente.
  3. Predicción de futuro: cómo anticipar lo que viene.
  4. Acción introspectiva: cómo decidir qué hacer con lo que se deduce y predice.

1.4 Tecnologías que usaremos

  • Node.js (v18+)
  • FAISS (via servidor Python o faiss-js)
  • pgvector + PostgreSQL para persistencia vectorial
  • CLIP (OpenAI) y sentence-transformers (via API) para embeddings multimodales
  • Transformers ligeros vía API o worker Python
  • WebSocket/HTTP/gRPC para comunicación entre módulos (según preferencia)

1.5 Empezamos a construir: Estructura base del proyecto

Creamos el esqueleto del proyecto:

mkdir agente-inteligente
cd agente-inteligente
npm init -y
npm install express pg dotenv uuid

Creamos esta estructura de carpetas:

/agente-inteligente
│
├── /src
│   ├── index.js               # entrada del sistema
│   ├── config.js              # configuración global
│   ├── db.js                  # conexión y queries a PostgreSQL
│   ├── memory/
│   │   ├── detailStore.js     # inserción y recuperación de detalles
│   ├── embeddings/
│   │   ├── embedder.js        # API de embeddings
│   ├── inference/
│   │   ├── contextBuilder.js  # deduce estado presente
│   ├── prediction/
│   │   ├── predictor.js       # anticipa eventos futuros
│   ├── agent/
│       ├── brain.js           # orquestador y evaluador introspectivo

Archivo .env:

DATABASE_URL=postgresql://usuario:clave@localhost:5432/agente

1.6 Definimos la estructura de memoria

Creamos una tabla detail en PostgreSQL:

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "vector";

CREATE TABLE detail (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    ts TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    embedding VECTOR(768) NOT NULL,
    type TEXT NOT NULL, -- 'text' | 'image'
    content TEXT,
    prev_id UUID REFERENCES detail(id),
    next_id UUID REFERENCES detail(id)
);

Y una tabla opcional para predicciones:

CREATE TABLE prediction (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    detail_ref_id UUID REFERENCES detail(id),
    horizon_min INT,
    pred_ts TIMESTAMPTZ,
    embedding VECTOR(768),
    text TEXT,
    policy_tag TEXT,
    confidence REAL,
    realized_id UUID,
    reward REAL
);

1.7 Primer módulo: insertamos detalles

Creamos /src/db.js:

const { Pool } = require('pg');
require('dotenv').config();

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
});

module.exports = {
  query: (text, params) => pool.query(text, params),
};

Creamos /src/memory/detailStore.js:

const db = require('../db');
const { v4: uuidv4 } = require('uuid');

async function insertDetail({ embedding, type, content, prevId = null }) {
  const id = uuidv4();
  await db.query(
    `INSERT INTO detail (id, embedding, type, content, prev_id)
     VALUES ($1, $2, $3, $4, $5)`,
    [id, embedding, type, content, prevId]
  );
  return id;
}

module.exports = { insertDetail };

1.8 Probamos con un ejemplo

Creamos src/index.js:

const { insertDetail } = require('./memory/detailStore');

async function main() {
  // Remplazar por un servicio de embedding real, esto es un mock
  const dummyEmbedding = Array(768).fill(0).map(() => Math.random());

  const id = await insertDetail({
    embedding: dummyEmbedding,
    type: 'text',
    content: 'Reunión con cliente sobre IA en 15 min',
  });

  console.log('Detalle insertado con ID:', id);
}

main();

1.9 Próximo paso

En el Capítulo 2 vamos a:

  • Implementar un servicio que genera embeddings reales de texto e imágenes.
  • Integrar CLIP y Sentence Transformers como APIs locales o remotas.
  • Insertar eventos automáticamente en el sistema a partir de inputs naturales.

Capítulo 2 — Darle sentidos al agente: Embeddings multimodales

2.1 ¿Qué son los embeddings y por qué los necesitamos?

Un embedding es una representación matemática (vectorial) de un concepto. En lugar de analizar palabras o imágenes directamente, las convertimos en vectores de números que capturan significado y contexto.

Para nuestro agente, cada “detalle” que percibe será convertido en un vector que:

  • Tiene dimensión fija (ej: 768)
  • Puede ser comparado con otros (similaridad)
  • Puede ser utilizado para deducir estados, generar texto o anticipar eventos

2.2 Tipos de entradas

Vamos a aceptar dos tipos de entradas:

  • Texto libre: frases, resúmenes, pensamientos del usuario, descripciones.
  • Imágenes: fotos de pantalla, objetos, expresiones, entorno.

Ambos se convertirán a embeddings usando modelos preentrenados.


2.3 Modelos que usaremos

Para cada modalidad:

  • Texto: sentence-transformers/all-MiniLM-L6-v2 (o similar)
  • Imagen: openai/clip-vit-base-patch32 para convertir imágenes en vectores

Podés correrlos en:

  1. Un servidor Python local con Flask/FastAPI
  2. Un microservicio en otra máquina (API REST)
  3. En Node.js usando wrappers, pero menos recomendado

En este caso, vamos a usar la opción 1 por simplicidad y robustez.


2.4 Servidor de embeddings en Python

Creamos una carpeta aparte para el servidor:

/embedder-service
│
├── app.py
├── requirements.txt

requirements.txt

fastapi
uvicorn
sentence-transformers
transformers
torch
Pillow

app.py

from fastapi import FastAPI, UploadFile, File
from sentence_transformers import SentenceTransformer
from transformers import CLIPProcessor, CLIPModel
from PIL import Image
import torch
import io

app = FastAPI()

text_model = SentenceTransformer("all-MiniLM-L6-v2")
clip_model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
clip_processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")

@app.post("/embed/text")
async def embed_text(data: dict):
    text = data.get("text", "")
    embedding = text_model.encode(text).tolist()
    return {"embedding": embedding}

@app.post("/embed/image")
async def embed_image(file: UploadFile = File(...)):
    image = Image.open(io.BytesIO(await file.read())).convert("RGB")
    inputs = clip_processor(images=image, return_tensors="pt")
    outputs = clip_model.get_image_features(inputs)
    embedding = outputs[0].detach().numpy().tolist()
    return {"embedding": embedding}

Para correrlo:

uvicorn app:app --host 0.0.0.0 --port 8001

2.5 Cliente Node.js del servicio

Creamos /src/embeddings/embedder.js:

const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');

const EMBEDDER_URL = 'http://localhost:8001';

async function embedText(text) {
  const response = await axios.post(`${EMBEDDER_URL}/embed/text`, { text });
  return response.data.embedding;
}

async function embedImage(path) {
  const form = new FormData();
  form.append('file', fs.createReadStream(path));

  const response = await axios.post(`${EMBEDDER_URL}/embed/image`, form, {
    headers: form.getHeaders(),
  });
  return response.data.embedding;
}

module.exports = { embedText, embedImage };

2.6 Probamos con texto

Editamos src/index.js:

const { insertDetail } = require('./memory/detailStore');
const { embedText } = require('./embeddings/embedder');

async function main() {
  const text = 'Reunión estratégica de IA a las 15:00';
  const embedding = await embedText(text);

  const id = await insertDetail({
    embedding,
    type: 'text',
    content: text,
  });

  console.log('Detalle insertado:', id);
}

main();

2.7 Probamos con una imagen

const { embedImage } = require('./embeddings/embedder');

async function main() {
  const path = './images/reunion.png';
  const embedding = await embedImage(path);

  const id = await insertDetail({
    embedding,
    type: 'image',
    content: 'reunion.png',
  });

  console.log('Imagen insertada con ID:', id);
}

2.8 Pensamos en el futuro: embeddings homogéneos

Cuando lleguemos a la inferencia contextual, necesitaremos comparar o relacionar directamente información proveniente de distintas modalidades, como texto e imagen. Para que esto sea posible, los embeddings generados por cada tipo de dato deben convivir en un mismo espacio vectorial o, al menos, ser proyectados a uno común que permita calcular similitudes, alineamientos o relaciones semánticas útiles.

Por eso elegimos CLIP y SentenceTransformers: - CLIP genera embeddings tanto de imágenes como de texto en un espacio compartido. Fue entrenado con pares imagen-texto, lo cual le da la capacidad de “entender” el significado visual y verbal de un concepto dentro del mismo sistema de coordenadas semánticas. - SentenceTransformers, en cambio, está optimizado para generar embeddings de texto con alta fidelidad semántica, y es ideal para tareas como recuperación de contexto, búsqueda y generación de prompts.

Para alinear o combinar embeddings de ambos mundos —cuando no están en el mismo espacio desde el inicio— existen técnicas como: - PCA (Análisis de Componentes Principales): permite reducir y proyectar vectores de diferentes dominios a un subespacio común, capturando las variaciones más relevantes y descartando ruido. - Autoencoders multimodales: redes neuronales diseñadas para aprender una codificación conjunta de datos heterogéneos (por ejemplo, una imagen y una descripción textual). - Pooling promedio (Average Pooling): útil para representar un grupo de embeddings (como varios textos o fragmentos de una imagen) como un único vector representativo, simplificando el cómputo de similitud y contextualización.

Más adelante veremos cómo aplicar estas estrategias para lograr que nuestro agente interprete imágenes y textos dentro de una misma lógica de contexto y significado.


2.9 Aseguramos coherencia

Agregamos validaciones básicas en detailStore.js:

if (!Array.isArray(embedding) || embedding.length !== 768) {
  throw new Error("Embedding inválido. Debe tener 768 dimensiones.");
}

Podés también normalizar el vector antes de guardar:

function normalize(vec) {
  const norm = Math.sqrt(vec.reduce((sum, x) => sum + x * x, 0));
  return vec.map(x => x / norm);
}

2.10 Conclusión de este capítulo

Tu agente ya puede:

  • Leer texto o imágenes del entorno.
  • Convertirlos en embeddings numéricos.
  • Guardarlos como detalles en una memoria persistente.

¡Acaba de percibir por primera vez! Ya tiene sentidos.


Capítulo 3 — Inferencia contextual: entender el “ahora”

3.1 ¿Qué es el estado presente?

El estado presente es un resumen vectorial de lo que está pasando ahora desde el punto de vista del agente. Es como una fotografía mental construida a partir de:

  • Los eventos recientes (detalles que ha percibido)
  • Su relevancia (cuán importantes o recientes son)
  • Su combinación semántica

Este “embedding de ahora” lo vamos a llamar E_now.


3.2 ¿Cómo se calcula?

Usaremos una técnica conocida como attention pooling ponderado. La idea es:

  1. Tomar los últimos N detalles almacenados.
  2. Asignarles un peso wᵢ en función del tiempo y la relevancia.
  3. Combinar todos los vectores usando esos pesos:E_now = ∑ (wᵢ × embeddingᵢ)

Esto nos da un vector compacto que resume el contexto actual.


3.3 Implementación del módulo

Creamos /src/inference/contextBuilder.js:

const db = require('../db');

function expDecayWeight(ts, now, tau = 15 * 60 * 1000) {
  const delta = now - new Date(ts).getTime(); // en ms
  return Math.exp(-delta / tau);
}

function normalize(vec) {
  const norm = Math.sqrt(vec.reduce((sum, x) => sum + x * x, 0));
  return vec.map(x => x / norm);
}

function weightedAverage(vectors, weights) {
  const sum = Array(vectors[0].length).fill(0);
  for (let i = 0; i < vectors.length; i++) {
    for (let j = 0; j < sum.length; j++) {
      sum[j] += vectors[i][j] * weights[i];
    }
  }
  const totalWeight = weights.reduce((a, b) => a + b, 0);
  return normalize(sum.map(x => x / totalWeight));
}

async function buildCurrentState({ windowSize = 10, tau = 15 * 60 * 1000 }) {
  const now = Date.now();
  const res = await db.query(
    `SELECT embedding, ts FROM detail ORDER BY ts DESC LIMIT $1`,
    [windowSize]
  );

  const embeddings = res.rows.map(r => r.embedding);
  const timestamps = res.rows.map(r => r.ts);

  const weights = timestamps.map(ts => expDecayWeight(ts, now, tau));
  const E_now = weightedAverage(embeddings, weights);

  return E_now;
}

module.exports = { buildCurrentState };

3.4 Probamos el estado actual

Editamos src/index.js:

const { buildCurrentState } = require('./inference/contextBuilder');

async function main() {
  const state = await buildCurrentState({});
  console.log('Estado actual del agente (E_now):');
  console.log(state.slice(0, 10), '...'); // mostramos primeras 10 dimensiones
}

main();

3.5 ¿Qué podemos hacer con 

E_now

?

Este vector no es solo un resumen. Es una puerta de entrada a acciones. Podemos:

  • Convertirlo en texto: “Estoy procesando múltiples eventos técnicos.”
  • Clasificarlo: foco_alto, ansiedad_media, esperando estímulo
  • Compararlo con estados previos
  • Usarlo como entrada para predicción de eventos futuros

3.6 Proyección textual del presente (opcional)

Creamos un servicio adicional en el servidor Python para convertir un embedding a texto (decoding aproximado):

En app.py del servidor:

from fastapi import Body
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer

decoder_model = AutoModelForSeq2SeqLM.from_pretrained("google/flan-t5-base")
decoder_tokenizer = AutoTokenizer.from_pretrained("google/flan-t5-base")

@app.post("/decode/embedding")
async def decode_embedding(data: dict = Body(...)):
    prompt = f"Describe el estado a partir del siguiente embedding: {data['embedding'][:10]} ..."
    inputs = decoder_tokenizer(prompt, return_tensors="pt")
    outputs = decoder_model.generate(inputs, max_new_tokens=50)
    decoded = decoder_tokenizer.decode(outputs[0], skip_special_tokens=True)
    return {"description": decoded}

Y desde Node.js (embedder.js):

async function decodeEmbedding(embedding) {
  const response = await axios.post(`${EMBEDDER_URL}/decode/embedding`, {
    embedding,
  });
  return response.data.description;
}

module.exports = { embedText, embedImage, decodeEmbedding };

3.7 Probamos la descripción del estado

const { buildCurrentState } = require('./inference/contextBuilder');
const { decodeEmbedding } = require('./embeddings/embedder');

async function main() {
  const E_now = await buildCurrentState({});
  const texto = await decodeEmbedding(E_now);

  console.log('Descripción del presente:', texto);
}

main();

3.8 ¿Qué sigue?

Ya tenemos un agente que:

  • Registra percepciones como detalles
  • Calcula el estado presente
  • Puede describirlo en lenguaje natural

Capítulo 4 — Anticipación: predecir el futuro cercano

4.1 ¿Qué es una predicción en este contexto?

Una predicción es una estimación vectorial (y opcionalmente textual) de lo que probablemente ocurrirá en un horizonte próximo. Se puede expresar como:

  • Un embedding futuro: lo que el agente espera sentir/ver.
  • Una descripción anticipada: “Habrá un ticket urgente en 10 minutos.”
  • Una acción sugerida: “Prepará el entorno técnico.”

4.2 ¿Con qué se predice?

El agente usará:

  1. El embedding del presente (E_now)
  2. La secuencia reciente de embeddings [Eₜ₋ₙ … Eₜ]

Opcionalmente, usamos:

  • La posición temporal real (timestamps)
  • Metadatos (tipo, urgencia, autor, etc.)

4.3 Arquitectura del predictor

Usaremos un modelo autoregresivo simple. Entrenado o inferido vía API externa, se comportará como:

Input: [Eₜ₋ₙ, …, E_now]  →  Output: [Eₜ₊₁, Eₜ₊₂, …]

Cada vector futuro puede luego traducirse a texto, clasificado o usado directamente.


4.4 Diseño del módulo

Creamos /src/prediction/predictor.js:

const db = require('../db');
const { buildCurrentState } = require('../inference/contextBuilder');
const axios = require('axios');

const PREDICTOR_URL = 'http://localhost:8002';

async function getRecentEmbeddings(windowSize = 10) {
  const res = await db.query(
    `SELECT embedding FROM detail ORDER BY ts DESC LIMIT $1`,
    [windowSize]
  );
  return res.rows.map(r => r.embedding).reverse();
}

async function predictFutureEmbeddings({ horizons = [5, 30] }) {
  const sequence = await getRecentEmbeddings(10);
  const E_now = await buildCurrentState({});

  const response = await axios.post(`${PREDICTOR_URL}/predict`, {
    context: [...sequence, E_now],
    horizons,
  });

  return response.data.predictions; // {horizon: embedding}
}

module.exports = { predictFutureEmbeddings };

4.5 Servidor Python de predicción

/predictor-service/app.py

from fastapi import FastAPI
from pydantic import BaseModel
import numpy as np
import torch
from torch import nn

app = FastAPI()

class DummyPredictor(nn.Module):
    def __init__(self, dim):
        super().__init__()
        self.lstm = nn.LSTM(input_size=dim, hidden_size=dim, num_layers=1, batch_first=True)
        self.linear = nn.Linear(dim, dim)

    def forward(self, seq):
        out, _ = self.lstm(seq)
        pred = self.linear(out[:, -1, :])
        return pred

model = DummyPredictor(768)

class PredictionRequest(BaseModel):
    context: list
    horizons: list

@app.post("/predict")
def predict(req: PredictionRequest):
    context = torch.tensor([req.context], dtype=torch.float32)
    out = model(context).detach().numpy()[0].tolist()
    # mock para múltiples horizontes
    return {"predictions": {h: out for h in req.horizons}}

Para correrlo:

uvicorn app:app --host 0.0.0.0 --port 8002

4.6 Traducción y acción

Agregamos función en embedder.js:

async function decodeEmbedding(embedding) {
  const response = await axios.post(`${EMBEDDER_URL}/decode/embedding`, {
    embedding,
  });
  return response.data.description;
}

Y en predictor.js, un wrapper completo:

const { decodeEmbedding } = require('../embeddings/embedder');

async function predictTextualFutures() {
  const futureEmbeddings = await predictFutureEmbeddings({});
  const results = {};

  for (const [horizon, emb] of Object.entries(futureEmbeddings)) {
    const desc = await decodeEmbedding(emb);
    results[horizon] = desc;
  }

  return results;
}

module.exports = { predictTextualFutures };

4.7 Probamos la predicción

En index.js:

const { predictTextualFutures } = require('./prediction/predictor');

async function main() {
  const futures = await predictTextualFutures();
  console.log('Futuros anticipados:');
  for (const [horizon, text] of Object.entries(futures)) {
    console.log(`En ${horizon} minutos: ${text}`);
  }
}

main();

4.8 Feedback loop

Creamos tabla en PostgreSQL:

CREATE TABLE prediction (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    detail_ref_id UUID REFERENCES detail(id),
    horizon_min INT,
    pred_ts TIMESTAMPTZ,
    embedding VECTOR(768),
    text TEXT,
    confidence REAL,
    realized_id UUID,
    reward REAL
);

Cada predicción se guarda junto con su tiempo estimado. Un cronjob evalúa su cumplimiento comparando con eventos reales futuros.


4.9 ¿Qué sigue?

Ya tenemos un agente que:

  • Percibe su entorno
  • Construye su estado presente
  • Predice lo que vendrá

Capítulo 5 — Introspección: actuar según lo que sabe y espera

5.1 ¿Qué es la introspección en un agente?

Introspección es la capacidad de:

  1. Observar su propio estado (emocional, operativo, contextual)
  2. Comparar eso con una expectativa, intención o patrón interno
  3. Decidir una acción correctiva, reflexiva o estratégica

Para lograr esto, necesitaremos un módulo central (el brain) que:

  • Lea el estado presente (E_now)
  • Evalúe las predicciones
  • Compare con un “estado ideal” o plan
  • Decida: actuar, esperar, redirigir, reflexionar

5.2 Diseño conceptual

                    ┌────────────┐
                    │  Detalles  │
                    └────┬───────┘
                         │
                 ┌───────▼────────┐
                 │ E_now (context)│
                 └───────┬────────┘
                         │
        ┌────────────────┼──────────────────┐
        │                │                  │
  Estado ideal       Predicción        Meta deseada
        │                │                  │
        └──────┬─────────┴──────────┬───────┘
               │      Comparación   │
               └───────┬────────────┘
                       ▼
                 ┌────────────┐
                 │  Decisión  │
                 └────┬───────┘
                      ▼
                Acción, Log, Reflexión

5.3 Estado ideal

Definimos un estado ideal E_ideal con un propósito. Podés fijarlo por tipo de día, perfil, estilo o contexto (ej: “operativo productivo”).

Creamos /src/agent/brain.js:

const { buildCurrentState } = require('../inference/contextBuilder');
const { predictTextualFutures } = require('../prediction/predictor');
const { decodeEmbedding } = require('../embeddings/embedder');

// Simulación de un vector ideal fijo
const E_ideal = Array(768).fill(0).map((_, i) => Math.sin(i / 40));

function cosineSimilarity(a, b) {
  const dot = a.reduce((sum, val, i) => sum + val * b[i], 0);
  const normA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
  const normB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
  return dot / (normA * normB);
}

async function introspect() {
  const E_now = await buildCurrentState({});
  const similarity = cosineSimilarity(E_now, E_ideal);

  const predicted = await predictTextualFutures();

  console.log('Evaluando estado actual...');
  if (similarity < 0.7) {
    console.log(`Estado desalineado (${(similarity * 100).toFixed(2)}%)`);
    console.log('Propuesta: reorganizar tareas o tomar pausa.');
  } else {
    console.log(`Estado alineado (${(similarity * 100).toFixed(2)}%)`);
    console.log('Continuar operativo.');
  }

  console.log('\nAnticipaciones:');
  for (const [h, text] of Object.entries(predicted)) {
    console.log(`En ${h} min: ${text}`);
  }

  return { E_now, similarity, predicted };
}

module.exports = { introspect };

5.4 Probamos el cerebro

En src/index.js:

const { introspect } = require('./agent/brain');

async function main() {
  await introspect();
}

main();

5.5 Agregamos acciones posibles

Podemos definir un archivo /src/agent/actions.js:

function ejecutarAccion(tag) {
  const acciones = {
    'reorganizar': () => console.log('[ACTION] Reorganizar tareas del día.'),
    'descanso': () => console.log('[ACTION] Sugerencia: tomá un break.'),
    'focus': () => console.log('[ACTION] Todo en orden, mantener el rumbo.'),
  };

  if (acciones[tag]) acciones[tag]();
  else console.log('[ACTION] Sin acción definida para:', tag);
}

module.exports = { ejecutarAccion };

Y modificar el brain.js para disparar acciones según el análisis:

const { ejecutarAccion } = require('./actions');

if (similarity < 0.7) {
  ejecutarAccion('reorganizar');
} else {
  ejecutarAccion('focus');
}

5.6 Agregamos log introspectivo

Creamos tabla:

CREATE TABLE introspection_log (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    ts TIMESTAMPTZ DEFAULT now(),
    similarity REAL,
    action TEXT,
    prediction JSONB,
    embedding VECTOR(768)
);

Y guardamos el log:

await db.query(
  `INSERT INTO introspection_log (similarity, action, prediction, embedding)
   VALUES ($1, $2, $3, $4)`,
  [similarity, similarity < 0.7 ? 'reorganizar' : 'focus', predicted, E_now]
);

5.7 Reflexión diaria

Podés agregar una función diaria que recorra los introspections y genere un resumen:

  • ¿Cuántas veces estuvo alineado?
  • ¿Qué predicciones se cumplieron?
  • ¿Cuándo debió intervenir y no lo hizo?

Lo dejamos para el capítulo siguiente como desafío.


5.8 Conclusión

Tu agente ahora:

  • Se autoevalúa
  • Compara su estado con un ideal
  • Anticipa y reacciona
  • Aprende sobre sí mismo en tiempo real

Capítulo 6 — Memoria evolutiva: reflexionar y aprender de sí mismo

6.1 ¿Por qué necesita memoria a largo plazo?

Hasta ahora el agente tiene:

  • Memoria de corto plazo (últimos detalles)
  • Predicciones de corto alcance
  • Introspección inmediata

Pero no recuerda lo vivido en bloque, ni puede detectar hábitos, errores o logros. La memoria evolutiva permite:

  • Consolidar experiencias pasadas
  • Detectar tendencias (fatiga recurrente, productividad cíclica)
  • Construir un modelo propio de comportamiento
  • Ajustar objetivos a largo plazo

6.2 ¿Qué vamos a construir?

  1. Un sistema de resumen periódico: toma los eventos de un día y genera un embedding resumen.
  2. Un repositorio de introspecciones pasadas, consultable.
  3. Un sistema de patrones: busca eventos repetidos, errores o aciertos.
  4. Una base para el aprendizaje continuo del agente.

6.3 Consolidación diaria: resumen del día

Creamos una nueva tabla:

CREATE TABLE daily_summary (
    date DATE PRIMARY KEY,
    embedding VECTOR(768),
    text TEXT,
    stats JSONB
);

Y una función /src/agent/reflection.js:

const db = require('../db');
const { decodeEmbedding } = require('../embeddings/embedder');

async function getDayEmbeddings(date) {
  const res = await db.query(
    `SELECT embedding FROM detail WHERE ts::date = $1`,
    [date]
  );
  return res.rows.map(r => r.embedding);
}

function averageEmbedding(embeddings) {
  const sum = Array(embeddings[0].length).fill(0);
  for (const emb of embeddings) {
    for (let i = 0; i < emb.length; i++) {
      sum[i] += emb[i];
    }
  }
  return sum.map(x => x / embeddings.length);
}

async function summarizeDay(date) {
  const embeddings = await getDayEmbeddings(date);
  if (embeddings.length === 0) return null;

  const summaryEmbedding = averageEmbedding(embeddings);
  const description = await decodeEmbedding(summaryEmbedding);

  await db.query(
    `INSERT INTO daily_summary (date, embedding, text, stats)
     VALUES ($1, $2, $3, $4)
     ON CONFLICT (date) DO UPDATE SET embedding = EXCLUDED.embedding`,
    [date, summaryEmbedding, description, {}]
  );

  return { date, description };
}

module.exports = { summarizeDay };

6.4 Agregamos resumen automático diario

Podés correrlo con un cron diario tipo:

const { summarizeDay } = require('./agent/reflection');
const { format } = require('date-fns');

async function main() {
  const yesterday = format(new Date(Date.now() - 86400000), 'yyyy-MM-dd');
  const result = await summarizeDay(yesterday);
  console.log('Resumen de ayer:', result?.description || 'Sin datos.');
}

main();

6.5 Análisis de patrones: evolución

Para detectar ciclos, temas recurrentes o patrones de evolución en la información semanal, creamos un módulo específico /src/agent/patterns.js. Este módulo permite analizar la similitud entre los resúmenes diarios almacenados en la base de datos, apoyándose en representaciones numéricas (embeddings) de esos textos. Abajo se detalla el proceso y cada función relevante.

1. Extracción de resúmenes históricos

La base de datos contiene una tabla llamada daily_summary, donde cada entrada incluye una fecha (date), una representación vectorial (embedding) y el texto resumido (text).

async function getAllSummaries() {
  const res = await db.query(`SELECT date, embedding, text FROM daily_summary ORDER BY date`);
  return res.rows;
}

Esta función obtiene todos los resúmenes, ordenados por fecha, permitiendo hacer análisis cronológicos.

2. Cálculo de similitud entre vectores

Para comparar si dos resúmenes son conceptualmente similares, se calcula la similitud coseno entre sus embeddings. Es decir, medir “cuán alineados” son dos textos en el espacio vectorial. Cuanto más cercana a 1 sea la similitud, mayor es el parecido.

function similarity(a, b) {
  const dot = a.reduce((sum, val, i) => sum + val * b[i], 0);
  const normA = Math.sqrt(a.reduce((sum, x) => sum + x * x, 0));
  const normB = Math.sqrt(b.reduce((sum, x) => sum + x * x, 0));
  return dot / (normA * normB);
}
  • Producto punto: suma de las multiplicaciones elemento a elemento entre dos vectores.
  • Norma: magnitud de cada vector.
  • Resultado: valor entre -1 (opuesto) y 1 (idénticos). Usamos el valor para decidir si dos resúmenes son “iguales” o muestran el mismo patrón.

3. Identificación de ciclos y patrones recurrentes

Se compara cada resumen con los posteriores para detectar pares con una similitud mayor o igual a un umbral (threshold), que por defecto es 0.95 (muy parecido). Cada par encontrado se guarda en una lista con su fecha, nivel de similitud y textos asociados.

async function findCycles(threshold = 0.95) {
  const summaries = await getAllSummaries();
  const cycles = [];

  for (let i = 0; i < summaries.length - 1; i++) {
    for (let j = i + 1; j < summaries.length; j++) {
      const sim = similarity(summaries[i].embedding, summaries[j].embedding);
      if (sim >= threshold) {
        cycles.push({
          date1: summaries[i].date,
          date2: summaries[j].date,
          similarity: sim.toFixed(3),
          summary1: summaries[i].text,
          summary2: summaries[j].text,
        });
      }
    }
  }

  return cycles;
}

¿Qué se obtiene?

  • Cada elemento en el array cycles señala un potencial ciclo o repetición temática entre dos fechas.
  • Permite visualizar la evolución de las preocupaciones, desafíos o actividades del equipo a través del tiempo.

4. Exportación del módulo

Se expone la función findCycles para que otros componentes del sistema puedan consultar los ciclos identificados y desplegarlos, reportar recurrencias o generar alertas de patrones repetidos.

module.exports = { findCycles };

Resumen

Este artefacto permite transformar datos secundarios (resúmenes diarios) en conocimiento estratégico, ayudando a identificar cuándo un problema o tema vuelve a surgir, si existe estancamiento en ciertas tareas, o si hay evolución real en la actividad semanal del equipo.---

6.6 Probamos la evolución

const { findCycles } = require('./agent/patterns');

async function main() {
  const results = await findCycles();
  console.log('Ciclos encontrados:');
  for (const r of results) {
    console.log(`[${r.date1}] ≈ [${r.date2}] (${r.similarity})`);
    console.log(`→ ${r.summary1}`);
    console.log(`→ ${r.summary2}\n`);
  }
}

main();

6.7 Aplicaciones concretas

  • Detectar que cada lunes el agente está disperso > sugerir rutina fuerte.
  • Detectar días pico de atención > mover tareas críticas ahí.
  • Ver si tras predicciones fallidas, el estado empeora > ajustar el modelo.

6.8 Próximos pasos opcionales

  • Entrenar embeddings propios con feedback supervisado
  • Integrar objetivos adaptativos (meta-learning)
  • Incorporar emociones o energía como dimensiones latentes
  • Aprender de otros agentes (transferencia de memoria)

6.9 Conclusión

Con este capítulo:

  • Tu agente ahora recuerda sus días
  • Detecta cuándo repite patrones
  • Tiene capacidad de evaluación evolutiva

Deja de ser solo reactivo, ahora puede revisarse, ajustar y evolucionar.


Capítulo 7 — Activación total: despliegue y operación del agente


7.1 ¿Qué tenemos?

Durante los capítulos anteriores construimos:

  • Percepción multimodal (texto + imágenes → embeddings)
  • Memoria secuencial con detalle vinculado
  • Inferencia contextual (E_now)
  • Predicción de estados futuros
  • Evaluación introspectiva
  • Reflexión diaria y análisis de patrones

Ahora lo conectamos todo.


7.2 API de control y consulta

Instalamos:

npm install express cors

Creamos /src/api/server.js:

const express = require('express');
const { insertDetail } = require('../memory/detailStore');
const { embedText, embedImage, decodeEmbedding } = require('../embeddings/embedder');
const { buildCurrentState } = require('../inference/contextBuilder');
const { predictTextualFutures } = require('../prediction/predictor');
const { introspect } = require('../agent/brain');
const { summarizeDay } = require('../agent/reflection');
const { findCycles } = require('../agent/patterns');

const app = express();
app.use(express.json());

app.post('/input/text', async (req, res) => {
  const { text } = req.body;
  const embedding = await embedText(text);
  const id = await insertDetail({ embedding, type: 'text', content: text });
  res.json({ id });
});

app.get('/state/now', async (_, res) => {
  const E_now = await buildCurrentState({});
  const description = await decodeEmbedding(E_now);
  res.json({ description, embedding: E_now });
});

app.get('/predict', async (_, res) => {
  const result = await predictTextualFutures();
  res.json(result);
});

app.get('/introspect', async (_, res) => {
  const result = await introspect();
  res.json(result);
});

app.get('/reflect/:date', async (req, res) => {
  const result = await summarizeDay(req.params.date);
  res.json(result || { message: 'No data' });
});

app.get('/patterns/cycles', async (_, res) => {
  const result = await findCycles();
  res.json(result);
});

module.exports = app;

Creamos el lanzador principal: src/index.js:

const app = require('./api/server');

app.listen(3000, () => {
  console.log('Agente activo en http://localhost:3000');
});

7.3 CLI para testeo rápido

Creamos /cli.js:

#!/usr/bin/env node
const axios = require('axios');

async function run() {
  const arg = process.argv[2];
  if (arg === 'estado') {
    const res = await axios.get('http://localhost:3000/state/now');
    console.log('Estado actual:', res.data.description);
  } else if (arg === 'predecir') {
    const res = await axios.get('http://localhost:3000/predict');
    console.log('Predicciones:');
    console.log(res.data);
  } else if (arg === 'introspección') {
    const res = await axios.get('http://localhost:3000/introspect');
    console.log('Introspección:', res.data);
  } else {
    console.log('Comandos disponibles: estado | predecir | introspección');
  }
}

run();

Lo hacemos ejecutable:

chmod +x cli.js

Y lo corrés como:

./cli.js estado

7.4 Despliegue y persistencia

Recomendaciones:

  • PM2 o Docker para mantener el proceso activo
  • PostgreSQL remoto o RDS
  • Agendador diario con cron o node-cron para summarizeDay
  • FAISS en modo servidor o faiss-js para vector search
  • Monitoreo con Grafana (usando métricas sobre introspection_log)

7.5 Evolución futura del agente

Este sistema es expansible. Ideas:

  • Entrenar E_ideal dinámico (autoaprendizaje de propósito)
  • Agregar “estados internos” como variables latentes (cansancio, flow)
  • Enviar alertas proactivas (email, Slack, etc.)
  • Comprimir semanas completas como “memoria episódica”
  • Integrar logs de actividad del sistema operativo o tareas del usuario

7.6 Conclusión

Acabás de construir un agente inteligente completo, con:

  • Percepción
  • Memoria
  • Evaluación introspectiva
  • Predicción
  • Reflexión
  • Acción

Capítulo Extra — Integración de herramientas vía MCP: convertir al agente en operador


En este capítulo:

  1. Explicamos qué es MCP y cómo el agente lo usará como capa de abstracción para interactuar con APIs o aplicaciones.
  2. Construimos un módulo de MCP Client que:
    • Envía queries en formato estructurado
    • Recibe respuestas normalizadas
    • Ejecuta acciones o decide en base a eso
  3. Le damos al agente la capacidad de actuar en herramientas externas, como si tuviera dedos.

Te propongo este flujo de integración MCP:

     [Estado actual]     [Predicción]
           │                │
           └────┬──────┬────┘
                ▼      ▼
           [Necesidad de acción]
                │
        ┌───────▼────────┐
        │   MCP Query    │  → ej: "consultar clima" / "crear tarea"
        └───────┬────────┘
                ▼
          [MCP Handler] → llama API
                │
           Respuesta estructurada
                ▼
        Acción / reflexión del agente

Capítulo Extra — Integración de herramientas: el agente como operador via MCP


1. ¿Qué es el Model Context Protocol (MCP)?

El Model Context Protocol (MCP) es un patrón diseñado para estructurar la comunicación entre un agente inteligente (por ejemplo, un sistema basado en IA) y herramientas externas tales como APIs, servicios, plugins o aplicaciones. Su propósito principal es establecer una interfaz clara y robusta que facilite la interacción eficiente entre los agentes y los diferentes módulos o fuentes de información externas.

¿Qué aporta el MCP?

  • Formato estandarizado de consulta y respuesta: Define reglas claras sobre cómo se debe estructurar la información que el agente solicita y la que recibe, facilitando la interoperabilidad entre diversos sistemas y módulos, y minimizando errores de interpretación.
  • Separación de intención y ejecución: El agente primero expresa su intención (lo que necesita u objetivo), y luego delega la ejecución de las acciones a componentes externos. Esto desacopla la lógica de decisión (del agente) de la lógica de acción (de las herramientas o servicios).
  • Compatibilidad total con modelos de lenguaje y pipelines de IA: MCP está concebido desde su origen para integrarse perfectamente con modelos de IA, especialmente modelos de lenguaje, permitiendo que éstos puedan consumir o generar acciones/consultas estructuradas como parte de procesos más amplios.

¿Cómo funciona en la práctica?

En otras palabras, MCP le permite al agente expresar peticiones como:

“Quiero obtener esta información, con estos parámetros, en este formato, y usarla como parte de mi contexto.”

Eso significa que cada interacción entre el agente y sus herramientas externas es completamente trazable, flexible y compatible con flujos de trabajo automatizados y escalables.

2. ¿Cómo se integra en nuestro agente?

Vamos a crear un módulo mcpClient que:

  1. Recibe un query estructurado (ej: "consultar clima en Buenos Aires").
  2. Lo convierte en una petición HTTP a una herramienta externa.
  3. Recibe la respuesta.
  4. La devuelve como objeto enriquecido listo para insertar en la memoria o usar como input de inferencia.

3. Estructura base del query MCP

Definimos el formato estándar (como JSON):

{
  "tool": "weather",
  "action": "get_forecast",
  "params": {
    "location": "Buenos Aires"
  },
  "format": "structured",
  "use_case": "inform_context"
}

4. Diseño del módulo MCP Client

Creamos /src/tools/mcpClient.js:

const axios = require('axios');

const TOOLS = {
  weather: {
    get_forecast: async (params) => {
      const res = await axios.get(`https://wttr.in/${params.location}?format=j1`);
      const condition = res.data.current_condition[0];
      return {
        summary: `El clima en ${params.location} es ${condition.weatherDesc[0].value}, ${condition.temp_C}°C`,
        raw: condition
      };
    }
  },
  tasks: {
    create: async (params) => {
      // Simulado: crear una tarea en sistema externo
      return {
        summary: `Tarea creada: ${params.title}`,
        raw: { id: 'mock-1234', ...params }
      };
    }
  }
};

async function handleMCPQuery(query) {
  const tool = TOOLS[query.tool];
  if (!tool || !tool[query.action]) {
    throw new Error('Herramienta o acción desconocida');
  }

  const result = await tool[query.action](query.params);
  return {
    format: query.format || 'structured',
    context_use: query.use_case || 'inform_context',
    ...result
  };
}

module.exports = { handleMCPQuery };

5. Usamos el agente para operar

Creamos src/index.js:

const { handleMCPQuery } = require('./tools/mcpClient');

async function main() {
  const query = {
    tool: 'weather',
    action: 'get_forecast',
    params: { location: 'Buenos Aires' },
    format: 'structured',
    use_case: 'inform_context'
  };

  const result = await handleMCPQuery(query);
  console.log('Resultado MCP:', result.summary);
}

main();

6. ¿Qué puede hacer el agente ahora?

Ya puede:

  • Consultar servicios con un protocolo común
  • Recibir respuestas contextuales
  • Incorporarlas a su memoria o reflexión
  • Tomar decisiones basadas en datos del mundo real

Esto convierte al agente en un verdadero operador contextual, no sólo un observador.


7. ¿Qué sigue?

En la próxima sección de este capítulo:

  • Integramos el módulo MCP al flujo introspectivo
  • Permitimos que el agente detecte cuándo necesita una herramienta
  • Y haga la consulta como parte de su razonamiento automático

Capítulo Extra (Parte 2) — Agente autónomo: detectar, consultar y actuar vía MCP


1. ¿Cuándo debería el agente usar una herramienta?

Durante el proceso de introspección, el agente analiza su estado (E_now), lo compara con el ideal (E_ideal) y anticipa el futuro.

Pero hay momentos en los que:

  • Le falta información contextual (ej: clima, calendario)
  • Necesita consultar una fuente externa para confirmar una predicción
  • Desea ejecutar una acción que va más allá de su memoria

Ejemplo:

Predicción: “Podría llover esta tarde.” → Acción: Consultar herramienta del clima para confirmar y decidir.

2. Integración en el módulo 

brain.js

Ampliamos brain.js para incorporar lógica de consulta MCP basada en predicción o estado:

const { buildCurrentState } = require('../inference/contextBuilder');
const { predictTextualFutures } = require('../prediction/predictor');
const { decodeEmbedding } = require('../embeddings/embedder');
const { handleMCPQuery } = require('../tools/mcpClient');
const db = require('../db');

const E_ideal = Array(768).fill(0).map((_, i) => Math.sin(i / 40));

function cosineSimilarity(a, b) {
  const dot = a.reduce((sum, val, i) => sum + val * b[i], 0);
  const normA = Math.sqrt(a.reduce((sum, x) => sum + x * x, 0));
  const normB = Math.sqrt(b.reduce((sum, x) => sum + x * x, 0));
  return dot / (normA * normB);
}

async function introspect() {
  const E_now = await buildCurrentState({});
  const similarity = cosineSimilarity(E_now, E_ideal);
  const predicted = await predictTextualFutures();

  let extraContext = [];

  // Detecta necesidad de herramienta
  const needsWeather = Object.values(predicted).some(text =>
    text.toLowerCase().includes("lluvia") || text.toLowerCase().includes("clima")
  );

  if (needsWeather) {
    const weatherQuery = {
      tool: 'weather',
      action: 'get_forecast',
      params: { location: 'Buenos Aires' },
      format: 'structured',
      use_case: 'confirm_prediction'
    };
    const weatherInfo = await handleMCPQuery(weatherQuery);
    extraContext.push(weatherInfo.summary);
  }

  // Log introspectivo enriquecido
  await db.query(
    `INSERT INTO introspection_log (similarity, action, prediction, embedding)
     VALUES ($1, $2, $3, $4)`,
    [
      similarity,
      similarity < 0.7 ? 'reorganizar' : 'focus',
      { predicted, tools_used: extraContext },
      E_now
    ]
  );

  return { similarity, predicted, tools_used: extraContext };
}

module.exports = { introspect };

3. Resultado: introspección activa y autónoma

Ahora el agente:

  • Predice lo que puede pasar
  • Detecta si necesita una herramienta
  • Llama esa herramienta (vía MCP)
  • La usa como parte de su razonamiento

Ejemplo de salida:

{
  "similarity": 0.62,
  "predicted": {
    "30": "Es probable que llueva a la tarde."
  },
  "tools_used": [
    "El clima en Buenos Aires es Lluvia moderada, 18°C"
  ]
}

4. Qué podemos extender desde acá

  • Hacer que el agente proponga tareas si detecta un evento (ej: “crear recordatorio si llueve”)
  • Integrar herramientas como calendar, crm, ERP, base de conocimiento
  • Implementar un dispatcher automático de queries cuando la introspección detecta brechas contextuales

Capítulo Extra (Parte 3) — Dispatcher MCP: decisiones dinámicas de integración


1. ¿Qué es el dispatcher MCP?

Es un router inteligente de herramientas: recibe señales (estado, predicciones, metas), interpreta si falta contexto, y decide:

  • Qué herramienta usar
  • Qué acción ejecutar
  • Cómo usar la respuesta

Funciona como una capa de decisión entre el agente y su arsenal de APIs, de forma autónoma.


2. ¿Cómo lo pensás?

El dispatcher:

  • Recibe: E_now, predicciones, objetivos opcionales, flags
  • Evalúa: ¿necesito una herramienta externa?
  • Arma el query MCP
  • Llama a handleMCPQuery()
  • Devuelve un bloque contextual_enrichment

3. Creamos el módulo 

/src/tools/mcpDispatcher.js

const { handleMCPQuery } = require('./mcpClient');

async function decideAndQuery({ predicted, stateDescription }) {
  const toolsUsed = [];

  // Heurística simple basada en predicción
  if (
    Object.values(predicted).some(t =>
      t.toLowerCase().includes('lluvia') || t.toLowerCase().includes('clima')
    )
  ) {
    toolsUsed.push({
      tool: 'weather',
      action: 'get_forecast',
      params: { location: 'Buenos Aires' },
      format: 'structured',
      use_case: 'confirm_prediction'
    });
  }

  // Ejemplo: si el estado menciona reuniones, usar herramienta de agenda
  if (stateDescription.toLowerCase().includes('reunión')) {
    toolsUsed.push({
      tool: 'calendar',
      action: 'list_upcoming',
      params: { window_min: 60 },
      format: 'structured',
      use_case: 'verify_conflict'
    });
  }

  const results = [];
  for (const q of toolsUsed) {
    try {
      const result = await handleMCPQuery(q);
      results.push({ tool: q.tool, summary: result.summary });
    } catch (e) {
      results.push({ tool: q.tool, summary: 'Error de consulta', error: e.message });
    }
  }

  return results;
}

module.exports = { decideAndQuery };

4. Lo conectamos al brain.js

Modificamos el flujo introspectivo:

const { decodeEmbedding } = require('../embeddings/embedder');
const { decideAndQuery } = require('../tools/mcpDispatcher');

async function introspect() {
  const E_now = await buildCurrentState({});
  const similarity = cosineSimilarity(E_now, E_ideal);
  const predicted = await predictTextualFutures();
  const stateText = await decodeEmbedding(E_now);

  const extraContext = await decideAndQuery({ predicted, stateDescription: stateText });

  await db.query(
    `INSERT INTO introspection_log (similarity, action, prediction, embedding)
     VALUES ($1, $2, $3, $4)`,
    [
      similarity,
      similarity < 0.7 ? 'reorganizar' : 'focus',
      { predicted, tools_used: extraContext },
      E_now
    ]
  );

  return { similarity, predicted, tools_used: extraContext };
}

5. El resultado

El agente ahora:

  • Analiza lo que puede pasar
  • Evalúa su estado actual
  • Decide si necesita una herramienta
  • Ejecuta la acción correcta sin ayuda humana

Ejemplo:

{
  "predicted": {
    "30": "Es probable que llueva a la tarde.",
    "60": "Podrías tener una reunión importante"
  },
  "tools_used": [
    {
      "tool": "weather",
      "summary": "El clima en Buenos Aires es lluvia ligera, 17°C"
    },
    {
      "tool": "calendar",
      "summary": "Reunión con equipo técnico a las 15:00"
    }
  ]
}

Capítulo Extra (Parte 4) — Llevando al siguiente nivel: hacia un agente autoexpandible


1. ¿Qué significa “llevarlo al extremo”?

Significa convertir al agente en algo más que un consumidor de APIs:

  • Que razone sobre qué necesita saber o hacer
  • Que genere los queries MCP de forma dinámica
  • Que aprenda de sus decisiones pasadas
  • Que optimice sus interacciones con herramientas externas
  • Y que, eventualmente, modifique su propio comportamiento

2. Componente clave: generador de intención MCP

¿Qué haría esto?

A partir de una predicción o estado actual, el agente le pregunta a un modelo LLM:

“¿Qué herramientas debería consultar y con qué parámetros para enriquecer mi contexto actual?”

Implementación conceptual

{
  "input": {
    "estado_actual": "Baja energía, posible lluvia, reunión inminente",
    "predicciones": [
      "Lluvia a la tarde",
      "Conflicto de calendario"
    ]
  },
  "salida esperada": [
    {
      "tool": "weather",
      "action": "get_forecast",
      "params": { "location": "Buenos Aires" }
    },
    {
      "tool": "calendar",
      "action": "list_upcoming",
      "params": { "window_min": 60 }
    }
  ]
}

Cómo implementarlo

  1. Cargás un prompt preformateado en un LLM (local o remoto)
  2. Le pasás estado + predicción
  3. El modelo devuelve un bloque MCP-ready
  4. Lo ejecutás como siempre

3. Aprendizaje del uso de herramientas

Cada vez que el agente llama a una herramienta:

  • Se registra el resultado, éxito o falla
  • Se mide su impacto en la introspección (¿mejoró la predicción? ¿resolvió la duda?)
  • Se ajusta un ranking interno de confianza o valor

Esto permite, por ejemplo:

  • Preferir ciertas herramientas
  • Evitar redundancias (“esto ya lo sé”)
  • Pedir ayuda sólo cuando vale la pena

4. Optimización de queries

El agente puede:

  • Reformular parámetros según contexto (ej: adaptar la ciudad a la geolocalización)
  • Elegir el mejor formato de respuesta (structured, natural, raw)
  • Ajustar frecuencia de consulta (no volver a consultar lo mismo en intervalos cortos)

Esto se logra con un módulo de control de diálogo interno o historial de acciones (tool_usage_log en PostgreSQL, por ejemplo).


5. Meta-introspección

El agente puede usar su propio log introspectivo para:

  • Detectar qué tipo de predicciones fallan seguido
  • Saber cuándo usar una herramienta anticipa mejor los eventos
  • Identificar errores en su razonamiento y compensar

Ejemplo:

“Cada vez que ignoro el clima, termino sugiriendo tareas afuera que luego son fallidas.” → “Aumentar prioridad de la herramienta weather cuando hay tareas al aire libre.”

6. Multiplicación de agentes

Una vez este sistema funciona:

  • Podés clonarlo para otros perfiles (agente de logística, agente financiero, agente emocional)
  • Podés crear un agente supervisor que observe múltiples agentes y optimice su coordinación
  • Podés permitir que agentes compartan información vía MCP como si fueran módulos entre sí

7. ¿Qué podría venir después?

  • Integración con asistentes reales (Siri, Alexa, WhatsApp, Slack)
  • Autoedición de su pipeline (el agente se reconfigura)
  • Entrenamiento de un modelo de embeddings personalizado con datos históricos del agente
  • Visualización gráfica de su línea de tiempo + decisiones

8. En resumen: lo que logramos

Nuestro agente:

  • Tiene memoria, predicción, reflexión y acción
  • Se conecta a herramientas, pero también decide cuándo y cómo
  • Es capaz de evolucionar su contexto
  • Puede llevar este poder a cualquier aplicación del mundo real