Generar contenido con abundante texto usando Google Gemini

Publicado el martes 20 de febrero del 2024 por Lino Uruñuela

 

 

El otro día vimos cómo podíamos utilizar la API de Vertex para utilizar la tecnología de IA generativa de Google Gemini. Hoy quiero profundizar en la redacción de textos utilizando Gemini, y cómo resolver algunos de los problemas comunes que suelen ocurrir cuando generamos textos con este tipo de tecnología, ya sea Gemini, chatGPT o cualquier otro LLM (Modelo Grande de Lenguaje).

 

Cuando intentas generar con IA el contenido que requiere una mínima longitud, por ejemplo el contenido generado en base a determinadas características de productos, el contenido total de un artículo sobre algún tema, guías completas sobre ciertos temas, etc me he encontrado con dos problemas iniciales:

  • Textos demasiado cortos
    Cuando queremos crear el texto completo para un artículo normalmente la respuesta que nos da es un texto demasiado corto para ser el contenido completo de un artículo, sin entrar a valorar su calidad y/o utilidad. 

  • Textos repetitivos
    Una vez superamos el problema con los textos demasiados cortos y que nos devuelva un texto lo suficientemente extenso como para ser un contenido válido, en muchas ocasiones se repite la información en el resultado final, siendo mucha información redundante / repetitiva dentro del mismo artículo.

Generar textos SEO con Google Gemini

 

 

Como indicar la longitud de la respuesta a Google Gemini

En primer lugar, debemos saber que lo que determina la longitud máxima de la respuesta depende de los límites de cada modelo, y por esa razón debemos informarnos sobre esta y otras características en la documentación del modelo concreto que estemos usando, en este caso Gemini pro. Esta cantidad de texto normalmente viene dado en tokens, que aproximadamente podemos decir que 100 tokens corresponden a casi 60 u 80 palabras.

En el caso de Google Gemini pro, a día de hoy , la cantidad máxima de tokens que se pueden generar en la respuesta es de 8192 tokens (juraría que la semana pasada solamente eran 2048! Esto quiere decir que va a ir mejorando, y esto siempre es una buena noticia :)), que son 4.915 palabras, una cantidad de palabras que a priori es más que suficiente para cualquier temática que queramos abordar.

 

Recordemos que en nuestro ejemplo base, que vimos en el artículo Cómo utilizar la API de Google Gemini, teníamos una función que procesaba el prompt, aquí el código de esta función

 

def genera_texto(ejecutaPrompt: str) -> str:
    # Initialize Vertex AI

    # Load the model
    model = GenerativeModel("gemini-pro")
    response = model.generate_content(
            ejecutaPrompt,
            generation_config={
                "max_output_tokens": 8192,
                "temperature": 0.9,
                "top_p": 1
            },
        )

    return response

En ella vemos un parámetro denominado "max_output_tokens" con un valor de 8192 que es dónde indicamos la cantidad máxima de tokens que puede tener la respuesta.

 

Tal como decíamos anteriormente, 8.192 tokens representan más o menos unas 4.915 palabras, y debería ser más que suficiente para el contenido de un artículo. Pero la realidad es que cuando ejecutas el modelo, aunque pongas explícitamente que el resultado debe tener un mínimo de X palabras, raramente hace caso explícito a esa orden y el resultado casi siempre es menor (mucho menor) al número de palabras que le has solicitado. 

 

Ejemplo de prompt para generar el contenido completo de un artículo

Siguiendo con nuestro ejemplo base, definimos nuestro prompt para generer un artículo sobre el juego 'Fortnite', dónde indicamos el mínimo de palabras de la respuesta  

#DEFINE TU PROMPT PARA EJECUTAR SOBRE GOOGLE GEMINI

prompt='''
Eres un redactor de contenido experto en videojuegos,
redacta un artículo con diferentes seccciones sobre el juego 'Fortnite' que tenga como mínimo 2.000 palabras.
GENERA LA SALIDA EN FORMATO HTML
'''

genera_texto(prompt)

Al ejecutar este prompt, nos devuelve el siguiente HTML

<!DOCTYPE html>
<html>
<head>
  <title>Fortnite: Una Guía Integral</title>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="description" content="Un análisis en profundidad del popular videojuego Fortnite, que incluye jugabilidad, estrategia y consejos para principiantes y jugadores avanzados.">
  <style>
    body {
      font-family: Arial, sans-serif;
      line-height: 1.6;
    }

    h1, h2, h3 {
      margin-bottom: 16px;
    }

    h2 {
      font-size: 1.5em;
    }

    h3 {
      font-size: 1.2em;
    }

    ul, ol {
      padding-left: 1.5em;
      margin-bottom: 16px;
    }

    li {
      margin-bottom: 8px;
    }

    code {
      font-family: monospace;
      padding: 4px;
      background-color: #eee;
    }
  </style>
</head>
<body>
  <h1>Fortnite: Una Guía Integral</h1>

  <h2>Introducción</h2>

  <p>Fortnite es un videojuego multijugador en línea desarrollado por Epic Games y People Can Fly. Se lanzó por primera vez en 2017 y desde entonces se ha convertido en uno de los juegos más populares del mundo, con más de 350 millones de jugadores registrados.</p>

  <p>Fortnite es un juego de disparos en tercera persona en el que los jugadores compiten en equipos o solos para ser el último jugador o equipo en pie. El juego cuenta con una gran variedad de armas, vehículos y elementos que los jugadores pueden usar para sobrevivir y derrotar a sus oponentes.</p>

  <h2>Jugabilidad</h2>

  <p>Fortnite tiene dos modos de juego principales: Battle Royale y Save the World.</p>

  <h3>Battle Royale</h3>

  <p>Battle Royale es el modo de juego más popular de Fortnite. En este modo, 100 jugadores se enfrentan en una isla cada vez más pequeña. Los jugadores deben recolectar armas y recursos, construir estructuras y luchar contra otros jugadores para ser el último jugador en pie.</p>

  <h3>Save the World</h3>

  <p>Save the World es un modo de juego cooperativo en el que los jugadores trabajan juntos para completar misiones y defenderse de las hordas de enemigos controladas por computadora.</p>

  <h2>Estrategia</h2>

  <p>Hay una serie de estrategias diferentes que los jugadores pueden utilizar para tener éxito en Fortnite. Algunos de los consejos más importantes incluyen:</p>

  <ul>
    <li>Aterrizar en un lugar apartado para evitar el combate inicial.</li>
    <li>Recoger recursos y construir estructuras para protegerse de los enemigos.</li>
    <li>Usar armas y vehículos de forma eficaz.</li>
    <li>Trabajar en equipo con otros jugadores para aumentar tus posibilidades de ganar.</li>
  </ul>

  <h2>Consejos para principiantes</h2>

  <p>Si eres nuevo en Fortnite, aquí tienes algunos consejos para ayudarte a empezar:</p>

  <ul>
    <li>Juega el tutorial para aprender los conceptos básicos del juego.</li>
    <li>Salta a partidas de Battle Royale en solitario para practicar tus habilidades.</li>
    <li>Experimenta con diferentes armas y vehículos para encontrar los que más te gustan.</li>
    <li>No te desanimes si mueres al principio. ¡Todos los jugadores comienzan en algún lugar!</li>
  </ul>

  <h2>Consejos para jugadores avanzados</h2>

  <p>Una vez que hayas dominado los conceptos básicos, aquí tienes algunos consejos para ayudarte a mejorar tu juego:</p>

  <ul>
    <li>Aprende a construir estructuras de forma rápida y eficiente.</li>
    <li>Usa las coberturas y el terreno en tu beneficio.</li>
    <li>Domina el uso de armas y vehículos avanzados.</li>
    <li>Trabaja en equipo con otros jugadores para mejorar tus posibilidades de ganar.</li>
  </ul>

  <h2>Conclusión</h2>

  <p>Fortnite es un juego complejo y emocionante que requiere habilidad, estrategia y trabajo en equipo. Ya seas un principiante o un jugador experimentado, siempre hay algo nuevo que aprender. Con estos consejos, puedes mejorar tu juego y divertirte más con Fortnite.</p>
</body>
</html>

Este resultado no está nada mal! (aquí está el resultado HTML) pero no es suficientemente largo para que sea el contenido completo de un artículo.

 

Predefinir secciones del artículo

Una manera de generar un contenido más extenso es predefinir las secciones principales que tendrá cada artículo y realizar una llamada al modelo de lenguaje para que genere el texto de cada una de las secciones del artículo.

Tomamos como secciones del artículo las siguientes:

  • Historia y Desarrollo
  • Mecánica de juego
  • Modos de juego
  • Personajes y cosméticos
  • Impacto cultural

 

Y ejecutamos una  petición para cada una de las secciones (ver ejemplo concreto en Colab).

TextoFinal = ""
prompt='''
Eres un redactor de contenido experto en videojuegos,
Redacta el contenido de la sección 'Historia y Desarrollo' que será parte de un artículo sobre el juego 'Fortnite'.
Escribe párrafos largos, descriptivos con un estilo narrativo. Genera diferentes párrafos por cada subsección, hasta un máximo de 4 párrafos. En algunos casos usar jerarquía de etiquetas <H3>,<H4>.
Genera la respuesta en código HTML, este HTML se añadirá a un HTML ya existente entre <div> y </div> por lo tanto no puede contener <body> ni <head> ni otros elementos que van fuera de <body>.
La respuesta debe comenzar con '<H2>Historia y Desarrollo</H2> \n .
'''

resultado = genera_texto(prompt)
TextoFinal = TextoFinal+resultado.text

prompt='''
Eres un redactor de contenido experto en videojuegos,
Redacta el contenido de la sección 'Mecánica de juego' que será parte de un artículo sobre el juego 'Fortnite'.
Escribe párrafos largos, descriptivos con un estilo narrativo. Genera diferentes párrafos por cada subsección, hasta un máximo de 4 párrafos. En algunos casos usar jerarquía de etiquetas <H3>,<H4>.
Genera la respuesta en código HTML, este HTML se añadirá a un HTML ya existente entre <div> y </div> por lo tanto no puede contener <body> ni <head> ni otros elementos que van fuera de <body>.
La respuesta debe comenzar con '<H2>Mecánica de juego</H2>'
'''

resultado = genera_texto(prompt)
TextoFinal = TextoFinal+resultado.text

prompt='''
Eres un redactor de contenido experto en videojuegos,
Redacta el contenido de la sección 'Modos de juego' que será parte de un artículo sobre el juego 'Fortnite'.
Escribe párrafos largos, descriptivos con un estilo narrativo. Genera diferentes párrafos por cada subsección, hasta un máximo de 4 párrafos. En algunos casos usar jerarquía de etiquetas <H3>,<H4>.
Genera la respuesta en código HTML, este HTML se añadirá a un HTML ya existente entre <div> y </div> por lo tanto no puede contener <body> ni <head> ni otros elementos que van fuera de <body>.
La respuesta debe comenzar con '<H2>Modos de juego</H2>'
'''

resultado = genera_texto(prompt)
TextoFinal = TextoFinal+resultado.text

prompt='''
Eres un redactor de contenido experto en videojuegos,
Redacta el contenido de la sección 'Personajes y cosméticos' que será parte de un artículo sobre el juego 'Fortnite'.
Escribe párrafos largos, descriptivos con un estilo narrativo. Genera diferentes párrafos por cada subsección, hasta un máximo de 4 párrafos. En algunos casos usar jerarquía de etiquetas <H3>,<H4>.
Genera la respuesta en código HTML, este HTML se añadirá a un HTML ya existente entre <div> y </div> por lo tanto no puede contener <body> ni <head> ni otros elementos que van fuera de <body>.
La respuesta debe comenzar con '<h2>Personajes y cosméticos</h2>
'''

resultado = genera_texto(prompt)
TextoFinal = TextoFinal+resultado.text

prompt='''
Eres un redactor de contenido experto en videojuegos,
Redacta el contenido de la sección 'Impacto cultural' que será parte de un artículo sobre el juego 'Fortnite'.
Escribe párrafos largos, descriptivos con un estilo narrativo. Genera diferentes párrafos por cada subsección, hasta un máximo de 4 párrafos. En algunos casos usar jerarquía de etiquetas <H3>,<H4>.
Genera la respuesta en código HTML, este HTML se añadirá a un HTML ya existente entre <div> y </div> por lo tanto no puede contener <body> ni <head> ni otros elementos que van fuera de <body>.
La respuesta debe comenzar con '<H2>Impacto cultural</H2>'
'''

resultado = genera_texto(prompt)
TextoFinal = TextoFinal+resultado.text


print(TextoFinal)

 

Esto nos da un resultado mucho más extenso que en el anterior ejemplo, aquí el resultado tras ejecutarlo y montar el HTML, y ahora sí podemos decir que cumple el  requisito de un mínimo de contenido. Pero esto nos limita a tener que predefiir previamente las secciones del artículo y puede ser un problema cuando quieres generar contenido para una diversidad de temas diferentes ya que puede que no todos los temas responden a una misma estructura de contenido. 

 

Solicitar a Gemini que defina las secciones que tendrá cada artículo

Podemos hacer que sea el propio Gemini quién nos defina las secciones principales para cada artículo y así no tener que predefinir cada sección en cada artículo.

Para este caso vamos a solicitar las secciones principales para la redacción de un artículo sobre el vídeo juego 'Fortnite', este término lo definimos en la variable tituloPost.

Ya en texto del propio prompt le indicamos en qué formato de salida exacto queremos los resultados (así luego recorrerlos fácilmente). Para que el resultado se ajuste a un formato concreto, hemos definido la variable 'ejemploEsquema', con el esquema del resultado que nos gustaría recibir. Y al final de nuestro prompt sustituiremos '<ejemploEsquema>' por el contenido de nuestra variable para obtener secciones de un artículo concreto en formato determinado.

 

Ver este ejemplo concreto en Colab

ejemploEsquema="""
                {
                 "secciones": [
                    "Nombre Seccion 1",
                    "Nombre Seccion 2",
                    "Nombre Seccion 3",
                    "Nombre Seccion 4",
                    "Nombre Seccion 5"
                ]
                }
    """
tituloPost = "Fortnite"

prompt='''
Eres un redactor de contenido experto en videojuegos.
Ddefine entre 4 y 6 secciones que serán parte de un artículo sobre <titulo>.
Es MUY IMPORTANTE que la lista no contenga secciones que pudiesen contener elementos muy parecidos que otra seccion de la lista.
Devuelve una unica lista en formato JSON con el título para cada seción, cada seciión tendrá como máximo 10 palabras.
El esquema del JSON debe ser exactamente como esté: <ejemploEsquema>
'''

promptProcesado=prompt.replace('<titulo>',tituloPost).replace('<ejemploEsquema>',ejemploEsquema)

ResultadoPrompt = genera_texto(promptProcesado)
print(ResultadoPrompt.text)

 

Esto nos da el siguiente resultado:

{
    "secciones": [
        "Fundamentos del juego y mecánicas",
        "Modos de juego y objetivos",
        "Sistema de construcción y edición",
        "Personajes, armas y equipo",
        "Estrategia y tácticas avanzadas"
    ]
}

Perfecto! De esta manera podremos recorrer cada una de las secciones propuestas y solicitar que genere el texto completo para cada una de ellas.

 

Recorrer cada sección devuelta por Gemini

Para procesar correctamente la respuesta anterior vamos a importar alguna librería en python que trate los resultados JSON y los convierta a una lista de manera que la podamos recorrer fácilmente.

Siguiendo nuestro ejemplo anterior vamos a recorrer cada sección que hemos almacenado en ResultadoPrompt.text

import json

def convertir_JSON_a_lista(json_data):
    def extraer_elementos(data, clave, items, max_items):
        if clave in data and isinstance(data[clave], list):
            items.extend(data[clave][:max_items])

    items = []
    clave_poco = None

    # Obtener la primera clave
    first_key = next(iter(json_data))

    # Extraer 5 elementos de la primera clave
    extraer_elementos(json_data, first_key, items, 5)

    # Si se encontró una clave con 'poco', extraer 4 elementos de esa clave
    if clave_poco:
        extraer_elementos(json_data, clave_poco, items, 4)

    return items

jsondata = json.loads(ResultadoPrompt.text.replace('```json','').replace('```','').replace('JSON',''))

secciones = convertir_JSON_a_lista(jsondata)
cabeceras=[]
miContexto = ""
elementos=0
texto_Articulo=f'<h1>Eejemplo de bienes muebles</h1> '
for seccion in secciones:
    seccion_lower = seccion.lower()
    print(seccion)

Esto nos da como resultado:

Fundamentos del juego y mecánicas
Modos de juego y objetivos
Sistema de construcción y edición
Personajes, armas y equipo
Estrategia y tácticas avanzadas

 

 

Ok, vemos que podemos recorrer cada una de las secciones, lo único que nos falta es crear el contenido adecuado en cada una de ellas. Aquí está el código específico en Google colab.

import json

def convertir_JSON_a_lista(json_data):
    def extraer_elementos(data, clave, items, max_items):
        if clave in data and isinstance(data[clave], list):
            items.extend(data[clave][:max_items])

    items = []
    clave_poco = None

    # Obtener la primera clave
    first_key = next(iter(json_data))

    # Extraer 5 elementos de la primera clave
    extraer_elementos(json_data, first_key, items, 5)

    # Si se encontró una clave con 'poco', extraer 4 elementos de esa clave
    if clave_poco:
        extraer_elementos(json_data, clave_poco, items, 4)

    return items

jsondata = json.loads(ResultadoPrompt.text.replace('```json','').replace('```','').replace('JSON',''))

secciones = convertir_JSON_a_lista(jsondata)
cabeceras=[]
miContexto = ""
elementos=0
TextoFinal=f'<h1><titulo></h1> '.replace('<titulo>',tituloPost)
for seccion in secciones:
    seccion_lower = seccion.lower()
    prompt='''
    Eres un redactor de contenido experto en videojuegos,
    Redacta el contenido de la sección '<seccion>' que será parte de un artículo sobre el juego '<titulo>'.
    Escribe párrafos largos, descriptivos con un estilo narrativo. Genera diferentes párrafos por cada subsección, con un máximo de 4 párrafos. En algunos casos usar jerarquía de etiquetas <H3>,<H4>.
    Genera la salida en formato HTML pero no puede contener <body> ni <head> ni otros elementos que van fuera de <body> porque este HTML se añadirá a un HTML ya existente.
    La salida debe comenzar con '<h2><seccion></h2>
    '''

    resultado = genera_texto(prompt.replace('<seccion>',seccion).replace('<titulo>',tituloPost))
    TextoFinal = TextoFinal+resultado.text

print(TextoFinal)

 

El resultado completo puedes verlo en este enlace, creo que en cuanto a  cantidad de contenido dado un tema concreto es suficiente, pero todavía nos quedan cosas importantes por mejorar, una de ellas es la repetitividad de cierto contenido.

 

Cómo evitar que repita contenido.

En este ejemplo que acabamos de ver no ha ocurrido, pero mientras creaba este artículo tuve varios casos en los que había información repetida o redundante, por ejemplo, este es un resultado que generé previamente usando este mismo prompt, si nos fijamos en su contenido tenemos que dentro de las secciones 'Mecánica de juego' y 'Modos de juego' existen subsecciones que se repiten, por ejemplo 'Salvar el mundo' y 'Salvamento del Mundo' o 'Battle Royale', es decir, el contenido de una y otra sección se solapan / pisan / duplican.

Este tipo de repetitividad es mucho más común de lo que quisiéramos, por lo que una de las maneras que se me han ocurrido para evitar esta repetitividad ha sido la de indicar al modelo qué información ya hemos ido añadiendo al artículo cada vez que solicitamos el texto de una nueva sección.

 

Para conseguir esto he creado una variable que se llama 'cabeceras', dónde iré incluyendo cada Hx y cada <li> que haya en las respuestas anteriores. Esto lo conseguimos creando una función 'ExtraerCabeceras' que extrae determinados tags HTML com Hx, o <li> que suelen ser los puntos importantes de cada subsección, esta información se la pasaremos al modelo para indicarle que el texto que va a crear no puede contener información que haga referencia a alguno de estas cabeceras, digamos que le estamos proporcionando un contexto negativo, es decir, la información que no debe incluir en el contenido de la sección que esté tratando.

from bs4 import BeautifulSoup
def ExtraerCabeceras(html_text):
    soup = BeautifulSoup(html_text, 'html.parser')
    
    # Inicializar la variable de contenido
    content = []

    # Extraer encabezados
    headers = [tag.text for tag in soup.find_all(['h1', 'h2', 'h3', 'h4', 'h5', 'h6'])]
    content.extend(headers)  # Agregar encabezados a la lista de contenido

    # Extraer listas ordenadas
    for ol in soup.find_all('ol'):
        items = [li.text for li in ol.find_all('li')]
        content.append('Ordered List:')
        content.extend(items)  # Agregar elementos de lista ordenada a la lista de contenido

    # Extraer listas desordenadas
    for ul in soup.find_all('ul'):
        items = [li.text for li in ul.find_all('li')]
        content.append('Unordered List:')
        content.extend(items)  # Agregar elementos de lista desordenada a la lista de contenido
    
    return content    

 

Haciendo zoom en la parte del código en la que usaremos esta variable cabecera, dónde por cada texto generado de cada sección añadimos a la variable 'cabeceras' las cabeceras (hx, li) con la información relevante de cada sección para no repetirla al generar el texto de las siguientes secciones.

Por cada sección creamos una variable llamada 'miContexto', que generará una frase, la cual añadiremos a nuestro prompt indicando que información NO debe añadir a la sección actual porque previamente ha sido añadida.

La frase que añadiremos al prompt en cada sección tiene la estructura siguiente

"No puedes añadir la siguiente información que ya está en el artículo y sería información redundante y duplicada: <INFORMACION YA GENERADA> por lo tanto redacta el contenido con información complementaria a la que ya tiene"

Dónde sustituiremos <INFORMACION YA GENERADA> por las cabeceras de cada sección previamente generada.

Código fuente en colab

secciones = convertir_JSON_a_lista(jsondata)
cabeceras=[]
miContexto = ""
elementos=0
TextoFinal=f'<h1><titulo></h1> '.replace('<titulo>',tituloPost)
for seccion in secciones:
    seccion_lower = seccion.lower()
    if len(cabeceras) > 1:
        miContexto = "No puedes añadir la siguiente información que ya está en el artículo y sería información redundante y duplicada: \"" + ',\n '.join(cabeceras)+' '
        miContexto=miContexto+'" por lo tanto redacta el contenido con información complementaria a la que ya tiene.'
    prompt='''
    Eres un redactor de contenido experto en videojuegos,
    Redacta el contenido de la sección '<seccion>' que será parte de un artículo sobre el juego '<titulo>'.
    Escribe párrafos largos, descriptivos con un estilo narrativo. Genera diferentes párrafos por cada subsección, con un máximo de 4 párrafos. En algunos casos usar jerarquía de etiquetas <H3>,<H4>.
    Genera la respuesta en HTML, este HTML se añadirá a un HTML ya existente entre <div> y </div> por lo tanto no puede contener <body> ni <head> ni otros elementos que van fuera de <body>.
    La respuesta debe comenzar con '<H2><seccion></H2> \n .
    <miContexto>
    '''
    print("Prompt para la Seccion:",seccion)
    print(prompt.replace('<seccion>',seccion).replace('<titulo>',tituloPost).replace('<miContexto>',miContexto))
    resultado = genera_texto(prompt.replace('<seccion>',seccion).replace('<titulo>',tituloPost).replace('<miContexto>',miContexto))
    TextoFinal = TextoFinal+resultado.text
    cabeceras.extend(ExtraerCabeceras(resultado.text))

print("HTML RESULTADO FINAL:")
print(TextoFinal)

 

Ejemplo de cómo se van formando los prompts concatenando las cabeceras de las secciones anteriores (podéis acceder a este código en concreto).

Prompt para la Seccion: Fundamentos del juego y mecánicas

    Eres un redactor de contenido experto en videojuegos,
    Redacta el contenido de la sección 'Fundamentos del juego y mecánicas' que será parte de un artículo sobre el juego 'Fortnite'.
    Escribe párrafos largos, descriptivos con un estilo narrativo. Genera diferentes párrafos por cada subsección, con un máximo de 4 párrafos. En algunos casos usar jerarquía de etiquetas <H3>,<H4>.
    Genera la respuesta en HTML, este HTML se añadirá a un HTML ya existente entre <div> y </div> por lo tanto no puede contener <body> ni <head> ni otros elementos que van fuera de <body>.
    La respuesta debe comenzar con '<H2>Fundamentos del juego y mecánicas</H2> 
 .
    
    
Prompt para la Seccion: Modos de juego y objetivos

    Eres un redactor de contenido experto en videojuegos,
    Redacta el contenido de la sección 'Modos de juego y objetivos' que será parte de un artículo sobre el juego 'Fortnite'.
    Escribe párrafos largos, descriptivos con un estilo narrativo. Genera diferentes párrafos por cada subsección, con un máximo de 4 párrafos. En algunos casos usar jerarquía de etiquetas <H3>,<H4>.
    Genera la respuesta en HTML, este HTML se añadirá a un HTML ya existente entre <div> y </div> por lo tanto no puede contener <body> ni <head> ni otros elementos que van fuera de <body>.
    La respuesta debe comenzar con '<H2>Modos de juego y objetivos</H2> 
 .
    No puedes añadir la siguiente información que ya está en el artículo y sería información redundante y duplicada: "Fundamentos del juego y mecánicas,
 El bucle de juego de Fortnite,
 El sistema de construcción,
 Combate y armas " por lo tanto redacta el contenido con información complementaria a la que ya tiene.
    
Prompt para la Seccion: Sistema de construcción y edición

    Eres un redactor de contenido experto en videojuegos,
    Redacta el contenido de la sección 'Sistema de construcción y edición' que será parte de un artículo sobre el juego 'Fortnite'.
    Escribe párrafos largos, descriptivos con un estilo narrativo. Genera diferentes párrafos por cada subsección, con un máximo de 4 párrafos. En algunos casos usar jerarquía de etiquetas <H3>,<H4>.
    Genera la respuesta en HTML, este HTML se añadirá a un HTML ya existente entre <div> y </div> por lo tanto no puede contener <body> ni <head> ni otros elementos que van fuera de <body>.
    La respuesta debe comenzar con '<H2>Sistema de construcción y edición</H2> 
 .
    No puedes añadir la siguiente información que ya está en el artículo y sería información redundante y duplicada: "Fundamentos del juego y mecánicas,
 El bucle de juego de Fortnite,
 El sistema de construcción,
 Combate y armas,
 Modos de juego y objetivos,
 Battle Royale,
 Salvar el mundo,
 Cooperativo para 4 jugadores,
 Creativo " por lo tanto redacta el contenido con información complementaria a la que ya tiene.
    
Prompt para la Seccion: Personajes, armas y equipo

    Eres un redactor de contenido experto en videojuegos,
    Redacta el contenido de la sección 'Personajes, armas y equipo' que será parte de un artículo sobre el juego 'Fortnite'.
    Escribe párrafos largos, descriptivos con un estilo narrativo. Genera diferentes párrafos por cada subsección, con un máximo de 4 párrafos. En algunos casos usar jerarquía de etiquetas <H3>,<H4>.
    Genera la respuesta en HTML, este HTML se añadirá a un HTML ya existente entre <div> y </div> por lo tanto no puede contener <body> ni <head> ni otros elementos que van fuera de <body>.
    La respuesta debe comenzar con '<H2>Personajes, armas y equipo</H2> 
 .
    No puedes añadir la siguiente información que ya está en el artículo y sería información redundante y duplicada: "Fundamentos del juego y mecánicas,
 El bucle de juego de Fortnite,
 El sistema de construcción,
 Combate y armas,
 Modos de juego y objetivos,
 Battle Royale,
 Salvar el mundo,
 Cooperativo para 4 jugadores,
 Creativo,
 Sistema de construcción y edición,
 Construyendo tu camino hacia la victoria,
 Materiales versátiles,
 Edición precisa y formas complejas,
 Trabajo en equipo y colaboración " por lo tanto redacta el contenido con información complementaria a la que ya tiene.
    
Prompt para la Seccion: Estrategia y tácticas avanzadas

    Eres un redactor de contenido experto en videojuegos,
    Redacta el contenido de la sección 'Estrategia y tácticas avanzadas' que será parte de un artículo sobre el juego 'Fortnite'.
    Escribe párrafos largos, descriptivos con un estilo narrativo. Genera diferentes párrafos por cada subsección, con un máximo de 4 párrafos. En algunos casos usar jerarquía de etiquetas <H3>,<H4>.
    Genera la respuesta en HTML, este HTML se añadirá a un HTML ya existente entre <div> y </div> por lo tanto no puede contener <body> ni <head> ni otros elementos que van fuera de <body>.
    La respuesta debe comenzar con '<H2>Estrategia y tácticas avanzadas</H2> 
 .
    No puedes añadir la siguiente información que ya está en el artículo y sería información redundante y duplicada: "Fundamentos del juego y mecánicas,
 El bucle de juego de Fortnite,
 El sistema de construcción,
 Combate y armas,
 Modos de juego y objetivos,
 Battle Royale,
 Salvar el mundo,
 Cooperativo para 4 jugadores,
 Creativo,
 Sistema de construcción y edición,
 Construyendo tu camino hacia la victoria,
 Materiales versátiles,
 Edición precisa y formas complejas,
 Trabajo en equipo y colaboración,
 Personajes, armas y equipo,
 Personajes,
 Armas,
 Equipo,
 Otros artículos " por lo tanto redacta el contenido con información complementaria a la que ya tiene.

 

Estos serían los prompts ejecutados en cada sección, dónde podemos ver cómo le vamos dando los elementos generados en secciones anteriores para que no los repita, en este ejemplo vemos que:

  • En la primera iteración no se ve la variable miContexto porque es la primera sección 'Fundamentos del juego y mecánicas' y por lo tanto no tendrá limitaciones sobre qué contenido NO debe crear.
  • En las siguientes secciones se van añadiendo la información ya generada en secciones previas y que no debe añadir a la sección que está procesando actualmente.
  • Por ejemplo, al crear la sección 'Modos de juego y objetivos' se le indica que no debe contener información sobre "'Fundamentos del juego y mecánicas' o ' El bucle de juego de Fortnite' o ' El sistema de construcción' o ' Combate y armas'".
  • Repetimos esto con el resto de secciones.

 

 

HTML Final

El resultado final podemos verlo en esta URL , tiene un total de más de 1.300 palabras!

Con un resultado HTML mucho más extenso que realizándose todo de golpe y con unas mínimas posibilidades de que exista información redundante entre las diferentes secciones podríamos decir que este sistema genera contenido extenso, en formato HTML y con mínimas duplicidades.

 

Vamos a poner el código completo de este script

from google.colab import auth
auth.authenticate_user()
import vertexai
from vertexai.preview.generative_models import GenerativeModel, Part
import json
from bs4 import BeautifulSoup


IDPROYECTO="ID_PROYECTO"
ZONA_PROYECTO='us-central1'
tituloPost = "Mario Kart"

vertexai.init(project=IDPROYECTO, location=ZONA_PROYECTO)
def genera_texto(ejecutaPrompt: str) -> str:
    # Initialize Vertex AI

    # Load the model
    model = GenerativeModel("gemini-pro")
    response = model.generate_content(
            ejecutaPrompt,
            generation_config={
                "max_output_tokens": 8192,
                "temperature": 0.9,
                "top_p": 1
            },
        )

    return response


def ExtraerCabeceras(html_text):
    soup = BeautifulSoup(html_text, 'html.parser')
    
    # Inicializar la variable de contenido
    content = []

    # Extraer encabezados
    headers = [tag.text for tag in soup.find_all(['h1', 'h2', 'h3', 'h4', 'h5', 'h6'])]
    content.extend(headers)  # Agregar encabezados a la lista de contenido

    # Extraer listas ordenadas
    for ol in soup.find_all('ol'):
        items = [li.text for li in ol.find_all('li')]
        content.append('Ordered List:')
        content.extend(items)  # Agregar elementos de lista ordenada a la lista de contenido

    # Extraer listas desordenadas
    for ul in soup.find_all('ul'):
        items = [li.text for li in ul.find_all('li')]
        content.append('Unordered List:')
        content.extend(items)  # Agregar elementos de lista desordenada a la lista de contenido
    
    return content   

def convertir_JSON_a_lista(json_data):
    def extraer_elementos(data, clave, items, max_items):
        if clave in data and isinstance(data[clave], list):
            items.extend(data[clave][:max_items])

    items = []
    clave_poco = None

    # Obtener la primera clave
    first_key = next(iter(json_data))

    # Extraer 5 elementos de la primera clave
    extraer_elementos(json_data, first_key, items, 5)

    # Si se encontró una clave con 'poco', extraer 4 elementos de esa clave
    if clave_poco:
        extraer_elementos(json_data, clave_poco, items, 4)

    return items

ejemploEsquema="""
                {
                 "secciones": [
                    "Nombre Seccion 1",
                    "Nombre Seccion 2",
                    "Nombre Seccion 3",
                    "Nombre Seccion 4",
                    "Nombre Seccion 5"
                ]
                }
    """


prompt='''
Eres un redactor de contenido experto en videojuegos.
Ddefine entre 4 y 6 secciones que serán parte de un artículo sobre <titulo>.
Es MUY IMPORTANTE que la lista no contenga secciones que pudiesen contener elementos muy parecidos que otra seccion de la lista.
Devuelve una unica lista en formato JSON con el título para cada seción, cada seciión tendrá como máximo 10 palabras.
El esquema del JSON debe ser exactamente como esté: <ejemploEsquema>
'''

promptProcesado=prompt.replace('<titulo>',tituloPost).replace('<ejemploEsquema>',ejemploEsquema)

ResultadoPrompt = genera_texto(promptProcesado)
print(ResultadoPrompt.text)

jsondata = json.loads(ResultadoPrompt.text.replace('```json','').replace('```','').replace('JSON',''))

secciones = convertir_JSON_a_lista(jsondata)
cabeceras=[]
miContexto = ""
elementos=0
TextoFinal=f'<h1><titulo></h1> '.replace('<titulo>',tituloPost)
for seccion in secciones:
    seccion_lower = seccion.lower()
    if len(cabeceras) > 1:
        miContexto = "No puedes añadir la siguiente información que ya está en el artículo y sería información redundante y duplicada: \"" + ',\n '.join(cabeceras)+' '
        miContexto=miContexto+'" por lo tanto redacta el contenido con información complementaria a la que ya tiene.'
    prompt='''
    Eres un redactor de contenido experto en videojuegos,
    Redacta el contenido de la sección '<seccion>' que será parte de un artículo sobre el juego '<titulo>'.
    Escribe párrafos largos, descriptivos con un estilo narrativo. Genera diferentes párrafos por cada subsección, con un máximo de 4 párrafos. En algunos casos usar jerarquía de etiquetas <H3>,<H4>.
    Genera la respuesta en HTML, este HTML se añadirá a un HTML ya existente entre <div> y </div> por lo tanto no puede contener <body> ni <head> ni otros elementos que van fuera de <body>.
    La respuesta debe comenzar con '<H2><seccion></H2> \n .
    <miContexto>
    '''
    print("Prompt para la Seccion:",seccion)
    print(prompt.replace('<seccion>',seccion).replace('<titulo>',tituloPost).replace('<miContexto>',miContexto))
    resultado = genera_texto(prompt.replace('<seccion>',seccion).replace('<titulo>',tituloPost).replace('<miContexto>',miContexto))
    TextoFinal = TextoFinal+resultado.text
    cabeceras.extend(ExtraerCabeceras(resultado.text))

print("HTML RESULTADO FINAL:")
print(TextoFinal)

 

Aspectos a mejorar

Hay mucho que se puede mejorar en este prompt, pero en cuanto a los problemas que intenta resolver se puede mejorar:

  • Contador de tokens, para que calcule los tokens de contexto para reducir ese contexto en caso de que exceda un límite que impida la salida con el máximo número de tokens recorte el contexto.
  • Mejorar el prompt para que no use tanta lista y realmente genere párrafos más largos
  • Velocidad, ¿cómo podríamos aumentar su velocidad?. Para este problema ya tengo la solución que publicaré en el siguiente artículo de esta saga :)

 

 

 

 

 




Lea otros artículos de Google Gemini

Últimos posts

Últimos comentarios


Resham Singh Mahal

Post: Experimento para comprobar la teoría del primer enlace

Joakim Hov Johnsen

Post: Experimento para comprobar la teoría del primer enlace

Dana

Post: Experimento para comprobar la teoría del primer enlace

JaviLazaro
Ya me has dado la necesidad de crear un comaando en bash para hacer estas cosas. Gracias Lino por estos tips
Post: Obtener KWs de varias fuentes usando la línea de comandos

Señor Muñoz
Lino, el 11% más de clicks y el 47% más de impresiones diarias ¿es algo constante o depende de cada sitio web?
Post: Diferencias entre la exportación de datos de Search Console usando BigQuery o usando la API

Carlos
Hola En mi blog tengo artículos atemporales (es decir, no caducan nunca, de manera que sirve para quien lo lea hoy o lo lea dentro de 5
Post: Tratamiento de urls que tienen un tiempo de vida muy corto

Profe Ray
Veo que hay comentarios de hace 5 años y de hace 3 años. ¿Habrá algun post actualizado sobre este tema o sigue funcionando? Lo cierto es
Post: Cómo cargar css y js y no bloquear la carga de contenido

Pepe
Muchas gracias por el articulo!! Muy buena información.
Post: Qué es ofuscar enlaces y cómo mejora el enlazado interno

María
Sí, he buscado el archivo robots.txt y todo está correcto. La última versión vista con error fue el 08/11/2021 y la última vez que el
Post: Errores críticos originados por el robots.txt

Lino
@María un placer verte por aquí :) Lo primero, a veces, con el robots.txt no se puede "forzar" a que lo rastree, si tu site no es muy p
Post: Errores críticos originados por el robots.txt