如何优雅地处理工具调用错误:LangChain实战指南

275 阅读3分钟

引言

在使用大型语言模型(LLM)调用工具时,我们经常遇到的问题是模型可能会尝试调用一个不存在的工具,或者未能返回匹配请求架构的参数。通过简化架构、减少一次传递的工具数以及提供良好的名称和描述可以帮助缓解这一风险,但并不万无一失。本指南旨在帮助开发者在处理工具调用错误时设计更健壮的链条。

主要内容

准备工作

在开始之前,请确保您已经安装了以下包:

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

并设置必要的环境变量:

import getpass
import os

# os.environ["LANGCHAIN_TRACING_V2"] = "true"
# os.environ["LANGCHAIN_API_KEY"] = getpass.getpass()

链条设置

假设我们有一个复杂的工具以及一个调用链条,我们将故意设计这个工具来挑战模型,看看它会如何处理。

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

工具调用链

在这个链条中,我们尝试调用一个工具,但模型可能会忽略所需的dict_arg参数。

llm_with_tools = llm.bind_tools([complex_tool])

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

chain.invoke(
    "use complex tool. the args are 5, 2.1, empty dictionary. don't forget dict_arg"
)

常见问题和解决方案

使用Try/Except处理错误

最简单的方法是使用try/except语句来捕获调用错误,并返回一个提示信息。

from typing import Any

def try_except_tool(tool_args: dict, config: Any) -> Any:
    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}"

chain = llm_with_tools | (lambda msg: msg.tool_calls[0]["args"]) | try_except_tool

使用备用模型

我们可以尝试在工具调用错误时切换到一个更好的模型。

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(
    "use complex tool. the args are 5, 2.1, empty dictionary. don't forget dict_arg"
)

使用异常自动重试

通过自动重试链条,并将异常作为消息传递给模型,可以提高成功率。

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: Any) -> Any:
    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": "use complex tool. the args are 5, 2.1, empty dictionary. don't forget dict_arg"})

总结和进一步学习资源

通过上面的策略,我们可以显著提高工具调用的可靠性。您可以进一步学习如何:

  • 使用工具进行少样本提示
  • 流式工具调用
  • 将运行时值传递给工具

参考资料

  1. LangChain 官方文档
  2. OpenAI API 参考
  3. LangSmith 追踪工具

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

---END---