Heim >Web-Frontend >js-Tutorial >Beherrschen des Builder-Musters: Erstellen Sie eine dynamische AI-Prompt-Generator-CLI

Beherrschen des Builder-Musters: Erstellen Sie eine dynamische AI-Prompt-Generator-CLI

Susan Sarandon
Susan SarandonOriginal
2024-11-15 05:30:031046Durchsuche

Ist Ihnen auf Ihrer Entwicklungsreise schon einmal ein Fall begegnet, in dem Sie mit komplexen Objekten zu tun hatten? Vielleicht, weil sie entweder zu viele Parameter haben, die sogar verschachtelt werden können, oder weil sie viele Bauschritte und eine komplexe Logik zum Aufbau erfordern.

Vielleicht möchten Sie ein Modul mit einer übersichtlichen und einfachen Benutzeroberfläche entwerfen, ohne jedes Mal den Erstellungscode Ihrer komplexen Objekte zerstreuen oder darüber nachdenken zu müssen!

Hier kommt das Builder-Designmuster ins Spiel!

In diesem Tutorial erklären wir alles über das Builder-Entwurfsmuster. Anschließend erstellen wir eine CLI-Node.js-Anwendung zum Generieren einer DALL-E 3-optimierten Bildgenerierungsaufforderung unter Verwendung des Builder-Entwurfsmusters .

Der endgültige Code ist in diesem Github-Repository verfügbar.

Überblick

Problem

Builder ist ein kreatives Designmuster, eine Kategorie von Designmustern, die sich mit den verschiedenen Problemen befasst, die mit der nativen Art der Objekterstellung mit dem Neuen einhergehen Schlüsselwort oder Operator.

Das Builder Design Pattern konzentriert sich auf die Lösung der folgenden Probleme:

  1. Bereitstellung einer einfachen Schnittstelle zum Erstellen komplexer Objekte: Stellen Sie sich ein tief verschachteltes Objekt mit vielen erforderlichen Initialisierungsschritten vor.

  2. Trennung des Konstruktionscodes vom Objekt selbst, was die Erstellung mehrerer Darstellungen oder Konfigurationen aus demselben Objekt ermöglicht.

Lösung

Das Builder Design Pattern löst diese beiden Probleme, indem es die Verantwortung für die Objekterstellung an spezielle Objekte namens Builder delegiert.

Das Builder-Objekt setzt das Originalobjekt zusammen und unterteilt den Erstellungsprozess in mehrere Phasen oder Schritte.

Jeder Schritt wird durch eine Methode im Builder-Objekt definiert, die eine Teilmenge der Objektattribute basierend auf einer Geschäftslogik initialisiert.

class PromptBuilder {
  private prompt: Prompt

  constructor() {
    this.reset()
  }

  reset() {
    this.prompt = new Prompt()
  }

  buildStep1() {
    this.prompt.subject = "A cheese eating a burger"
    //initialization code...
    return this
  }

  buildStep2() {
    //initialization code...
    return this
  }

  buildStep3() {
    //initialization code...
    return this
  }

  build() {
    const result = structuredClone(this.prompt) // deep clone
    this.reset()
    return result
  }
}

Client-Code: Wir müssen nur den Builder verwenden und die einzelnen Schritte aufrufen

const promptBuilder = new PromptBuilder()
const prompt1 = promptBuilder
  .buildStep1() // optional
  .buildStep2() // optional
  .buildStep3() // optional
  .build() // we've got a prompt

const prompt2 = promptBuilder
  .buildStep1() // optional
  .buildStep3() // optional
  .build() // we've got a prompt

Das typische Designmuster für Bauherren

Mastering the Builder Pattern: Create a Dynamic AI Prompt Generator CLI

Das typische Builder-Entwurfsmuster besteht aus 4 Hauptklassen:

  1. Builder: Die Builder-Schnittstelle sollte nur die Konstruktionsmethoden definieren, ohne die Methode build(), die für die Rückgabe der erstellten Entität verantwortlich ist.

  2. Concrete Builder-Klassen: Jeder konkrete Builder stellt seine eigene Implementierung der Builder Interface-Methoden bereit, sodass er seine eigene Variante des Objekts (Instanz von Produkt1 oder Produkt2 ).

  3. Client: Sie können sich den Client als den Top-Level-Konsumenten unserer Objekte, den Benutzer, der Bibliotheksmodule importiert, oder den Einstiegspunkt unserer Anwendung vorstellen.

  4. Direktor: Sogar das gleiche Builder-Objekt kann viele Varianten des Objekts erzeugen.

class PromptBuilder {
  private prompt: Prompt

  constructor() {
    this.reset()
  }

  reset() {
    this.prompt = new Prompt()
  }

  buildStep1() {
    this.prompt.subject = "A cheese eating a burger"
    //initialization code...
    return this
  }

  buildStep2() {
    //initialization code...
    return this
  }

  buildStep3() {
    //initialization code...
    return this
  }

  build() {
    const result = structuredClone(this.prompt) // deep clone
    this.reset()
    return result
  }
}

Wie Sie dem obigen Code entnehmen können, besteht ein großer Bedarf an einer Entität, die die Verantwortung für die Steuerung oder Orchestrierung der verschiedenen möglichen Kombinationssequenzen von Aufrufen der Builder-Methoden übernimmt, da jede Sequenz ein anderes resultierendes Objekt erzeugen kann.

Können wir den Prozess also weiter abstrahieren und eine noch einfachere Schnittstelle für den Client-Code bereitstellen?

Hier kommt die

Director-Klasse ins Spiel. Der Director übernimmt mehr Verantwortung vom Kunden und ermöglicht es uns, alle diese Builder-Sequenzaufrufe zu berücksichtigen und bei Bedarf wiederzuverwenden.

const promptBuilder = new PromptBuilder()
const prompt1 = promptBuilder
  .buildStep1() // optional
  .buildStep2() // optional
  .buildStep3() // optional
  .build() // we've got a prompt

const prompt2 = promptBuilder
  .buildStep1() // optional
  .buildStep3() // optional
  .build() // we've got a prompt

Kundencode


const promptBuilder = new PromptBuilder()
const prompt1 = promptBuilder.buildStep1().buildStep2().build()

const prompt2 = promptBuilder.buildStep1().buildStep3().build()

Wie Sie dem obigen Code entnehmen können, muss der Clientcode die Details zum Erstellen von prompt1 oder prompt2 nicht kennen. Es ruft einfach den Director auf, legt das richtige Builder-Objekt fest und ruft dann die makePrompt-Methoden auf.

Praktisches Szenario

Um die Nützlichkeit des Builder-Entwurfsmusters weiter zu demonstrieren, erstellen wir ein

KI-CLI-Tool zur sofortigen Generierung von Engineering-Bildernvon Grund auf.

Der Quellcode für diese CLI-App ist hier verfügbar.

Das CLI-Tool funktioniert wie folgt:

    Die CLI fordert den Benutzer auf, einen Aufforderungsstil auszuwählen: Realistische oder digitale Kunst.
  1. Dann wird der Benutzer aufgefordert, einen Betreff für seine Eingabeaufforderung einzugeben, zum Beispiel: ein Käse, der einen Burger isst.
  2. Je nach Ihrer Wahl (Digitale Kunst oder Realistisch) erstellt das CLI-Tool komplexe Eingabeaufforderungsobjekte mit vielen Konfigurationsdetails.
Für die realistische Eingabeaufforderung müssen alle folgenden Konfigurationsattribute erstellt werden.

Datei: prompts.ts


class Director {
  private builder: PromptBuilder
  constructor() {}

  setBuilder(builder: PromptBuilder) {
    this.builder = builder
  }

  makePrompt1() {
    return this.builder.buildStep1().buildStep2().build()
  }

  makePrompt2() {
    return this.builder.buildStep1().buildStep3().build()
  }
}

Datei: prompts.ts


const director = new Director()
const builder = new PromptBuilder()
director.setBuilder(builder)
const prompt1 = director.makePrompt1()
const prompt2 = director.makePrompt2()

Wie Sie hier sehen können, erfordert jeder Eingabeaufforderungstyp die Erstellung vieler komplexer Attribute, wie z. B.

artStyle , colorPalette , lightingEffect , perspective , Kameratyp , usw.

Erkunden Sie gerne alle Attributdetails, die in der Datei

enums.ts unseres Projekts definiert sind.

enums.ts


class PromptBuilder {
  private prompt: Prompt

  constructor() {
    this.reset()
  }

  reset() {
    this.prompt = new Prompt()
  }

  buildStep1() {
    this.prompt.subject = "A cheese eating a burger"
    //initialization code...
    return this
  }

  buildStep2() {
    //initialization code...
    return this
  }

  buildStep3() {
    //initialization code...
    return this
  }

  build() {
    const result = structuredClone(this.prompt) // deep clone
    this.reset()
    return result
  }
}

Der Benutzer unserer CLI-App ist sich möglicherweise nicht aller dieser Konfigurationen bewusst. Vielleicht möchten sie einfach nur ein Bild basierend auf einem bestimmten Thema wie Käse essender Burger und einem bestimmten Stil (realistische oder digitale Kunst) erstellen.

Nachdem Sie das Github-Repository geklont haben, installieren Sie die Abhängigkeiten mit dem folgenden Befehl:

const promptBuilder = new PromptBuilder()
const prompt1 = promptBuilder
  .buildStep1() // optional
  .buildStep2() // optional
  .buildStep3() // optional
  .build() // we've got a prompt

const prompt2 = promptBuilder
  .buildStep1() // optional
  .buildStep3() // optional
  .build() // we've got a prompt

Führen Sie nach der Installation der Abhängigkeiten den folgenden Befehl aus:

const promptBuilder = new PromptBuilder()
const prompt1 = promptBuilder.buildStep1().buildStep2().build()

const prompt2 = promptBuilder.buildStep1().buildStep3().build()

Sie werden aufgefordert, einen Eingabeaufforderungstyp auszuwählen: Realistisch oder Digitale Kunst. Mastering the Builder Pattern: Create a Dynamic AI Prompt Generator CLI

Dann müssen Sie den Betreff Ihrer Eingabeaufforderung eingeben. Bleiben wir beim Cheese-Eating-Burger.

Abhängig von Ihrer Auswahl erhalten Sie als Ergebnis die folgenden Textaufforderungen:

Eingabeaufforderung für einen realistischen Stil:

class Director {
  private builder: PromptBuilder
  constructor() {}

  setBuilder(builder: PromptBuilder) {
    this.builder = builder
  }

  makePrompt1() {
    return this.builder.buildStep1().buildStep2().build()
  }

  makePrompt2() {
    return this.builder.buildStep1().buildStep3().build()
  }
}

Aufforderung zum digitalen Kunststil :

const director = new Director()
const builder = new PromptBuilder()
director.setBuilder(builder)
const prompt1 = director.makePrompt1()
const prompt2 = director.makePrompt2()

Kopieren Sie die vorherigen Befehle und fügen Sie sie dann in ChatGPT ein. ChatGPT verwendet das DALL-E 3-Modell, um die Bilder zu generieren.

Realistisches Ergebnis der Bildaufforderung

Eingabeaufforderungsergebnis für digitale Kunstbilder

Mastering the Builder Pattern: Create a Dynamic AI Prompt Generator CLI

Mastering the Builder Pattern: Create a Dynamic AI Prompt Generator CLI

Denken Sie an die Komplexität der Eingabeaufforderungsparameter und das Fachwissen, das zum Erstellen jeder Art von Eingabeaufforderung erforderlich ist, ganz zu schweigen von den hässlichen Konstruktoraufrufen, die erforderlich sind.

class RealisticPhotoPrompt {
  constructor(
    public subject: string,
    public location: string,
    public timeOfDay: string,
    public weather: string,
    public camera: CameraType,
    public lens: LensType,
    public focalLength: number,
    public aperture: string,
    public iso: number,
    public shutterSpeed: string,
    public lighting: LightingCondition,
    public composition: CompositionRule,
    public perspective: string,
    public foregroundElements: string[],
    public backgroundElements: string[],
    public colorScheme: ColorScheme,
    public resolution: ImageResolution,
    public postProcessing: string[]
  ) {}
}

Haftungsausschluss: Dieser hässliche Konstruktoraufruf stellt in JavaScript kein großes Problem dar, da wir ein Konfigurationsobjekt übergeben können, bei dem alle Eigenschaften nullbar sind.

Um den Prozess der Eingabeaufforderungserstellung zu abstrahieren und unseren Code für Erweiterungen zu öffnen und für Änderungen zu schließen (O in SOLID) zu machen und die Verwendung unserer Eingabeaufforderungsgenerierungsbibliothek für unsere Bibliothekskunden nahtlos oder einfacher zu gestalten, Wir werden uns für die Implementierung des Builder-Designmusters entscheiden.

Beginnen wir mit der Deklaration der generischen Prompt Builder-Schnittstelle.

Die Schnittstelle deklariert eine Reihe von Methoden:

  1. buildBaseProperties , buildTechnicalDetails und buildArtisticElements sind die Schritte zum Erstellen einer Eingabeaufforderung für Realistic oder Digital Art.
  2. setSubject ist eine gemeinsame Methode aller unserer Prompt-Builder; Es ist selbsterklärend und wird zur Festlegung des Betreffs der Aufforderung verwendet.

builders.ts

class PromptBuilder {
  private prompt: Prompt

  constructor() {
    this.reset()
  }

  reset() {
    this.prompt = new Prompt()
  }

  buildStep1() {
    this.prompt.subject = "A cheese eating a burger"
    //initialization code...
    return this
  }

  buildStep2() {
    //initialization code...
    return this
  }

  buildStep3() {
    //initialization code...
    return this
  }

  build() {
    const result = structuredClone(this.prompt) // deep clone
    this.reset()
    return result
  }
}

builders.ts

const promptBuilder = new PromptBuilder()
const prompt1 = promptBuilder
  .buildStep1() // optional
  .buildStep2() // optional
  .buildStep3() // optional
  .build() // we've got a prompt

const prompt2 = promptBuilder
  .buildStep1() // optional
  .buildStep3() // optional
  .build() // we've got a prompt

Wie Sie aus den obigen Implementierungen ersehen können, entscheidet sich jeder Builder dafür, seine eigene Art von Eingabeaufforderung zu erstellen (die endgültigen Eingabeaufforderungsformen sind unterschiedlich) und sich dabei an dieselben Erstellungsschritte zu halten, die im PromptBuilder-Vertrag definiert sind!

Jetzt kommen wir zu unserer Klassendefinition Direktor.

director.ts

const promptBuilder = new PromptBuilder()
const prompt1 = promptBuilder.buildStep1().buildStep2().build()

const prompt2 = promptBuilder.buildStep1().buildStep3().build()

Die Klasse Director umschließt einen PromptBuilder und ermöglicht uns die Erstellung einer Eingabeaufforderungskonfiguration, die aus dem Aufruf aller Builder-Methoden von setSubject bis besteht buildArtisticElements.

Dadurch wird unser Client-Code in der Datei index.ts vereinfacht, was wir im nächsten Abschnitt sehen werden.

serializers.ts

class Director {
  private builder: PromptBuilder
  constructor() {}

  setBuilder(builder: PromptBuilder) {
    this.builder = builder
  }

  makePrompt1() {
    return this.builder.buildStep1().buildStep2().build()
  }

  makePrompt2() {
    return this.builder.buildStep1().buildStep3().build()
  }
}

Um den endgültigen Eingabeaufforderungstext auf der Terminalkonsole zu drucken, habe ich einige Serialisierungsfunktionen für Dienstprogramme implementiert.

Jetzt ist unser Code zur sofortigen Bibliotheksgenerierung fertig. Nutzen wir es in der Datei index.ts.

index.ts

const director = new Director()
const builder = new PromptBuilder()
director.setBuilder(builder)
const prompt1 = director.makePrompt1()
const prompt2 = director.makePrompt2()

Der obige Code führt die folgenden Aktionen aus:

  1. Fordern Sie den Benutzer mithilfe des Inquirer-Pakets auf, einen Eingabeaufforderungsstil und dann einen Betreff auszuwählen: getUserInput.
  2. Nachdem der Client-Code sowohl den Betreff als auch den Kunststil vom Benutzer erhalten hat, verwendet er nur zwei Komponenten aus unserer Bibliothek: den PromptBuilder und den Director.
  3. Wir beginnen mit der Instanziierung des Direktors.
  4. Dann instanziieren wir je nach ausgewähltem Eingabeaufforderungsstil den entsprechenden Builder und setzen ihn auf die Klasse Director.
  5. Schließlich rufen wir die Methode director.makePrompt mit dem ausgewählten Betreff als Argument auf, rufen die Eingabeaufforderung vom Builder ab und geben die serialisierte Eingabeaufforderung auf der Terminalkonsole aus.

Denken Sie daran: Es ist nicht möglich, die Eingabeaufforderung vom Direktor zu erhalten, da die Form der Eingabeaufforderung von jedem Builder-Typ unterschiedlich ist.

Abschluss

Das Builder-Entwurfsmuster erweist sich als hervorragende Lösung für die Erstellung komplexer Objekte mit mehreren Konfigurationen, wie unsere CLI-Anwendung zur Generierung von KI-Bildaufforderungen zeigt. Hier erfahren Sie, warum das Builder-Muster in diesem Szenario von Vorteil war:

  1. Vereinfachte Objekterstellung: Das Muster ermöglichte es uns, komplizierte RealisticPhotoPrompt- und DigitalArtPrompt-Objekte zu erstellen, ohne ihren komplexen Konstruktionsprozess dem Client-Code auszusetzen.

  2. Flexibilität: Durch die Verwendung separater Builder-Klassen für jeden Eingabeaufforderungstyp können wir problemlos neue Eingabeaufforderungstypen hinzufügen oder vorhandene ändern, ohne den Client-Code zu ändern.

  3. Code-Organisation: Das Muster trug dazu bei, die Konstruktionslogik von der Darstellung zu trennen, wodurch der Code modularer und einfacher zu warten war.

  4. Wiederverwendbarkeit: Die PromptDirector-Klasse ermöglichte es uns, denselben Konstruktionsprozess für verschiedene Arten von Eingabeaufforderungen wiederzuverwenden und so die Wiederverwendbarkeit des Codes zu verbessern.

  5. Abstraktion: Der Client-Code in index.ts blieb einfach und konzentrierte sich auf Logik auf hoher Ebene, während die Komplexität der Eingabeaufforderungskonstruktion in den Builder-Klassen abstrahiert wurde.

Kontakt

Wenn Sie Fragen haben oder etwas weiter besprechen möchten, können Sie mich gerne hier kontaktieren.

Viel Spaß beim Codieren!

Das obige ist der detaillierte Inhalt vonBeherrschen des Builder-Musters: Erstellen Sie eine dynamische AI-Prompt-Generator-CLI. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn