¿Por qué Cloudflare como modelo?

Por su facilidad de poder usarlo y dado que si se te dificulta conseguir la apiKey de OpenAi, Cloudflare pone a disposición modelos como lo es llama2, mistral y otras variedades, pero no te digo más, pásate por acá

La imagen de portada fue extraída de: https://www.userlike.com/en/blog/chatbots

Para este caso particular usamos Generación de Texto: esto quiere decir que conversaremos con un modelo que genera texto:

No olvides crear tu cuenta cloudflare aqui te dejo 2 recursos para tener tu token y tu account id

import { ChatCloudflareWorkersAI } from "@langchain/cloudflare";

import { getEnvironmentVariable } from "@langchain/core/utils/env";
import { StringOutputParser } from "@langchain/core/output_parsers";

const CloudflareModel = new ChatCloudflareWorkersAI({
    model: "@cf/meta/llama-2-7b-chat-fp16",
    cloudflareAccountId: getEnvironmentVariable("CLOUDFLARE_ACCOUNT_ID"),
    cloudflareApiToken: getEnvironmentVariable("CLOUDFLARE_TOKEN_ID"),
    maxConcurrency: 2,
    maxRetries: 5,
    onFailedAttempt: (err) => {
        console.log(`Ocurrio un error: ${err?.message}`)
    },
}).pipe(new StringOutputParser())

Si hasta acá aún tienes algunas dudas, aprovecho para ofrecerte la oportunidad de acelerar tus conocimientos en AI creando Chatbots como todo un profesional guiado por alguien con más de 10 años de experiencia

En Langchain puede que llegues a usar mucho la utilidad pipe, no es más que la salida de algo es la entrada de otra herramienta, para hacer RAG necesitamos una salida aplanada no es igual esto:

const CloudflareModel = new ChatCloudflareWorkersAI({
    model: "@cf/meta/llama-2-7b-chat-fp16",
    cloudflareAccountId: getEnvironmentVariable("CLOUDFLARE_ACCOUNT_ID"),
    cloudflareApiToken: getEnvironmentVariable("CLOUDFLARE_TOKEN_ID"),
    maxConcurrency: 2,
    maxRetries: 5,
    onFailedAttempt: (err) => {
        console.log(`Ocurrio un error: ${err?.message}`)
    },
}).pipe(new StringOutputParser())

console.log(await CloudflareModel.invoke('Traduce I love programming into spanish'))

/*
"I love programming" in Spanish is "Me encanta programar".

Here are a few more options for expressing this sentiment in Spanish:

* "Me apasiona programar" (This is a more formal way of saying it, and it means "I am passionate about programming.")

* "Soy un programador fanático" (This means "I am a fanatical programmer.")

* "Programar me da felicidad" (This means "Programming makes me happy.")

* "Soy un gran programador" (This means "I am a great programmer.")
*/

Que esto:

const CloudflareModel = new ChatCloudflareWorkersAI({
    model: "@cf/meta/llama-2-7b-chat-fp16",
    cloudflareAccountId: getEnvironmentVariable("CLOUDFLARE_ACCOUNT_ID"),
    cloudflareApiToken: getEnvironmentVariable("CLOUDFLARE_TOKEN_ID"),
    maxConcurrency: 2,
    maxRetries: 5,
    onFailedAttempt: (err) => {
        console.log(`Ocurrio un error: ${err?.message}`)
    },
})

console.log(await CloudflareModel.invoke('Traduce I love programming into spanish'))

/*
AIMessage {
  lc_serializable: true,
  lc_kwargs: {
    content: "\"I love programming\" in Spanish is \"Me encanta programar\".\n\nHere are a few more options for expressing this sentiment in Spanish:\n\n* \"Me apasiona programar\" (This is a more formal way of saying it, and it means \"I am passionate about programming.\")\n\n* \"Soy un programador fanático\" (This means \"I am a fanatical programmer.\")\n\n* \"Programar me da felicidad\" (This means \"Programming makes me happy.\")\n\n* \"Soy un gran programador\" (This means \"I am a great programmer.\")\n\nI hope these suggestions are helpful! Let me know if you have any other questions.",
    additional_kwargs: {},
  },
  lc_namespace: [ "langchain_core", "messages" ],
  content: "\"I love programming\" in Spanish is \"Me encanta programar\".\n\nHere are a few more options for expressing this sentiment in Spanish:\n\n* \"Me apasiona programar\" (This is a more formal way of saying it, and it means \"I am passionate about programming.\")\n\n* \"Soy un programador fanático\" (This means \"I am a fanatical programmer.\")\n\n* \"Programar me da felicidad\" (This means \"Programming makes me happy.\")\n\n* \"Soy un gran programador\" (This means \"I am a great programmer.\")\n\nI hope these suggestions are helpful! Let me know if you have any other questions.",
  name: undefined,
  additional_kwargs: {},
  _getType: [Function: _getType],
  lc_aliases: [Getter],
  text: [Getter],
  toDict: [Function: toDict],
  toChunk: [Function: toChunk],
  lc_id: [Getter],
  lc_secrets: [Getter],
  lc_attributes: [Getter],
  toJSON: [Function: toJSON],
  toJSONNotImplemented: [Function: toJSONNotImplemented],
}
*/


​Como notas Langchain por defecto retorna un objeto lleno de mucha información esto para que posteriori podamos hacer aún mas cosas pero para nuestro proposito necesitamos algo ya elaborado.

¿Cómo hace Langchain su magia?

Como se describe en la imagen de arriba, es necesario hacer previamente la ingesta de datos, esto quiere decir que necesitamos tener nuestros datos almacenados en algun DataSource u source (Pinecone, Qdrant, LanceDb, ClouseVector … etc etc etc)

Una vez realizas una pregunta Langchain transforma tu pregunta a algo mas estandar para proveer de más detalles la query, luego de esto pasa por el embedding, si!, ese que vimos el episodio pasado, para luego de eso poder obtener los datos relacionados a nuestra pregunta.

Ya finalmente teniendo los datos, retorna la query, evalua el nuestro prompt o template (como te guste llamarlo) para pasarlo por nuestro modelo, (Cloudflare) y asi llega nuestra respuesta.

Ok pero…

¿Como aplico RAG en Langchain?

 const standaloneQuestionChain = RunnableSequence.from([
	   {
		     question: (input: Conversational) => input.question,
		     chat_history: (input: Conversational) =>
			      this.formatChatHistory(input.chat_history) // convertimos nuestro historial [pregunta, respuesta][] a un Human: ...\nAssistant: ...,
	    },
	    this.CONDENSE_QUESTION_PROMPT,
	    model,
	    new StringOutputParser() // aplaca la salida a string,
]);

const answerChain = RunnableSequence.from([
	   {
	       context: this.retriever.pipe(formatDocumentsAsString),
         question: new RunnablePassthrough(),
     },
     this.ANSWER_TEMPLATE,
     model,
]);

return standaloneQuestionChain.pipe(answerChain).invoke({query: 'Que precio tienen los new Balance?', chat_history: []})

/*
Los New Balance 990v5 cuestan $175 y los 991v2 cuestan $180
/*

Como ves arriba, Langchain nos provee de una clase llamada Runnable la cual se divide en diferentes maneras para lograr diferentes propósitos, en este caso haremos una secuencia.

Si hasta acá pudiste entender todo, te debería quedar una pregunta.

¿Por que necesitamos 2 templates?

El RAG necesita saber primero el flujo de la conversación y como está compuesta la conversación eso lo hace el primer témplate además que es allí donde le indicamos que su única fuente de la verdad son nuestros datos, esto para evitar las famosas alucinaciones ;).

El segundo témplate le indica el camino que debe seguir basado en el contexto para poder dar una respuesta coherente.

Ahora que sigue?…

pues no te pierdas el último Episodio donde te explico como combinar todo eso con tu propio chatbot usando la librería

Gracias por leer <3