# OpenAI Function Call

OpenAI's neue Funktion-Aufruf-API hat alles verändert, einschließlich MyScale.

Neue OpenAI-Veröffentlichung

OpenAI hat gerade viele Updates veröffentlicht, darunter ein günstigeres 16K-Context GPT-3.5 Turbo und mehr verfügbare GPT-4-Modelle. Wir haben auch eine neue API-Schnittstelle namens Funktionsaufruf (opens new window) erhalten, die ein leistungsstarkes Werkzeug für Entwickler sein kann.

Der Funktionsaufruf ändert die Art und Weise, wie wir mit Eingabeaufforderungen arbeiten. Es gibt bereits viele gute Versuche, sowohl strukturierte Abfragen als auch Vektor-Suche zu automatisieren, zum Beispiel LangChain's SQLChain (opens new window) und self-query retrievers (opens new window). Fast alle diese Ansätze benötigen große Eingabeaufforderungen, was bedeutet, dass sie teuer sind und schwer anzupassen sind. Der neue Funktionsaufruf zur Vervollständigung von OpenAI spart eine Menge Tokens für die Eingabeaufforderung - Sie müssen nicht mehrere lange Beispiele verwenden, um die Verwendung einer Funktion zu demonstrieren.

Wir haben festgestellt, dass der neu veröffentlichte GPT-3.5 (gpt-3.5-turbo-0613) gut mit dem neuen Funktionsaufruf funktioniert. Mit nur wenigen Versuchen konnten wir eine Eingabeaufforderung entwickeln, die dem selbstabfragenden Retriever von LangChain entspricht. Sie müssen keine Operatoren und Vergleichsoperatoren definieren, sondern nur eine Eingabeaufforderung verwenden. Sie können sehen, wie wir das auf GitHub (opens new window) erreicht haben.

# Bevor Sie beginnen...

  • Sie müssen einige Abhängigkeiten installieren:
pip3 install langchain openai clickhouse-connect

Sie wissen nicht wie? Schauen Sie sich unser offizielles Dokument zur Cluster-Verwaltung an.

# Schauen Sie sich die Eingabeaufforderung an

Für die Einfachheit haben wir LangChain's Vektorstores und einige Hilfsfunktionen aus den selbstabfragenden Retrievern wiederverwendet.

# Erstellen Sie einen Vektorstore und fügen Sie Daten ein

Wir stellen Ihnen Metadaten und Zusammenfassungen von 8 Artikeln (opens new window) von ArXiv zur Verfügung (Vielen Dank an arXiv (opens new window) für die Nutzung seiner Open-Access-Interoperabilität). Weitere Daten finden Sie in unserem AWS-Speicher (opens new window).

Jetzt zum Code:

import json
from langchain.schema import Document
from langchain.vectorstores import MyScale
from langchain.embeddings import HuggingFaceEmbeddings
def str2doc(_str):
    j = json.loads(_str)
    return Document(page_content=j['abstract'], metadata=j['metadata'])
with open('func_call_data.jsonl') as f:
    docs = [str2doc(l) for l in f.readlines()]
vectorstore = MyScale.from_documents(
    documents=docs, embedding=HuggingFaceEmbeddings())

# Metadatenspalten definieren

Hier haben wir _format_attribute_info von LangChain ausgeliehen. Es hilft uns, diese AttributeInfo in einfache Zeichenketten umzuwandeln, die besser zu den Eingabeaufforderungen passen.

Sie sind eingeladen, dies zu ändern, um mit mehr Datentypen und Funktionen zu experimentieren. Sie können Spalten mit Funktionen definieren, z.B. length(metadata.categories) bezieht sich auf die Länge dieser Kategorie. Versuchen Sie, weitere interessante Funktionen in der ClickHouse-Dokumentation (opens new window) zu finden, die wir nativ zusammen mit der Vektor-Suche unterstützen. Und vergessen Sie nicht, unserem Discord (opens new window) beizutreten, um Ihre neuen Erkenntnisse oder Gedanken zu teilen. Wir sind sehr gespannt darauf, sie mit Ihnen gemeinsam zu verbessern!

from langchain.chains.query_constructor.base import _format_attribute_info, AttributeInfo
metadata_field_info=[
    AttributeInfo(
        name="metadata.pubdate",
        description="Das Datum, an dem der Artikel veröffentlicht wurde. Verwenden Sie `parseDateTime32BestEffort()`, um Zeitstempel im Zeichenkettenformat in ein vergleichbares Format zu konvertieren.", 
        type="timestamp", 
    ),
    AttributeInfo(
        name="metadata.authors",
        description="Liste der Autorennamen", 
        type="list[string]", 
    ),
    AttributeInfo(
        name="metadata.title",
        description="Titel des Artikels", 
        type="string", 
    ),
    AttributeInfo(
        name="text",
        description="Zusammenfassung des Artikels", 
        type="string", 
    ),
    AttributeInfo(
        name="metadata.categories",
        description="ArXiv-Kategorien für diesen Artikel",
        type="list[string]"
    ),
    AttributeInfo(
        name="length(metadata.categories)",
        description="Länge der ArXiv-Kategorien für diesen Artikel",
        type="int"
    ),
]
formated = _format_attribute_info(metadata_field_info)

# Funktionsaufruf zur Konstruktion der gefilterten Vektor-Suche

Ähnlich wie bei den selbstabfragenden Retrievern gibt unser Funktionsaufruf-basierter Abfrager auch drei Argumente aus, um die Schnittstelle des Vektorstores aufzurufen:

  • query: Abfragezeichenkette, die später in Einbettungen umgewandelt wird.
  • where_str: Ein generischer WHERE-String zur Durchführung der Filterung. Aber ohne das Schlüsselwort WHERE.
  • limit: Es werden die obersten limit Elemente zurückgegeben. Standardmäßig auf 4 festgelegt.

So arbeiten wir mit dem Funktionsaufruf von OpenAI:

import openai
# Einfach, oder?
query = "Was ist ein Bayes'sches Netzwerk?"
# Lassen Sie uns einige Einschränkungen hinzufügen...
query += "Bitte verwenden Sie Artikel, die nach Februar 2013 veröffentlicht wurden "
# Noch mehr? Schlüsselwortfilter.
query += "und deren Zusammenfassung `künstliche` enthält "
# Das ist viel besser! Cross-Modale Artikel...
query += "mit mehr als 2 Kategorien "
# Ich bin ein CV-Typ, ich möchte nur einige CV-Artikel
query += "und `cs.CV` muss in seiner Kategorie enthalten sein."
# Jetzt die strukturierte Eingabeaufforderung:
completion = openai.ChatCompletion.create(
    # wir haben das neue gpt-Modell verwendet
    model="gpt-3.5-turbo-0613",
    # Temperatur auf 0 gesetzt, um stabiles Verhalten zu gewährleisten
    temperature=0,
    # Jetzt der Funktionsaufruf:
    # Sie müssen:
    # 1. einen Namen für die Funktion angeben.
    # 2. eine Beschreibung für Ihre Funktion angeben
    # 3. Parameterdefinitionen in Namen und Typen
    functions=[{"name": "to_structued_sql",
                "description":
                ("Konvertiert die Abfrage in eine Abfragezeichenkette und "
                 "einen WHERE-String zur Filterung dieser Abfrage. "
                 "Wenn Sie überprüfen möchten, ob Elemente in einer Liste enthalten sind, "
                 "verwenden Sie bitte `has(column, element)`."),
                "parameters": {"type": "object",
                               "properties": {
                                   "query": {"type": "string"},
                                   "where_str": {"type": "string", },
                                   "limit": {
                                       "type": "integer",
                                       "description": "Standardmäßig 4"}
                               },
                               "required": ["subject", "where_str", "limit"]
                               }
                },],
    # Der Name der Funktion, die aufgerufen werden soll, wenn 'auto' ist, entscheidet das Modell selbst
    function_call="auto",
    # Hier ist unser alter Freund: Eingabeaufforderung für Chat-Vervollständigung...
    # Sie können einige Regeln festlegen, um das Verhalten des Modells einzuschränken.
    messages=[
        {
            "role": "system",
            "content": ("Sie müssen `metadata` bereitstellen, um SQL zu konstruieren. "
                        "Ich werde `parseDateTime32BestEffort()` verwenden, um "
                        "Zeitstempel im Zeichenkettenformat "
                        "in ein vergleichbares Format zu konvertieren."),
        },
        {
            "role": "user",
            "content": f"Metadaten: {formated}"
        },
        {
            "role": "system",
            "content": "Jetzt können Sie Ihre Abfrage eingeben",
        },
        {
            "role": "user",
            "content": query
        },
    ],
)

Dann suchen!

Dies ist genau dasselbe im Vergleich zu LangChain selbstabfragenden Retrievern. Und es ist flexibler - es kann beliebige SQL... und sogar benutzerdefinierte Funktionen schreiben. Es gibt keine Begrenzung, also erstellen Sie sich einen Fall zum Ausführen!

ret = vectorstore.similarity_search(**search_kwargs)
for r in ret:
    print(r)

# Tipps zur Verwendung von Funktionsaufrufen

Hier sind einige Tipps zum Funktionsaufruf:

  1. Menschlich lesbare Namen liefern LLMs mehr Informationen, das gilt sowohl für die Verwendung dieses neuen Funktionsaufrufs als auch für traditionelle Eingabeaufforderungen.
  2. Wenn Sie Regeln für eine Spalte festlegen möchten, z.B. Typumwandlung beim Vergleich mit anderen Werten in unserem Beispiel, tun Sie dies bitte mit der Spaltendefinition. Regeln sollten nahe an den zugehörigen Daten liegen.
  3. LLMs neigen dazu, die Zuordnungsfunktion zwischen den Daten, mit denen sie vertraut sind, nachzuahmen. Wie in dieser Demo sind sowohl natürliche Sprache als auch SQL LLMs recht geläufig, daher ist das Ergebnis über den Erwartungen.

Wir glauben, dass Sie noch mehr darüber entdecken können! Warum teilen Sie Ihre Erkenntnisse nicht mit MyScale auf Discord (opens new window)?

# Am Ende

Wir glauben, dass dies die Zukunft dessen ist, wie Menschen oder intelligente Systeme mit Vektor-Datenbanken arbeiten. Wir sind sehr aufgeregt über solche Upgrades und neuen Möglichkeiten, ein skalierbareres und stabileres AGI-System zu entwickeln. 🚀 Wir möchten sehen, wie MyScale Ihnen beim Wachstum und Erfolg helfen kann, und wir werden das wahr machen! Treten Sie noch heute unserem Discord (opens new window) bei und erhalten Sie eine herzliche Umarmung! 🤗 🤗