Elegante versioning del prompt e configurazione del modello LLM con spacy-llm

Prompt versioning and LLM model configuration with spacy-llm

Utilizzare spacy-llm per semplificare la gestione dei prompt e creare task per l’estrazione dei dati

Una scrivania in ordine, così apparirà il tuo codice se usi spacy-llm haha

Gestire i prompt e gestire i fallimenti delle richieste di OpenAI può essere un compito impegnativo. Fortunatamente, spaCy ha rilasciato spacy-llm, un potente strumento che semplifica la gestione dei prompt ed elimina la necessità di creare una soluzione personalizzata da zero.

In questo articolo, imparerai come sfruttare spacy-llm per creare un task che estrae dati da un testo utilizzando un prompt. Approfondiremo le basi di spacy e esploreremo alcune delle funzionalità di spacy-llm.

spaCy e spacy-llm 101

spaCy è una libreria per l’elaborazione del linguaggio naturale avanzata in Python e Cython. Quando si lavora con dati testuali, sono generalmente necessari diversi passaggi di elaborazione, come la tokenizzazione e l’etichettatura POS. Per eseguire questi passaggi, spaCy fornisce il metodo nlp, che invoca una pipeline di elaborazione.

spaCy v3.0 introduce config.cfg, un file in cui è possibile includere impostazioni dettagliate di queste pipeline.

config.cfg utilizza confection, un sistema di configurazione che consente la creazione di alberi di oggetti arbitrari. Ad esempio, confection elabora il seguente config.cfg:

[training]patience = 10dropout = 0.2use_vectors = false[training.logging]level = "INFO"[nlp]# Questo utilizza il valore di training.use_vectorsuse_vectors = ${training.use_vectors}lang = "en"

in:

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

Ogni pipeline utilizza componenti e spacy-llm memorizza i componenti della pipeline nei registri utilizzando catalogo. Questa libreria, anche di Explosion, introduce registri di funzioni che consentono una gestione efficiente dei componenti. Un componente llm è definito in due impostazioni principali:

  • Un task, che definisce il prompt da inviare a LLM e la funzionalità per analizzare la risposta risultante
  • Un modello, che definisce il modello e come connettersi ad esso

Per includere un componente che utilizza un LLM nella nostra pipeline, è necessario seguire alcuni passaggi. Prima di tutto, dobbiamo creare un task e registrarlo nel registro. Successivamente, possiamo utilizzare un modello per eseguire il prompt e recuperare le risposte. Ora è il momento di fare tutto ciò per poter eseguire la pipeline

Creazione di un task per l’estrazione di dati da un testo

Utilizzeremo citazioni da https://dummyjson.com/ e creeremo un task per estrarre il contesto da ogni citazione. Creeremo il prompt, registreremo il task e infine creeremo il file di configurazione.

1. Il prompt

spacy-llm utilizza modelli Jinja per definire le istruzioni e gli esempi. Il {{ text }} sarà sostituito dalla citazione che forniremo. Questo è il nostro prompt:

Sei un esperto nell'estrazione del contesto da un testo. Il tuo compito è accettare una citazione come input e fornire il contesto della citazione. Questo contesto verrà utilizzato per raggruppare insieme le citazioni. Non inserire altro testo nella tua risposta e fornisci il contesto con un massimo di 3 parole.{# whitespace #}{# whitespace #}Ecco la citazione che necessita di classificazione{# whitespace #}{# whitespace #}Citazione:'''{{ text }}'''Contesto

2. La classe del task

Ora creiamo la classe per il task. La classe dovrebbe implementare due funzioni:

  • generate_prompts(docs: Iterable[Doc]) -> Iterable[str]: una funzione che prende in input una lista di oggetti Doc di spaCy e li trasforma in una lista di prompt
  • parse_responses(docs: Iterable[Doc], responses: Iterable[str]) -> Iterable[Doc]: una funzione per analizzare gli output di LLM e trasformarli in oggetti Doc di spaCy

generate_prompts utilizzerà il nostro template Jinja e parse_responses aggiungerà l’attributo “context” al nostro Doc. Questa è la classe 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:    """Leggi un template"""    path = TEMPLATE_DIR / f"{name}.jinja"    if not path.exists():        raise ValueError(f"{name} non è un template valido.")    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):     """Aggiungi estensione se necessario."""     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

Ora dobbiamo solo aggiungere il task al registro llm_tasks di spacy-llm:

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

3. Il file config.cfg

Utilizzeremo il modello GPT-3.5 di OpenAI. spacy-llm ha un modello per questo, quindi è sufficiente assicurarsi che la chiave segreta sia disponibile come variabile ambientale:

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

Per creare il metodo nlp che esegue il pipeline, utilizzeremo il metodo assemble di spacy-llm. Questo metodo legge da un file .cfg. Il file dovrebbe fare riferimento al modello GPT-3.5 (già presente nel registro) e al task che abbiamo creato:

[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. Esecuzione della pipeline

Ora dobbiamo solo mettere tutto insieme ed eseguire il codice:

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="Testo su cui eseguire la categorizzazione del testo."),    config_path: Path = Arg(..., help="Percorso del file di configurazione da utilizzare."),    verbose: bool = Opt(False, "--verbose", "-v", help="Mostra informazioni extra."),    # fmt: on):    if not os.getenv("OPENAI_API_KEY", None):        msg.fail(            "La variabile d'ambiente OPENAI_API_KEY non è stata trovata. "            "Impostala eseguendo 'export OPENAI_API_KEY=...' e riprova.",            exits=1,        )    msg.text(f"Caricamento della configurazione da {config_path}", show=verbose)    nlp = assemble(        config_path    )    doc = nlp(text)    msg.text(f"Citazione: {doc.text}")    msg.text(f"Contesto: {doc._.context}")if __name__ == "__main__":    typer.run(run_pipeline)

Esegui:

python3 run_pipeline.py "Dobbiamo bilanciare il consumo vistoso con il capitalismo consapevole." ./config.cfg>>> Citazione: Dobbiamo bilanciare il consumo vistoso con il capitalismo consapevole. Contesto: Etica aziendale.

Se desideri cambiare il prompt, basta creare un altro file Jinja e creare un task my_namespace.QuoteContextExtractTask.v2 nello stesso modo in cui abbiamo creato il primo. Se desideri cambiare la temperatura, basta modificare il parametro nel file config.cfg. Bello, vero?

Pensieri finali

La capacità di gestire le richieste REST di OpenAI e il suo approccio semplice per memorizzare e versionare i prompt sono le mie cose preferite di spacy-llm. Inoltre, la libreria offre una Cache per memorizzare prompt e risposte per documento, un metodo per fornire esempi per prompt a poche iterazioni e una funzionalità di registrazione, tra le altre cose.

Puoi dare un’occhiata all’intero codice di oggi qui: https://github.com/dmesquita/spacy-llm-elegant-prompt-versioning.

Come sempre, grazie per la lettura!