Condividi tramite


Prompt engineering in .NET

In questo articolo vengono esaminati i concetti fondamentali relativi al prompt engineering, ovvero l'ingegneria delle richieste. Molti modelli di intelligenza artificiale sono basati su richieste, ovvero rispondono al testo di input dell'utente (richiesta) con una risposta generata da algoritmi predittivi (completamento). I modelli più recenti spesso supportano completamenti sotto forma di chat, con messaggi basati sui ruoli (sistema, utente, assistente) e la cronologia delle chat per conservare le conversazioni.

Usare le richieste

Si consideri questo esempio di generazione di testo in cui il prompt è l'input dell'utente e il completamento è l'output del modello:

Prompt: "Il presidente che ha avuto il mandato più breve è stato"

Completamento: "Pedro Lascurain".

Il completamento sembra corretto, ma se a usare l'app fossero degli studenti di storia degli Stati Uniti? Il mandato di 45 minuti di Pedro Lascurain è il più breve di qualsiasi altro presidente, ma riguarda il Messico. È più probabile che la ricerca degli studenti di storia degli Stati Uniti riguardi "William Henry Harrsion". Chiaramente, l'app risponde meglio alle richieste degli utenti se si fornisce un po’ di contesto.

Il prompt engineering consente di aggiungere contesto alla richiesta fornendo istruzioni, esempi e segnali in modo che il modello produca completamenti migliori.

I modelli che supportano la generazione di testo spesso non richiedono un formato specifico, ma è consigliabile organizzare le richieste in modo che istruzioni ed esempi siano ben distinguibili. I modelli che supportano le app basate su chat usano tre ruoli per organizzare i completamenti: un ruolo sistema che controlla la chat, un ruolo utente per rappresentare l'input dell'utente e un ruolo assistente per rispondere agli utenti. Dividere le richieste in messaggi per ogni ruolo:

  • I messaggi di sistema forniscono al modello istruzioni sull'assistente. Un prompt può avere un solo messaggio di sistema e deve essere il primo messaggio.
  • I messaggi per l'utente includono le richieste dell'utente e mostrano esempi e richieste passate oppure contengono istruzioni per l'assistente. Un esempio di completamento della chat deve avere almeno un messaggio utente.
  • I messaggi per l'assistente mostrano completamenti di esempio o passati e devono contenere una risposta al messaggio per l'utente precedente. I messaggi dell'assistente non sono obbligatori, ma se si includono devono essere associati a un messaggio utente per formare un esempio.

Usare le istruzioni per migliorare il completamento

Un'istruzione è un testo che indica al modello come rispondere. Un'istruzione può essere una direttiva o un imperativo:

  • Le direttive indicano al modello come comportarsi, ma non sono semplici comandi, si pensi all'impostazione del personaggio di un attore di improvvisazione: "Stai aiutando gli studenti ad apprendere la storia degli Stati Uniti, quindi parla degli Stati Uniti, a meno che non chiedano specificamente di altri paesi".
  • Gli imperativi sono comandi non ambigui che il modello deve seguire. "Traduci in tagalog":

Le direttive sono più aperte e flessibili rispetto agli imperativi:

  • È possibile combinare più direttive in un'unica istruzione.
  • Le istruzioni in genere funzionano meglio quando le si usa con esempi. Tuttavia, poiché gli imperativi sono comandi non ambigui, i modelli non hanno bisogno di esempi per comprenderli (anche se si potrebbe usare un esempio per mostrare al modello come formattare le risposte). Poiché una direttiva non indica al modello esattamente cosa fare, ogni esempio può aiutare il modello a funzionare meglio.
  • In genere è meglio suddividere un'istruzione difficile in una serie di passaggi, che è possibile fare con una sequenza di direttive. Si dovrebbe anche indicare al modello di produrre il risultato di ogni passaggio, in modo da poter effettuare facilmente modifiche granulari. Anche se è possibile suddividere l'istruzione in passaggi, è più semplice indicare al modello di farlo e di fornire il risultato di ogni passaggio. Questo approccio è denominato prompt a catena di pensieri.

Il contenuto primario e quello di supporto consentono di aggiungere contesto

È possibile fornire contenuto per aggiungere ulteriore contesto alle istruzioni.

Il contenuto primario è il testo che il modello deve elaborare con un'istruzione. Qualsiasi azione comporti l'istruzione, il modello la eseguirà sul contenuto primario per produrre un completamento.

Il contenuto di supporto è il testo a cui si fa riferimento in un'istruzione, ma che non è l'obiettivo dell'istruzione. Il modello usa il contenuto di supporto per completare l'istruzione, il che significa che il contenuto di supporto viene visualizzato anche nei completamenti, in genere come un qualche tipo di struttura (ad esempio nelle intestazioni o nelle etichette delle colonne).

Usare le etichette con i contenuti informativi per aiutare il modello a capire come usarli con le istruzioni. Non è necessario preoccuparsi troppo della precisione, ovvero che le etichette corrispondano esattamente alle istruzioni, perché il modello gestirà aspetti come la forma delle parole e l'uso delle maiuscole.

Si supponga di usare l'istruzione "Riepiloga i successi presidenziali degli Stati Uniti" per produrre un elenco. Il modello può organizzarlo e ordinarlo in vari modi. Ma cosa succede se si vuole che l'elenco raggruppi i risultati in base a una serie specifica di categorie? Usare i contenuti di supporto per aggiungere queste informazioni all'istruzione.

Modificare l'istruzione in modo che il modello raggruppi per categorie e aggiunga contenuti di supporto che specifichino tali categorie:

prompt = """
Instructions: Summarize US Presidential accomplishments, grouped by category.
Categories: Domestic Policy, US Economy, Foreign Affairs, Space Exploration.
Accomplishments: 'George Washington
- First president of the United States.
- First president to have been a military veteran.
- First president to be elected to a second term in office.
- Received votes from every presidential elector in an election.
- Filled the entire body of the United States federal judges; including the Supreme Court.
- First president to be declared an honorary citizen of a foreign country, and an honorary citizen of France.
John Adams ...' ///Text truncated
""";

Usare gli esempi come guida per il modello

Un esempio è un testo che mostra al modello come rispondere fornendo un esempio di input utente e di output del modello. Il modello usa esempi per dedurre cosa includere nelle compilazioni. Gli esempi possono essere inseriti prima o dopo le istruzioni di un prompt ingegnerizzato, ma i due non devono essere intervallati.

Un esempio inizia con una richiesta e può facoltativamente includere un completamento. Un completamento in un esempio non deve necessariamente includere la risposta testuale, ma può contenere solo una parola formattata, il primo punto elenco in un elenco non ordinato o qualcosa di simile per indicare come deve iniziare ogni completamento.

Gli esempi vengono classificati come apprendimento zero-shot o apprendimento few-shot in base al fatto che contengano completamenti verbatim.

  • Gli esempi di apprendimento zero-shot includono un prompt senza completamento verbatim. Questo approccio consente di testare le risposte di un modello senza fornire dati di output di esempio. Le richieste zero-shot possono restituire completamenti che includono segnali, ad esempio per indicare che il modello deve produrre un elenco ordinato che include "1". come completamento.
  • Gli esempi di apprendimento few-shot includono diverse coppie di prompt con completamenti verbatim. L'apprendimento few-shot può modificare il comportamento del modello aggiungendo le conoscenze esistenti.

Informazioni sui segnali

Un segnale è un testo che comunica la struttura o il formato di output desiderato. Come un'istruzione, un segnale non viene elaborato dal modello come se fosse un input dell'utente. Ad esempio, un segnale mostra al modello ciò che si vuole, invece di dirgli cosa fare. È possibile aggiungere tutti i segnali necessari, in modo da eseguire l'iterazione per ottenere il risultato desiderato. I segnali vengono usati con un'istruzione o un esempio e devono trovarsi alla fine del prompt.

Si supponga di usare un'istruzione che indichi al modello di produrre un elenco di successi presidenziali per categoria, insieme a un contenuto di supporto che indichi al modello quali categorie usare. Si decide che il modello deve produrre un elenco annidato con tutte le maiuscole per le categorie, con i risultati di ogni presidente in ogni categoria elencati su una riga che inizia con il nome, con i presidenti elencati in ordine cronologico. Dopo le istruzioni e i contenuti di supporto, si possono aggiungere tre segnali per mostrare al modello come strutturare e formattare l'elenco:

prompt = """
Instructions: Summarize US Presidential accomplishments, grouped by category.
Categories: Domestic Policy, US Economy, Foreign Affairs, Space Exploration.
Accomplishments: George Washington
First president of the United States.
First president to have been a military veteran.
First president to be elected to a second term in office.
First president to receive votes from every presidential elector in an election.
First president to fill the entire body of the United States federal judges; including the Supreme Court.
First president to be declared an honorary citizen of a foreign country, and an honorary citizen of France.
John Adams ...  /// Text truncated

DOMESTIC POLICY
- George Washington: 
- John Adams:
""";
  • CRITERI INTERNI mostra il modello con cui si vuole avviare ogni gruppo con la categoria in maiuscolo.
  • - George Washington: mostra il modello con cui iniziare ogni sezione con i risultati di George Washington elencati su una riga.
  • - John Adams: mostra il modello che deve elencare i presidenti rimanenti in ordine cronologico.

Esempio di richiesta con .NET

In .NET sono disponibili vari strumenti per creare richieste e chattare con diversi modelli di intelligenza artificiale. Usare Semantic Kernel per connettersi a una vasta gamma di modelli e servizi di intelligenza artificiale, oltre ad altri SDK, ad esempio la libreria OpenAI .NET ufficiale. Semantic Kernel include strumenti per creare richieste con ruoli diversi e mantenere la cronologia delle chat, oltre a numerose altre funzionalità.

Osservare l'esempio di codice seguente:

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;

// Create a kernel with OpenAI chat completion
#pragma warning disable SKEXP0010
Kernel kernel = Kernel.CreateBuilder()
                    .AddOpenAIChatCompletion(
                        modelId: "phi3:mini",
                        endpoint: new Uri("http://localhost:11434"),
                        apiKey: "")
                    .Build();

var aiChatService = kernel.GetRequiredService<IChatCompletionService>();
var chatHistory = new ChatHistory();
chatHistory.Add(
    new ChatMessageContent(AuthorRole.System, "You are a helpful AI Assistant."));

while (true)
{
    // Get user prompt and add to chat history
    Console.WriteLine("Your prompt:");
    chatHistory.Add(new ChatMessageContent(AuthorRole.User, Console.ReadLine()));

    // Stream the AI response and add to chat history
    Console.WriteLine("AI Response:");
    var response = "";
    await foreach (var item in
        aiChatService.GetStreamingChatMessageContentsAsync(chatHistory))
    {
        Console.Write(item.Content);
        response += item.Content;
    }
    chatHistory.Add(new ChatMessageContent(AuthorRole.Assistant, response));
    Console.WriteLine();
}

Nel codice precedente vengono forniti esempi dei concetti seguenti:

  • Crea un servizio di cronologia delle chat per richiedere al modello di intelligenza artificiale i completamenti in base ai ruoli degli autori.
  • Configura l'intelligenza artificiale con un messaggio AuthorRole.System.
  • Accetta l'input dell'utente per consentire tipi diversi di richieste nel contesto di AuthorRole.User.
  • Trasmette in modo asincrono il completamento dall'intelligenza artificiale per offrire un'esperienza di chat dinamica.

Estendere le tecniche di prompt engineering

Per rendere più efficaci le richieste, è possibile usare due tecniche di prompt engineering avanzate trattate in modo approfondito nei rispettivi articoli.

  • I LLM hanno dei limiti di input del token che vincolano la quantità di testo che si può inserire in un prompt. Usare gli incorporamenti e le soluzioni di database vettoriali per ridurre il numero di token necessari per rappresentare un determinato testo.
  • Gli LLM non vengono sottoposti a training sui dati a meno che non si esegua personalmente il training, il che può essere costoso e richiedere molto tempo. Usare l'architettura RAG (Retrieval Augmented Generation) per rendere i dati disponibili per un modello LLM senza eseguirne il training.