TechLead
Lección 3 de 18
5 min de lectura
LangChain

Cadenas y memoria

Construye cadenas secuenciales y añade memoria de conversación a tus aplicaciones

Comprender las cadenas

Cadenas son los bloques de construcción centrales en LangChain. Permiten combinar múltiples componentes (modelos, prompts, herramientas, otras cadenas) en un único pipeline reutilizable. LangChain usa el Lenguaje de Expresión de LangChain (LCEL) para crear cadenas de forma declarativa.

⛓️ Conceptos de cadenas

  • LCEL: Forma declarativa de componer cadenas usando sintaxis de tubería (|)
  • Runnables: Bloques que se pueden encadenar
  • Secuencial: La salida de un paso se convierte en la entrada del siguiente
  • Paralelo: Ejecuta varias cadenas simultáneamente

Cadena básica con LCEL

LCEL usa sintaxis de tubería para conectar prompts, modelos y parsers en una sola cadena invocable.

import { ChatOpenAI } from "@langchain/openai";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";

const model = new ChatOpenAI({ modelName: "gpt-4" });

const prompt = ChatPromptTemplate.fromTemplate(
  "Tell me a {adjective} joke about {topic}"
);

// Create chain using LCEL pipe syntax
const chain = prompt.pipe(model).pipe(new StringOutputParser());

// Invoke the chain
const result = await chain.invoke({
  adjective: "funny",
  topic: "programming",
});

console.log(result);
// "Why do programmers prefer dark mode? Because light attracts bugs!"

Componentes de la cadena

Parsers de salida

Los parsers de salida convierten el texto del modelo en strings, JSON o datos estructurados.

import { StringOutputParser } from "@langchain/core/output_parsers";
import { JsonOutputParser } from "@langchain/core/output_parsers";

// String output
const stringChain = prompt.pipe(model).pipe(new StringOutputParser());

// JSON output
const jsonChain = prompt.pipe(model).pipe(new JsonOutputParser());

// Structured output with Zod
import { z } from "zod";
const schema = z.object({
  joke: z.string(),
  rating: z.number(),
});
const structuredChain = prompt.pipe(model.withStructuredOutput(schema));

RunnableLambda para lógica personalizada

RunnableLambda inserta transformaciones personalizadas antes o después de llamadas al modelo.

import { RunnableLambda } from "@langchain/core/runnables";

const processInput = new RunnableLambda({
  func: (input: string) => input.toLowerCase().trim(),
});

const processOutput = new RunnableLambda({
  func: (output: string) => `Result: ${output}`,
});

const chain = processInput
  .pipe(prompt)
  .pipe(model)
  .pipe(new StringOutputParser())
  .pipe(processOutput);

Cadenas en paralelo

Las cadenas en paralelo ejecutan múltiples ramas a la vez y devuelven un resultado combinado.

import { RunnableParallel } from "@langchain/core/runnables";

const jokeChain = jokePrompt.pipe(model).pipe(new StringOutputParser());
const poemChain = poemPrompt.pipe(model).pipe(new StringOutputParser());

// Run both chains in parallel
const parallelChain = RunnableParallel.from({
  joke: jokeChain,
  poem: poemChain,
});

const result = await parallelChain.invoke({ topic: "coding" });
console.log(result);
// { joke: "...", poem: "..." }

Memoria en LangChain

La memoria permite que tus cadenas recuerden interacciones previas, algo esencial para construir aplicaciones conversacionales de IA. LangChain ofrece varios tipos de memoria.

🧠 Tipos de memoria

  • Memoria buffer: Guarda todos los mensajes en memoria
  • Memoria de ventana: Conserva solo los últimos K mensajes
  • Memoria de resumen: Resume mensajes antiguos
  • Memoria con vector store: Usa embeddings para búsqueda semántica

Historial de mensajes simple

Almacena el historial por sesión y envuelve la cadena para incluir automáticamente el contexto anterior.

import { ChatMessageHistory } from "langchain/stores/message/in_memory";
import { RunnableWithMessageHistory } from "@langchain/core/runnables";

// Create a prompt with history placeholder
const prompt = ChatPromptTemplate.fromMessages([
  ["system", "You are a helpful assistant."],
  new MessagesPlaceholder("history"),
  ["human", "{input}"],
]);

const chain = prompt.pipe(model).pipe(new StringOutputParser());

// Store for message histories (keyed by session ID)
const messageHistories: Record = {};

const getMessageHistory = (sessionId: string) => {
  if (!messageHistories[sessionId]) {
    messageHistories[sessionId] = new ChatMessageHistory();
  }
  return messageHistories[sessionId];
};

// Wrap chain with message history
const chainWithHistory = new RunnableWithMessageHistory({
  runnable: chain,
  getMessageHistory,
  inputMessagesKey: "input",
  historyMessagesKey: "history",
});

// Use with session ID
const response1 = await chainWithHistory.invoke(
  { input: "My name is Alice" },
  { configurable: { sessionId: "user-123" } }
);

const response2 = await chainWithHistory.invoke(
  { input: "What's my name?" },
  { configurable: { sessionId: "user-123" } }
);
// "Your name is Alice!"

Memoria de ventana

La memoria de ventana conserva solo los mensajes más recientes para controlar el tamaño del contexto.

import { BufferWindowMemory } from "langchain/memory";

// Keep only the last 5 messages
const memory = new BufferWindowMemory({
  k: 5,
  returnMessages: true,
  memoryKey: "history",
});

// Add messages
await memory.saveContext(
  { input: "Hi, I'm learning LangChain" },
  { output: "Great! LangChain is powerful." }
);

// Load history
const history = await memory.loadMemoryVariables({});
console.log(history);

Patrón de cadena conversacional

Envuelve una cadena en una clase para gestionar el historial y exponer una API de chat limpia para tu app.

import { ChatOpenAI } from "@langchain/openai";
import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { HumanMessage, AIMessage } from "@langchain/core/messages";

class ConversationChain {
  private chain;
  private history: (HumanMessage | AIMessage)[] = [];

  constructor() {
    const model = new ChatOpenAI({ modelName: "gpt-4" });

    const prompt = ChatPromptTemplate.fromMessages([
      ["system", "You are a helpful coding assistant."],
      new MessagesPlaceholder("history"),
      ["human", "{input}"],
    ]);

    this.chain = prompt.pipe(model).pipe(new StringOutputParser());
  }

  async chat(input: string): Promise {
    const response = await this.chain.invoke({
      history: this.history,
      input,
    });

    // Update history
    this.history.push(new HumanMessage(input));
    this.history.push(new AIMessage(response));

    return response;
  }

  clearHistory() {
    this.history = [];
  }
}

// Usage
const conversation = new ConversationChain();
await conversation.chat("What is React?");
await conversation.chat("How do I use hooks?");
await conversation.chat("Show me an example");

💡 Puntos clave

  • • LCEL usa sintaxis de tubería (|) para encadenar componentes
  • • Los parsers de salida transforman las respuestas del modelo en formatos deseados
  • • RunnableParallel permite ejecución concurrente de cadenas
  • • La memoria mantiene el contexto conversacional entre interacciones
  • • Usa IDs de sesión para gestionar múltiples conversaciones

📚 Más recursos

Continuar aprendiendo