Erweiterte Funktionen des LangChain-Tools

Im vorherigen Abschnitt haben wir die grundlegende Verwendung von @tool kennengelernt. Dieser Abschnitt stellt erweiterte Funktionen von Tools vor: return_direct, InjectedToolCallId, ToolException und Fehlerbehandlung.


return_direct — Endergebnis direkt zurückgeben

Standardmäßig wird nach der Ausführung eines Tools das Ergebnis an das Modell zurückgegeben, das dann auf Grundlage des Tool-Ergebnisses die endgültige Antwort generiert. Manchmal ist jedoch das Tool-Ergebnis selbst die gewünschte endgültige Antwort.

Wenn manreturn_direct=Truesetzt, endet die Agent-Schleife sofort, sobald das Tool die Ausführung beendet hat, und der vom Tool zurückgegebene Inhalt wird direkt als endgültige Ausgabe verwendet.

Beispiel

from dotenv import load_dotenv
load_dotenv()

from langchain.tools import tool
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage


# Gewöhnliches Tool: Ergebnis wird an das Modell zurückgegeben, Modell fasst dann zusammen
@tool
def search_normal(keyword: str) -> str:
    """EXAMPLE Tutorial-Kurse durchsuchen (normaler Modus)"""
    return f"Suchergebnisse: Python3-Grundlagen-Tutorial, Python-Datenanalyse, Einführung in Python-Crawler"


# return_direct-Tool: Ergebnis wird direkt als endgültige Ausgabe verwendet
@tool(return_direct=True)
def search_direct(keyword: str) -> str:
    """Suche EXAMPLE Tutorial-Kurse (direkter Rückgabemodus).

Verwende dieses Tool, wenn der Benutzer nur Suchergebnisse benötigt und keine zusätzliche Analyse erforderlich ist.
    """

    return f"Suchergebnisse: Python3-Grundlagen-Tutorial, Python-Datenanalyse, Einführung in Python-Crawler"


model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)

# Vergleich: Normalmodus vs. direkter Rückgabemodus
agent_normal = create_agent(
    model=model,
    tools=[search_normal],
    system_prompt="Du bist der Studienberater für EXAMPLE Tutorial.",
)

agent_direct = create_agent(
    model=model,
    tools=[search_direct],
    system_prompt="Du bist der Studienberater für EXAMPLE Tutorial.",
)

# Normalmodus: Das Modell generiert eine Zusammenfassung basierend auf den Suchergebnissen
result = agent_normal.invoke({
    "messages": [HumanMessage(content="Python-Kurse suchen")]
})
print("=== Normalmodus (Modell verarbeitet weiter) ===")
print(result["messages"][-1].content[:150])

# Direkter Rückgabemodus: Das Tool-Ergebnis ist die endgültige Antwort
result = agent_direct.invoke({
    "messages": [HumanMessage(content="Python-Kurse suchen")]
})
print("\n=== Direkter Rückgabemodus (das Tool-Ergebnis ist die endgültige Antwort) ===")
print(result["messages"][-1].content[:150])

Ausführungsergebnis:

=== 普通模式(模型会再加工)===
在Example 中,我为您找到了以下 Python 相关课程:
1. Python3 基础教程 - 适合零基础入门
2. Python 数据分析 - 进阶学习
3. Python 爬虫入门 - 实战项目

=== 直接返回模式(工具结果即最终答案)===
搜索结果:Python3 基础教程、Python 数据分析、Python 爬虫入门
ModeNach der Tool-AusführungAnwendbare Szenarien
return_direct=False (Standard)Modell empfängt Tool-Ergebnis → Modell denkt weiter → generiert endgültige AntwortErfordert Analyse/Zusammenfassung/weitere Entscheidungsfindung
return_direct=TrueEndet sofort nach der Tool-Ausführung → Tool-Ergebnis ist die endgültige AusgabeAbfragetyp, Datenabruftyp, vorformatierte Ergebnisse

Wenn Sie return_direct=True setzen, überspringt der Agent nachfolgende Denkschritte des Modells und gibt direkt das Tool-Ergebnis zurück. Dies ist sehr wertvoll, um Tokens zu sparen und die Latenz zu reduzieren, aber es bedeutet auch, dass das Modell keine sekundäre Verarbeitung des Tool-Ergebnisses durchführt.

Hinweis:Wenn ein Agent mehrere Tools gleichzeitig eingebunden hat, darunter sowohl return_direct=True-Tools als auch gewöhnliche Tools, dann endet die Agent-Schleife sofort, sobald das Modell in dieser Aufrufrunde ein beliebiges return_direct-Tool auslöst – selbst wenn andere gewöhnliche Tools in derselben Runde parallel aufgerufen werden, werden ihre Ergebnisse nicht mehr vom Modell verarbeitet oder zusammengefasst. Beachten Sie dies beim Entwerfen eines Agenten mit mehreren Tools, um zu vermeiden, dass "Inhalte, die zusammengefasst werden sollten, übersprungen werden."


InjectedToolCallId — Abrufen der Tool Call ID

Manchmal muss ein Tool wissen, "wer es aufgerufen hat"—InjectedToolCallIdDu kannst die aktuelle tool_call_id in die Tool-Funktion injizieren:

Beispiel

from typing import Annotated

from langchain.messages import ToolCall
from langchain.tools import tool, InjectedToolCallId


@tool
def log_user_action(
    action: str,
    tool_call_id: Annotated[str, InjectedToolCallId],
) -> str:
    """Benutzeraktionen im Logging-System aufzeichnen.

    Argumente:
action: Beschreibung der Benutzeraktion
tool_call_id: automatisch injizierte Tool-Call-ID
    """


    # In einem echten Projekt würde dies in eine Datenbank geschrieben oder an einen Logging-Dienst gesendet werden

    return f"Vorgang aufgezeichnet (Call-ID: {tool_call_id}): {action}"


# Manuell einen vom Modell generierten ToolCall simulieren
tool_call: ToolCall = {
    "name": "log_user_action",
    "args": {
        "action": "Der Benutzer hat nach Python-Kursen gesucht"
    },
    "id": "call_log_001",
    "type": "tool_call",
}


# InjectedToolCallId wird automatisch aus tool_call.id injiziert
result = log_user_action.invoke(tool_call)

print(result)

Ausführungsergebnis:

content='操作已记录 (调用ID: call_log_001): 用户查询了 Python 课程' name='log_user_action' tool_call_id='call_log_001'

MitInjectedToolArgmarkierte Parameter müssen nicht vom Agent (Modell) bereitgestellt werden; diese Parameter werden bei der Ausführung des Tools automatisch von der LangChain-Runtime injiziert. Da diese Parameter nicht im Schema des Tools erscheinen, kann das Modell sie nicht sehen, deshalb sollten sie nicht als Tool-Parameter beschrieben werden, die der Benutzer ausfüllen muss.

Zum Beispiel istInjectedToolCallIdeine spezielle Art von injiziertem Parameter. LangChain übergibt während der Tool-Ausführung automatisch die eindeutige ID des aktuellen ToolCall. Er erscheint nicht in den vom Modell generierten Parametern, wird aber automatisch an den aktuellen Toolaufruf gebunden.

Das direkte Aufrufen von.invoke()und das manuelle Konstruieren vonToolCall, dient nur dazu, zu demonstrieren, wie LangChain den Mechanismus zur Parameterinjektion implementiert. In echten Projekten übergeben Sie diese ID normalerweise nicht manuell.

In Agent-Workflows wirdInjectedToolCallIdhäufiger in Szenarien verwendet, in denen der Kontext des aktuellen Aufrufs zugeordnet werden muss, zum Beispiel zusammen mit einemCommand-Objekt, um den Agent-Status innerhalb des Tools zu aktualisieren,messagesdie mit dem aktuellen Aufruf verknüpften Elemente an die Liste anhängen,ToolMessage, oder Funktionen wie Tool-Call-Tracing, Auditing und Log-Korrelation implementieren.

Das ist auch der Grund, warum es als "injizierter Parameter" statt als gewöhnlicher Parameter konzipiert ist: es repräsentiert den Kontext des ToolCall, der gerade von der LangChain Runtime ausgeführt wird, nicht Teil der Benutzereingabe.


ToolException — Tool-Ausnahmebehandlung

Bei der Ausführung von Tools können Fehler auftreten. Verwenden SieToolException, um eine klare Tool-Ausnahme auszulösen, damit der Agent weiß, dass etwas schiefgelaufen ist.

Beispiel

from langchain.tools import tool, ToolException

@tool
def get_user_info(user_id: int) -> str:
    """Benutzerinformationen anhand der Benutzer-ID abfragen.

    Argumente:
user_id: Benutzer-ID, muss eine positive Ganzzahl sein
    """

    # Datenvalidierung
    if user_id <= 0:
        # ToolException auslösen, keine reguläre Exception
        # ToolException wird vom Agenten abgefangen und dem Modell gemeldet
        raise ToolException(f"Benutzer-ID muss eine positive Ganzzahl sein, erhalten: {user_id}")

    # Datenbankabfrage simulieren
    users = {
        1: "Zhang San (VIP-Mitglied, registriert am 2024-01-15)",
        2: "Li Si (normaler Benutzer, registriert am 2024-03-20)",
    }

    if user_id not in users:
        raise ToolException(f"Benutzer mit ID {user_id} nicht gefunden")

    return users[user_id]


# Normaler Aufruf
print(get_user_info.invoke({"user_id": 1}))

# Ausnahmeaufruf 1: ungültige ID
try:
    get_user_info.invoke({"user_id": -1})
except ToolException as e:
    print(f"Tool-Ausnahme: {e}")

# Ausnahmeaufruf 2: Benutzer existiert nicht
try:
    get_user_info.invoke({"user_id": 999})
except ToolException as e:
    print(f"Tool-Ausnahme: {e}")

Ausführungsergebnis:

张三(VIP 会员,注册于 2024-01-15)
工具异常: 用户 ID 必须为正整数,收到了: -1
工具异常: 未找到 ID 为 999 的用户

Der Grund, warum ToolException hier mit try/except außerhalb von .invoke() abgefangen werden kann, ist, dass der Standardwert des Tools fürhandle_tool_errorFalse ist – die Ausnahme wird nicht vom Tool selbst verschluckt, sondern wie üblich nach oben geworfen. Wenn Sie möchten, dass das Tool "den Fehler selbst behandelt" und die Fehlermeldung in einen String umwandelt, der an das Modell zurückgegeben wird, anstatt eine Ausnahme auszulösen, dann ist das handle_tool_error, was im nächsten Abschnitt behandelt wird.


handle_tool_error — Das Tool Fehler selbst behandeln lassen

Wenn Sie möchten, dass ein Tool bei einem Fehler nicht das Programm unterbricht, sondern stattdessen die Fehlermeldung in einen Text umwandelt und sie dem Modell als normalen Rückgabewert übergibt, damit das Modell sie verstehen und korrigieren kann, können Sie dies beim Definieren des Tools festlegen.handle_tool_error(beachten Sie, dass es Singular ist, ohne s). Dies ist ein Attribut von BaseTool, und die einfachste Möglichkeit, es festzulegen, besteht darin, es direkt im @tool-Dekorator zu schreiben:

Beispiel

from langchain.tools import tool, ToolException
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage


# handle_tool_error=True: löst bei einem Fehler keine Ausnahme mehr aus,
# wandelt stattdessen den Inhalt von ToolException in einen String um und gibt ihn normal an das Modell zurück
@tool(handle_tool_error=True)
def get_weather(city: str) -> str:
    """Fragt das Wetter für eine angegebene Stadt ab.

    Argumente:
city: Stadtname, muss der vollständige chinesische Name sein, z. B. "Hangzhou", "Beijing"
    """

    weather_data = {
        "Hangzhou": "Sonnig, 25°C",
        "Beijing": "Bewölkt, 18°C",
        "Shanghai": Leichter Regen, 22°C,
    }
    if city not in weather_data:
        # ToolException auslösen, wenn die Stadt nicht in den Daten ist
        raise ToolException(
            f"Stadt '{city}' ist nicht in der Datenbank."
            f"Verfügbare Städte: {', '.join(weather_data.keys())}."
            f"Bitte verwenden Sie den vollständigen chinesischen Stadtnamen."
        )
    return f"{city} Wetter: {weather_data[city]}"


# Test: Aufruf mit einem falschen Stadtnamen
# Wenn handle_tool_error=True, wird die Fehlermeldung als normales Ergebnis zurückgegeben, anstatt eine Ausnahme auszulösen
result = get_weather.invoke({"city": "Northern Territory"})
print(f"handle_tool_error=True: {result}")


# Wenn alle Fehler der Tools einheitlich vom Agenten behandelt werden sollen (anstatt sie pro Tool festzulegen),
# können Sie dies auf der Ebene von ToolNode konfigurieren, die in create_agent verwendet wird.
# In neueren Versionen von langchain können Sie den Fehler-Fallback für den gesamten Agenten wie folgt aktivieren:
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
agent = create_agent(
    model=model,
    tools=[get_weather],
    system_prompt="Sie sind ein Assistent für Wetterabfragen.",
)

result = agent.invoke({
    "messages": [HumanMessage(content="Wetter in Northern Territory abfragen")]
})
print("\n=== Agent empfängt die von handle_tool_error umgewandelte Fehlermeldung und erklärt sie dem Benutzer entsprechend ===)
print(result["messages"][-1].content[:200])

Run result:

handle_tool_error=True: 未收录城市 '北境'。可使用城市:杭州, 北京, 上海。请使用中文城市全称。

=== Agent 收到 handle_tool_error 转换后的错误信息,并据此向用户解释 ===
抱歉,我没有查询到"北境"的天气数据。目前支持查询的城市有:杭州、北京、上海。
请问您是想查询其中哪一个城市呢?
handle_tool_errorVerhaltenAnwendbare Szenarien
False (Standard)ToolException wird wie üblich nach oben geworfen; der Aufrufer muss sie mit try/except behandelnNicht behebbare Fehler, die eine Unterbrechung des Ablaufs erfordern
TrueToolException abfangen und ihren Inhalt dem Modell als normalen Rückgabewert des Tools übergebenWenn der Agent die Fehlermeldung selbst lesen und korrigieren/erneut versuchen soll
strNach dem Abfangen der Ausnahme die Fehlermeldung durch eine angegebene feste Zeichenfolge ersetzenKeine spezifischen Fehlerdetails preisgeben wollen; nur einen einheitlichen Prompt ausgeben
Callable[[ToolException], str]Nach dem Abfangen der Ausnahme eine benutzerdefinierte Funktion verwenden, um sie zu verarbeiten und den Rückgabeinhalt zu erzeugenUnterschiedliche Prompts müssen je nach Ausnahmetyp/-inhalt angepasst werden

Hinweis:Verwenden Sie nichttool.with_config(handle_tool_errors=True)diese Syntax —with_config()Dies ist eine generische Laufzeitkonfigurationsmethode für Runnable, die nur zum Festlegen von Callbacks, Tags, Metadaten usw. verwendet wird und nicht das Fehlerbehandlungsverhalten des Tools ändert. Um die Fehlerbehandlung eines einzelnen Tools zu steuern, verwenden Sie@tool(handle_tool_error=...)(Singular); wenn Sie Fehlerbehandlungsstrategien für alle Tools auf Agent-/Graph-Ebene einheitlich konfigurieren möchten, legen Sie handle_tool_errors (Plural) auf dem zugrunde liegenden ToolNode fest, nicht auf einzelnen Tool-Objekten. Die beiden Feldnamen sind ähnlich, wirken aber auf unterschiedlichen Ebenen; achten Sie bei der Verwendung auf den Unterschied.


Vollständiges Beispiel — Agent mit Fehlerbehandlung

Beispiel

from langchain.tools import tool, ToolException
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage


@tool(handle_tool_error=True)
def book_course(user_name: str, course_name: str) -> str:
    """Buchen Sie einen Kurs auf EXAMPLE für den Benutzer.

    Argumente:
user_name: Name des Benutzers
course_name: Kursname
    """

    # Prüfen, ob der Benutzer existiert
    valid_users = {"Zhang San", "Li Si", "Wang Wu"}
    if user_name not in valid_users:
        raise ToolException(
            f"Benutzer '{user_name}' existiert nicht."
            f"Gültige Benutzer: {', '.join(sorted(valid_users))}"
        )

    # Prüfen, ob der Kurs existiert
    valid_courses = {"Python3-Grundkurs", "HTML-Grundkurs", "Java objektorientiert"}
    if course_name not in valid_courses:
        raise ToolException(
            f"Kurs '{course_name}' existiert nicht."
            f"Gültige Kurse: {', '.join(sorted(valid_courses))}"
        )

    return f"Buchung von {course_name} für {user_name} erfolgreich"


model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
agent = create_agent(
    model=model,
    tools=[book_course],
    system_prompt="Sie sind ein Kursberater bei EXAMPLE.",
)

# Normaler Aufruf
result = agent.invoke({
    "messages": [HumanMessage(content="Buche Python3-Grundkurs für Zhang San")]
})
print(f"Erfolg: {result['messages'][-1].content[:100]}")

# Fehleraufruf: Benutzer existiert nicht
result = agent.invoke({
    "messages": [HumanMessage(content="Buch Python3 Basis-Tutorial für Zhao Liu")]
})
print(f"\n"Fehler - Benutzer existiert nicht:")
for msg in result["messages"]:
    if msg.type == "tool":
        print(f" [{msg.type}] {msg.content[:80]}")
    elif msg.type == "ai" and msg.content:
        print(f" [{msg.type}] {msg.content[:100]}")

Run result:

Success: 已经为您成功预订《Python3 基础教程》,张三同学,祝您学习愉快!

错误-用户不存在:
  [tool] 用户 '赵六' 不存在。有效用户:张三, 李四, 王五
  [ai] 抱歉,系统中没有找到名为"赵六"的用户,暂时无法为您完成预订。
目前可预订的用户有:张三、李四、王五。请确认姓名后重新尝试。

Man kann sehen, dass nachdem das Tool mithandle_tool_error=Truekonfiguriert wurde, der Inhalt der ToolException normal im Gesprächsverlauf als ToolMessage erscheint (die [tool]-Zeile in der obigen Ausgabe), ohne das Programm zum Absturz zu bringen; nachdem das Modell diese Fehlermeldung gesehen hat, gibt es sie in natürlicher Sprache an den Benutzer weiter und bietet verfügbare Alternativen an. Dies ist der typische Effekt der Kombination von return_direct, InjectedToolCallId, ToolException und handle_tool_error: Es gewährleistet die Robustheit von Tool-Aufrufen, ohne die Benutzererfahrung zu beeinträchtigen.

Weitere Erweiterungen