Skip to content

Commit 59392dd

Browse files
committed
translated rag examples
1 parent b4939c0 commit 59392dd

14 files changed

+97697
-3
lines changed

rag_documents_flow.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050

5151
# Now we can use the matches to generate a response
5252
SYSTEM_MESSAGE = """
53-
You are a helpful assistant that answers questions about Maya civilization.
53+
You are a helpful assistant that answers questions about insects.
5454
You must use the data set to answer the questions,
5555
you should not provide any info that is not in the provided sources.
5656
Cite the sources you used to answer the question inside square brackets.

rag_documents_hybrid.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ def hybrid_search(query, limit):
123123

124124
# Now we can use the matches to generate a response
125125
SYSTEM_MESSAGE = """
126-
You are a helpful assistant that answers questions about Maya civilization.
126+
You are a helpful assistant that answers questions about insects.
127127
You must use the data set to answer the questions,
128128
you should not provide any info that is not in the provided sources.
129129
Cite the sources you used to answer the question inside square brackets.

spanish/README.md

+27-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
Este repositorio contiene una colección de scripts en Python que demuestran cómo usar la API de OpenAI para generar completados de chat.
44

5-
En orden creciente de complejidad, los scripts son:
5+
## Librería OpenAI
6+
7+
Estos scripts demuestran como usar la libreria de OpenAI. En orden creciente de complejidad, los scripts son:
68

79
1. [`chat.py`](./chat.py): Un script simple que demuestra cómo usar la API de OpenAI para generar completados de chat.
810
2. [`chat_stream.py`](./chat_stream.py): Añade `stream=True` a la llamada de API para devolver un generador que transmite el completado mientras se está generando.
@@ -13,9 +15,33 @@ Además de estos scripts para demostrar características adicionales:
1315

1416
* [`chat_safety.py`](./chat_safety.py): El script simple con manejo de excepciones para errores de filtro de Seguridad de Contenido de Azure AI.
1517
* [`chat_async.py`](./chat_async.py): Utiliza los clientes asíncronos para hacer llamadas asincrónicas, incluyendo un ejemplo de envío de múltiples solicitudes a la vez usando `asyncio.gather`.
18+
19+
## Popular LLM libraries
20+
21+
Estos scripts usan librerías populares para LLMs y demuestran como usarlas con la API de OpenAI:
22+
1623
* [`chat_langchain.py`](./chat_langchain.py): Utiliza el SDK de langchain para generar completados de chat. [Aprende más en la documentación de Langchain](https://python.langchain.com/docs/get_started/quickstart)
1724
* [`chat_llamaindex.py`](./chat_llamaindex.py): Utiliza el SDK de LlamaIndex para generar completados de chat. [Aprende más en la documentación de LlamaIndex](https://docs.llamaindex.ai/en/stable/)
25+
* [`chat_pydanticai.py`](./chat_pydanticai.py): Utiliza el SDK de PydanticAI para general completados de chat. [Aprende más en la documentación de PydanticAI](https://ai.pydantic.dev/)
26+
27+
## Generación Aumentada con Recuperación (RAG)
28+
29+
Estos scripts demuestran cómo usar la API de OpenAI para tareas de Generación Aumentada con Recuperación (RAG), donde el modelo recupera información relevante de una fuente y la utiliza para generar una respuesta.
30+
31+
Primero instala las dependencias de RAG:
32+
33+
```bash
34+
python -m pip install -r requirements-rag.txt
35+
```
36+
37+
Luego ejecuta los scripts (en orden de complejidad creciente):
1838

39+
* [`rag_csv.py`](./rag.py): Recupera resultados coincidentes de un archivo CSV y los utiliza para responder a la pregunta del usuario.
40+
* [`rag_multiturn.py`](./rag_multiturn.py): La misma idea, pero con una interfaz de chat bidireccional usando `input()` que mantiene un registro de mensajes anteriores y los envía con cada llamada de completado de chat.them with each chat completion call.
41+
* [`rag_queryrewrite.py`](./rag_queryrewrite.py): Añade un paso de reescritura de consulta al proceso RAG, donde la pregunta del usuario se reescribe para mejorar los resultados de recuperación.
42+
* [`rag_documents_ingestion.py`](./rag_ingestion.py): Ingesta PDFs usando pymupdf para convertirlos a markdown, luego usa Langchain para dividirlos en fragmentos, después usa OpenAI para incrustar los fragmentos, y finalmente los almacena en un archivo JSON local.
43+
* [`rag_documents_flow.py`](./rag_pdfs.py): Un flujo RAG que recupera resultados coincidentes del archivo JSON local creado por `rag_documents_ingestion.py`.
44+
* [`rag_documents_hybrid.py`](./rag_documents_hybrid.py): Un flujo RAG que implementa una recuperación híbrida con búsqueda vectorial y por palabras clave, fusionando con Reciprocal Rank Fusion (RRF), y reclasificación semántica con un modelo cross-encoder.
1945

2046
## Configuración del entorno
2147

spanish/data/Apis_mellifera.pdf

434 KB
Binary file not shown.

spanish/data/Centris_pallida.pdf

260 KB
Binary file not shown.

spanish/data/Syrphidae.pdf

374 KB
Binary file not shown.

spanish/data/Xylocopa_californica.pdf

146 KB
Binary file not shown.

spanish/rag_csv.py

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import csv
2+
import os
3+
4+
import azure.identity
5+
import openai
6+
from dotenv import load_dotenv
7+
from lunr import lunr
8+
9+
# Setup the OpenAI client to use either Azure, OpenAI.com, or Ollama API
10+
load_dotenv(override=True)
11+
API_HOST = os.getenv("API_HOST")
12+
13+
if API_HOST == "azure":
14+
token_provider = azure.identity.get_bearer_token_provider(
15+
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
16+
)
17+
client = openai.AzureOpenAI(
18+
api_version=os.environ["AZURE_OPENAI_VERSION"],
19+
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
20+
azure_ad_token_provider=token_provider,
21+
)
22+
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]
23+
24+
elif API_HOST == "ollama":
25+
client = openai.OpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")
26+
MODEL_NAME = os.environ["OLLAMA_MODEL"]
27+
28+
elif API_HOST == "github":
29+
client = openai.OpenAI(base_url="https://models.inference.ai.azure.com", api_key=os.environ["GITHUB_TOKEN"])
30+
MODEL_NAME = os.environ["GITHUB_MODEL"]
31+
32+
else:
33+
client = openai.OpenAI(api_key=os.environ["OPENAI_KEY"])
34+
MODEL_NAME = os.environ["OPENAI_MODEL"]
35+
36+
# Indexamos los datos del CSV
37+
with open("hybridos.csv") as file:
38+
reader = csv.reader(file)
39+
rows = list(reader)
40+
documents = [{"id": (i + 1), "body": " ".join(row)} for i, row in enumerate(rows[1:])]
41+
index = lunr(ref="id", fields=["body"], documents=documents)
42+
43+
# Obteneemos la pregunta del usuario
44+
user_question = "¿qué tan rápido es el Prius v?"
45+
46+
# Buscaamos en el índice la pregunta del usuario
47+
results = index.search(user_question)
48+
matching_rows = [rows[int(result["ref"])] for result in results]
49+
50+
# Formateamos como tabla markdown, ya que los llms entienden markdown
51+
matches_table = " | ".join(rows[0]) + "\n" + " | ".join(" --- " for _ in range(len(rows[0]))) + "\n"
52+
matches_table += "\n".join(" | ".join(row) for row in matching_rows)
53+
54+
print("Found matches:")
55+
print(matches_table)
56+
57+
# Ahora podemos usar los resultados para generar una respuesta
58+
SYSTEM_MESSAGE = """
59+
Eres un asistente útil que responde preguntas sobre automóviles basándote en un conjunto de datos de autos híbridos.
60+
Debes utilizar el conjunto de datos para responder las preguntas, no debes proporcionar ninguna información que no esté en las fuentes proporcionadas.
61+
"""
62+
63+
response = client.chat.completions.create(
64+
model=MODEL_NAME,
65+
temperature=0.3,
66+
messages=[
67+
{"role": "system", "content": SYSTEM_MESSAGE},
68+
{"role": "user", "content": f"{user_question}\nSources: {matches_table}"},
69+
],
70+
)
71+
72+
print(f"\nResponse from {API_HOST}: \n")
73+
print(response.choices[0].message.content)

spanish/rag_documents_flow.py

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import json
2+
import os
3+
4+
import azure.identity
5+
import openai
6+
from dotenv import load_dotenv
7+
from lunr import lunr
8+
9+
# Setup the OpenAI client to use either Azure, OpenAI.com, or Ollama API
10+
load_dotenv(override=True)
11+
API_HOST = os.getenv("API_HOST")
12+
13+
if API_HOST == "azure":
14+
token_provider = azure.identity.get_bearer_token_provider(
15+
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
16+
)
17+
client = openai.AzureOpenAI(
18+
api_version=os.environ["AZURE_OPENAI_VERSION"],
19+
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
20+
azure_ad_token_provider=token_provider,
21+
)
22+
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]
23+
24+
elif API_HOST == "ollama":
25+
client = openai.OpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")
26+
MODEL_NAME = os.environ["OLLAMA_MODEL"]
27+
28+
elif API_HOST == "github":
29+
client = openai.OpenAI(base_url="https://models.inference.ai.azure.com", api_key=os.environ["GITHUB_TOKEN"])
30+
MODEL_NAME = os.environ["GITHUB_MODEL"]
31+
32+
else:
33+
client = openai.OpenAI(api_key=os.environ["OPENAI_KEY"])
34+
MODEL_NAME = os.environ["OPENAI_MODEL"]
35+
36+
# Indexar los datos del JSON - cada objeto tiene id, texto y embedding
37+
with open("rag_ingested_chunks.json") as file:
38+
documents = json.load(file)
39+
documents_by_id = {doc["id"]: doc for doc in documents}
40+
index = lunr(ref="id", fields=["text"], documents=documents)
41+
42+
# Obtener la pregunta del usuario
43+
user_question = "¿dónde vive la abeja solitaria?"
44+
45+
# Buscar la pregunta del usuario en el índice
46+
results = index.search(user_question)
47+
retrieved_documents = [documents_by_id[result["ref"]] for result in results]
48+
print(f"Recuperados {len(retrieved_documents)} documentos coincidentes, enviando sólo los primeros 5.")
49+
context = "\n".join([f"{doc['id']}: {doc['text']}" for doc in retrieved_documents[0:5]])
50+
51+
# Ahora podemos usar las coincidencias para generar una respuesta
52+
SYSTEM_MESSAGE = """
53+
Eres un asistente útil que responde preguntas sobre insectos regionales.
54+
Debes utilizar el conjunto de datos para responder las preguntas,
55+
no debes proporcionar ninguna información que no esté en las fuentes proporcionadas.
56+
Cita las fuentes que utilizaste para responder la pregunta entre corchetes.
57+
Las fuentes están en el formato: <id>: <texto>.
58+
"""
59+
60+
response = client.chat.completions.create(
61+
model=MODEL_NAME,
62+
temperature=0.3,
63+
messages=[
64+
{"role": "system", "content": SYSTEM_MESSAGE},
65+
{"role": "user", "content": f"{user_question}\nFuentes: {context}"},
66+
],
67+
)
68+
69+
print(f"\nRespuesta de {MODEL_NAME} en {API_HOST}: \n")
70+
print(response.choices[0].message.content)

spanish/rag_documents_hybrid.py

+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# pip install sentence-transformers
2+
import json
3+
import os
4+
5+
import azure.identity
6+
import openai
7+
from dotenv import load_dotenv
8+
from lunr import lunr
9+
from sentence_transformers import CrossEncoder
10+
11+
# Setup the OpenAI client to use either Azure, OpenAI.com, or Ollama API
12+
load_dotenv(override=True)
13+
API_HOST = os.getenv("API_HOST")
14+
15+
if API_HOST == "azure":
16+
token_provider = azure.identity.get_bearer_token_provider(
17+
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
18+
)
19+
client = openai.AzureOpenAI(
20+
api_version=os.environ["AZURE_OPENAI_VERSION"],
21+
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
22+
azure_ad_token_provider=token_provider,
23+
)
24+
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]
25+
26+
elif API_HOST == "ollama":
27+
client = openai.OpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")
28+
MODEL_NAME = os.environ["OLLAMA_MODEL"]
29+
30+
elif API_HOST == "github":
31+
client = openai.OpenAI(base_url="https://models.inference.ai.azure.com", api_key=os.environ["GITHUB_TOKEN"])
32+
MODEL_NAME = os.environ["GITHUB_MODEL"]
33+
34+
else:
35+
client = openai.OpenAI(api_key=os.environ["OPENAI_KEY"])
36+
MODEL_NAME = os.environ["OPENAI_MODEL"]
37+
38+
# Indexar los datos del JSON - cada objeto tiene id, texto y embedding
39+
with open("rag_ingested_chunks.json") as file:
40+
documents = json.load(file)
41+
documents_by_id = {doc["id"]: doc for doc in documents}
42+
index = lunr(ref="id", fields=["text"], documents=documents)
43+
44+
def full_text_search(query, limit):
45+
"""
46+
Realizar una búsqueda de texto completo en los documentos indexados.
47+
"""
48+
results = index.search(query)
49+
retrieved_documents = [documents_by_id[result["ref"]] for result in results[:limit]]
50+
return retrieved_documents
51+
52+
53+
def vector_search(query, limit):
54+
"""
55+
Realizar una búsqueda vectorial en los documentos indexados
56+
utilizando una función simple de similitud de coseno.
57+
"""
58+
59+
def cosine_similarity(a, b):
60+
return sum(x * y for x, y in zip(a, b)) / ((sum(x * x for x in a) ** 0.5) * (sum(y * y for y in b) ** 0.5))
61+
62+
query_embedding = client.embeddings.create(model="text-embedding-3-small", input=query).data[0].embedding
63+
similarities = []
64+
for doc in documents:
65+
doc_embedding = doc["embedding"]
66+
similarity = cosine_similarity(query_embedding, doc_embedding)
67+
similarities.append((doc, similarity))
68+
similarities.sort(key=lambda x: x[1], reverse=True)
69+
70+
retrieved_documents = [doc for doc, _ in similarities[:limit]]
71+
return retrieved_documents
72+
73+
74+
def reciprocal_rank_fusion(text_results, vector_results, alpha=0.5):
75+
"""
76+
Realizar la Fusión de Rango Recíproco en los resultados de búsquedas de texto y vectoriales.
77+
"""
78+
text_ids = {doc["id"] for doc in text_results}
79+
vector_ids = {doc["id"] for doc in vector_results}
80+
81+
combined_results = []
82+
for doc in text_results:
83+
if doc["id"] in vector_ids:
84+
combined_results.append((doc, alpha))
85+
else:
86+
combined_results.append((doc, 1 - alpha))
87+
for doc in vector_results:
88+
if doc["id"] not in text_ids:
89+
combined_results.append((doc, alpha))
90+
combined_results.sort(key=lambda x: x[1], reverse=True)
91+
return [doc for doc, _ in combined_results]
92+
93+
94+
def rerank(query, retrieved_documents):
95+
"""
96+
Reclasificar los resultados utilizando un cross-enconder modelo .
97+
"""
98+
encoder = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")
99+
scores = encoder.predict([(query, doc["text"]) for doc in retrieved_documents])
100+
scored_documents = [v for _, v in sorted(zip(scores, retrieved_documents), reverse=True)]
101+
return scored_documents
102+
103+
104+
def hybrid_search(query, limit):
105+
"""
106+
Realizar una búsqueda híbrida utilizando tanto búsqueda de texto completo como vectorial.
107+
"""
108+
text_results = full_text_search(query, limit * 2)
109+
vector_results = vector_search(query, limit * 2)
110+
combined_results = reciprocal_rank_fusion(text_results, vector_results)
111+
combined_results = rerank(query, combined_results)
112+
return combined_results[:limit]
113+
114+
115+
# Obtener la pregunta del usuario
116+
user_question = "gris y solitario"
117+
118+
# Buscar la pregunta del usuario en el índice
119+
retrieved_documents = hybrid_search(user_question, limit=5)
120+
print(f"Recuperados {len(retrieved_documents)} documentos coincidentes.")
121+
context = "\n".join([f"{doc['id']}: {doc['text']}" for doc in retrieved_documents[0:5]])
122+
123+
# Ahora podemos usar las coincidencias para generar una respuesta
124+
SYSTEM_MESSAGE = """
125+
Eres un asistente útil que responde preguntas sobre insectos.
126+
Debes utilizar el conjunto de datos para responder las preguntas,
127+
no debes proporcionar ninguna información que no esté en las fuentes proporcionadas.
128+
Cita las fuentes que utilizaste para responder la pregunta entre corchetes.
129+
Las fuentes están en el formato: <id>: <texto>.
130+
"""
131+
132+
response = client.chat.completions.create(
133+
model=MODEL_NAME,
134+
temperature=0.3,
135+
messages=[
136+
{"role": "system", "content": SYSTEM_MESSAGE},
137+
{"role": "user", "content": f"{user_question}\nFuentes: {context}"},
138+
],
139+
)
140+
141+
print(f"\nRespuesta de {MODEL_NAME} en {API_HOST}: \n")
142+
print(response.choices[0].message.content)

0 commit comments

Comments
 (0)