位置:首页 > 进阶教程 > LangChain框架入门18:十分钟带你搞定LLM工具调用完全教程

LangChain框架入门18:十分钟带你搞定LLM工具调用完全教程

时间:2026-07-25  |  作者:318050  |  阅读:0

上一篇文章详细介绍了工具调用的基本流程和工具创建方法。工具创建完成后,接下来的关键一步,就是让大语言模型(LLM)真正“学会”调用这些工具。不过,第一次接触这个概念的读者可能会以为,LLM会直接去执行工具本身——比如去调用API、计算加法——其实并不是这样。LLM的职责是根据用户的问题,判断需要调用哪个工具,并生成所需的参数。至于工具是否真的被调用、如何执行,这些都由应用自身来掌控。

整个工具调用的流程可以概括为:工具创建 → 工具绑定 → LLM返回调用信息 → 应用执行工具 → 返回结果给LLM → 生成最终回答。下面,我们就一步步展开,看看具体怎么实现。

一、工具创建

先来创建一个根据IP获取用户地理位置信息的工具。这个工具会调用高德地图的IP定位API,API文档地址可以直接查。在.env文件中配置好高德开放平台的API KEY

# 高德开放平台API KEY
GAODE_API_KEY=***************************

然后,通过继承BaseTool类来创建工具。在_run方法中实现核心逻辑——其实就是调用指定API,传入IP地址和API KEY。具体代码可以结合文档理解:

class GaoDeIPLocationInput(BaseModel):
    """IP定位入参"""
    ip: str = Field(description="ip地址")

class GaoDeIPLocationTool(BaseTool):
    """根据IP定位位置工具"""
    name = "ip_location_tool"
    description = "当你需要根据IP,获取定位信息时,可以调用这个工具"
    args_schema = GaoDeIPLocationInput

    def _run(self, ip: str) -> str:
        api_key = os.getenv("GAODE_API_KEY")
        if api_key is None:
            return "请配置GAODE_API_KEY"
        url = "https://restapi.amap.com/v3/ipip={ip}&key={key}".format(ip=ip, key=api_key)
        session = requests.session()
        response = session.request(method="GET",
                                   url=url,
                                   headers={"Content-Type": "application/json; charset=UTF-8"},
                                   )
        result = response.json()
        return result.get("province") + result.get("city")

另一个工具是之前已经创建过的加法计算器,实现同样直接:

class AddNumberInput(BaseModel):
    """加法工具入参"""
    num1: int = Field(description="第一个数")
    num2: int = Field(description="第二个数")

class AddNumberTool(BaseTool):
    """加法工具"""
    name = "add_number_tool"
    description = "两数相加工具"
    args_schema = AddNumberInput

    def _run(self, num1: int, num2: int) -> int:
        return num1 + num2

创建两个工具对象后,把它们放到一个字典里,key是工具名称,value是工具对象。这样做的原因后面会看到——当LLM返回工具调用信息时,我们可以根据工具名称快速获取对应的工具实例,从而执行调用。

# 创建工具对象
add_number_tool = AddNumberTool()
gaode_ip_location_tool = GaoDeIPLocationTool()

tools_dict = {
    add_number_tool.name: add_number_tool,
    gaode_ip_location_tool.name: gaode_ip_location_tool,
}

二、工具绑定

工具创建好了,但不能直接扔给LLM。需要先使用.bind_tools()方法将工具列表绑定到LLM对象上。只有绑定了工具列表,LLM在执行时才知道它可以调用哪些工具、每个工具需要什么参数。

这里有一个容易被忽略的细节:一定要用新的变量(比如llm_with_tool)来接收绑定后的LLM,后续调用时也要用这个新变量。如果继续用原来的llm,大模型不会返回任何工具调用信息。

# 1.创建Prompt
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个智能助手,可以帮助用户解决问题。当问题需要使用工具时,必须调用提供的工具,并一次性调用所有必要工具,避免分步调用。"),
    ("human", "{query}"),
])

# 2.创建LLM并绑定工具
llm = ChatOpenAI(model="gpt-4o")
llm_with_tool = llm.bind_tools(tools=[tool for tool in tools_dict.values()])

三、调用工具

绑定完成后,构建一个链(chain)并传入查询。LLM会返回一个AIMessage,消息内部如果涉及工具调用,会包含一个tool_calls列表,里面每个元素都包含tool_call_id(工具调用的唯一标识)、工具名称、以及生成的参数等信息。

# 3.创建链
chain = prompt | llm_with_tool

# 4.调用链
query = "帮我查询一下IP为122.234.134.158用户的位置"
resp = chain.invoke({"query": query})
print("LLM生成内容:", resp.content)
print("LLM生成调用信息:", resp.tool_calls)

接下来,需要构建一个消息列表messages,把之前渲染模板得到的消息保存下来。这个列表的作用是记录历史消息,后续还要把工具调用结果(工具消息)也加进去,一起发给LLM生成最终回答。

保存好消息记录后,通过判断AIMessage中的tool_calls是否为None,就可以知道LLM是否决定调用工具。如果存在工具调用,就遍历tool_calls,根据每个tool_call中的name属性,从tools_dict中取出对应的工具对象。

然后,调用工具的invoke方法,传入tool_call中的args(即LLM生成的参数),得到工具执行结果。最后,构建一个ToolMessage(工具消息),包含tool_call_id和结果内容,并追加到messages列表中。

# 5.构建消息列表,插入AI返回的AIMessage
messages = prompt.invoke({"query": query}).to_messages()
messages.append(resp)

# 6.判断是否需要工具调用
if resp.tool_calls is None:
    print(resp.content)
else:
    # 7.遍历工具调用信息
    for tool_call in resp.tool_calls:
        # 8.根据调用的工具名称获取工具对象
        print("工具{tool_name}调用信息:{tool_call}".format(tool_name=tool_call.get("name"), tool_call=tool_call))
        target_tool = tools_dict.get(tool_call.get("name"))
        # 9.执行工具调用
        result = target_tool.invoke(tool_call.get("args"))
        print("工具{tool_name}调用结果:{result}".format(tool_name=target_tool.name, result=result))
        tool_call_id = tool_call.get("id")
        # 10.创建工具消息
        tool_message = ToolMessage(
            tool_call_id=tool_call_id,
            content=result,
        )
        # 11.将工具消息添加到消息列表中
        messages.append(tool_message)

四、返回调用结果给大模型

最后,把包含工具消息的messages列表传给LLM,大模型会基于这些信息生成最终的回答。

print("最终结果:" + llm.invoke(messages).content)

执行结果如下,完全符合预期:

LLM生成内容: 
LLM生成调用信息: [{'name': 'ip_location_tool', 'args': {'ip': '122.234.134.158'}, 'id': 'call_R36kxZo9S78PlOakiXk6Ib0M', 'type': 'tool_call'}]
工具ip_location_tool调用信息:{'name': 'ip_location_tool', 'args': {'ip': '122.234.134.158'}, 'id': 'call_R36kxZo9S78PlOakiXk6Ib0M', 'type': 'tool_call'}
工具ip_location_tool调用结果:浙江省杭州市
最终结果:IP地址为122.234.134.158的用户位于中国浙江省杭州市。

附完整案例代码,供参考:

import os
import dotenv
import requests
from langchain_core.messages import ToolMessage
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import BaseTool
from langchain_openai import ChatOpenAI
from pydantic.v1 import BaseModel, Field

# 读取env配置
dotenv.load_dotenv()

class GaoDeIPLocationInput(BaseModel):
    """IP定位入参"""
    ip: str = Field(description="ip地址")

class GaoDeIPLocationTool(BaseTool):
    """根据IP定位位置工具"""
    name = "ip_location_tool"
    description = "当你需要根据IP,获取定位信息时,可以调用这个工具"
    args_schema = GaoDeIPLocationInput

    def _run(self, ip: str) -> str:
        api_key = os.getenv("GAODE_API_KEY")
        if api_key is None:
            return "请配置GAODE_API_KEY"
        url = "https://restapi.amap.com/v3/ipip={ip}&key={key}".format(ip=ip, key=api_key)
        session = requests.session()
        response = session.request(method="GET",
                                   url=url,
                                   headers={"Content-Type": "application/json; charset=UTF-8"},
                                   )
        result = response.json()
        return result.get("province") + result.get("city")

class AddNumberInput(BaseModel):
    """加法工具入参"""
    num1: int = Field(description="第一个数")
    num2: int = Field(description="第二个数")

class AddNumberTool(BaseTool):
    """加法工具"""
    name = "add_number_tool"
    description = "两数相加工具"
    args_schema = AddNumberInput

    def _run(self, num1: int, num2: int) -> int:
        return num1 + num2

# 创建工具对象
add_number_tool = AddNumberTool()
gaode_ip_location_tool = GaoDeIPLocationTool()

tools_dict = {
    add_number_tool.name: add_number_tool,
    gaode_ip_location_tool.name: gaode_ip_location_tool,
}

# 1.创建Prompt
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个智能助手,可以帮助用户解决问题。当问题需要使用工具时,必须调用提供的工具,并一次性调用所有必要工具,避免分步调用。"),
    ("human", "{query}"),
])

# 2.创建LLM并绑定工具
llm = ChatOpenAI(model="gpt-4o")
llm_with_tool = llm.bind_tools(tools=[tool for tool in tools_dict.values()])

# 3.创建链
chain = prompt | llm_with_tool

# 4.调用链
query = "帮我查询一下IP为122.234.134.158用户的位置"
resp = chain.invoke({"query": query})
print("LLM生成内容:", resp.content)
print("LLM生成调用信息:", resp.tool_calls)

# 5.构建消息列表,插入AI返回的AIMessage
messages = prompt.invoke({"query": query}).to_messages()
messages.append(resp)

# 6.判断是否需要工具调用
if resp.tool_calls is None:
    print(resp.content)
else:
    # 7.遍历工具调用信息
    for tool_call in resp.tool_calls:
        # 8.根据调用的工具名称获取工具对象
        print("工具{tool_name}调用信息:{tool_call}".format(tool_name=tool_call.get("name"), tool_call=tool_call))
        target_tool = tools_dict.get(tool_call.get("name"))
        # 9.执行工具调用
        result = target_tool.invoke(tool_call.get("args"))
        print("工具{tool_name}调用结果:{result}".format(tool_name=target_tool.name, result=result))
        tool_call_id = tool_call.get("id")
        # 10.创建工具消息
        tool_message = ToolMessage(
            tool_call_id=tool_call_id,
            content=result,
        )
        # 11.将工具消息添加到消息列表中
        messages.append(tool_message)

print("最终结果:" + llm.invoke(messages).content)

五、总结

本文从工具创建、LLM工具绑定、调用工具、返回调用结果给LLM,到最后LLM生成最终回答,完整展示了LLM工具调用的全过程。其中有几个关键点值得特别留意:

  1. LLM本身并不会直接执行工具,它的作用是决定是否调用工具、调用哪个工具、生成哪些参数。实际的工具调用逻辑由应用层控制。
  2. 即使绑定了工具,大模型也不一定会调用工具——是否调用、如何调用,完全由LLM自身的判断决定。
  3. 在调用工具完成后,传递工具消息时,必须带上原始工具调用传入的tool_call_id,这样LLM才能将工具调用结果与之前的调用请求正确匹配。

通过本文的实践,相信你已经掌握了如何通过LLM进行工具调用。实际上,这个案例代码已经是一个Agent智能体的雏形。下一篇文章,我们会深入介绍LangChain中的Agent智能体,敬请期待。

来源:整理自互联网
免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多