一、核心背景:大模型的能力局限与解决方案

我们已经知道,大模型是一个“超级聪明的大脑”,但它有个大缺点:知识是固定的,无法获取实时数据、私有数据(比如你公司的客户信息、当天的日期),这就是它的“能力局限”。

举个最直观的例子:你直接问大模型“今天是几年几月几日”,它答不上来——因为它的知识里没有“实时更新的日期”,就像我们没背过“明天的日期”,自然不知道一样。

针对这个问题,Tools工具机制就是最有效的解决方案,它的核心逻辑特别简单,用大白话讲就是:给大模型“找个外援”,让大模型通过“API接口”,调用外部的小工具(比如获取日期的工具、查询天气的工具),获取它自己没有的数据,再结合自己的聪明才智,给出准确答案——相当于给大模型“装了一个外接大脑”,扩展了它的能力。

重要补充:现在几乎所有主流大模型(比如阿里云百炼、ChatGPT、文心一言)都支持Tools工具机制,而且它是我们用Python做“本地大模型应用”的关键(比如做一个能查天气、查日期的大模型小程序),具体的详细用法,还可以参考LangChain官方文档。

二、深入理解Tools工具机制

2.1 从实际问题切入:大模型的“知识盲区”

我们还是用“大模型能不能知道今天是几月几号”这个问题,来理解大模型的局限:

大模型的训练数据是“静态的”,就像一本固定的百科全书,里面没有“实时更新的日期”——比如它可能学过“2025年12月31日是星期三”,但它不知道“2026年4月21日是星期几”,因为这个日期在它训练的时候还没到,自然没有相关知识。

所以,我们直接调用大模型,问它“今天是几年几月几号”,它要么答不上来,要么给出一个错误的答案——下面我们看具体的代码,我会逐行解读,告诉大家每一步是什么意思,运行的时候会发生什么。

示例代码(直接调用大模型获取实时日期,无法得到准确结果):

# 第一行:从config.load_key这个文件里,导入load_key这个函数(作用:加载API密钥)
# 注意:config.load_key是我们自己写的一个小文件,里面只有一个功能——读取我们保存的API密钥
from config.load_key import load_key
# 第二行:从langchain_openai这个库(我们之前安装的)里,导入ChatOpenAI这个工具
# ChatOpenAI的作用:帮我们连接大模型(比如阿里云百炼、ChatGPT)
from langchain_openai import ChatOpenAI
# 第三行:定义一个名叫llm的变量,把“阿里云百炼大模型客户端”存进去
# 简单说:llm就是我们和阿里云百炼大模型沟通的“桥梁”
llm = ChatOpenAI(
 model="qwen-plus",  # 这里指定我们要用的大模型:阿里云百炼的qwen-plus模型
 base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",  # 大模型的API接口地址(沟通桥梁的地址)
 openai_api_key=load_key("BAILIAN_API_KEY"),  # 加载API密钥(相当于我们的“访问密码”,没有密码进不去)
)
# 第四行:调用大模型,问它“今天是几年几月几号?”,并获取它的回答内容
# invoke():就是“让大模型做事”的方法,括号里是我们问的问题
# .content:就是“获取大模型回答的内容”(比如大模型答“不知道”,我们就通过.content拿到这个回答)
llm.invoke("今天是几年几月几号?").content

代码运行过程解读:

  1. 我们运行这段代码后,电脑会先执行前两行“import”,导入需要的工具(load_key函数、ChatOpenAI工具);

  2. 然后执行“llm = ChatOpenAI(...)”,连接阿里云百炼大模型——这一步相当于“我们给大模型打了个电话,打通了沟通渠道”;

  3. 最后执行“llm.invoke(...)”,给大模型发问题“今天是几年几月几号?”,让大模型回答;

  4. 最终结果:大模型会返回一个不准确的答案(比如“2025年12月31日”),或者直接说“不知道”——因为它没有实时日期数据。

而Tools工具机制,就是解决这个问题的:我们给大模型“配一个获取日期的小工具”,让大模型不知道答案的时候,调用这个工具,获取实时日期,再给我们回答——相当于给大模型“找了个助手”,助手帮它查日期,它再把答案告诉我们。

2.2 Tools工具机制的核心逻辑

Tools工具机制的核心思想特别简单,就是“大模型+外部工具”一起合作,解决大模型自己解决不了的问题,具体流程拆成4步,用大白话讲透:

  1. 第一步:我们写的本地应用(比如我们的Python代码),给大模型发问题的时候,顺便告诉大模型:“我这里有一些工具,比如能查今天的日期、能查城市天气,你要是不会回答,就调用这些工具”;

  2. 第二步:大模型收到问题后,会自己判断:“这个问题我能自己回答吗?”——如果能,就直接回答;如果不能(比如查日期),就返回一个“工具调用请求”,告诉我们:“我需要调用‘获取日期’这个工具,才能回答你的问题”;

  3. 第三步:我们的本地应用(Python代码),收到大模型的“工具调用请求”后,就去执行这个工具(比如运行“获取日期”的代码),拿到工具返回的结果(比如“2026-04-21”);

  4. 第四步:我们的本地应用,把工具返回的结果(日期),和原来的问题(今天是几月几号),一起再发给大模型;大模型结合这个结果,就能给出准确的回答(比如“今天是2026年4月21日”)。

核心价值:让大模型的“聪明大脑”(知识推理能力),和我们本地工具的“特殊能力”(获取实时数据、私有数据)结合起来,让大模型变得更厉害,能解决以前解决不了的问题。

三、定制本地Tool工具

前面我们知道了Tools工具机制的逻辑,现在我们实操一下:定制一个“获取当前日期”的本地工具,绑定到大模型上,让大模型能通过这个工具,准确回答“今天是几月几号”——全程代码逐行解读,运行过程、每句含义都讲透。

3.1 完整示例代码

# 第一行:导入datetime库(Python自带的,用于处理日期和时间,不用额外安装)
import datetime
# 第二行:从langchain.tools库(我们安装的)里,导入tool注解(用于给函数贴“工具”标签)
from langchain.tools import tool

# 第三行:用@tool注解,给下面的函数贴“工具”标签,告诉大模型和LangChain:这是一个可调用的工具
@tool
# 第四行:定义一个名叫get_current_date的函数(这个函数就是我们定制的“获取日期”工具)
# def是“定义函数”,get_current_date是函数名,括号里没有参数(因为获取当前日期不需要额外信息)
def get_current_date():
    """获取今天日期"""  # 函数注释(给大模型看的“说明书”,告诉大模型这个工具能做什么)
    # 第五行:返回当前日期,格式是“年-月-日”(比如2026-04-21)
    # datetime.datetime.today():获取当前的日期和时间(比如2026-04-21 15:30:00)
    # strftime("%Y-%m-%d"):把日期时间格式化成“年-月-日”的样子,只保留日期,去掉时间
    return datetime.datetime.today().strftime("%Y-%m-%d")

# 第六行:导入load_key函数(用于加载API密钥),和前面的代码一致
from config.load_key import load_key
# 第七行:导入ChatOpenAI工具(用于连接大模型),和前面的代码一致
from langchain_openai import ChatOpenAI
# 第八行:构建阿里云百炼大模型客户端,和前面的代码一致(相当于打通和大模型的沟通渠道)
llm = ChatOpenAI(
 model="qwen-plus",
 base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
 openai_api_key=load_key("BAILIAN_API_KEY"),
)

# 第九行:给大模型绑定我们定制的工具(get_current_date)
# bind_tools():是LangChain的方法,作用是“把工具绑定给大模型”,让大模型知道有这个工具可以调用
# [get_current_date]:把我们的工具放进一个列表里(如果有多个工具,都可以放进去)
llm_with_tools = llm.bind_tools([get_current_date])

# 第十行:定义一个工具容器(用字典表示,相当于“装工具的盒子”)
# 字典的key是工具名称(get_current_date),value是工具本身(get_current_date函数)
# 作用:后面大模型说要调用“get_current_date”工具时,我们能快速从这个“盒子”里找到对应的工具
all_tools = {"get_current_date": get_current_date}

# 第十一行:定义用户的查询问题(就是我们要问大模型的话)
query = "今天是几月几号"  # 用户的问题
# 第十二行:定义一个消息列表,用于存储所有的“对话内容”(问题、大模型的响应、工具的结果)
# 相当于我们和大模型、工具的“聊天记录”,方便后续传递上下文
messages = [query]

# 第十三行:第一次调用大模型(绑定了工具的大模型)
# llm_with_tools.invoke(messages):让绑定了工具的大模型,处理我们的问题(messages里的query)
# 此时大模型会判断:“这个问题我自己不会,需要调用get_current_date工具”,然后返回一个“工具调用请求”
# 我们把这个请求存到ai_msg变量里
ai_msg = llm_with_tools.invoke(messages)
# 第十四行:把大模型的响应(工具调用请求),加入到消息列表里(更新“聊天记录”)
messages.append(ai_msg)

# 第十五行:打印大模型需要调用的工具(查看大模型的“工具调用请求”,确认它要调用哪个工具)
# ai_msg.tool_calls:就是大模型返回的“工具调用请求”,里面包含了要调用的工具名称
print(ai_msg.tool_calls)

# 第十六行:判断大模型是否需要调用工具(如果ai_msg.tool_calls有内容,说明需要调用工具)
if ai_msg.tool_calls:
    # 第十七行:遍历所有需要调用的工具(如果有多个工具,就一个一个处理)
    for tool_call in ai_msg.tool_calls:
        # 第十八行:根据工具名称,从工具容器(all_tools)里找到对应的工具
        # tool_call["name"].lower():获取大模型要调用的工具名称(转成小写,避免大小写错误)
        # all_tools[工具名称]:从“工具盒子”里取出对应的工具
        selected_tool = all_tools[tool_call["name"].lower()]
        # 第十九行:执行这个工具,获取工具返回的结果(比如“2026-04-21”)
        tool_msg = selected_tool.invoke(tool_call)
        # 第二十行:把工具的执行结果,加入到消息列表里(更新“聊天记录”,让大模型能看到)
        messages.append(tool_msg)

# 第二十一行:第二次调用大模型,结合工具结果,给出准确答案
# 此时messages里有:用户的问题、大模型的工具调用请求、工具的执行结果
# 大模型结合这些内容,就能给出“今天是2026年4月21日”这样的准确答案
# .content:获取大模型的最终回答内容
llm_with_tools.invoke(messages).content

3.2 代码运行全过程解读

我们按照代码的执行顺序,一步步讲清楚,运行的时候,电脑会做什么,大模型会做什么,工具会做什么:

  1. 第一步:导入工具(代码1-2行)——电脑导入处理日期的datetime库,和定义工具的@tool注解,为后续写工具做准备;

  2. 第二步:定义工具(代码3-5行)——我们写了一个get_current_date函数,用@tool注解贴了“工具”标签,这个函数的作用就是“获取当前日期”,并返回“年-月-日”格式的结果;

  3. 第三步:连接大模型(代码6-8行)——电脑通过ChatOpenAI,连接阿里云百炼大模型,相当于“打通了和大模型的电话”,此时大模型还不知道我们有“获取日期”的工具;

  4. 第四步:绑定工具(代码9行)——我们把get_current_date工具,绑定给大模型,相当于“告诉大模型:我有一个能查日期的工具,你不会回答的时候可以用”;

  5. 第五步:准备问题和消息列表(代码10-12行)——我们定义了要问的问题(今天是几月几号),并创建了一个消息列表,用于存储“聊天记录”;

  6. 第六步:第一次调用大模型(代码13-14行)——我们把问题发给绑定了工具的大模型,大模型判断:“这个问题我不会,需要调用get_current_date工具”,于是返回一个“工具调用请求”,我们把这个请求存起来,加入到消息列表;

  7. 第七步:查看工具调用请求(代码15行)——我们打印大模型的请求,能看到类似“[{"name":"get_current_date", "parameters":{}}]”的内容,意思是“我要调用get_current_date这个工具”;

  8. 第八步:执行工具(代码16-20行)——电脑判断大模型需要调用工具,于是从“工具盒子”里找到get_current_date工具,执行它,获取到当前日期(比如2026-04-21),再把这个结果加入到消息列表;

  9. 第九步:第二次调用大模型(代码21行)——我们把“问题+工具调用请求+工具结果”一起发给大模型,大模型结合这些内容,就能给出准确的回答:“今天是2026年4月21日”。

3.3 代码关键细节解读

  • 工具定义:必须用@tool注解给函数贴标签,不然大模型不知道这是一个工具;函数注释(比如“获取今天日期”)一定要写清楚——这是给大模型看的“说明书”,大模型就是根据这个注释,判断要不要调用这个工具。

  • 模型绑定工具:必须用bind_tools()方法,把工具绑定给大模型,不然大模型不知道有这个工具存在;如果有多个工具,都可以放进bind_tools()的列表里(比如[get_current_date, get_city_weather])。

  • 工具容器:用字典存储工具,key是工具名称,value是工具函数——这样大模型调用工具时,我们能快速找到对应的工具,避免找不到工具的错误。

  • 消息传递:一定要把“问题、大模型响应、工具结果”都加入消息列表——大模型需要看到完整的“聊天记录”,才能结合工具结果给出准确答案,少了任何一步,大模型都可能回答错误。

四、深入理解@tool注解

我们前面一直在用@tool注解,它是LangChain官方提供的“工具标记工具”,核心作用就是“告诉大模型和LangChain:这个函数是一个可调用的工具”。使用的时候,有两个关键细节,必须搞懂,不然会出错,我们逐一看:

4.1 自定义工具名称(给工具起名字,方便大模型调用)

@tool注解可以接受一个参数,这个参数就是我们给工具起的“自定义名称”;如果我们不指定这个参数,工具的名称就默认和函数名一样(比如get_current_date函数,默认名称就是get_current_date)。

示例代码(自定义工具名称,注释拉满):

# 导入需要的库和工具,和前面一致
import datetime
from langchain.tools import tool

# 这里我们给工具自定义名称:@tool("get_current_date")
# 括号里的"get_current_date",就是工具的自定义名称,和函数名一样,也可以改成其他名字(比如"get_date")
@tool("get_current_date")
def get_current_date():
    """获取今天日期"""  # 工具注释,给大模型看的说明书
    # 返回格式化后的日期,和前面一致
    return datetime.datetime.today().strftime("%Y-%m-%d")

注意事项(重点,避免踩坑):

工具的自定义名称,必须和“工具容器”(all_tools字典)里的key保持一致!比如我们给工具起名为"get_current_date",那么all_tools字典的key也必须是"get_current_date"(不能写成"getdate"、"GetCurrentDate"),否则电脑找不到工具,会报错。

比如:如果工具名称是"get_date",那么工具容器就要写成all_tools = {"get_date": get_current_date},不然会出错。

4.2 自定义工具描述(给大模型写“说明书”,让它知道怎么用工具)

工具描述,就是给大模型看的“说明书”,大模型会根据这个描述,判断:① 这个工具能解决什么问题;② 我要不要调用这个工具;③ 如果需要调用,该传递什么参数(比如查天气需要传递“城市名称”)。

工具描述有两种定义方式,我们都讲清楚,可以任选一种:

  1. 方式一:在工具函数内部添加注释(推荐)——就是我们前面写的“"""获取今天日期"""”,直接写在函数里面,大模型能自动识别。

  2. 方式二:在@tool注解的description属性中定制(更灵活,适合工具功能复杂的情况)——比如@tool(description="获取某个城市的天气,需要传入具体城市名称作为参数")。

补充重点:如果工具需要接收参数(比如“查询城市天气”的工具,需要知道“哪个城市”),必须在描述中添加“参数说明”,告诉大模型“需要传递什么参数”,不然大模型不知道该给工具传什么,会调用失败。

示例代码(带参数的工具,自定义工具描述):

# 导入需要的库和工具,和前面一致
import datetime
from langchain.tools import tool

# 方式二:通过@tool注解的description属性,自定义工具描述(给大模型看的说明书)
# description里写清楚:工具能做什么(获取某个城市的天气)、需要什么参数(具体城市名称)
@tool(description="获取某个城市的天气,需要传入具体城市名称作为参数")
# 定义工具函数get_city_weather,接收一个参数city(类型是str,就是字符串,比如"北京")
# city:str 表示这个参数是字符串类型,提醒我们只能传入城市名称(不能传入数字)
def get_city_weather(city:str):
    """获取某个城市的天气(方式一:函数内部注释,和description作用一样,双重保险)
    Args:  # Args表示“参数说明”,告诉大模型这个工具需要什么参数
    city: 具体城市(参数描述,明确告诉大模型:这个参数是“具体的城市名称”,比如北京、上海)
    """
    # 模拟返回天气结果(实际场景中,这里可以调用真实的天气API,获取真实天气数据)
    # 这里只是简单拼接字符串,演示工具的作用,实际用的时候可以替换成真实的API调用代码
    return "城市"+city+",今天天气不错"

# 后续工具绑定、调用流程,和前面“获取日期”的工具一致,逐行解读
# 1. 连接阿里云百炼大模型(和前面一致)
from config.load_key import load_key
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
 model="qwen-plus",
 base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
 openai_api_key=load_key("BAILIAN_API_KEY"),
)

# 2. 给大模型绑定“查询天气”的工具(把get_city_weather工具绑定给大模型)
llm_with_tools = llm.bind_tools([get_city_weather])

# 3. 定义工具容器(装工具的盒子,key是工具名称,和工具自定义名称一致)
all_tools = {"get_city_weather": get_city_weather}

# 4. 定义用户的查询问题(问大模型“北京今天的天气怎么样?”)
query = "北京今天的天气怎么样?"
# 5. 定义消息列表,存储聊天记录(问题、大模型响应、工具结果)
messages = [query]

# 6. 第一次调用大模型:大模型判断需要调用工具
# 大模型看到问题“北京今天的天气怎么样?”,再看工具描述,知道需要调用get_city_weather工具,并且传递“北京”作为参数
ai_msg = llm_with_tools.invoke(messages)
messages.append(ai_msg)  # 把大模型的工具调用请求加入消息列表

# 7. 打印大模型需要调用的工具(能看到调用的工具名称是get_city_weather,参数是{"city":"北京"})
print(ai_msg.tool_calls)

# 8. 执行工具,获取结果,反馈给大模型
if ai_msg.tool_calls:  # 判断需要调用工具
    for tool_call in ai_msg.tool_calls:  # 遍历工具调用请求
        # 根据工具名称,从工具容器里找到对应的工具
        selected_tool = all_tools[tool_call["name"].lower()]
        # 执行工具,传入参数(大模型会自动把“北京”传给city参数)
        tool_msg = selected_tool.invoke(tool_call)
        # 把工具结果加入消息列表
        messages.append(tool_msg)

# 9. 第二次调用大模型,结合工具结果,给出准确答案(比如“城市北京,今天天气不错”)
llm_with_tools.invoke(messages).content

关键提醒:

工具描述(包括参数描述)必须清晰、准确,不能模糊!比如不能只写“获取天气”,要写“获取某个城市的天气,需要传入具体城市名称作为参数”——如果描述模糊,大模型会不知道要不要调用工具,或者传递错误的参数(比如不传递城市名称),导致工具调用失败。

五、构建Agent执行工具(延伸逻辑)

前面我们写的代码,是“人工辅助”大模型调用工具(比如我们自己写代码,判断大模型是否需要调用工具、执行工具);而Agent(智能体),就是一个“全自动的智能助手”,能替代人工,完成“判断是否调用工具、调用哪个工具、执行工具、反馈结果”的全流程——不用我们手动写代码干预,大模型就能自动调用工具,给出答案。

结合我们前面的示例,Agent的执行逻辑,用大白话拆成5步,能轻松看懂:

  1. 第一步:Agent接收用户的查询(比如“今天是几月几号”“北京天气怎么样”)——相当于Agent收到了我们的“求助”;

  2. 第二步:Agent调用“绑定了工具的大模型”,让大模型判断:“这个问题我能自己回答吗?需要调用工具吗?调用哪个工具?”;

  3. 第三步:如果大模型说需要调用工具,Agent就根据大模型返回的“工具调用请求”,从工具容器里找到对应的工具,自动执行工具,获取工具返回的结果;

  4. 第四步:Agent把工具执行结果,自动反馈给大模型,让大模型结合结果,生成最终的准确答案;

  5. 第五步:Agent把大模型的最终答案,返回给用户——整个过程,我们不用手动写代码干预,Agent全自动化完成。

简言之,Agent就是“大模型的专属助手”,它帮大模型“打理”工具调用的所有琐事,让大模型能专注于“思考问题、生成答案”,也让我们不用手动写复杂的代码,就能实现“大模型+工具”的协同工作——这也是Tools工具机制落地的关键,能让大模型真正具备“自主扩展能力”。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐