Elegante versión de la prompt y configuración del modelo LLM con spacy-llm

Elegante prompt y configuración LLM con spacy-llm

Usando spacy-llm para simplificar la gestión de indicaciones y crear tareas para la extracción de datos

Un escritorio ordenado, así es como se verá tu código si usas spacy-llm jaja

Administrar indicaciones y manejar fallas en las solicitudes de OpenAI puede ser una tarea desafiante. Afortunadamente, spaCy lanzó spacy-llm, una poderosa herramienta que simplifica la gestión de indicaciones y elimina la necesidad de crear una solución personalizada desde cero.

En este artículo, aprenderás cómo aprovechar spacy-llm para crear una tarea que extrae datos de un texto utilizando una indicación. Nos adentraremos en los conceptos básicos de spacy y exploraremos algunas de las características de spacy-llm.

spaCy y spacy-llm 101

spaCy es una biblioteca para PLN avanzado en Python y Cython. Al tratar datos de texto, generalmente se requieren varios pasos de procesamiento, como tokenización y etiquetado POS. Para ejecutar estos pasos, spaCy proporciona el método nlp, que invoca una canalización de procesamiento.

spaCy v3.0 introduce config.cfg, un archivo donde podemos incluir configuraciones detalladas de estas canalizaciones.

config.cfg utiliza confection, un sistema de configuración que permite la creación de árboles de objetos arbitrarios. Por ejemplo, confection analiza el siguiente config.cfg:

[training]patience = 10dropout = 0.2use_vectors = false[training.logging]level = "INFO"[nlp]# Esto utiliza el valor de training.use_vectorsuse_vectors = ${training.use_vectors}lang = "en"

en:

{  "training": {    "patience": 10,    "dropout": 0.2,    "use_vectors": false,    "logging": {      "level": "INFO"    }  },  "nlp": {    "use_vectors": false,    "lang": "en"  }}

Cada canalización utiliza componentes, y spacy-llm almacena los componentes de la canalización en registros utilizando catalogue. Esta biblioteca, también de Explosion, introduce registros de funciones que permiten una gestión eficiente de los componentes. Un componente llm se define en dos configuraciones principales:

  • Una tarea, que define la indicación a enviar a LLM y la funcionalidad para analizar la respuesta resultante
  • Un modelo, que define el modelo y cómo conectarse a él

Para incluir un componente que utiliza un LLM en nuestra canalización, debemos seguir algunos pasos. Primero, necesitamos crear una tarea y registrarla en el registro. A continuación, podemos usar un modelo para ejecutar la indicación y obtener las respuestas. Ahora es el momento de hacer todo eso para poder ejecutar la canalización

Creando una tarea para extraer datos de texto

Utilizaremos citas de https://dummyjson.com/ y crearemos una tarea para extraer el contexto de cada cita. Crearemos la indicación, registraremos la tarea y finalmente crearemos el archivo de configuración.

1. La indicación

spacy-llm utiliza plantillas Jinja para definir las instrucciones y ejemplos. El {{ text }} será reemplazado por la cita que proporcionaremos. Esta es nuestra indicación:

Eres un experto en extraer contexto de texto. Tu tarea es aceptar una cita como entrada y proporcionar el contexto de la cita. Este contexto se utilizará para agrupar las citas juntas. No incluyas ningún otro texto en tu respuesta y proporciona el contexto en un máximo de 3 palabras.{# espacio en blanco #}{# espacio en blanco #}Aquí está la cita que necesita clasificación{# espacio en blanco #}{# espacio en blanco #}Cita: '''{{ text }}'''Contexto

2. La clase de la tarea

Ahora creemos la clase para la tarea. La clase debe implementar dos funciones:

  • generate_prompts(docs: Iterable[Doc]) -> Iterable[str]: una función que toma una lista de objetos Doc de spaCy y los transforma en una lista de indicaciones
  • parse_responses(docs: Iterable[Doc], responses: Iterable[str]) -> Iterable[Doc]: una función para analizar las salidas del LLM y convertirlas en objetos Doc de spaCy

generate_prompts utilizará nuestra plantilla de Jinja y parse_responses agregará el atributo contexto a nuestro Doc. Esta es la clase QuoteContextExtractTask:

from pathlib import Pathfrom spacy_llm.registry import registryimport jinja2from typing import Iterablefrom spacy.tokens import DocTEMPLATE_DIR = Path("templates")def read_template(name: str) -> str:    """Lee una plantilla"""    path = TEMPLATE_DIR / f"{name}.jinja"    if not path.exists():        raise ValueError(f"{name} no es una plantilla válida.")    return path.read_text()class QuoteContextExtractTask:  def __init__(self, template: str = "quotecontextextract.jinja", field: str = "context"):    self._template = read_template(template)    self._field = field  def _check_doc_extension(self):     """Agrega la extensión si es necesario."""     if not Doc.has_extension(self._field):         Doc.set_extension(self._field, default=None)  def generate_prompts(self, docs: Iterable[Doc]) -> Iterable[str]:    environment = jinja2.Environment()    _template = environment.from_string(self._template)    for doc in docs:        prompt = _template.render(            text=doc.text,        )        yield prompt    def parse_responses(      self, docs: Iterable[Doc], responses: Iterable[str]  ) -> Iterable[Doc]:    self._check_doc_extension()    for doc, prompt_response in zip(docs, responses):            try:        setattr(            doc._,            self._field,            prompt_response.replace("Context:", "").strip(),        ),      except ValueError:        setattr(doc._, self._field, None)              yield doc

Ahora solo necesitamos agregar la tarea al registro llm_tasks de spacy-llm:

@registry.llm_tasks("my_namespace.QuoteContextExtractTask.v1")def make_quote_extraction() -> "QuoteContextExtractTask":    return QuoteContextExtractTask()

3. El archivo config.cfg

Utilizaremos el modelo GPT-3.5 de OpenAI. spacy-llm tiene un modelo para eso, por lo que solo necesitamos asegurarnos de que la clave secreta esté disponible como una variable ambiental:

export OPENAI_API_KEY="sk-..."export OPENAI_API_ORG="org-..."

Para construir el método nlp que ejecuta el pipeline, utilizaremos el método assemble de spacy-llm. Este método lee desde un archivo .cfg. El archivo debe hacer referencia al modelo GPT-3.5 (que ya está en el registro) y la tarea que hemos creado:

[nlp]lang = "en"pipeline = ["llm"]batch_size = 128[components][components.llm]factory = "llm"[components.llm.model]@llm_models = "spacy.GPT-3-5.v1"config = {"temperature": 0.1}[components.llm.task]@llm_tasks = "my_namespace.QuoteContextExtractTask.v1"

4. Ejecutando el pipeline

Ahora solo necesitamos juntar todo y ejecutar el código:

import osfrom pathlib import Pathimport typerfrom wasabi import msgfrom spacy_llm.util import assemblefrom quotecontextextract import QuoteContextExtractTaskArg = typer.ArgumentOpt = typer.Optiondef run_pipeline(    # fmt: off    text: str = Arg("", help="Texto en el que realizar la categorización de texto."),    config_path: Path = Arg(..., help="Ruta al archivo de configuración a utilizar."),    verbose: bool = Opt(False, "--verbose", "-v", help="Mostrar información adicional."),    # fmt: on):    if not os.getenv("OPENAI_API_KEY", None):        msg.fail(            "No se encontró la variable de entorno OPENAI_API_KEY. "            "Establézcala ejecutando 'export OPENAI_API_KEY=...' y vuelva a intentarlo.",            exits=1,        )    msg.text(f"Cargando la configuración desde {config_path}", show=verbose)    nlp = assemble(        config_path    )    doc = nlp(text)    msg.text(f"Cita: {doc.text}")    msg.text(f"Contexto: {doc._.context}")if __name__ == "__main__":    typer.run(run_pipeline)

Y ejecutar:

python3 run_pipeline.py "Debemos equilibrar el consumo conspicuo con el capitalismo consciente." ./config.cfg>>> Cita: Debemos equilibrar el consumo conspicuo con el capitalismo consciente.Contexto: Ética empresarial.

Si quieres cambiar el indicador, simplemente crea otro archivo Jinja y crea una tarea my_namespace.QuoteContextExtractTask.v2 de la misma manera en que hemos creado la primera. Si deseas cambiar la temperatura, simplemente cambia el parámetro en el archivo config.cfg. ¿Bonito, verdad?

Pensamientos finales

La capacidad para manejar solicitudes REST de OpenAI y su enfoque sencillo para almacenar y versionar indicadores son mis cosas favoritas de spacy-llm. Además, la biblioteca ofrece una caché para almacenar indicadores y respuestas por documento, un método para proporcionar ejemplos para indicadores de pocos datos y una función de registro, entre otras cosas.

Puedes ver todo el código de hoy aquí: https://github.com/dmesquita/spacy-llm-elegant-prompt-versioning.

¡Como siempre, gracias por leer!