Instalamos la extensión de Postgres llamada pgvector, que permite que las tablas tengan columnas de tipo vector, donde vector es un conjunto de números de punto flotante. En este ejemplo, utilizamos un vector de 768 dimensiones, es decir, un vector de longitud 768. Creamos una tabla que guardará los artículos de nuestra base de conocimientos: el texto de cada artículo, el título del artículo y una incrustación vectorial del texto del artículo. Llamamos a la tabla artículos y a las columnas título, texto e incrustación. Extraemos el contenido de cuatro URL de Wikipedia y separamos el título y el contenido de cada una. Limpiamos el cuerpo de cada artículo, dividimos el texto en fragmentos de 500 caracteres y utilizamos un modelo de incrustación para crear un vector de 768 dimensiones a partir de cada fragmento. El vector es una representación numérica (un punto flotante) del significado del fragmento de texto. Guardamos el título, un fragmento del cuerpo y el vector de incrustación del fragmento en una fila de la base de datos. Para cada artículo, hay tantos vectores como fragmentos. Indexamos la columna de vectores para la búsqueda de similitud en la Parte 2. import psycopg2 from sentence_transformers import SentenceTransformer import requests from bs4 import BeautifulSoup import re import ollama # Sus parámetros de conexión aquí MY_DB_HOST = ‘localhost’ MY_DB_PORT = 5432 MY_DB_NAME = ‘nitin’ MY_DB_USER = ‘nitin’ MY_DB_PASSWORD = » # Configure la conexión de la base de datos conn = psycopg2.connect( host=MY_DB_HOST, port=MY_DB_PORT, dbname=MY_DB_NAME, user=MY_DB_USER, password=MY_DB_PASSWORD ) cur = conn.cursor() # Cree la tabla de artículos con la extensión pgvector # Si la extensión pgvector no está instalada en su máquina, deberá instalarla. # Consulte https://github.com/pgvector/pgvector o instancias en la nube con pgvector instalado. # Primero crea la extensión pgvector, luego una tabla con una columna de vector de 768 dimensiones para incrustaciones. # Ten en cuenta que el título y el texto completo del artículo también se guardan con la incrustación. # Esto permite la búsqueda de similitud de vectores en la columna de incrustación, devolviendo texto coincidente # junto con incrustaciones coincidentes según lo que se necesite. # Después de ejecutar este comando SQL tendremos # a) una extensión pgvector instalada si no existía ya # b) una tabla vacía con una columna de tipo vector junto con dos columnas, # una para guardar el título del artículo y otra para guardar un fragmento de texto. # Postgres no pone un límite en la cantidad de dimensiones en las incrustaciones de pgvector. # Vale la pena experimentar con longitudes mayores, pero ten en cuenta que deben coincidir con la longitud de las incrustaciones # creadas por el modelo que uses. Las incrustaciones de ~1k, 2k o más dimensiones son comunes entre las API de incrustaciones. cur.execute(»’ CREATE EXTENSION IF NOT EXISTS vector; DROP TABLE IF EXISTS articles; CREATE TABLE articles ( id SERIAL PRIMARY KEY, title TEXT, text TEXT, embedding VECTOR(768) ); »’) conn.commit() # A continuación se muestran las fuentes de contenido para crear incrustaciones que se insertarán en nuestra base de datos vectorial de demostración. # Siéntase libre de agregar sus propios enlaces, pero tenga en cuenta que diferentes fuentes distintas de Wikipedia pueden # tener diferentes caracteres basura y pueden requerir un preprocesamiento diferente. # Para comenzar, pruebe otras páginas de Wikipedia, luego amplíe a otras fuentes. urls= [
‘https://en.wikipedia.org/wiki/Pentax_K-x’,
‘https://en.wikipedia.org/wiki/2008_Tour_de_France’,
‘https://en.wikipedia.org/wiki/Onalaska,_Texas’,
‘https://en.wikipedia.org/wiki/List_of_extinct_dog_breeds’
]

# Obtener el HTML en un enlace dado y extraer solo el texto, separando el título y el contenido. # Usaremos este texto para extraer contenido de los artículos de Wikipedia para responder consultas. def extract_title_and_content(url): try: response = requests.get(url) if response.status_code == 200: # success # Crear un objeto BeautifulSoup para analizar el contenido HTML soup = BeautifulSoup(response.content, ‘html.parser’) # Extraer el título de la página title = soup.title.string.strip() if soup.title else «» # Extraer el contenido de texto de la página content = soup.get_text(separator=» «) return {«title»: title, «text»: content} else: print(f»Error al recuperar el contenido de {url}. Código de estado: {response.status_code}») return None except requests.exceptions.RequestException as e: print(f»Error ocurrido al recuperar contenido de {url}: {str(e)}») return None # Crear el modelo de incrustación # Este es el modelo que usamos para generar incrustaciones, es decir, para codificar fragmentos de texto en vectores numéricos de flotantes. # Sentence Transformers (sbert.net) es una colección de modelos de transformadores diseñados para crear incrustaciones # a partir de oraciones. Estos se entrenan en conjuntos de datos utilizados para diferentes aplicaciones. Usamos uno ajustado para preguntas y respuestas, # de ahí el ‘qa’ en el nombre. Hay otros modelos de incrustación, algunos ajustados para la velocidad, otros para la amplitud, etc. # Vale la pena estudiar el sitio sbert.net para elegir el modelo correcto para otros usos. También vale la pena mirar # los modelos de incrustación de proveedores como OpenAI, Cohere, etc. para aprender las diferencias, pero tenga en cuenta que # el uso de un modelo en línea implica una posible pérdida de privacidad. embedding_model = SentenceTransformer(‘multi-qa-mpnet-base-dot-v1′) articles = []
incrustaciones = []

# Extrae el título y el contenido de cada URL y guárdalos en la lista. for url in urls: article = extract_title_and_content(url) if article: articles.append(article) for article in articles: raw_text = article[«text»]
# Preprocesamiento: Reemplace grandes porciones de espacio en blanco con un espacio, elimine caracteres basura. # Esto variará con cada fuente y necesitará una limpieza personalizada. text = re.sub(r’\s+’, ‘ ‘, raw_text) text = text.replace(«]», «»).replace(«[«, «»)

# chunk into 500 character chunks, this is a typical size, could be lower if total size of article is small.
chunks = [text[i:i + 500] para i en rango (0, len (texto), 500)]para fragmento en fragmentos: # Aquí es donde invocamos nuestro modelo para generar una lista de puntos flotantes. # El modelo de incrustación devuelve un ndarray numeroso de puntos flotantes. # Psycopg convierte la lista en un vector para su inserción. embedding = embedding_model.encode([chunk])[0].tolist() cur.execute(»’ INSERT INTO artículos (título, texto, incrustación) VALORES (%s, %s, %s); »’, (artículo[«title»]chunk, embedding) ) embeddings.append(embedding) conn.commit() # Crear un índice # pgvector permite diferentes índices para la búsqueda de similitud. # Vea los documentos en el README en https://github.com/pgvector/pgvector para explicaciones detalladas. # Aquí usamos ‘hnsw’ que es un índice que asume un modelo de mundos pequeños de red jerárquica. # HNSW es ​​un patrón visto en modelos de red de lenguaje. Por lo tanto, este es uno de los índices # que se espera que funcione bien para incrustaciones de lenguaje. Para esta pequeña demostración, probablemente no hará mucha diferencia # qué índice use, y los otros también vale la pena probar. # Los parámetros proporcionados en la cláusula ‘USING’ son ‘embedding vector_cosine_ops’ # El primero, ‘embedding’ en este caso, debe coincidir con el nombre de la columna que contiene incrustaciones. # El segundo, ‘vector_cosine_ops’, es la operación utilizada para la búsqueda de similitud, es decir, similitud de coseno. # El mismo documento README en GitHub ofrece otras opciones, pero para los usos más comunes hace poca diferencia # por lo tanto, la similitud de coseno se utiliza como nuestro valor predeterminado. cur.execute(»’ CREATE INDEX ON articles USING hnsw (embedding vector_cosine_ops); »’) conn.commit() cur.close() conn.close() # Fin del archivo Parte 2. Recuperar el contexto de la base de datos de vectores y consultar el LLM En la parte 2, hacemos una pregunta en lenguaje natural de nuestra base de conocimientos, utilizando la búsqueda de similitud para encontrar un contexto y utilizando un LLM (en este caso, Llama 3 de Meta) para generar una respuesta a la pregunta en el contexto proporcionado. Los pasos: Codificamos nuestra consulta en lenguaje natural como un vector utilizando el mismo modelo de incrustación que usamos para codificar los fragmentos de texto que extrajimos de las páginas de Wikipedia. Realizamos una búsqueda de similitud en este vector utilizando una consulta SQL. La similitud, o específicamente la similitud de coseno, es una forma de encontrar los vectores en nuestra base de datos que están más cerca de la consulta de vector. Una vez que encontramos los vectores más cercanos, podemos usarlos para recuperar el texto correspondiente que se guarda con cada vector. Ese es el contexto de nuestra consulta al LLM. Agregamos este contexto a nuestro texto de consulta en lenguaje natural, indicando explícitamente al LLM que el texto proporcionado debe tomarse como el contexto para responder la consulta. Usamos un contenedor programático alrededor de Ollama para pasar la consulta en lenguaje natural y el texto contextual a la API de solicitud del LLM y obtener la respuesta. Enviamos tres consultas y recibimos la respuesta en contexto para cada consulta. A continuación, se muestra una captura de pantalla de ejemplo para la primera consulta. IDG