Anpassen von LLM

Im aktuellen Bereich der KI-Modelle gibt es eine Vielzahl von Modellen, und die offizielle Integration von LangChain deckt nicht alle Modelle ab. Manchmal müssen Sie möglicherweise ein Modell anpassen und in das LangChain-Framework integrieren.

In diesem Kapitel wird erläutert, wie Sie einen benutzerdefinierten LLM-Wrapper erstellen können, um es Ihnen zu erleichtern, Ihr eigenes Modell oder Modelle, die nicht von LangChain unterstützt werden, zu verwenden.

In LangChain können Sie, wenn Sie Ihr eigenes LLM oder einen anderen Wrapper als den von LangChain unterstützten verwenden möchten, einen benutzerdefinierten LLM-Wrapper erstellen. Ein benutzerdefinierter LLM muss nur zwei erforderliche Methoden implementieren:

  • Eine _call-Methode, die einen String als Eingabe und optionale Stoppwörter entgegennimmt und einen String zurückgibt, der die Modellaufruf-Implementierung in der _call-Methode darstellt.
  • Ein _llm_type-Attribut, das einen String zurückgibt, der den Modellnamen repräsentiert und ausschließlich für Protokollierungszwecke verwendet wird.

Neben den erforderlichen Methoden kann ein benutzerdefiniertes LLM auch eine optionale Methode implementieren:

  • Ein _identifying_params-Attribut, das verwendet wird, um die Klasse auszudrucken. Es sollte ein Wörterbuch zurückgeben.

Implementieren eines einfachen benutzerdefinierten LLM

Lassen Sie uns einen sehr einfachen benutzerdefinierten LLM implementieren, der nur die ersten n Zeichen der Eingabe zurückgibt.

from typing import Any, List, Mapping, Optional

from langchain_core.callbacks.manager import CallbackManagerForLLMRun
from langchain_core.language_models.llms import LLM

class CustomLLM(LLM):
    n: int

    @property
    def _llm_type(self) -> str:
        return "benutzerdefiniert"

    def _call(
        self,
        prompt: str,
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> str:
        if stop is not None:
            raise ValueError("Stopp-Wörter sind nicht erlaubt.")
        return prompt[: self.n]

    @property
    def _identifying_params(self) -> Mapping[str, Any]:
        """Die Identifikationsparameter abrufen."""
        return {"n": self.n}

Jetzt können wir dieses benutzerdefinierte LLM genauso wie jedes andere LLM verwenden.

Verwenden des benutzerdefinierten LLM

Wir können das benutzerdefinierte LLM-Objekt instanziieren und verwenden, um zu demonstrieren, wie man das benutzerdefinierte LLM aufruft und die Ausgabe individuell anpasst.

llm = CustomLLM(n=10)
llm.invoke("Das ist eine tolle Sache")
'Das ist ein'

Außerdem können wir das LLM drucken und seine individualisierte Ausgabe anzeigen.

print(llm)
CustomLLM
Parameter: {'n': 10}

Anpassen des Chat-Modells

Hier erklären wir, wie Sie das Chat-Modell von LangChain anpassen können.

Nachrichteneingabe und -ausgabe

Im Chat-Modell stehen Nachrichten im Mittelpunkt von Eingabe und Ausgabe. Eine Nachricht ist der Inhalt, den der Benutzer eingibt, und die Antwort, die vom Modell generiert wird.

Nachrichten

Das Chat-Modell nimmt Nachrichten als Eingabe entgegen und generiert dann eine oder mehrere Nachrichten als Ausgabe. In LangChain gibt es verschiedene integrierte Nachrichtentypen, darunter:

  • SystemMessage: Wird zur Initialisierung des KI-Verhaltens verwendet, normalerweise als erste Nachricht in einer Reihe von Eingabe-Nachrichten.
  • HumanMessage: Stellt die Interaktion des Benutzers mit dem Chat-Modell dar.
  • AIMessage: Stellt Nachrichten des Chat-Modells dar, die Text oder Tool-Aufrufanforderungen sein können.
  • FunctionMessage / ToolMessage: Wird verwendet, um das Ergebnis eines Tool-Aufrufs an das Modell zurückzugeben.

Die Verwendung dieser Nachrichtentypen kann entsprechend spezifischer Anforderungen erweitert und angepasst werden, z. B. durch Anpassen basierend auf den Parametern function und tool von OpenAI.

from langchain_core.messages import (
    AIMessage,
    BaseMessage,
    FunctionMessage,
    HumanMessage,
    SystemMessage,
)

Fluent-Varianten

Alle Chat-Nachrichten haben eine fließende Variante mit Chunk in ihren Namen.

from langchain_core.messages import (
    AIMessageChunk,
    FunctionMessageChunk,
    HumanMessageChunk,
    SystemMessageChunk,
    ToolMessageChunk,
)

Diese Chunk werden beim Streamen des Chat-Modells verwendet und jeder von ihnen definiert ein akkumulatives Attribut!

Beispiel

AIMessageChunk(content="Hallo") + AIMessageChunk(content=" Welt!")

Gibt zurück

AIMessageChunk(content='Hallo Welt!')

Einfaches Chat-Modell

Die Vererbung von SimpleChatModel ermöglicht die schnelle Implementierung eines einfachen Chat-Modells.

Obwohl es möglicherweise nicht alle für ein Chat-Modell erforderlichen Funktionen enthält, ermöglicht es eine schnelle Implementierung. Wenn mehr Funktionen erforderlich sind, ist ein Übergang zum unten beschriebenen BaseChatModell möglich.

Die Vererbung von SimpleChatModel erfordert die Implementierung des folgenden Interfaces:

  • _call-Methode - Implementierung externer Modell-API-Aufrufe.

Zusätzlich können folgende Punkte spezifiziert werden:

  • _identifying_params-Attribut - zur Aufzeichnung modellspezifischer Informationen.

Optional:

  • _stream-Methode - zur Implementierung von Streaming-Ausgabe.

Basis-Chat-Modell

Die Vererbung von BaseChatModel erfordert die Implementierung der _generate-Methode und des _llm_type-Attributs. Optional ist auch die Implementierung von _stream, _agenerate, _astream und _identifying_params möglich.

Beispiel eines benutzerdefinierten Chat-Modells

In diesem Abschnitt zeigen wir die Code-Implementierung eines benutzerdefinierten Chat-Modells namens CustomChatModelAdvanced, einschließlich der Generierung von Chat-Ergebnissen, der Streaming-Ausgabe und der asynchronen Stream-Implementierung.

from typing import Any, AsyncIterator, Dict, Iterator, List, Optional

from langchain_core.callbacks import (
    AsyncCallbackManagerForLLMRun,
    CallbackManagerForLLMRun,
)
from langchain_core.language_models import BaseChatModel, SimpleChatModel
from langchain_core.messages import AIMessageChunk, BaseMessage, HumanMessage
from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
from langchain_core.runnables import run_in_executor


class CustomChatModelAdvanced(BaseChatModel):
    """Implementieren Sie ein benutzerdefiniertes Chat-Modell, das die ersten `n` Zeichen der letzten Nachricht zurückgibt."""


    n: int
    """Benutzerdefinierte Modellparameter"""

    def _generate(
        self,
        messages: List[BaseMessage],
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> ChatResult:
        """Hier wird die Modellaufruflogik implementiert, normalerweise durch Aufrufen der API eines Drittanbietermodells und dann das Ergebnis, das von der API zurückgegeben wird, in ein Format zu verpacken, das von Langchain erkannt werden kann.
        Schlüsselparametererklärung:
            messages: Eine Liste von Prompts, die aus Nachrichten zusammengesetzt sind
        """
        last_message = messages[-1]
        tokens = last_message.content[: self.n]
        message = AIMessage(content=tokens)
        generation = ChatGeneration(message=message)
        return ChatResult(generations=[generation])

    def _stream(
        self,
        messages: List[BaseMessage],
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> Iterator[ChatGenerationChunk]:
        """Implementierung von Modell-Streaming-Ausgabe, ähnlich der Methode `_generate`, aber mit der Verarbeitung von Streaming-Ausgabe."""
        last_message = messages[-1]
        tokens = last_message.content[: self.n]

        for token in tokens:
            chunk = ChatGenerationChunk(message=AIMessageChunk(content=token))

            if run_manager:
                run_manager.on_llm_new_token(token, chunk=chunk)

            yield chunk

    async def _astream(
        self,
        messages: List[BaseMessage],
        stop: Optional[List[str]] = None,
        run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> AsyncIterator[ChatGenerationChunk]:
        """Asynchrone Version der `stream`-Methode Implementierung"""
        result = await run_in_executor(
            None,
            self._stream,
            messages,
            stop=stop,
            run_manager=run_manager.get_sync() if run_manager else None,
            **kwargs,
        )
        for chunk in result:
            yield chunk

    @property
    def _llm_type(self) -> str:
        """Gibt den Tag des benutzerdefinierten Modells zurück"""
        return "echoing-chat-model-advanced"

    @property
    def _identifying_params(self) -> Dict[str, Any]:
        """Gibt benutzerdefinierte Debug-Informationen zurück"""
        return {"n": self.n}

Testen des benutzerdefinierten Chat-Modells

Lassen Sie uns das Chat-Modell testen, einschließlich der Verwendung der invoke, batch, stream-Methoden und der asynchronen Stream-Implementierung.

model = CustomChatModelAdvanced(n=3)

model.invoke([HumanMessage(content="Hallo!")])

model.invoke("Hallo")

model.batch(["Hallo", "Auf Wiedersehen"])

for chunk in model.stream("Katze"):
    print(chunk.content, end="|")

async for chunk in model.astream("Katze"):
    print(chunk.content, end="|")

async for event in model.astream_events("Katze", version="v1"):
    print(event)