如何优雅地处理LangChain工具调用中的错误

378 阅读3分钟

如何优雅地处理LangChain工具调用中的错误

在使用LangChain和聊天模型时,调用工具通常比纯提示更加可靠,但仍然存在一些挑战。本指南将指导您在链中实现错误处理,以减少这些失败模式带来的影响。

引言

LangChain在调用工具时提供了许多优势,但并不完美。有时模型可能会尝试调用不存在的工具,或者无法返回匹配请求模式的参数。通过保持模式简单、减少传递工具的数量以及良好的命名和描述,可以部分缓解这些问题。本文旨在介绍如何在LangChain中处理工具调用错误。

主要内容

1. 环境设置

首先,我们需要安装必要的包:

%pip install --upgrade --quiet langchain-core langchain-openai

2. 工具定义和链创建

假设我们有一个复杂的工具和一个调用该工具的链:

from langchain_core.tools import tool

@tool
def complex_tool(int_arg: int, float_arg: float, dict_arg: dict) -> int:
    """执行复杂操作的工具。"""
    return int_arg * float_arg

# 将工具绑定到模型
llm_with_tools = llm.bind_tools([complex_tool])

# 定义链
chain = llm_with_tools | (lambda msg: msg.tool_calls[0]["args"]) | complex_tool

3. 错误处理

在调用链时,可能会出现参数缺失等问题。我们可以使用 try/except 来更优雅地处理这些错误:

from typing import Any
from langchain_core.runnables import Runnable, RunnableConfig

def try_except_tool(tool_args: dict, config: RunnableConfig) -> Runnable:
    try:
        complex_tool.invoke(tool_args, config=config)
    except Exception as e:
        return f"调用工具时出错:\n\n{tool_args}\n\n错误信息:\n\n{type(e)}: {e}"

# 使用try/except处理的链
chain = llm_with_tools | (lambda msg: msg.tool_calls[0]["args"]) | try_except_tool

print(
    chain.invoke(
        "使用复杂工具。参数为5, 2.1, 空字典。不要忘记dict_arg"
    )
)

4. 备用模型

当工具调用出错时,可以尝试使用备用模型:

better_model = ChatOpenAI(model="gpt-4-1106-preview", temperature=0).bind_tools([complex_tool], tool_choice="complex_tool")
better_chain = better_model | (lambda msg: msg.tool_calls[0]["args"]) | complex_tool

chain_with_fallback = chain.with_fallbacks([better_chain])

chain_with_fallback.invoke(
    "使用复杂工具。参数为5, 2.1, 空字典。不要忘记dict_arg"
)

5. 使用异常重试

自动重新运行链并传入异常信息,以便模型能更正其行为:

from langchain_core.messages import AIMessage, HumanMessage, ToolCall, ToolMessage
from langchain_core.prompts import ChatPromptTemplate

class CustomToolException(Exception):
    def __init__(self, tool_call: ToolCall, exception: Exception) -> None:
        super().__init__()
        self.tool_call = tool_call
        self.exception = exception

def tool_custom_exception(msg: AIMessage, config: RunnableConfig) -> Runnable:
    try:
        return complex_tool.invoke(msg.tool_calls[0]["args"], config=config)
    except Exception as e:
        raise CustomToolException(msg.tool_calls[0], e)

def exception_to_messages(inputs: dict) -> dict:
    exception = inputs.pop("exception")
    messages = [
        AIMessage(content="", tool_calls=[exception.tool_call]),
        ToolMessage(tool_call_id=exception.tool_call["id"], content=str(exception.exception)),
        HumanMessage(content="上次调用引发异常。请重新调用工具并修正参数。")
    ]
    inputs["last_output"] = messages
    return inputs

prompt = ChatPromptTemplate.from_messages([("human", "{input}"), ("placeholder", "{last_output}")])
chain = prompt | llm_with_tools | tool_custom_exception

self_correcting_chain = chain.with_fallbacks([exception_to_messages | chain], exception_key="exception")

self_correcting_chain.invoke(
    {
        "input": "使用复杂工具。参数为5, 2.1, 空字典。不要忘记dict_arg"
    }
)

常见问题和解决方案

  1. 工具调用失败:确保传递给工具的参数正确无误。
  2. 备用模型选择:根据需求选择合适的备用模型以提高工具调用的成功率。

总结和进一步学习资源

本指南展示了如何在LangChain中处理工具调用错误。您可以进一步了解以下概念:

  • 使用工具的Few shot提示
  • 工具调用的流式处理
  • 向工具传递运行时值
  • 使用模型获取结构化输出

参考资料

如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!

---END---