Die Kernidee des LangChain Agenten ist es, LLM als Gehirn zu verwenden, um automatisch zu denken, Entscheidungen zu treffen und verschiedene Aktionen auszuführen, um letztendlich unsere Ziel-Aufgaben zu erfüllen.

Hinweis: Aus der Entwicklungsperspektive bedeutet dies, dass wir im Voraus verschiedene APIs entwickeln und dann den Agenten mit einer Aufgabe betrauen, um LLM zu analysieren, welche API aufgerufen werden soll, um die Aufgabe zu erledigen.

Um das Problem, das LangChain Agent lösen möchte, besser zu verstehen, betrachten wir ein Beispiel.

Zum Beispiel:

Wenn wir untersuchen möchten, ob "Docker als Produktionsbereitstellungs-Lösung verwendet werden kann", würden wir zuerst "Einführung in Docker" auf Baidu suchen, die Suchergebnisse durchsuchen und dann weiter nach "Vor- und Nachteile der Docker-Bereitstellung" suchen und die Ergebnisse durchsuchen, bis wir zu einer Schlussfolgerung gelangen.

LangChain Agent zielt darauf ab, diesen Prozess zu simulieren. Ich kann eine Reihe von Tools (z. B. Baidu-Suche, URL-Inhaltsextraktions-Tools) vorverpacken und dann dem Agenten die Ziel-Aufgabe "Kann Docker als Produktionsbereitstellungs-Lösung verwendet werden?" geben. Der Agent wird dann Aufforderungen erstellen, um LLM aufzurufen. Um diese Aufgabe zu erreichen, ist der nächste Schritt die Ausführung welcher Aktion (d. h. welches Tool aufzurufen). Die KI wird das aufzurufende Tool zurückgeben, der Code wird dieses Tool ausführen und die Ergebnisse der Tool-Ausführung an die KI zurückgeben und nach dem nächsten Schritt fragen, um welches Tool als nächstes aufgerufen werden soll. Durch Wiederholung dieses Prozesses wird die erwähnte Aufgabe abgeschlossen.

Hinweis: Seit der Veröffentlichung des GPT-Modells handelt es sich hierbei um eine explosive Fähigkeit, die es LLM ermöglicht, als Gehirn zu fungieren, aktiv zu denken und dann verschiedene von uns entwickelte APIs aufzurufen. Dies erweitert die Fähigkeiten von LLM erheblich. Derzeit ist diese Funktion noch in der experimentellen Phase und wird LLM wiederholt aufrufen, weshalb sie ziemlich viele Tokens verbraucht. Die Durchführung einer Aufgabe kann in Minuten zehntausende Tokens kosten. Wenn Sie Geld sparen möchten, wird empfohlen, LLM zunächst einfache logische Beurteilungsaufgaben durchführen zu lassen.

Kernkonzepte

Lassen Sie uns nun die zugehörigen Komponenten und Konzepte vorstellen.

Agent

Ein Agent kann als unser Assistent verstanden werden, der in unserem Namen Entscheidungen trifft. In der zugrunde liegenden Implementierung des LangChain Agenten erfolgt dies durch LLM, das die nächste Aktion (oder den API-Aufruf) bestimmt. Der ReAct-Modus beschreibt den KI-Entscheidungsprozess. Interessierte können dies weiter erkunden.

LangChain bietet mehrere Arten von Agenten für verschiedene Szenarien.

Tools

Ich denke, Tools werden besser als APIs verstanden, die mit verschiedenen funktionalen APIs vorverpackt sind, die dazu dienen, die Fähigkeiten von LLM zu erweitern. LLM bestimmt, welche spezifische API aufgerufen werden soll, um eine Aufgabe zu erledigen.

Toolkits

Toolkit bietet LLM in der Regel nicht nur ein oder zwei Tools, sondern eine Reihe von Tools, um LLM mehr Optionen bei der Erfüllung von Aufgaben zu bieten.

AgentExecutor

Der Proxy-Executor ist dafür verantwortlich, das vom LLM ausgewählte Tool (API) auszuführen. Der Pseudocode für diese Laufzeit sieht wie folgt aus:

next_action = agent.get_action(...)
while next_action != AgentFinish:
    observation = run(next_action)
    next_action = agent.get_action(..., next_action, observation)
return next_action

Obwohl der Ausführungsprozess nicht kompliziert ist, behandelt der Executor viele detaillierte Probleme, hauptsächlich:

  1. Behandlung der Situation, in der der Agent ein nicht vorhandenes Tool auswählt
  2. Behandlung von Toolfehler-Situationen
  3. Behandlung von Situationen, in denen der Agent Ausgaben erzeugt, die nicht als Tool-Aufruf gelöst werden können
  4. Debugging-Probleme.

Schnellstart

Dieser Abschnitt führt in die grundlegende Verwendung des LangChain-Agenten ein.

1. LLM laden

Lassen Sie uns zunächst das Sprachmodell (LLM) laden, das wir zur Steuerung des Agenten verwenden werden.

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

2. Tools definieren

Als nächstes definieren wir einige Tools, die vom Agenten aufgerufen werden sollen. Wir werden eine sehr einfache Python-Funktion schreiben, um die Länge des Eingabeworts zu berechnen.

from langchain.agents import tool

@tool
def get_word_length(word: str) -> int:
    """Gibt die Länge des Worts zurück."""
    return len(word)

get_word_length.invoke("abc")

Hinweis: Funktionskommentare sind sehr wichtig. Sie sagen LLM, welche Probleme es lösen kann, wenn es sie aufruft. Die Funktion get_word_length teilt LLM mit, dass es die Länge eines Wortes berechnen kann.

3

Definieren eines Satzes von Tools

tools = [get_word_length]

3. Erstellen eines Eingabeaufforderung

Lassen Sie uns nun eine Eingabeaufforderung erstellen. Da der OpenAI Function Calling für die Werkzeugnutzung optimiert wurde, benötigen wir kaum Anweisungen zur Argumentation oder Ausgabeformatierung. Wir haben nur zwei Eingabevariablen: input und agent_scratchpad. input repräsentiert die Benutzereingabefrage, und agent_scratchpad ist ein Platzhalter für die Aufrufanweisungen des Agenten, die beim Ausführen des Befehls zur Werkzeugnutzung in die Muster-Vorlage eingefügt werden.

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "Du bist ein sehr leistungsstarker Assistent, verstehst aber nicht die aktuelle Situation.",
        ),
        ("user", "{input}"),
        MessagesPlaceholder(variable_name="agent_scratchpad"),
    ]
)

4. Werkzeuge an LLM binden

Wie erfährt der Agent, welche Werkzeuge er verwenden kann?

Dies hängt von der OpenAI-Funktion zum Werkzeugaufruf ab (viele Modelle unterstützen ähnliche Funktionen). Wir müssen dem Modell nur das definierte Werkzeugaufrufformat mitteilen.

llm_with_tools = llm.bind_tools(tools)

5. Einen Agenten erstellen

Nun, da wir den vorherigen Inhalt integriert haben, können wir mit der Erstellung des Proxy-Programms fortfahren. Wir werden die letzten beiden praktischen Hilfsfunktionen importieren: eine zur Formatierung von Zwischenschritten (Proxy-Aktionen, Werkzeugausgaben) in Eingabemeldungen, die an das Modell gesendet werden können, und eine weitere zur Umwandlung von Ausgabemeldungen in Proxy-Aktionen/Endungen.

from langchain.agents.format_scratchpad.openai_tools import format_to_openai_tool_messages
from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser

agent = (
    {
        "input": lambda x: x["input"],
        "agent_scratchpad": lambda x: format_to_openai_tool_messages(x["intermediate_steps"]),
    }
    | prompt
    | llm_with_tools
    | OpenAIToolsAgentOutputParser()
)

# Definition des Agenten-Ausführers
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

Die Funktionsweise des Proxy-Programms kann durch folgendes Beispiel veranschaulicht werden:

list(agent_executor.stream({"input": "Wie viele Buchstaben hat das Wort 'Eudca'?"}))

Beispiel für Agentenausgabeprotokoll

> Neuer Agentenausführungskette betreten...

Aufruf: Ausführen von `get_word_length` mit dem Parameter `{'word': 'educa'}`

Das Wort "educa" hat 5 Buchstaben.

> Ausführungskette abgeschlossen.

Durch dieses Beispiel haben wir den gesamten Ablauf des Proxy-Programms demonstriert.

Hinzufügen von Speicherfunktionalität zum Agenten

Wenn wir möchten, dass der Agent sich an vorherige Gespräche erinnert, ist dies eigentlich ganz einfach: Wir müssen nur die vom KI zurückgegebenen Inhalte in die Eingabeaufforderung einfügen und sie zusammen an die KI senden.

Modifizierung der Eingabeaufforderungs-Vorlage

Nachfolgend ändern wir die Eingabeaufforderungs-Vorlage so ab, dass sie eine Vorlagenvariable für den Gesprächsverlauf enthält.

from langchain.prompts import MessagesPlaceholder

MEMORY_KEY = "chat_history"
prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "Du bist ein großartiger Assistent, aber nicht gut darin, die Länge von Wörtern zu berechnen.",
        ),
        MessagesPlaceholder(variable_name=MEMORY_KEY),
        ("user", "{input}"),
        MessagesPlaceholder(variable_name="agent_scratchpad"),
    ]
)

Modifizierung der Agentenprozessdefinition

Ändern Sie die Agentenprozessdefinition, um Daten zum Gesprächsverlauf für die Eingabeaufforderungsvorlage bereitzustellen, wie im folgenden Code gezeigt, und fügen Sie die Behandlung des Parameters chat_history hinzu.

agent = (
    {
        "input": lambda x: x["input"],
        "agent_scratchpad": lambda x: format_to_openai_tool_messages(
            x["intermediate_steps"]
        ),
        "chat_history": lambda x: x["chat_history"],
    }
    | prompt
    | llm_with_tools
    | OpenAIToolsAgentOutputParser()
)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
chat_history = []

input1 = "Wie viele Buchstaben hat das Wort 'educa'?"
result = agent_executor.invoke({"input": input1, "chat_history": chat_history})
chat_history.extend(
    [
        HumanMessage(content=input1),
        AIMessage(content=result["output"]),
    ]
)
agent_executor.invoke({"input": "Existiert dieses Wort wirklich?", "chat_history": chat_history})