Vereinheitlichte API revolutioniert den Tool-Einsatz in KI-Modellen

Kategorien:
No items found.
Freigegeben:
August 15, 2024

Einheitliche Schnittstelle für Tool-Nutzung: Ein Meilenstein in der KI-Entwicklung

Am 12. August 2024 verkündete Matthew Carrigan von Hugging Face eine bedeutende Neuerung: Eine einheitliche API für die Nutzung von Tools über verschiedene Modelle hinweg. Diese API unterstützt Modelle von MistralAI, AIatMeta, Cohere, NousResearch und weiteren Anbietern. Diese Entwicklung ermöglicht es, einheitliche, portable Codes zu verwenden, um Tool-Fähigkeiten zu diesen Modellen hinzuzufügen.

Einführung

Die Nutzung von Tools ist ein faszinierendes Feature – jeder hält es für großartig, aber die meisten haben es noch nicht selbst ausprobiert. Konzeptuell ist es sehr einfach: Man gibt einem großen Sprachmodell (LLM) einige Tools (aufrufbare Funktionen), und es kann entscheiden, diese zu verwenden, um auf Benutzeranfragen zu reagieren. Vielleicht gibt man ihm einen Taschenrechner, damit es sich nicht auf seine internen, unzuverlässigen Rechenfähigkeiten verlassen muss. Vielleicht lässt man es im Internet suchen oder den Kalender einsehen, oder man gewährt ihm (nur lesenden!) Zugriff auf eine Firmendatenbank, damit es Informationen abrufen oder technische Dokumentationen durchsuchen kann.

Die Tool-Nutzung überwindet viele der Kernbeschränkungen von LLMs. Viele LLMs sind flüssig und gesprächig, aber oft ungenau bei Berechnungen und Fakten und unscharf in Bezug auf spezifische Details zu Nischenthemen. Sie wissen nichts über Ereignisse nach ihrem Trainingsstichtag. Sie sind Generalisten; sie kommen ohne Wissen über den Benutzer oder dessen Arbeitsplatz ins Gespräch, abgesehen von dem, was ihnen im Systemnachricht mitgeteilt wurde. Tools bieten ihnen Zugang zu strukturierten, spezifischen, relevanten und aktuellen Informationen, die ihnen sehr helfen können, wirklich hilfreiche Partner zu werden, anstatt nur faszinierende Neuheiten zu sein.

Herausforderungen bei der Implementierung von Tool-Nutzung

Die Probleme entstehen jedoch, wenn man versucht, die Tool-Nutzung tatsächlich zu implementieren. Die Dokumentation ist oft spärlich, inkonsistent und sogar widersprüchlich – und das gilt sowohl für geschlossene APIs als auch für offene Modelle! Obwohl die Tool-Nutzung theoretisch einfach ist, wird sie in der Praxis oft zum Albtraum: Wie übergibt man Tools an das Modell? Wie stellt man sicher, dass die Tool-Prompts den Formaten entsprechen, mit denen es trainiert wurde? Wenn das Modell ein Tool aufruft, wie integriert man das in den Chat? Wenn man versucht hat, die Tool-Nutzung zu implementieren, hat man wahrscheinlich festgestellt, dass diese Fragen überraschend knifflig sind und die Dokumentation nicht immer vollständig und hilfreich war.

Schlimmer noch, verschiedene Modelle können sehr unterschiedliche Implementierungen der Tool-Nutzung haben. Selbst auf der grundlegendsten Ebene der Definition der verfügbaren Tools erwarten einige Anbieter JSON-Schemata, während andere Python-Funktionsheader erwarten. Selbst unter denen, die JSON-Schemata erwarten, unterscheiden sich kleine Details oft und erzeugen große API-Inkompatibilitäten. Dies schafft viel Reibung und vertieft generell die Verwirrung der Benutzer. Was können wir also dagegen tun?

Chat-Templating

Engagierte Fans des Hugging Face Cinematic Universe werden sich erinnern, dass die Open-Source-Community in der Vergangenheit vor einer ähnlichen Herausforderung stand, als es um Chat-Modelle ging. Chat-Modelle verwenden Kontrolltokens wie <|start_of_user_turn|> oder <|end_of_message|>, um dem Modell mitzuteilen, was im Chat vor sich geht, aber verschiedene Modelle wurden mit völlig unterschiedlichen Kontrolltokens trainiert, was bedeutete, dass Benutzer spezifischen Formatierungscode für jedes Modell schreiben mussten, das sie verwenden wollten. Damals war das ein großes Ärgernis.

Unsere Lösung dafür waren Chat-Templates – im Wesentlichen kamen Modelle mit einem kleinen Jinja-Template, das Chats mit dem richtigen Format und den richtigen Kontrolltokens für jedes Modell renderte. Chat-Templates bedeuteten, dass Benutzer Chats in einem universellen, modellunabhängigen Format schreiben konnten, wobei sie sich darauf verlassen konnten, dass die Jinja-Templates jede modell-spezifische Formatierung übernehmen.

Der offensichtliche Ansatz zur Unterstützung der Tool-Nutzung bestand also darin, Chat-Templates zu erweitern, um auch Tools zu unterstützen. Und genau das haben wir getan, aber Tools schufen viele neue Herausforderungen für das Templating-System. Lassen Sie uns diese Herausforderungen und deren Lösungen durchgehen. Dabei werden Sie hoffentlich ein tieferes Verständnis dafür gewinnen, wie das System funktioniert und wie Sie es für sich nutzen können.

Tools an ein Chat-Template übergeben

Unser erstes Kriterium bei der Gestaltung der Tool-Nutzung-API war, dass es intuitiv sein sollte, Tools zu definieren und an das Chat-Template zu übergeben. Wir stellten fest, dass die meisten Benutzer zuerst ihre Tool-Funktionen schrieben und dann überlegten, wie sie Tool-Definitionen daraus generieren und an das Modell übergeben konnten. Dies führte zu einem offensichtlichen Ansatz: Was wäre, wenn Benutzer einfach Funktionen direkt an das Chat-Template übergeben könnten und es die Tool-Definitionen für sie generieren würde?

Das Problem dabei ist jedoch, dass „Funktionen übergeben“ eine sehr sprachspezifische Sache ist, und viele Leute greifen über JavaScript oder Rust auf Chat-Modelle zu, anstatt über Python. Also fanden wir einen Kompromiss, von dem wir glauben, dass er das Beste aus beiden Welten bietet: Chat-Templates erwarten, dass Tools als JSON-Schema definiert werden, aber wenn man Python-Funktionen an das Template übergibt, werden diese automatisch in ein JSON-Schema umgewandelt. Dies ergibt eine schöne, saubere API:

def get_current_temperature(location: str):
    """
    Gets the temperature at a given location.

    Args:
        location: The location to get the temperature for
    """
    return 22.0  # bug: Sometimes the temperature is not 22. low priority

tools = [get_current_temperature]    

chat = [
    {"role": "user", "content": "Hey, what's the weather like in Paris right now?"}
]

tool_prompt = tokenizer.apply_chat_template(
    chat, 
    tools=tools,
    add_generation_prompt=True,
    return_tensors="pt"
)

Intern wird die Funktion get_current_temperature in ein vollständiges JSON-Schema erweitert. Wenn man das generierte Schema sehen möchte, kann man die Funktion get_json_schema verwenden:

from transformers.utils import get_json_schema

get_json_schema(get_current_weather)
{
    "type": "function",
    "function": {
        "name": "get_current_temperature",
        "description": "Gets the temperature at a given location.",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "The location to get the temperature for"
                }
            },
            "required": [
                "location"
            ]
        }
    }
}

Wenn man manuelle Kontrolle bevorzugt oder in einer anderen Sprache als Python programmiert, kann man JSON-Schemata wie diese direkt an das Template übergeben. Wenn man jedoch in Python arbeitet, kann man den direkten Umgang mit JSON-Schemata vermeiden. Alles, was man tun muss, ist, seine Tool-Funktionen mit klaren Namen, genauen Typ-Hinweisen und vollständigen Docstrings zu definieren, einschließlich Argument-Docstrings, da all dies verwendet wird, um das JSON-Schema zu generieren, das vom Template gelesen wird. Vieles davon ist ohnehin gute Python-Praxis, und wenn man ihr folgt, wird man feststellen, dass keine zusätzliche Arbeit erforderlich ist – die Funktionen sind bereits als Tools nutzbar!

Denken Sie daran: Genaue JSON-Schemata, ob aus Docstrings und Typ-Hinweisen generiert oder manuell spezifiziert, sind entscheidend dafür, dass das Modell versteht, wie es Ihre Tools verwenden kann. Das Modell wird den Code innerhalb Ihrer Funktionen nie sehen, aber es wird die JSON-Schemata sehen. Je sauberer und genauer sie sind, desto besser!

Tool-Aufrufe zum Chat hinzufügen

Ein Detail, das oft von Benutzern (und der Modell-Dokumentation 😬) übersehen wird, ist, dass ein Modell, wenn es ein Tool aufruft, tatsächlich zwei Nachrichten zur Chat-Historie hinzufügen muss. Die erste Nachricht ist der Assistent, der das Tool aufruft, und die zweite ist die Tool-Antwort, die Ausgabe der aufgerufenen Funktion.

Sowohl Tool-Aufrufe als auch Tool-Antworten sind notwendig – denken Sie daran, dass das Modell nur weiß, was in der Chat-Historie steht, und es wird nicht in der Lage sein, eine Tool-Antwort zu verstehen, wenn es den Aufruf, den es gemacht hat, und die Argumente, die es übergeben hat, um diese Antwort zu erhalten, nicht sehen kann. „22“ allein ist nicht sehr informativ, aber es ist sehr hilfreich, wenn man weiß, dass die vorhergehende Nachricht get_current_temperature("Paris, France") war.

Dies ist ein Bereich, der zwischen verschiedenen Anbietern extrem unterschiedlich sein kann, aber der Standard, den wir festgelegt haben, ist, dass Tool-Aufrufe ein Feld von Assistenten-Nachrichten sind, etwa so:

message = {
    "role": "assistant",
    "tool_calls": [
        {
            "type": "function",
             "function": {
                 "name": "get_current_temperature", 
                 "arguments": {
                     "location": "Paris, France"
                }
            }
        }
    ]
}
chat.append(message)

Tool-Antworten zum Chat hinzufügen

Tool-Antworten sind viel einfacher, insbesondere wenn Tools nur eine einzelne Zeichenkette oder Zahl zurückgeben.

message = {
    "role": "tool", 
    "name": "get_current_temperature", 
    "content": "22.0"
}
chat.append(message)

Tool-Nutzung in der Praxis

Lassen Sie uns den bisherigen Code nehmen und ein vollständiges Beispiel für das Aufrufen von Tools erstellen. Wenn Sie Tools in Ihren eigenen Projekten verwenden möchten, empfehlen wir, mit dem hier gezeigten Code zu experimentieren – versuchen Sie, ihn selbst auszuführen, Tools hinzuzufügen oder zu entfernen, Modelle auszutauschen und Details anzupassen, um ein Gefühl für das System zu bekommen. Diese Vertrautheit wird die Implementierung der Tool-Nutzung in Ihrer Software erheblich erleichtern! Um dies zu erleichtern, ist dieses Beispiel auch als Notebook verfügbar.

Erstens richten wir unser Modell ein. Wir verwenden Hermes-2-Pro-Llama-3-8B, weil es klein, leistungsfähig, ungated und toolfähig ist. Für komplexere Aufgaben könnten Sie jedoch bessere Ergebnisse erzielen, wenn Sie ein größeres Modell verwenden!

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

checkpoint = "NousResearch/Hermes-2-Pro-Llama-3-8B"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForCausalLM.from_pretrained(checkpoint, torch_dtype=torch.bfloat16, device_map="auto")

Als nächstes richten wir unser Tool und den Chat ein, den wir verwenden möchten. Lassen Sie uns das obige Beispiel get_current_temperature verwenden:

def get_current_temperature(location: str):
    """
    Gets the temperature at a given location.

    Args:
        location: The location to get the temperature for
    """
    return 22.0

tools = [get_current_temperature]

chat = [
    {"role": "user", "content": "Hey, what's the weather like in Paris right now?"}
]

tool_prompt = tokenizer.apply_chat_template(
    chat, 
    tools=tools,
    add_generation_prompt=True,
    return_tensors="pt"
)

Fazit

Die Einführung einer einheitlichen API für die Tool-Nutzung über verschiedene Modelle hinweg ist ein bedeutender Schritt in der Entwicklung von KI. Diese API beseitigt viele der Hindernisse, die bisher die Implementierung von Tools erschwert haben, und macht es einfacher, diese leistungsstarken Funktionen in einer Vielzahl von Anwendungen zu nutzen. Mit der neuen API und den erweiterten Chat-Templates können Entwickler nun effizienter arbeiten und die Fähigkeiten ihrer Modelle erweitern, ohne sich mit den komplexen und oft inkonsistenten Details der Tool-Integration auseinandersetzen zu müssen.

Quellen

- https://twitter.com/carrigmat/status/1822995378767007995 - https://huggingface.co/blog/unified-tool-use - https://twitter.com/huggingface?lang=kn - https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.1/discussions/26 - https://huggingface.co/mistralai/Mistral-7B-v0.1 - https://www.youtube.com/watch?v=rU3nkr0YX7U - https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.3 - https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.3/discussions/38
Was bedeutet das?