Montar un proyecto de Retrieval Augmented Generation exige músculo de cómputo que tu laptop no puede dar, por eso el primer paso práctico es desplegar recursos en Azure con un script de bash que automatice todo. Esta guía te muestra cómo hacerlo de forma económica, ideal para quienes están aprendiendo RAG sin gastar de más.
¿Por qué usar la nube en lugar de un entorno local para RAG?
Aunque es tentador instalar todo en tu máquina, la inteligencia artificial aplicada a RAG necesita más de lo que ofrece una PC, incluso una gamer de gama alta.
Procesamiento intensivo que un equipo personal no sostiene.
Configuración y compatibilidad de herramientas que rara vez funcionan al primer intento.
Servicios especializados como búsqueda vectorial y modelos de lenguaje que solo viven en la nube.
¿Puedo correr RAG en mi computadora? Técnicamente sí, pero te vas a topar con límites de procesamiento y problemas de instalación. Para un flujo realista, necesitas un proveedor de nube.
Por eso el camino natural es Azure, donde además puedes aprovechar créditos gratis y elegir las configuraciones más baratas mientras aprendes [00:46].
¿Qué necesitas antes de ejecutar el despliegue en Azure?
Antes de tocar la terminal, asegúrate de tener dos cosas listas. Sin esto, el script no va a correr.
Una suscripción activa de Azure asociada a tu cuenta.
La Azure CLI instalada en tu equipo, que te permite operar desde la terminal en lugar de hacer clics interminables en el portal [01:23].
La CLI ahorra muchísimo tiempo, sobre todo cuando vas a desplegar varios servicios encadenados como en este caso.
¿Qué hace el script deploy.sh para levantar tu infraestructura?
Dentro del repositorio de GitHub, en el folder de la clase siete, vas a crear un archivo llamado deploy.sh. Ese pequeño script de bash es el que orquesta todo el despliegue [01:38].
Recursos que despliega el script
El script crea cada pieza que tu sistema RAG necesita para funcionar.
Un grupo de recursos de Azure que agrupa todo el proyecto.
Una instancia de Azure OpenAI con el modelo ChatGPT-4 desplegado.
Una cuenta de almacenamiento para guardar los PDFs que vas a indexar, los cuales ya están en la carpeta data sample del repositorio.
Un servicio de Azure AI Search configurado con SKU Basic.
Un detalle clave: los espacios de nombre de OpenAI y Search deben ser únicos a nivel global, parecido a un nombre de dominio. Si ya usaste Platzi, tendrás que cambiarlo a Platzi2 o algo equivalente para evitar conflictos [02:10].
Por qué elegir SKU Basic en Azure AI Search
La elección del SKU no es decorativa, impacta directo en tu factura.
¿Qué es el SKU en Azure AI Search? Es el nivel de servicio que defines al crear el recurso. Determina capacidad, precio y límites. El estándar puede costar hasta cuatro veces más que el Basic [02:43].
Para aprender, Basic sobra. Cuando un proyecto pase a producción real, ahí sí evalúas escalar.
¿Cómo ejecutar el despliegue y guardar las claves correctamente?
Con el script listo, vas a tu terminal, te ubicas en la carpeta de la clase y corres el comando.
bash
cd clase7
ls
bash deploy.sh
El proceso tarda algunos minutos. Al terminar, el script imprime en consola dos llaves esenciales: la de Azure OpenAI y la de Azure AI Search [03:21].
Cópialas inmediatamente a un bloc de notas o a tu gestor de secretos preferido.
No las pierdas, las vas a usar durante prácticamente todo el resto del curso.
Acompáñalas de los endpoints, que el script también devuelve para ambos servicios.
¿Para qué sirven las claves de OpenAI y Search? Son las credenciales que autentican tus llamadas desde el código a los servicios de Azure. Sin ellas, ninguna petición a los modelos o al índice de búsqueda funciona.
¿Cómo verificar que tus recursos quedaron desplegados en Azure?
Una vez que el script termina con el mensaje de éxito, conviene confirmar visualmente en el portal.
Entras a tu portal de Azure, navegas a Grupos de recursos, abres el que corresponde a tu proyecto (por ejemplo, RAG-Platzi2) y deberías ver tres servicios listados [04:05].
Servicio de Azure AI Search.
Servicio de Azure OpenAI.
Cuenta de almacenamiento.
Si los tres aparecen, todo está en orden y puedes seguir construyendo tu pipeline de RAG sobre esta base. Cuéntame en los comentarios qué nombre le pusiste a tu grupo de recursos y si tuviste que ajustar el sufijo por temas de unicidad.
En inicio, se va a desplegar en la infraestructura de Azure mediante un archivo .sh, aquí lo más importante es que se van a generar un total de 3 recursos: Un recurso de OpenAI (el LLM a consumir), otro recurso de Azure Search (el RAG) y otro recurso de storage con acceso público
Aquí se saltaron varios pedazos que voy a completar
El primero, es que se necesita instalar el cli de Azure, eso se puede hacer con
Luego, hay que crear una cuenta en azure, posteriormente, hay que crear una suscripción para nuestro perfil (es decir, capa gratuita, pay as you go, u otra) (en la gratuita recibí 200 dólares por 1 mes), lo pueden hacer desde
Les va a pedir datos, teléfono y tarjeta de crédito / débito. No se va a debitar nada extra
Ahora sí, hay que logearnos desde nuestra consola con
az login
Al hacerlo, veremos que la suscripción existe (en la consola), presionamos enter y ya tendremos el setup listo para correr el .sh
Dentro del .sh se definen las variables de los servicios a utilizar
RESOURCE_GROUP="rg-rag-platzi2" - El nombre del resource group, es decir, el contenedor de nuestros recursos
LOCATION="eastus2” - La región donde estarán alojados los recursos
OPENAI_NAME="openai-rag-platzi2" - El nombre del recurso del chat LLM
SEARCH_NAME="search-rag-platzi2" - El nombre del recurso de RAG
DEPLOYMENT_NAME="gpt-4o" - El nombre del modelo a llamar (es un nombre propio, podríamos poner otro, pero en este caso se llama igual al LLM que vamos a consumir)
MODEL_NAME="gpt-4o" - Este si es el nombre canónico del LLM dentro de Azure
MODEL_VERSION="2024-11-20" - La versión del LLM a desplegar
SKU="S0" - Es la “escala” en la que queremos desplegar, es decir, la intensidad de recursos de los servicios que estamos desplegando. S0 es el más básico y es de pago
Si reciben error desplegando el modelo, pueden correr
az cognitiveservices account list-models
--name openai-rag-platzi2
--resource-group rg-rag-platzi2
--output table
Y elegir otro, y cambiar de modelo en el script si no está disponible, por ejemplo
MODEL_VERSION="2024-11-20"
a
MODEL_VERSION="2024-08-06"
Y si por alguna razón reciben problemas para crear el storage, pueden correr
az provider register --namespace Microsoft.Storage --wait
Con eso ya tienen todo listo hasta el punto del fin del video
Ojalá puedan seguir el curso con este unblocker
Muchas gracias Sebastián ! me sirvió. Dejo por acá también mi pequeño aporte:
Para el error que sale de:
No se reconoce la ruta definida / bash no es un termino correcto.
Deben tener instalado Git y en la terminal de VSC (en la parte derecha superior) le dan al + y seleccionan la terminal Git Bash
Instalar Git:
Genial, también si no se reconoce los comandos con az luego de instalar azure cli, agregar la siguiente línea en el archivo bash, con tu ubicación donde se instaló azure cli, ejemplo:
Falto decir que había que instalar azure cli o algo parecido para que funcione este script.
Documentacion para instalar Azure CLI degun OS
Lo aclara en el minuto 1:25 :)
bueno creo que hasta aqui llegue por ahora, me falta conocimientos en azure, cuando sepa mejor esa parte continuare, porque no entendí ni papas de lo que hizo acá.
Veo varios cursos chevres pero todos orientados a Azure, para quienes trabajamos con AWS es un poco fail, sin embargo lo mas fail son esos saltitos cuanticos, como si ya supieras Azure.
Para aprovisionar RAG en Azure desde Bash, debes utilizar un script que despliegue los recursos necesarios. En el contexto de tu clase, se menciona un script llamado deploy.sh que se utiliza para crear la infraestructura necesaria en Azure, incluyendo recursos como Azure OpenAI y Azure AI Search. Asegúrate de tener tu terminal configurada y de ejecutar el script desde el directorio correspondiente. Esto te permitirá gestionar tus recursos de manera eficiente.
IMPORTANTE:
Les recomiendo usar la versión free de AI Search porque el plan Basic que esta en el script cuesta 75 USD mensuales
Puta madre estuve una hora y media en esta clase rompiendome el puto coco. Realmente todos los errores no tienen que ver con que el script esté mal. Pero si falta mencionar cosas sobre la plataforma de Azure.
Les recomiendo
1. Cambien los nombres que tienen dentro de los valores de las variables saquen todo lo que diga platzi y pongan el nombre de su practica
Elijan Location= swedencentral, el que dice en el curso esta saturado y el modelo del LLM solamente esta libre para usarse en esta otra region.
El valor de S0 está mal, hay que ponerlo como Standard Les comparto mi deploy.sh
Entiendo que quieran que veamos sus otros cursos pero esto no es nada practico y es hasta un poco desmotivante que no enseñen toda la instalacion para cada ambiente, pueden perfectamente hacerlo, hasta aqui llego con este curso...
En mi caso tampoco pude seguir, no pude con lo de azure, dejare este curso hasta esta clase y seguire otros.
Si gente, para poder seguir la clase, se debe tener:
1. una cuenta en azure (Si entras con un correo de estudiante te dan 100USD)
tener azure CLI instalado, para evitar problemas.
tener git en tu pc
crear tu grupo de recurso.
Considero que debes estar familiarizado con azure, para que no te parezca tan dificil. el profe va paso a paso y explica bien, pero estos conocimientos si que son necesarios
Obtuve este error
}
(InvalidResourceProperties) The deployment sku name 'S0' is invalid.
Code: InvalidResourceProperties
Message: The deployment sku name 'S0' is invalid.
{
Usa F en vez de S0 para gratis, la basica es $75 y es B en lugar de S0
Hola a todos, me costo un poco implementarlo para tenerlo funcionando ya que estoy usando windows.
Lo primero que hice fue crear una cuenta en azure con la suscripción de un mes.
Posteriormente lo que hice fue loguearme en azure, pero para ello necesitaba tener configurado el acceso en mi computador, para ello ingrese este comando powershell
Para verificar la instalación es necesario usar el siguiente comando en la terminal de powershell:
az version
Si estas usando la terminal del visual studio code es posible que te toque cerrarlo y volverlo abrir o en su defecto reiniciar el equipo.
Despues tienes que ingresar este comando
az login --use-device-code
Esto creará un código que aparecerá por la misma consola donde ingresaste el comando y a la vez abrirá un navegador donde te pedirá el código que te dio y la cuenta a la que deseas loguearte
Despues ingrese use este código y lo guarde con el nombre deploy.sh
En la terminal de git bash utilice el siguiente comando
chmod +x deploy.sh./deploy.sh
En mi caso esto fue suficiente, saludos.
Excelente aporte estimado, me ayudo mucho ya que con el script original no podía crear todos los recursos, algunos estaban con SKU inexistentes o en localidades sin cuotas disponibles 👏👏
Es muchisimo más fácil utilizar una vectore store como Pinecone o Qdrant, hasta incluso tienen planes gratuitos. Aunque todavía no se tocó el tema de cómo crear los embeddings, esa es la parte más "complicada" de utilziar una vectore store.
Wow Profe creo que para hacerlo mas facil y en local o en un VPS como contabo o hetzner con dokploy o easypanel le facilitas la vida a la gente o en tal caso con railway o alternativas asi serian mil veces mas facil en azure o en aws cuando pones la tarjeta y te pasas de los creditos gratis a mas de uno me incluyo me paso una vez me cobraron por uso de una base de datos que por desconocimiento hace tiempo no sabia que hasta cierto punto los creditos se acababan y por minuto me cobraban y tuve deuda de 120 dolares por una db
si les da error deploy.sh: line 2: $'\r': command not found
es porque están en windows y tienes que cambiar el "End of line Sequence"
si estás en VS-Code, le das click y cambias de: CRLF por LF
Este profe es muy bueno, se ve que sabe bastante, pero la manera en la que hace todo en su archivo bash y magicamente todo funciona me parece inadecuado.
Por ejemplo, para que yo pudiera llegar a tener lo que tiene el profesor tuve que:
Crear una cuenta azure
Descargar azure cli para windows
Adaptar el archivo bash a mi region y cambiarlo los nombres a los recursos
Pedir una quota para hacer el deploy de un llm, porque no es tan facil como solo hacer el deploy
Y una vez que pedi el aumento de la quota, no si se tenga que hacer alguna otra cosa más despues.
@aminespinoza creo que deberian mejorar esta clase sugiero que lo puedan hacer con Easypanel o Dokploy en ubuntu 24 con una pc decente de 16de Ram y 100gb libres de discoduro montas cualquier POC de Rag con docker compose incluso con N8n de la manera mas sencilla del mundo. y sino pues un VPS en contabo en 4$ o en hetzner 5$ un mes completo de pruebas con todo.
Buen punto
Clase no se aclaro que se utiliza Linux para correr script deploy.sh, otra opcion, para los que usan Windows, es correrlo desde Azure Portal
- Inicia sesión en Azure Portal
- Haz clic en el icono de Cloud Shell (el símbolo >_ en la barra superior).
- Al abrirlo, selecciona el entorno Bash.
- El terminal ya tiene la última versión de Azure CLI preinstalada y está autenticado en tu cuenta.
- Sube tu archivo .sh al Cloud Shell con botón de subir/bajar archivos o arrastrarlo a la Ventana.
- ejecútalo: bash tu_script.sh
Para correr los archivos .sh desde windows debes hacer hacerlo con Azure Cloud Shell No tienes que instalar nada en tu computadora y ya viene con la CLI de Azure configurada.
****
Entra a azure.com e inicia sesión.
Arriba a la derecha, haz clic en el icono de la terminal (>_) para abrir Cloud Shell.
Selecciona Bash (no PowerShell).
Sube tu archivo .sh usando el botón de cargar archivos (icono de la nube con una flecha).
Ejecútalo escribiendo: ./tu-archivo.sh
script para borrar todo luego de terminar para no consumir creditos: #!/bin/bash
# Terminar el script inmediatamente si ocurre un error
set -e
# 1. Variables de configuración
SUBSCRIPTION_ID="tu-id-de-suscripcion-aquí"
RESOURCE_GROUP="rg-rag-TDALE"
# 2. Conectar a la suscripción correcta
echo "🔄 Cambiando a la suscripción: $SUBSCRIPTION_ID..."
az account set --subscription "$SUBSCRIPTION_ID"
# 3. Eliminar todos los recursos dentro del grupo
echo "⚠️ Eliminando todos los recursos dentro de '$RESOURCE_GROUP'..."
echo "⏱️ Esto puede tomar unos minutos dependiendo de la cantidad de recursos..."
az resource delete --ids $(az resource list --resource-group "$RESOURCE_GROUP" --query "[].id" --output tsv) --verbose
echo "✅ ¡Todos los recursos dentro de '$RESOURCE_GROUP' han sido eliminados!"
Dejo aqui un script con verificación en cada paso:
98% Claude Sonnet 5 | 2% Yo
#!/bin/bash# VariablesRESOURCE_GROUP="rg-rag"LOCATION="brazilsouth"OPENAI_NAME="openai-rag-1783625551"SEARCH_NAME="search-rag-1783629216"DEPLOYMENT_NAME="gpt-4o-rag"MODEL_NAME="gpt-5-mini"MODEL_VERSION="2025-08-07"STORAGE_NAME="storageragtigre01"SKU="S0"DEPLOYMENT_SKU="GlobalStandard"ENV_FILE=".env"#
# 1.Crear grupo de recursos
# ─────────────────────────────────────────────────────────────
# ¿QUÉ ESUN"RESOURCE GROUP"?#
# Es una carpeta lógica donde Azure agrupa todos los recursos
# de un mismo proyecto(en este caso: la cuenta de OpenAI, el
# storage y el servicio de Search).No es un recurso que "hace"# algo por sí mismo — es solo organización y control de ciclo
# de vida.#
# Ventajas de agrupar todo en un mismo resource group:# -Podés borrar TODO el proyecto de una sola vez borrando
# el grupo(útil para no dejar recursos huérfanos pagando).# -Lospermisos(RBAC) se pueden asignar a nivel de grupo,# en vez de recurso por recurso.# -Facilita ver costos y facturación por proyecto.#
# A diferencia del registro de providers(que es a nivel de
# TODA la suscripción), el resource group es específico de
# este proyecto — por eso lo creamos con un nombre propio
# ($RESOURCE_GROUP) y todo lo demás se crea DENTRO de él.#
# Esta operación es síncrona: cuando el comando termina, el
# grupo ya está listo para usarse(a diferencia del registro
# de providers, que puede quedar "en progreso" un rato).# ─────────────────────────────────────────────────────────────
echo "🔍 Verificando si el grupo de recursos existe..."EXISTS=$(az group exists --name "$RESOURCE_GROUP")if["$EXISTS"=="true"]; then
echo " $RESOURCE_GROUP ya existe (no se vuelve a crear)"else echo " $RESOURCE_GROUP no existe, creando..." az group create --name "$RESOURCE_GROUP"--location"$LOCATION"fi
echo "⏳ Esperando que el grupo de recursos esté listo..."whiletrue;doSTATE=$(az group show --name "$RESOURCE_GROUP"--query "properties.provisioningState"-o tsv 2>/dev/null) echo " $RESOURCE_GROUP -> $STATE"if["$STATE"=="Succeeded"]; then
echo "✅ Grupo de recursos listo."break fi
echo "---- esperando 10s para volver a chequear ----" sleep 10done
# 2.RegistrarProviders# ─────────────────────────────────────────────────────────────
# ¿QUÉ ESREGISTRARUN"RESOURCE PROVIDER"?#
# Azure separa sus servicios en "providers"(namespaces tipo
# Microsoft.CognitiveServices,Microsoft.Search,Microsoft.Storage).# Cada suscripción nueva solo trae activados los básicos
# (Compute,Network,Storage).Los demás, como CognitiveServices# o Search, hay que "activarlos" una vez por suscripción antes
# de poder crear recursos de ese tipo.#
# Es un paso único: una vez registrado, queda así para siempre,# aunque crees y borres recursos de ese tipo muchas veces.# Por eso primero chequeamos el estado antes de registrar de nuevo.#
# Si no lo hacés,Azure te tira el error:# "MissingSubscriptionRegistration... not registered to use
# namespace 'Microsoft.CognitiveServices'"
# ─────────────────────────────────────────────────────────────
echo "⏳ Esperando registro de providers..."PROVIDERS=("Microsoft.CognitiveServices""Microsoft.Search""Microsoft.Storage")echo "🔍 Verificando estado de providers..."forPROVIDERin"${PROVIDERS[@]}";doSTATE=$(az provider show --namespace "$PROVIDER"--query "registrationState"-o tsv)if["$STATE"=="Registered"]||["$STATE"=="Registering"]; then
echo " $PROVIDER -> $STATE (no se vuelve a registrar)"else echo " $PROVIDER -> $STATE (registrando...)" az provider register --namespace "$PROVIDER" fi
done
whiletrue;doALL_REGISTERED=trueforPROVIDERin"${PROVIDERS[@]}";doSTATE=$(az provider show --namespace "$PROVIDER"--query "registrationState"-o tsv) echo " $PROVIDER -> $STATE"if["$STATE"!="Registered"]; then
ALL_REGISTERED=false fi
done
if["$ALL_REGISTERED"=true]; then
echo "✅ Todos los providers están registrados."break fi
echo "---- esperando 15s para volver a chequear ----" sleep 15done
# 3.Verificar que el modelo de IA existe con su version en el location seleccionado
# ─────────────────────────────────────────────────────────────
#En caso de no existir termina el proceso y deja impresa la lista de modelos disponibles
#para que puedas seleccionar una válido
# ─────────────────────────────────────────────────────────────
echo "📋 Modelos disponibles en $LOCATION:"az cognitiveservices model list --location"$LOCATION" \
--query "[].{Nombre:model.name, Version:model.version, Formato:model.format}" \
-o table
echo ""echo "🔍 Verificando si $MODEL_NAME versión $MODEL_VERSION está disponible en $LOCATION..."MODEL_EXISTS=$(az cognitiveservices model list --location"$LOCATION" \
--query "[?model.name=='$MODEL_NAME' && model.version=='$MODEL_VERSION'] | length(@)" \
-o tsv)if["$MODEL_EXISTS"-gt 0]; then
echo "✅ Modelo $MODEL_NAME ($MODEL_VERSION) disponible en $LOCATION. Continuando..."else echo "❌ El modelo $MODEL_NAME versión $MODEL_VERSION NO está disponible en $LOCATION." echo "Revisá la tabla de arriba para ver nombres y versiones válidas." exit 1fi
# 4.Crear recurso de AzureOpenAI# ─────────────────────────────────────────────────────────────
# ¿QUÉ ESUNACUENTADEAZUREOPENAI?#
# Es el recurso "contenedor" que te da acceso a los modelos de
# OpenAI(GPT-4o, embeddings, etc.) dentro de Azure.Todavía no
# tiene ningún modelo desplegado — eso se hace en el paso
# siguiente(deployment).Pensalo como "abrir la cuenta" antes
# de "elegir qué modelo instalar en ella".#
# El--custom-domain es necesario porque los endpoints de Azure# OpenAI requieren una URL única tipo
# https://openai-rag.openai.azure.com/ para poder llamarlos
# desde tu código.# ─────────────────────────────────────────────────────────────
# ─────────────────────────────────────────────────────────────
# Verificar si la cuenta de OpenAI ya existe.#
# Nota: no ofrecemos "eliminar y recrear" automáticamente acá
# porque Azure hace soft-delete de las cuentas de Cognitive# Services por 48hs.Aunque la borres, el --custom-domain queda
# reservado ese tiempo y el create va a volver a fallar con
# CustomDomainInUse.La forma más rápida de seguir es usar
# otro nombre.# ─────────────────────────────────────────────────────────────
echo "🔍 Verificando si la cuenta de OpenAI '$OPENAI_NAME' ya existe..."az cognitiveservices account show --name "$OPENAI_NAME"--resource-group "$RESOURCE_GROUP"&>/dev/nullif[ $?-eq 0]; then
echo " ℹ️ La cuenta '$OPENAI_NAME' ya existe en '$RESOURCE_GROUP'. No se vuelve a crear, continuando..."else echo " $OPENAI_NAME no existe, creando..." az cognitiveservices account create \
--name "$OPENAI_NAME" \
--resource-group "$RESOURCE_GROUP" \
--kind OpenAI \
--sku "$SKU" \
--location"$LOCATION" \
--yes \
--custom-domain "$OPENAI_NAME"if[ $?-ne 0]; then
echo "" echo "❌ No se pudo crear la cuenta de OpenAI '$OPENAI_NAME'." echo "" echo " Si el error dice 'CustomDomainInUse', el nombre ya está" echo " ocupado en Azure (posiblemente por un intento anterior" echo " tuyo, aunque lo hayas borrado — Azure lo reserva 48hs)." echo "" echo " 👉 Lo más rápido para seguir ahora: cambiá el valor de" echo " OPENAI_NAME al inicio del script por algo distinto," echo " por ejemplo:" echo " OPENAI_NAME=\"openai-rag-$(date +%s)\"" echo "" exit 1 fi
fi
echo "⏳ Esperando que la cuenta de OpenAI esté lista..."RETRIES=0MAX_RETRIES=12whiletrue;doSTATE=$(az cognitiveservices account show --name "$OPENAI_NAME"--resource-group "$RESOURCE_GROUP" \
--query "properties.provisioningState"-o tsv 2>/dev/null) echo " $OPENAI_NAME -> $STATE"if["$STATE"=="Succeeded"]; then
echo "✅ Cuenta de OpenAI lista."break fi
RETRIES=$((RETRIES+1))if["$RETRIES"-ge "$MAX_RETRIES"]; then
echo "❌ Timeout esperando a que la cuenta esté lista." exit 1 fi
sleep 10done
# 5.Crear deployment
# ─────────────────────────────────────────────────────────────
# ¿QUÉ ESUN"DEPLOYMENT"DEMODELO?#
# La cuenta de OpenAI que creamos en el paso anterior es solo
# el "contenedor" — todavía no tiene ningún modelo activo.Un# deployment es la acción de tomar un modelo del catálogo de
# OpenAI(ej.gpt-4o versión 2024-11-20) y activarlo dentro de
# tu cuenta con un nombre propio($DEPLOYMENT_NAME) y una
# capacidad asignada(cuántas unidades de throughput podés usar
# en paralelo).Ese nombre de deployment es el que después vas
# a usar en tu código para llamar al modelo(no el nombre del
# modelo en sí).#
# A diferencia de la cuenta de OpenAI, un deployment SÍ se
# puede borrar y recrear sin problemas de "nombre reservado" —
# no tiene el problema de soft-delete de 48hs que vimos antes.# ─────────────────────────────────────────────────────────────
echo "🔍 Verificando si el deployment '$DEPLOYMENT_NAME' ya existe..."
az cognitiveservices account deployment show \
--name "$OPENAI_NAME" \
--resource-group "$RESOURCE_GROUP" \
--deployment-name "$DEPLOYMENT_NAME"&>/dev/nullif[ $?-eq 0]; then
echo " ℹ️ El deployment '$DEPLOYMENT_NAME' ya existe. No se vuelve a crear, continuando..."else echo " $DEPLOYMENT_NAME no existe, creando con modelo $MODEL_NAME ($MODEL_VERSION)..."ERROR_OUTPUT=$(az cognitiveservices account deployment create \
--name "$OPENAI_NAME" \
--resource-group "$RESOURCE_GROUP" \
--deployment-name "$DEPLOYMENT_NAME" \
--model-name "$MODEL_NAME" \
--model-version "$MODEL_VERSION" \
--model-format OpenAI \
--sku-name "$DEPLOYMENT_SKU" \
--sku-capacity 12>&1)if[ $?-ne 0]; then
echo ""if echo "$ERROR_OUTPUT"| grep -q "ServiceModelDeprecating"; then
echo "❌ El modelo '$MODEL_NAME' versión '$MODEL_VERSION' está deprecado para deployments nuevos." echo "" echo " 👉 Pasos para resolverlo:" echo " 1. Revisá la lista de modelos disponibles en $LOCATION:" echo " az cognitiveservices model list --location $LOCATION -o table" echo " 2. Elegí otro modelo/versión de esa lista (ej. gpt-5-mini, gpt-4o-mini)." echo " 3. Actualizá MODEL_NAME y MODEL_VERSION al inicio del script." echo " 4. Volvé a correr el script."else echo "❌ Error al crear el deployment '$DEPLOYMENT_NAME':"if echo "$ERROR_OUTPUT"| grep -q "InsufficientQuota"; then
echo "Tabla de modelos y quota disponibles" az cognitiveservices usage list --location"$LOCATION"--query "[].{Modelo:name.value, Actual:currentValue, Limite:limit, Region:scopeType}"-o table
else echo "$ERROR_OUTPUT" fi
fi
exit 1 fi
fi
# Crear cuenta de almacenamiento con acceso público
#az storage account create --name $STORAGE_NAME--resource-group $RESOURCE_GROUP--location $LOCATION--sku Standard_LRS--kind StorageV2--public-network-access Enabled--allow-blob-public-access true# 6.Crear cuenta de almacenamiento
# ─────────────────────────────────────────────────────────────
# ¿PARAQUÉ SIRVEESTESTORAGEACCOUNTENUNPROYECTORAG?#
# Vas a necesitar un lugar donde guardar los documentos fuente
# (PDFs,Word, texto) que después se van a indexar en AzureAI# Search.AzureAISearch puede leer directamente desde un
# storage account mediante un "indexer" — vos subís los archivos
# acá, y Search los procesa,trocea(chunking) y vectoriza sin
# que vos tengas que mover los datos manualmente.#
# A diferencia de la cuenta de OpenAI, el nombre de un storage
# account SÍ es único a nivel global(no solo dentro de tu
# suscripción) y tiene reglas más estrictas: solo minúsculas y
# números, entre 3 y 24 caracteres, sin guiones ni símbolos.# Por eso hay que tener cuidado extra con el valor de
# $STORAGE_NAME.# ─────────────────────────────────────────────────────────────
echo "🔍 Verificando si la cuenta de almacenamiento '$STORAGE_NAME' ya existe..."az storage account show --name "$STORAGE_NAME"--resource-group "$RESOURCE_GROUP"&>/dev/nullif[ $?-eq 0]; then
echo " ℹ️ La cuenta de almacenamiento '$STORAGE_NAME' ya existe. No se vuelve a crear, continuando..."else echo " $STORAGE_NAME no existe, creando..."ERROR_OUTPUT=$(az storage account create \
--name "$STORAGE_NAME" \
--resource-group "$RESOURCE_GROUP" \
--location"$LOCATION" \
--sku Standard_LRS \
--kind StorageV2 \
--public-network-access Enabled \
--allow-blob-public-access true2>&1)if[ $?-ne 0]; then
echo ""if echo "$ERROR_OUTPUT"| grep -q "StorageAccountAlreadyTaken"; then
echo "❌ El nombre '$STORAGE_NAME' ya está en uso por OTRA cuenta en todo Azure" echo " (los nombres de storage son únicos globalmente, no solo en tu suscripción)." echo "" echo " 👉 Cambiá STORAGE_NAME al inicio del script por algo único, ej:" echo " STORAGE_NAME=\"storagerag$(date +%s)\""else echo "❌ Error al crear la cuenta de almacenamiento '$STORAGE_NAME':" echo "$ERROR_OUTPUT" fi
exit 1 fi
fi
echo "⏳ Esperando que la cuenta de almacenamiento esté lista..."RETRIES=0MAX_RETRIES=12whiletrue;doSTATE=$(az storage account show --name "$STORAGE_NAME"--resource-group "$RESOURCE_GROUP" \
--query "provisioningState"-o tsv 2>/dev/null) echo " $STORAGE_NAME -> $STATE"if["$STATE"=="Succeeded"]; then
echo "✅ Cuenta de almacenamiento lista."break fi
RETRIES=$((RETRIES+1))if["$RETRIES"-ge "$MAX_RETRIES"]; then
echo "❌ Timeout esperando a que la cuenta de almacenamiento esté lista." exit 1 fi
sleep 10done
# 7.CrearAzureAISearch# ─────────────────────────────────────────────────────────────
# ¿QUÉ ESAZUREAISEARCHENESTEPROYECTORAG?#
# Es el motor que va a indexar tus documentos(los que subiste
# al storage account) y permitir búsquedas semánticas/vectoriales
# sobre ellos — es la pieza que convierte "documentos sueltos"# en algo que tu aplicación RAG puede consultar y traer como
# contexto para el modelo de OpenAI.#
# Parámetros clave:# --sku basic: nivel de servicio más económico con capacidad
# real de producción(el tier "free" existe pero es muy
# limitado en tamaño de índice y no permite todas las
# funciones que vas a necesitar en el curso).# --partition-count /--replica-count: controlan escalado
# horizontal.En1/1 tenés la unidad mínima — suficiente
# para aprender, sin gastar de más.#
# A diferencia del storage account, el nombre de AzureAISearch# también es único a nivel global(mismo problema potencial de
# "nombre ocupado" que vimos con OpenAI y Storage).# ─────────────────────────────────────────────────────────────
echo "🔍 Verificando si el servicio de Search '$SEARCH_NAME' ya existe..."az search service show --name "$SEARCH_NAME"--resource-group "$RESOURCE_GROUP"&>/dev/nullif[ $?-eq 0]; then
echo " ℹ️ El servicio de Search '$SEARCH_NAME' ya existe. No se vuelve a crear, continuando..."else echo " $SEARCH_NAME no existe, creando..."ERROR_OUTPUT=$(az search service create \
--name "$SEARCH_NAME" \
--resource-group "$RESOURCE_GROUP" \
--location"$LOCATION" \
--sku basic \
--partition-count 1 \
--replica-count 12>&1)if[ $?-ne 0]; then
echo ""if echo "$ERROR_OUTPUT"| grep -qi "already exists\|already in use\|name.*taken\|NameAlreadyInUse\|ServiceNameUnavailable"; then
echo "❌ El nombre '$SEARCH_NAME' ya está en uso (posiblemente por otra cuenta en Azure," echo " los nombres de Search son únicos a nivel global — o por un intento anterior tuyo" echo " en otra suscripción, como pasó con este mismo nombre antes)." echo "" echo " 👉 Cambiá SEARCH_NAME al inicio del script por algo único, ej:" echo " SEARCH_NAME=\"search-rag-$(date +%s)\"" elif echo "$ERROR_OUTPUT"| grep -qi "quota\|limit"; then
echo "❌ Alcanzaste el límite de servicios de Search permitidos en tu suscripción/región." echo " Revisá tus servicios existentes con:" echo " az search service list -o table"else echo "❌ Error al crear el servicio de Search '$SEARCH_NAME':" echo "$ERROR_OUTPUT" fi
exit 1 fi
fi
echo "⏳ Esperando que el servicio de Search esté listo..."RETRIES=0MAX_RETRIES=12whiletrue;doSTATE=$(az search service show --name "$SEARCH_NAME"--resource-group "$RESOURCE_GROUP" \
--query "provisioningState"-o tsv 2>/dev/null) echo " $SEARCH_NAME -> $STATE"if["$STATE"=="succeeded"]||["$STATE"=="Succeeded"]; then
echo "✅ Servicio de Search listo."break fi
RETRIES=$((RETRIES+1))if["$RETRIES"-ge "$MAX_RETRIES"]; then
echo "❌ Timeout esperando a que el servicio de Search esté listo." exit 1 fi
sleep 10done
echo "⏳ Esperando propagación de permisos antes de leer las claves..."sleep 45# 5.Obtener claves y endpoints
#echo "🔑 Azure OpenAI Key:"#az cognitiveservices account keys list --name $OPENAI_NAME--resource-group $RESOURCE_GROUP
# ═════════════════════════════════════════════════════════════
# 8.Obtener claves y endpoints
# ─────────────────────────────────────────────────────────────
# Sololectura(GET) de recursos ya creados
# ═════════════════════════════════════════════════════════════
echo "Obteniendo claves y endpoints"echo "🔑 Azure OpenAI:"OPENAI_ENDPOINT=$(az cognitiveservices account show \
--name "$OPENAI_NAME"--resource-group "$RESOURCE_GROUP" \
--query "properties.endpoint"-o tsv 2>&1)if[ $?-ne 0]; then
echo "❌ No se pudo obtener el endpoint de OpenAI:" echo "$OPENAI_ENDPOINT" exit 1fi
OPENAI_KEY=$(az cognitiveservices account keys list \
--name "$OPENAI_NAME"--resource-group "$RESOURCE_GROUP" \
--query "key1"-o tsv 2>&1)if[ $?-ne 0]; then
echo "❌ No se pudo obtener la key de OpenAI:" echo "$OPENAI_KEY" exit 1fi
echo " Endpoint: $OPENAI_ENDPOINT"echo " Key: $OPENAI_KEY"echo ""echo "🔑 Azure AI Search:"SEARCH_ENDPOINT="https://${SEARCH_NAME}.search.windows.net"SEARCH_KEY=$(az search admin-key show \
--service-name "$SEARCH_NAME"--resource-group "$RESOURCE_GROUP" \
--query "primaryKey"-o tsv 2>&1)if[ $?-ne 0]; then
echo "❌ No se pudo obtener la key de Search:" echo "$SEARCH_KEY" echo "" echo " Si el error es AuthorizationFailed, probablemente el recurso" echo " se acaba de crear y falta propagación de permisos — esperá" echo " un minuto y corré solo esta última sección de nuevo." exit 1fi
echo " Endpoint: $SEARCH_ENDPOINT"echo " Key: $SEARCH_KEY"
# ═════════════════════════════════════════════════════════════
# 9.Escribir variables en .env# ─────────────────────────────────────────────────────────────
# El.env puede que ya tenga otras variables tuyas.No lo
# borramos ni sobreescribimos entero: la función actualiza SOLO# las líneas que empiezan con la variable que le pasamos — si
# ya existe la reemplaza, si no existe la agrega al final.Todo# lo demás en el archivo queda intacto.# ═════════════════════════════════════════════════════════════
set_env_var(){ local key="$1" local value="$2" local file="$3" touch "$file"if grep -qF "${key}=""$file"; then
local tmp_file
tmp_file=$(mktemp) grep -vF "${key}=""$file">"$tmp_file" echo "${key}=${value}">>"$tmp_file" mv "$tmp_file""$file"else echo "${key}=${value}">>"$file" fi
}echo "📝 Escribiendo variables en $ENV_FILE..."set_env_var "AZURE_OPENAI_ENDPOINT""$OPENAI_ENDPOINT""$ENV_FILE"set_env_var "AZURE_OPENAI_KEY""$OPENAI_KEY""$ENV_FILE"set_env_var "AZURE_OPENAI_DEPLOYMENT_NAME""$DEPLOYMENT_NAME""$ENV_FILE"set_env_var "AZURE_OPENAI_MODEL_NAME""$MODEL_NAME""$ENV_FILE"set_env_var "AZURE_OPENAI_API_VERSION""2024-10-21""$ENV_FILE"set_env_var "AZURE_SEARCH_ENDPOINT""$SEARCH_ENDPOINT""$ENV_FILE"set_env_var "AZURE_SEARCH_KEY""$SEARCH_KEY""$ENV_FILE"echo "✅ Variables escritas en $ENV_FILE."echo ""echo "═══════════════════════════════════════════════════"echo "✅ ¡Entorno RAG listo para usar!"echo "═══════════════════════════════════════════════════"echo " Deployment de OpenAI: $DEPLOYMENT_NAME (modelo: $MODEL_NAME $MODEL_VERSION)"echo " Resource group: $RESOURCE_GROUP"echo "✅ ¡Entorno RAG listo para usar!"
#!/bin/bash# VariablesRESOURCE_GROUP="rg-rag"LOCATION="brazilsouth"OPENAI_NAME="openai-rag-1783625551"SEARCH_NAME="search-rag-1783629216"DEPLOYMENT_NAME="gpt-4o-rag"MODEL_NAME="gpt-5-mini"MODEL_VERSION="2025-08-07"STORAGE_NAME="storageragtigre01"SKU="S0"DEPLOYMENT_SKU="GlobalStandard"ENV_FILE=".env"## 1. Crear grupo de recursos# ─────────────────────────────────────────────────────────────# ¿QUÉ ES UN "RESOURCE GROUP"?## Es una carpeta lógica donde Azure agrupa todos los recursos# de un mismo proyecto (en este caso: la cuenta de OpenAI, el# storage y el servicio de Search). No es un recurso que "hace"# algo por sí mismo — es solo organización y control de ciclo# de vida.## Ventajas de agrupar todo en un mismo resource group:# - Podés borrar TODO el proyecto de una sola vez borrando# el grupo (útil para no dejar recursos huérfanos pagando).# - Los permisos (RBAC) se pueden asignar a nivel de grupo,# en vez de recurso por recurso.# - Facilita ver costos y facturación por proyecto.## A diferencia del registro de providers (que es a nivel de# TODA la suscripción), el resource group es específico de# este proyecto — por eso lo creamos con un nombre propio# ($RESOURCE_GROUP) y todo lo demás se crea DENTRO de él.## Esta operación es síncrona: cuando el comando termina, el# grupo ya está listo para usarse (a diferencia del registro# de providers, que puede quedar "en progreso" un rato).# ─────────────────────────────────────────────────────────────echo "🔍 Verificando si el grupo de recursos existe..."EXISTS=$(az group exists --name "$RESOURCE_GROUP")if [ "$EXISTS" == "true" ]; then echo " $RESOURCE_GROUP ya existe (no se vuelve a crear)"else echo " $RESOURCE_GROUP no existe, creando..." az group create --name "$RESOURCE_GROUP" --location "$LOCATION"fiecho "⏳ Esperando que el grupo de recursos esté listo..."while true; do STATE=$(az group show --name "$RESOURCE_GROUP" --query "properties.provisioningState" -o tsv 2>/dev/null) echo " $RESOURCE_GROUP -> $STATE" if [ "$STATE" == "Succeeded" ]; then echo "✅ Grupo de recursos listo." break fi echo "---- esperando 10s para volver a chequear ----" sleep 10done# 2. Registrar Providers# ─────────────────────────────────────────────────────────────# ¿QUÉ ES REGISTRAR UN "RESOURCE PROVIDER"?## Azure separa sus servicios en "providers" (namespaces tipo# Microsoft.CognitiveServices, Microsoft.Search, Microsoft.Storage).# Cada suscripción nueva solo trae activados los básicos# (Compute, Network, Storage). Los demás, como Cognitive Services# o Search, hay que "activarlos" una vez por suscripción antes# de poder crear recursos de ese tipo.## Es un paso único: una vez registrado, queda así para siempre,# aunque crees y borres recursos de ese tipo muchas veces.# Por eso primero chequeamos el estado antes de registrar de nuevo.## Si no lo hacés, Azure te tira el error:# "MissingSubscriptionRegistration ... not registered to use# namespace 'Microsoft.CognitiveServices'"# ─────────────────────────────────────────────────────────────echo "⏳ Esperando registro de providers..."PROVIDERS=("Microsoft.CognitiveServices" "Microsoft.Search" "Microsoft.Storage")echo "🔍 Verificando estado de providers..."for PROVIDER in "${PROVIDERS[@]}"; do STATE=$(az provider show --namespace "$PROVIDER" --query "registrationState" -o tsv) if [ "$STATE" == "Registered" ] || [ "$STATE" == "Registering" ]; then echo " $PROVIDER -> $STATE (no se vuelve a registrar)" else echo " $PROVIDER -> $STATE (registrando...)" az provider register --namespace "$PROVIDER" fidonewhile true; do ALL_REGISTERED=true for PROVIDER in "${PROVIDERS[@]}"; do STATE=$(az provider show --namespace "$PROVIDER" --query "registrationState" -o tsv) echo " $PROVIDER -> $STATE" if [ "$STATE" != "Registered" ]; then ALL_REGISTERED=false fi done if [ "$ALL_REGISTERED" = true ]; then echo "✅ Todos los providers están registrados." break fi echo "---- esperando 15s para volver a chequear ----" sleep 15done# 3. Verificar que el modelo de IA existe con su version en el location seleccionado# ─────────────────────────────────────────────────────────────#En caso de no existir termina el proceso y deja impresa la lista de modelos disponibles#para que puedas seleccionar una válido# ─────────────────────────────────────────────────────────────echo "📋 Modelos disponibles en $LOCATION:"az cognitiveservices model list --location "$LOCATION" \ --query "[].{Nombre:model.name, Version:model.version, Formato:model.format}" \ -o tableecho ""echo "🔍 Verificando si $MODEL_NAME versión $MODEL_VERSION está disponible en $LOCATION..."MODEL_EXISTS=$(az cognitiveservices model list --location "$LOCATION" \ --query "[?model.name=='$MODEL_NAME' && model.version=='$MODEL_VERSION'] | length(@)" \ -o tsv)if [ "$MODEL_EXISTS" -gt 0 ]; then echo "✅ Modelo $MODEL_NAME ($MODEL_VERSION) disponible en $LOCATION. Continuando..."else echo "❌ El modelo $MODEL_NAME versión $MODEL_VERSION NO está disponible en $LOCATION." echo "Revisá la tabla de arriba para ver nombres y versiones válidas." exit 1fi# 4. Crear recurso de Azure OpenAI# ─────────────────────────────────────────────────────────────# ¿QUÉ ES UNA CUENTA DE AZURE OPENAI?## Es el recurso "contenedor" que te da acceso a los modelos de# OpenAI (GPT-4o, embeddings, etc.) dentro de Azure. Todavía no# tiene ningún modelo desplegado — eso se hace en el paso# siguiente (deployment). Pensalo como "abrir la cuenta" antes# de "elegir qué modelo instalar en ella".## El --custom-domain es necesario porque los endpoints de Azure# OpenAI requieren una URL única tipo# https://openai-rag.openai.azure.com/ para poder llamarlos# desde tu código.# ─────────────────────────────────────────────────────────────# ─────────────────────────────────────────────────────────────# Verificar si la cuenta de OpenAI ya existe.## Nota: no ofrecemos "eliminar y recrear" automáticamente acá# porque Azure hace soft-delete de las cuentas de Cognitive# Services por 48hs. Aunque la borres, el --custom-domain queda# reservado ese tiempo y el create va a volver a fallar con# CustomDomainInUse. La forma más rápida de seguir es usar# otro nombre.# ─────────────────────────────────────────────────────────────echo "🔍 Verificando si la cuenta de OpenAI '$OPENAI_NAME' ya existe..."az cognitiveservices account show --name "$OPENAI_NAME" --resource-group "$RESOURCE_GROUP" &> /dev/nullif [ $? -eq 0 ]; then echo " ℹ️ La cuenta '$OPENAI_NAME' ya existe en '$RESOURCE_GROUP'. No se vuelve a crear, continuando..."else echo " $OPENAI_NAME no existe, creando..." az cognitiveservices account create \ --name "$OPENAI_NAME" \ --resource-group "$RESOURCE_GROUP" \ --kind OpenAI \ --sku "$SKU" \ --location "$LOCATION" \ --yes \ --custom-domain "$OPENAI_NAME" if [ $? -ne 0 ]; then echo "" echo "❌ No se pudo crear la cuenta de OpenAI '$OPENAI_NAME'." echo "" echo " Si el error dice 'CustomDomainInUse', el nombre ya está" echo " ocupado en Azure (posiblemente por un intento anterior" echo " tuyo, aunque lo hayas borrado — Azure lo reserva 48hs)." echo "" echo " 👉 Lo más rápido para seguir ahora: cambiá el valor de" echo " OPENAI_NAME al inicio del script por algo distinto," echo " por ejemplo:" echo " OPENAI_NAME=\"openai-rag-$(date +%s)\"" echo "" exit 1 fifiecho "⏳ Esperando que la cuenta de OpenAI esté lista..."RETRIES=0MAX_RETRIES=12while true; do STATE=$(az cognitiveservices account show --name "$OPENAI_NAME" --resource-group "$RESOURCE_GROUP" \ --query "properties.provisioningState" -o tsv 2>/dev/null) echo " $OPENAI_NAME -> $STATE" if [ "$STATE" == "Succeeded" ]; then echo "✅ Cuenta de OpenAI lista." break fi RETRIES=$((RETRIES+1)) if [ "$RETRIES" -ge "$MAX_RETRIES" ]; then echo "❌ Timeout esperando a que la cuenta esté lista." exit 1 fi sleep 10done# 5. Crear deployment# ─────────────────────────────────────────────────────────────# ¿QUÉ ES UN "DEPLOYMENT" DE MODELO?## La cuenta de OpenAI que creamos en el paso anterior es solo# el "contenedor" — todavía no tiene ningún modelo activo. Un# deployment es la acción de tomar un modelo del catálogo de# OpenAI (ej. gpt-4o versión 2024-11-20) y activarlo dentro de# tu cuenta con un nombre propio ($DEPLOYMENT_NAME) y una# capacidad asignada (cuántas unidades de throughput podés usar# en paralelo). Ese nombre de deployment es el que después vas# a usar en tu código para llamar al modelo (no el nombre del# modelo en sí).## A diferencia de la cuenta de OpenAI, un deployment SÍ se# puede borrar y recrear sin problemas de "nombre reservado" —# no tiene el problema de soft-delete de 48hs que vimos antes.# ─────────────────────────────────────────────────────────────echo "🔍 Verificando si el deployment '$DEPLOYMENT_NAME' ya existe..."az cognitiveservices account deployment show \ --name "$OPENAI_NAME" \ --resource-group "$RESOURCE_GROUP" \ --deployment-name "$DEPLOYMENT_NAME" &> /dev/nullif [ $? -eq 0 ]; then echo " ℹ️ El deployment '$DEPLOYMENT_NAME' ya existe. No se vuelve a crear, continuando..."else echo " $DEPLOYMENT_NAME no existe, creando con modelo $MODEL_NAME ($MODEL_VERSION)..." ERROR_OUTPUT=$(az cognitiveservices account deployment create \ --name "$OPENAI_NAME" \ --resource-group "$RESOURCE_GROUP" \ --deployment-name "$DEPLOYMENT_NAME" \ --model-name "$MODEL_NAME" \ --model-version "$MODEL_VERSION" \ --model-format OpenAI \ --sku-name "$DEPLOYMENT_SKU" \ --sku-capacity 1 2>&1) if [ $? -ne 0 ]; then echo "" if echo "$ERROR_OUTPUT" | grep -q "ServiceModelDeprecating"; then echo "❌ El modelo '$MODEL_NAME' versión '$MODEL_VERSION' está deprecado para deployments nuevos." echo "" echo " 👉 Pasos para resolverlo:" echo " 1. Revisá la lista de modelos disponibles en $LOCATION:" echo " az cognitiveservices model list --location $LOCATION -o table" echo " 2. Elegí otro modelo/versión de esa lista (ej. gpt-5-mini, gpt-4o-mini)." echo " 3. Actualizá MODEL_NAME y MODEL_VERSION al inicio del script." echo " 4. Volvé a correr el script." else echo "❌ Error al crear el deployment '$DEPLOYMENT_NAME':" if echo "$ERROR_OUTPUT" | grep -q "InsufficientQuota"; then echo "Tabla de modelos y quota disponibles" az cognitiveservices usage list --location "$LOCATION" --query "[].{Modelo:name.value, Actual:currentValue, Limite:limit, Region:scopeType}" -o table else echo "$ERROR_OUTPUT" fi fi exit 1 fifi# Crear cuenta de almacenamiento con acceso público#az storage account create --name $STORAGE_NAME --resource-group $RESOURCE_GROUP --location $LOCATION --sku Standard_LRS --kind StorageV2 --public-network-access Enabled --allow-blob-public-access true# 6. Crear cuenta de almacenamiento# ─────────────────────────────────────────────────────────────# ¿PARA QUÉ SIRVE ESTE STORAGE ACCOUNT EN UN PROYECTO RAG?## Vas a necesitar un lugar donde guardar los documentos fuente# (PDFs, Word, texto) que después se van a indexar en Azure AI# Search. Azure AI Search puede leer directamente desde un# storage account mediante un "indexer" — vos subís los archivos# acá, y Search los procesa, trocea (chunking) y vectoriza sin# que vos tengas que mover los datos manualmente.## A diferencia de la cuenta de OpenAI, el nombre de un storage# account SÍ es único a nivel global (no solo dentro de tu# suscripción) y tiene reglas más estrictas: solo minúsculas y# números, entre 3 y 24 caracteres, sin guiones ni símbolos.# Por eso hay que tener cuidado extra con el valor de# $STORAGE_NAME.# ─────────────────────────────────────────────────────────────echo "🔍 Verificando si la cuenta de almacenamiento '$STORAGE_NAME' ya existe..."az storage account show --name "$STORAGE_NAME" --resource-group "$RESOURCE_GROUP" &> /dev/nullif [ $? -eq 0 ]; then echo " ℹ️ La cuenta de almacenamiento '$STORAGE_NAME' ya existe. No se vuelve a crear, continuando..."else echo " $STORAGE_NAME no existe, creando..." ERROR_OUTPUT=$(az storage account create \ --name "$STORAGE_NAME" \ --resource-group "$RESOURCE_GROUP" \ --location "$LOCATION" \ --sku Standard_LRS \ --kind StorageV2 \ --public-network-access Enabled \ --allow-blob-public-access true 2>&1) if [ $? -ne 0 ]; then echo "" if echo "$ERROR_OUTPUT" | grep -q "StorageAccountAlreadyTaken"; then echo "❌ El nombre '$STORAGE_NAME' ya está en uso por OTRA cuenta en todo Azure" echo " (los nombres de storage son únicos globalmente, no solo en tu suscripción)." echo "" echo " 👉 Cambiá STORAGE_NAME al inicio del script por algo único, ej:" echo " STORAGE_NAME=\"storagerag$(date +%s)\"" else echo "❌ Error al crear la cuenta de almacenamiento '$STORAGE_NAME':" echo "$ERROR_OUTPUT" fi exit 1 fifiecho "⏳ Esperando que la cuenta de almacenamiento esté lista..."RETRIES=0MAX_RETRIES=12while true; do STATE=$(az storage account show --name "$STORAGE_NAME" --resource-group "$RESOURCE_GROUP" \ --query "provisioningState" -o tsv 2>/dev/null) echo " $STORAGE_NAME -> $STATE" if [ "$STATE" == "Succeeded" ]; then echo "✅ Cuenta de almacenamiento lista." break fi RETRIES=$((RETRIES+1)) if [ "$RETRIES" -ge "$MAX_RETRIES" ]; then echo "❌ Timeout esperando a que la cuenta de almacenamiento esté lista." exit 1 fi sleep 10done# 7. Crear Azure AI Search# ─────────────────────────────────────────────────────────────# ¿QUÉ ES AZURE AI SEARCH EN ESTE PROYECTO RAG?## Es el motor que va a indexar tus documentos (los que subiste# al storage account) y permitir búsquedas semánticas/vectoriales# sobre ellos — es la pieza que convierte "documentos sueltos"# en algo que tu aplicación RAG puede consultar y traer como# contexto para el modelo de OpenAI.## Parámetros clave:# --sku basic: nivel de servicio más económico con capacidad# real de producción (el tier "free" existe pero es muy# limitado en tamaño de índice y no permite todas las# funciones que vas a necesitar en el curso).# --partition-count / --replica-count: controlan escalado# horizontal. En 1/1 tenés la unidad mínima — suficiente# para aprender, sin gastar de más.## A diferencia del storage account, el nombre de Azure AI Search# también es único a nivel global (mismo problema potencial de# "nombre ocupado" que vimos con OpenAI y Storage).# ─────────────────────────────────────────────────────────────echo "🔍 Verificando si el servicio de Search '$SEARCH_NAME' ya existe..."az search service show --name "$SEARCH_NAME" --resource-group "$RESOURCE_GROUP" &> /dev/nullif [ $? -eq 0 ]; then echo " ℹ️ El servicio de Search '$SEARCH_NAME' ya existe. No se vuelve a crear, continuando..."else echo " $SEARCH_NAME no existe, creando..." ERROR_OUTPUT=$(az search service create \ --name "$SEARCH_NAME" \ --resource-group "$RESOURCE_GROUP" \ --location "$LOCATION" \ --sku basic \ --partition-count 1 \ --replica-count 1 2>&1) if [ $? -ne 0 ]; then echo "" if echo "$ERROR_OUTPUT" | grep -qi "already exists\|already in use\|name.*taken\|NameAlreadyInUse\|ServiceNameUnavailable"; then echo "❌ El nombre '$SEARCH_NAME' ya está en uso (posiblemente por otra cuenta en Azure," echo " los nombres de Search son únicos a nivel global — o por un intento anterior tuyo" echo " en otra suscripción, como pasó con este mismo nombre antes)." echo "" echo " 👉 Cambiá SEARCH_NAME al inicio del script por algo único, ej:" echo " SEARCH_NAME=\"search-rag-$(date +%s)\"" elif echo "$ERROR_OUTPUT" | grep -qi "quota\|limit"; then echo "❌ Alcanzaste el límite de servicios de Search permitidos en tu suscripción/región." echo " Revisá tus servicios existentes con:" echo " az search service list -o table" else echo "❌ Error al crear el servicio de Search '$SEARCH_NAME':" echo "$ERROR_OUTPUT" fi exit 1 fifiecho "⏳ Esperando que el servicio de Search esté listo..."RETRIES=0MAX_RETRIES=12while true; do STATE=$(az search service show --name "$SEARCH_NAME" --resource-group "$RESOURCE_GROUP" \ --query "provisioningState" -o tsv 2>/dev/null) echo " $SEARCH_NAME -> $STATE" if [ "$STATE" == "succeeded" ] || [ "$STATE" == "Succeeded" ]; then echo "✅ Servicio de Search listo." break fi RETRIES=$((RETRIES+1)) if [ "$RETRIES" -ge "$MAX_RETRIES" ]; then echo "❌ Timeout esperando a que el servicio de Search esté listo." exit 1 fi sleep 10doneecho "⏳ Esperando propagación de permisos antes de leer las claves..."sleep 45# 5. Obtener claves y endpoints#echo "🔑 Azure OpenAI Key:"#az cognitiveservices account keys list --name $OPENAI_NAME --resource-group $RESOURCE_GROUP# ═════════════════════════════════════════════════════════════# 8. Obtener claves y endpoints# ─────────────────────────────────────────────────────────────# Solo lectura (GET) de recursos ya creados# ═════════════════════════════════════════════════════════════echo "Obteniendo claves y endpoints"echo "🔑 Azure OpenAI:"OPENAI_ENDPOINT=$(az cognitiveservices account show \ --name "$OPENAI_NAME" --resource-group "$RESOURCE_GROUP" \ --query "properties.endpoint" -o tsv 2>&1)if [ $? -ne 0 ]; then echo "❌ No se pudo obtener el endpoint de OpenAI:" echo "$OPENAI_ENDPOINT" exit 1fiOPENAI_KEY=$(az cognitiveservices account keys list \ --name "$OPENAI_NAME" --resource-group "$RESOURCE_GROUP" \ --query "key1" -o tsv 2>&1)if [ $? -ne 0 ]; then echo "❌ No se pudo obtener la key de OpenAI:" echo "$OPENAI_KEY" exit 1fiecho " Endpoint: $OPENAI_ENDPOINT"echo " Key: $OPENAI_KEY"echo ""echo "🔑 Azure AI Search:"SEARCH_ENDPOINT="https://${SEARCH_NAME}.search.windows.net"SEARCH_KEY=$(az search admin-key show \ --service-name "$SEARCH_NAME" --resource-group "$RESOURCE_GROUP" \ --query "primaryKey" -o tsv 2>&1)if [ $? -ne 0 ]; then echo "❌ No se pudo obtener la key de Search:" echo "$SEARCH_KEY" echo "" echo " Si el error es AuthorizationFailed, probablemente el recurso" echo " se acaba de crear y falta propagación de permisos — esperá" echo " un minuto y corré solo esta última sección de nuevo." exit 1fiecho " Endpoint: $SEARCH_ENDPOINT"echo " Key: $SEARCH_KEY"# ═════════════════════════════════════════════════════════════# 9. Escribir variables en .env# ─────────────────────────────────────────────────────────────# El .env puede que ya tenga otras variables tuyas. No lo# borramos ni sobreescribimos entero: la función actualiza SOLO# las líneas que empiezan con la variable que le pasamos — si# ya existe la reemplaza, si no existe la agrega al final. Todo# lo demás en el archivo queda intacto.# ═════════════════════════════════════════════════════════════set_env_var() { local key="$1" local value="$2" local file="$3" touch "$file" if grep -qF "${key}=" "$file"; then local tmp_file tmp_file=$(mktemp) grep -vF "${key}=" "$file" > "$tmp_file" echo "${key}=${value}" >> "$tmp_file" mv "$tmp_file" "$file" else echo "${key}=${value}" >> "$file" fi}echo "📝 Escribiendo variables en $ENV_FILE..."set_env_var "AZURE_OPENAI_ENDPOINT" "$OPENAI_ENDPOINT" "$ENV_FILE"set_env_var "AZURE_OPENAI_KEY" "$OPENAI_KEY" "$ENV_FILE"set_env_var "AZURE_OPENAI_DEPLOYMENT_NAME" "$DEPLOYMENT_NAME" "$ENV_FILE"set_env_var "AZURE_OPENAI_MODEL_NAME" "$MODEL_NAME" "$ENV_FILE"set_env_var "AZURE_OPENAI_API_VERSION" "2024-10-21" "$ENV_FILE"set_env_var "AZURE_SEARCH_ENDPOINT" "$SEARCH_ENDPOINT" "$ENV_FILE"set_env_var "AZURE_SEARCH_KEY" "$SEARCH_KEY" "$ENV_FILE"echo "✅ Variables escritas en $ENV_FILE."echo ""echo "═══════════════════════════════════════════════════"echo "✅ ¡Entorno RAG listo para usar!"echo "═══════════════════════════════════════════════════"echo " Deployment de OpenAI: $DEPLOYMENT_NAME (modelo: $MODEL_NAME $MODEL_VERSION)"echo " Resource group: $RESOURCE_GROUP"echo "✅ ¡Entorno RAG listo para usar!"