Resumen Ejecutivo

Construimos un sistema de grafo de conocimiento desde cero utilizando MongoDB para el almacenamiento de episodios, Amazon S3 para la persistencia de vectores, embeddings de OpenAI para búsqueda semántica y LangChain para la integración con herramientas de IA. El objetivo era una solución escalable y rentable para gestionar relaciones complejas en datos de salud sin pagar por una base de datos de grafos externa.

Qué es un Grafo de Conocimiento y Por Qué lo Construimos

Un grafo de conocimiento almacena información como entidades y relaciones interconectadas, permitiendo que los sistemas de IA entiendan contexto, patrones y conexiones entre diferentes tipos de datos. En salud y fitness, esto significa comprender cómo las rutinas de ejercicio, los hábitos alimenticios, los estados emocionales y las mediciones de progreso se influyen mutuamente a lo largo del tiempo.

Nuestro Caso de Uso Específico: Plataforma de Salud Goal Weight

Goal Weight es una aplicación de salud, fitness y nutrición que necesitaba rastrear datos complejos de salud en múltiples dimensiones (ejercicio, nutrición, emociones, sueño, mediciones), habilitar búsqueda semántica para asistentes de IA, generar insights personalizados conectando patrones entre diferentes episodios de salud, dar soporte a conversaciones con IA con una rica comprensión contextual y escalar sin dependencias externas.

Por Qué Elegimos una Implementación Nativa

En lugar de usar Neo4j o un servicio de grafos basado en la nube, construimos el nuestro porque:

  1. La información sensible de salud permanece dentro de nuestra infraestructura.
  2. Sin tarifas de servicios externos que escalan de forma impredecible.
  3. Optimizaciones personalizadas para nuestros patrones específicos de datos de salud.
  4. Integración nativa con herramientas LangChain.
  5. Mejor alineación con los requisitos de GDPR/HIPAA.

Descripción General de la Arquitectura

Nuestro sistema de grafo de conocimiento nativo tiene cuatro capas principales:

┌────────────────────────────────────────────────────────────────────────┐
│                          Knowledge Graph System                        │
├────────────────────────────────────────────────────────────────────────┤
│  LangChain Tools  │    AI Insights    │    Semantic Search             │
├────────────────────────────────────────────────────────────────────────┤
│           KnowledgeGraphService (Business Logic Layer)                 │
├────────────────────────────────────────────────────────────────────────┤
│   MongoDB         │    OpenAI         │    Amazon S3    │  Vector      │
│ (Episode Store)   │  (Embeddings)     │  (Vector Store) │ Search       │
│                   │                   │                 │ Engine       │
│ - Episodes        │ - text-embedding  │ - Vector Index  │ - Cosine     │
│ - Metadata        │ - 3-small         │ - Backup/HA     │ - Similarity │
│ - Relationships   │ - Semantic        │ - Scalability   │ - Ranking    │
└────────────────────────────────────────────────────────────────────────┘

Stack de Tecnología Principal

1. Capa de Almacenamiento de Datos

MongoDB con TypeGoose

@modelOptions({
    schemaOptions: {
        timestamps: true,
        collection: 'knowledgeepisodes'
    }
})
@index({ userId: 1, date: -1 })
@index({ userId: 1, type: 1, date: -1 })
export class KnowledgeEpisode {
    @prop({ required: true, ref: () => User })
    userId!: Types.ObjectId;

    @prop({ required: true, enum: Object.values(EpisodeType) })
    type!: EpisodeType;

    @prop({ required: true })
    date!: Date;

    @prop({ required: true, type: () => Object })
    body!: Record<string, any>; // Flexible health data structure

    @prop({ required: true })
    summary!: string; // Human-readable for vector search

    @prop({ type: () => [String] })
    tags?: string[]; // Additional categorization

    @prop({ type: () => Object })
    metadata?: Record<string, any>; // Context and source info
}

El esquema flexible almacena datos complejos de salud como JSON, con índices compuestos optimizados para consultas basadas en usuario y temporales. La integración con TypeScript proporciona validación en tiempo de compilación.

Almacenamiento de Vectores en Amazon S3

export default class VectorStoreHelper {
    private static readonly s3Client = new S3Client({
        credentials: {
            accessKeyId: process.env.AWS_ACCESSKEY,
            secretAccessKey: process.env.AWS_SECRETKEY
        },
        region: process.env.AWS_REGION || 'us-east-1'
    });

    static async storeDocument(document: VectorDocument): Promise<void> {
        // Generate embedding if not provided
        if (!document.embedding) {
            document.embedding = await this.createEmbedding(document.content);
        }

        // Store in S3 with JSON format
        const vectorCommand = new PutObjectCommand({
            Bucket: this.bucket,
            Key: `${this.vectorFolder}/${document.id}.json`,
            Body: JSON.stringify(storedVector, null, 2),
            ContentType: 'application/json'
        });

        await this.s3Client.send(vectorCommand);
    }
}

S3 ofrece redundancia integrada, almacenamiento de pago por uso y caché en memoria para vectores de acceso frecuente.

2. Motor de Búsqueda Semántica

Integración de Embeddings de OpenAI

static async createEmbedding(text: string): Promise<number[]> {
    const response = await this.openai.embeddings.create({
        model: 'text-embedding-3-small', // Optimized for cost and performance
        input: text
    });
    
    return response.data[0].embedding;
}

static async searchSimilar(query: SearchQuery): Promise<SearchResult[]> {
    // Generate embedding for the query
    const queryEmbedding = await this.createEmbedding(query.query);
    
    // Calculate similarity with all cached vectors
    const results: SearchResult[] = [];
    
    for (const [id, vector] of this.vectorCache) {
        const similarity = this.cosineSimilarity(queryEmbedding, vector.embedding);
        
        results.push({
            id: vector.id,
            content: vector.content,
            metadata: vector.metadata,
            score: similarity
        });
    }
    
    // Sort by similarity score and return top-k
    return results.sort((a, b) => b.score - a.score).slice(0, query.k || 10);
}

El motor de búsqueda maneja términos de salud en portugués e inglés, convierte fechas relativas como "la semana pasada" a formatos absolutos y soporta filtrado por tipo y metadatos.

3. Modelado del Dominio de Salud

Sistema de Tipos de Episodio

export enum EpisodeType {
    EXERCISE = 'exercise',      // Workout sessions, training data
    NUTRITION = 'nutrition',    // Meals, calorie tracking, macros
    EMOTION = 'emotion',        // Mood tracking, emotional states
    REFLECTION = 'reflection',  // Daily thoughts, insights
    GOAL = 'goal',              // Objectives, targets, milestones
    MEASUREMENT = 'measurement', // Weight, body composition
    SLEEP = 'sleep',            // Sleep quality, duration
    MEDICATION = 'medication',  // Supplements, prescriptions
    SYMPTOM = 'symptom',        // Health issues, discomfort
    MOOD = 'mood',              // Emotional tracking
    ENERGY = 'energy',          // Energy levels, fatigue
    STRESS = 'stress',          // Stress management, levels
    PAIN = 'pain',              // Physical discomfort tracking
    OTHER = 'other'             // Miscellaneous health data
}

Generación Inteligente de Resúmenes

static generateSummaryFromBody(type: EpisodeType, body: Record<string, any>, date: Date): string {
    const formattedDate = moment(date).format('YYYY-MM-DD');
    
    switch (type) {
        case EpisodeType.EXERCISE: {
            if (body.exercises && Array.isArray(body.exercises)) {
                const totalWeight = body.totalWeight || 0;
                const exerciseNames = body.exercises.map((ex: any) => ex.name).join(', ');
                return `User trained ${exerciseNames} on ${formattedDate} with total weight ${totalWeight}kg`;
            }
            return `User did exercise training on ${formattedDate}`;
        }
        
        case EpisodeType.NUTRITION: {
            if (body.calories) {
                return `User consumed ${body.calories} calories on ${formattedDate}`;
            }
            return `User logged nutrition on ${formattedDate}`;
        }
        
        // ... more specialized summarization logic for each health domain
    }
}

Análisis en Profundidad de la Implementación

1. Sistema de Registro de Episodios

export default class KnowledgeGraphService {
    static async registerEpisode(params: RegisterEpisodeParams): Promise<DocumentType<KnowledgeEpisode>> {
        // 1. Validate user and prevent duplicates
        const user = await UserService.findById(params.userId);
        if (!user) throw new Error('User not found');
        
        const uniqueId = this.generateUniqueId(params);
        const existingEpisode = await KnowledgeEpisodeModel.findOne({
            userId: params.userId,
            body: params.body,
            type: params.type,
            date: params.date
        });
        
        if (existingEpisode) return existingEpisode;
        
        // 2. Generate human-readable summary
        const summary = this.generateSummaryFromBody(params.type, params.body, params.date);
        
        // 3. Save to MongoDB
        const episode = new KnowledgeEpisodeModel({
            userId: params.userId,
            type: params.type,
            date: params.date,
            body: params.body,
            summary,
            tags: params.tags,
            metadata: params.metadata
        });
        
        const savedEpisode = await episode.save();
        
        // 4. Create vector representation
        const vectorId = `episode-${savedEpisode._id}`;
        await VectorStoreHelper.storeDocument({
            id: vectorId,
            content: summary,
            metadata: {
                episodeId: savedEpisode._id.toString(),
                userId: params.userId,
                type: params.type,
                date: params.date.toISOString(),
                tags: params.tags || []
            }
        });
        
        return savedEpisode;
    }
}

2. Implementación de la Búsqueda Semántica

El sistema de búsqueda combina similaridad vectorial con filtrado tradicional:

static async searchEpisodes(params: SearchEpisodesParams): Promise<SearchResult[]> {
    // 1. Normalize and enhance the query
    const normalizedQuery = this.normalizeDatesAndExpressions(params.query);
    
    // 2. Build search filters
    const filters: Record<string, any> = { userId: params.userId };
    if (params.type) filters.type = params.type;
    
    // 3. Perform vector search
    const vectorResults = await VectorStoreHelper.searchSimilar({
        query: normalizedQuery,
        k: params.limit || 10,
        filter: filters
    });
    
    // 4. Get full episodes and apply date filters
    const results: SearchResult[] = [];
    for (const vectorResult of vectorResults) {
        const episode = await KnowledgeEpisodeModel.findById(vectorResult.metadata.episodeId);
        
        if (episode) {
            // Apply date filters if specified
            if (params.fromDate && episode.date < params.fromDate) continue;
            if (params.toDate && episode.date > params.toDate) continue;
            
            results.push({
                episode,
                relevanceScore: vectorResult.score
            });
        }
    }
    
    return results.sort((a, b) => b.relevanceScore - a.relevanceScore);
}

3. Generación de Insights con IA

static async generateInsight(params: InsightParams): Promise<string> {
    // 1. Get relevant episodes
    const searchResults = await this.searchEpisodes({
        userId: params.userId,
        query: params.context || `recent ${params.type || 'activity'} patterns and trends`,
        type: params.type,
        limit: 20
    });
    
    if (searchResults.length === 0) {
        return 'No sufficient data available to generate insights.';
    }
    
    // 2. Prepare context for LLM
    const episodeContexts = searchResults.map(result => ({
        date: moment(result.episode.date).format('YYYY-MM-DD'),
        type: result.episode.type,
        summary: result.episode.summary,
        body: result.episode.body
    }));
    
    // 3. Generate insights with GPT
    const prompt = `
        Analyze the following user episode data and generate actionable insights:
        
        User Episodes:
        ${episodeContexts.map(ep => `- ${ep.date}: ${ep.summary}`).join('\n')}
        
        Context: ${params.context || 'General health and fitness progress'}
        
        Please provide:
        1. Key patterns and trends
        2. Progress indicators  
        3. Areas for improvement
        4. Specific actionable recommendations
        
        Keep the response concise and actionable (max 200 words).
    `;
    
    const response = await this.openai.chat.completions.create({
        model: 'gpt-4o-mini',
        messages: [
            {
                role: 'system',
                content: 'You are a health and fitness coach analyzing user data to provide personalized insights and recommendations.'
            },
            {
                role: 'user', 
                content: prompt
            }
        ],
        max_tokens: 300,
        temperature: 0.7
    });
    
    return response.choices[0].message.content || 'Unable to generate insights at this time.';
}

Integración con LangChain para Agentes de IA

Implementación de Herramientas LangChain

// LangChain tool for knowledge graph search
const searchOnKnowledgeGraphTool = new DynamicStructuredTool({
    name: 'search_on_knowledge_graph',
    description: 'Search user\'s health and fitness knowledge graph for relevant information',
    schema: z.object({
        query: z.string().describe('Search query with absolute dates (YYYY-MM-DD) when relevant'),
        type: z.enum(['exercise', 'nutrition', 'emotion', 'sleep', 'measurement']).optional(),
        fromDate: z.string().optional().describe('Start date in YYYY-MM-DD format'),
        toDate: z.string().optional().describe('End date in YYYY-MM-DD format')
    }),
    func: async ({ query, type, fromDate, toDate }, config) => {
        const userId = config?.metadata?.userId;
        
        const searchParams: SearchEpisodesParams = {
            userId: userId.toString(),
            query,
            limit: 10
        };
        
        if (type) searchParams.type = type as EpisodeType;
        if (fromDate) searchParams.fromDate = new Date(fromDate);
        if (toDate) searchParams.toDate = new Date(toDate);
        
        const results = await KnowledgeGraphService.searchEpisodes(searchParams);
        
        if (results.length === 0) {
            return '⚠️ No relevant information found for this query.';
        }
        
        const formattedResults = results.map((result, index) => {
            const episode = result.episode;
            const date = moment(episode.date).format('YYYY-MM-DD');
            const relevance = (result.relevanceScore * 100).toFixed(1);
            
            return `[${index + 1}] ${date} (${episode.type}) - ${episode.summary} (Relevance: ${relevance}%)
Body data: ${JSON.stringify(episode.body, null, 2)}`;
        }).join('\n\n');
        
        return `Found ${results.length} relevant episodes:\n\n${formattedResults}`;
    }
});

Flujo de Conversación con IA

Cuando un usuario pregunta: "¿Cómo ha mejorado mi rendimiento en el entrenamiento de pecho durante el último mes?"

  1. El agente LangChain procesa la consulta en lenguaje natural.
  2. Selecciona search_on_knowledge_graph.
  3. Convierte la pregunta en parámetros de búsqueda estructurados:
    {
      "query": "chest workout exercises performance improvement",
      "type": "exercise", 
      "fromDate": "2024-06-30",
      "toDate": "2024-07-30"
    }
  4. El grafo de conocimiento recupera los episodios de ejercicio relevantes.
  5. GPT procesa los datos de los episodios para identificar patrones y mejoras.
  6. El agente devuelve una respuesta personalizada sobre la progresión en el entrenamiento de pecho.

Ejemplos de Uso en el Mundo Real

Ejemplo 1: Registro de Episodio de Ejercicio

const knowledgeGraph = new KnowledgeGraphService('user123');

await knowledgeGraph.registerEpisode({
    type: EpisodeType.EXERCISE,
    date: new Date('2024-07-30'),
    body: {
        totalWeight: 976,
        exercises: [
            { name: 'bench press', sets: 4, reps: 8, weight: 80 },
            { name: 'incline dumbbell press', sets: 3, reps: 10, weight: 35 },
            { name: 'chest flies', sets: 3, reps: 12, weight: 25 }
        ],
        duration: 75, // minutes
        location: 'gym',
        intensity: 'high'
    },
    tags: ['chest', 'strength', 'upper-body'],
    metadata: { 
        workoutPlan: 'push-pull-legs',
        trainer: 'self',
        equipment: ['barbell', 'dumbbells', 'cables']
    }
});

Resumen Generado: "User trained bench press, incline dumbbell press, chest flies on 2024-07-30 with total weight 976kg"

Ejemplo 2: Búsqueda Semántica de Patrones de Nutrición

const nutritionResults = await knowledgeGraph.searchEpisodes({
    query: 'high protein meals muscle building nutrition last 2 weeks',
    type: EpisodeType.NUTRITION,
    fromDate: new Date('2024-07-16'),
    toDate: new Date('2024-07-30'),
    limit: 15
});

console.log(`Found ${nutritionResults.length} nutrition episodes:`);
nutritionResults.forEach((result, index) => {
    console.log(`${index + 1}. ${result.episode.summary} (${(result.relevanceScore * 100).toFixed(1)}%)`);
    console.log(`   Calories: ${result.episode.body.calories}, Protein: ${result.episode.body.protein}g`);
});

Ejemplo 3: Insights de Salud Generados por IA

const healthInsight = await knowledgeGraph.generateInsight({
    context: 'overall fitness progress and consistency patterns over the last month'
});

console.log('Personalized Health Insight:');
console.log(healthInsight);

Ejemplo de Salida:

"Based on your recent activity, you've maintained excellent workout consistency with 18 sessions in the last month. Your strength progression in chest exercises shows a 12% increase in total volume. However, I notice irregular sleep patterns on workout days - consider maintaining 7-8 hours for optimal recovery. Your nutrition goals are well-aligned with muscle building objectives. Recommendation: Add 2 rest days and focus on sleep hygiene for enhanced performance gains."

Optimizaciones de Rendimiento

1. Estrategia de Caché para Búsqueda Vectorial

private static readonly vectorCache = new Map<string, StoredVector>();
private static cacheInitialized = false;

private static async initializeCache(): Promise<void> {
    if (this.cacheInitialized) return;
    
    try {
        const response = await this.s3Client.send(new GetObjectCommand({
            Bucket: this.bucket,
            Key: `${this.vectorFolder}/${this.indexFile}`
        }));
        
        const indexData = await response.Body?.transformToString();
        if (indexData) {
            const vectors: StoredVector[] = JSON.parse(indexData);
            for (const vector of vectors) {
                this.vectorCache.set(vector.id, vector);
            }
        }
    } catch (error) {
        console.log('Vector index not found, starting fresh');
    }
    
    this.cacheInitialized = true;
}

Los cálculos de vectores en memoria reducen los tiempos de búsqueda a menos de 100ms. S3 proporciona respaldo persistente. Para conjuntos de datos grandes, la evicción de caché LRU evita el uso excesivo de memoria.

2. Estrategia de Indexación de Base de Datos

@index({ userId: 1, date: -1 })           // User timeline queries
@index({ userId: 1, type: 1, date: -1 })  // Type-specific searches  
@index({ createdAt: -1 })                 // Recent episodes
@index({ 'tags': 1 })                     // Tag-based filtering

3. Optimización de Consultas

static normalizeDatesAndExpressions(query: string): string {
    const today = moment();
    let normalizedQuery = query;
    
    // Replace relative dates with absolute dates
    const dateReplacements = [
        { pattern: /today|hoje/gi, replacement: today.format('YYYY-MM-DD') },
        { pattern: /yesterday|ontem/gi, replacement: today.subtract(1, 'day').format('YYYY-MM-DD') },
        { pattern: /last week|semana passada/gi, replacement: `from ${today.subtract(7, 'days').format('YYYY-MM-DD')} to ${today.format('YYYY-MM-DD')}` }
    ];
    
    // Translate Portuguese health terms
    const translations = [
        { pattern: /treino|treinamento/gi, replacement: 'exercise training workout' },
        { pattern: /alimentação|comida/gi, replacement: 'nutrition food meal' }
    ];
    
    // Apply all transformations
    for (const replacement of dateReplacements) {
        normalizedQuery = normalizedQuery.replace(replacement.pattern, replacement.replacement);
    }
    
    return normalizedQuery;
}

Pruebas y Validación

Suite de Pruebas Completa

// knowledge-graph-playground.ts - Quick functionality test
async function quickTest() {
    const userId = '6830f457429e53400d4e7c4a';
    const knowledgeGraph = new KnowledgeGraphService(userId);
    
    // Test 1: Register episode
    const episode = await knowledgeGraph.registerEpisode({
        type: EpisodeType.EXERCISE,
        date: new Date(),
        body: {
            totalWeight: 500,
            exercises: [{ name: 'push ups', sets: 3, reps: 15, weight: 0 }],
            duration: 30
        },
        tags: ['bodyweight', 'home']
    });
    
    // Test 2: Search episodes
    const results = await knowledgeGraph.searchEpisodes({
        query: 'push ups exercise workout',
        limit: 3
    });
    
    // Test 3: Generate insight
    const insight = await knowledgeGraph.generateInsight({
        context: 'recent exercise activity'
    });
    
    console.log('All tests passed successfully!');
}

Benchmarks de Rendimiento

  • Registro de Episodio: < 500ms incluyendo generación de vector
  • Búsqueda Semántica: < 100ms para vectores en caché
  • Generación de Insight: < 3s incluyendo llamada a la API de GPT
  • Uso de Memoria: ~50MB para 10.000 vectores en caché
  • Eficiencia de Almacenamiento: ~1KB por episodio + 3KB por vector

Despliegue y Escalabilidad

Configuración de Infraestructura

# Dockerfile
FROM oven/bun:1.1.21-slim as base
WORKDIR /usr/src/app

# Install dependencies
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile

# Build application
COPY . .
RUN bun build ./src/index.ts --outdir=./dist --target=bun

# Production stage
FROM oven/bun:1.1.21-slim as release
WORKDIR /usr/src/app

COPY --from=base /usr/src/app/dist ./dist
COPY --from=base /usr/src/app/node_modules ./node_modules
COPY --from=base /usr/src/app/package.json ./

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

EXPOSE 3000
ENTRYPOINT ["bun", "run", "dist/index.js"]

Configuración del Entorno

# Database
MONGODB_STRING=mongodb://localhost:27017/pesocerto

# AI Services  
OPENAI_API_KEY=sk-...

# AWS S3 Vector Storage
AWS_ACCESSKEY=AKIA...
AWS_SECRETKEY=...
AWS_S3_BUCKET=peso-certo-vectors
AWS_REGION=us-east-1

# Performance Tuning
VECTOR_CACHE_SIZE=10000
SEARCH_RESULT_LIMIT=50
EMBEDDING_BATCH_SIZE=100

Monitoreo y Observabilidad

// Built-in performance monitoring
class KnowledgeGraphMetrics {
    static episodeRegistrations = 0;
    static searchQueries = 0;
    static insightGenerations = 0;
    static averageSearchTime = 0;
    static cacheHitRate = 0;
    
    static recordEpisodeRegistration(duration: number) {
        this.episodeRegistrations++;
        console.log(`Episode registered in ${duration}ms`);
    }
    
    static recordSearch(duration: number, resultsCount: number) {
        this.searchQueries++;
        this.averageSearchTime = (this.averageSearchTime + duration) / 2;
        console.log(`Search completed in ${duration}ms with ${resultsCount} results`);
    }
}

Análisis de Costos y Beneficios

Comparación de Costos

Servicio de base de datos de grafos externo (ej.: Neo4j Aura): $65/mes de base para 1GB, $300+ para características enterprise, más $0,01 por 1000 consultas y costos de egreso de red.

Nuestra solución nativa: $25/mes en MongoDB Atlas para 10GB, $0,02/GB para almacenamiento de vectores en S3, $0,0001 por 1K tokens para embeddings de OpenAI, sin límites de consultas ni costos de red.

Ahorro mensual estimado: $200 a $500 para uso moderado.

Beneficios de Rendimiento

  • Consultas 85% más rápidas, sin roundtrips de red para datos en caché
  • Sin limitación de tasa de API
  • Optimizaciones personalizadas para el dominio de salud
  • Resiliencia del sistema durante problemas de red

Beneficios para el Desarrollo

  • Toda la lógica en TypeScript
  • Sin restricciones de APIs externas
  • Visibilidad completa de las operaciones
  • Optimizaciones específicas para salud sin restricciones de plataforma

Mejoras Futuras

Corto plazo: indexación de vecino más cercano aproximado (FAISS, Annoy), notificaciones push en tiempo real, soporte de episodios multimodales para datos de imagen y audio.

Mediano plazo: aprendizaje federado para ML con preservación de privacidad, visualización de grafos para explorar conexiones de salud, integraciones con rastreadores de fitness y básculas inteligentes.

Largo plazo: insights de salud anónimos para investigación, propiedad descentralizada de datos de salud, análisis de tendencias de salud a nivel poblacional.

Lecciones Aprendidas

  1. El caché de vectores es la mayor palanca de rendimiento. Sin él, la búsqueda tardaba 2 segundos. Con él, por debajo de 100ms.
  2. La calidad del resumen determina directamente la relevancia de la búsqueda. Los resúmenes vagos producen resultados vagos.
  3. Los tipos de episodios y patrones de búsqueda enfocados en salud superan a las soluciones genéricas. La especificidad del dominio importa.
  4. El tipado fuerte de TypeScript evitó numerosos errores en tiempo de ejecución durante el desarrollo, especialmente en el pipeline de esquema y embeddings.

Para escalabilidad: particione usuarios en múltiples bases de datos, divida vectores por grupos de usuarios o periodos de tiempo, use Redis para caché de vectores entre instancias y envíe operaciones costosas a colas en segundo plano.

Conclusión

Construir un grafo de conocimiento nativo con MongoDB, S3 y LangChain funcionó muy bien para Goal Weight. Eliminamos el costo mensual de $200 a $500 de servicios de grafos externos, obtuvimos consultas 85% más rápidas mediante caché en memoria y mantuvimos control total sobre datos sensibles de salud.

Los patrones de arquitectura presentados aquí — tipos de episodios específicos para salud, namespacing de vectores por usuario, búsqueda semántica basada en resúmenes e integración de herramientas LangChain — pueden adaptarse a otras aplicaciones de salud o cualquier dominio donde se necesite que la IA razone sobre el historial personal de un usuario.

Empieza simple. Construye la funcionalidad principal primero, luego agrega características de IA de forma incremental. Diseña modelos de datos y estrategias de indexación para el crecimiento antes de necesitarlos, no después.