⼯具调⽤根本作⽤是让⼤语⾔模型(LLM)具备与外部世界交互的能⼒。
LLM本⾝是⼀个封闭的知识系统,其能⼒受限于其训练数据(存在滞后性)和内在的⽂本⽣成逻辑。
如果没有工具调用,它⽆法执⾏直接计算、查询实时信息、操作数据库或调⽤任何外部API。⼯具调⽤打破了这层壁垒,其
作⽤具体体现在:
- 扩展能⼒边界:模型可以借助⼯具完成它⾃⾝⽆法完成的任务,如执⾏数学计算、搜索⽹络、查询
数据库等。
- 保证信息实时性:通过调⽤搜索⼯具或数据库查询⼯具,LLM可以获取最新的、训练数据中不存在
的信息,避免回答过时或“⼀本正经地胡说⼋道”。
- 处理复杂任务:将⼀个复杂的⽤⼾请求(如“分析我上个⽉的消费趋势”)分解成多个步骤,并依
次调⽤不同的⼯具(如“从数据库获取数据”->“⽤Python进⾏数据分析”->“⽣成图表”)
来协同完成。协调这件事这更体现在Agent智能体上。
- 连接现有系统:可以将企业内部已有的系统、API和数据库封装成⼯具,让LLM成为⼀个⽤⾃然语
⾔驱动的统⼀接⼝,极⼤地提升了⾃动化和集成能⼒
- 如,当我们希望获取当前天⽓情况时,由于LLM⽆法获取实时信息,此时我们就可以借助
天气搜索⼯具,通过外部服务进⾏搜索完成查询
- 再如,当我们希望获取数据库表中的数据时,由于LLM⽆法直接获取表数据,此时我们就可以借助
数据库查询⼯具,通过与数据库交互完成查询
创建工具
langchain提供了几种创建工具的方式
LangChain提供了@tool修饰器from langchain_core.tools import tool
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| from langchain_core.tools import tool
@tool def multiply(a:int,b :int)->int: """ 两个整数相乘
Args: a: 第一个整数 b: 第二个整数 """ return a * b
print(multiply.invoke({"a": 2, "b": 3})) print(multiply.name) print(multiply.description) print(multiply.args)
|
可以看出,⼯具通过@tool修饰的Python函数实现,其中:
- 该装饰器默认使⽤函数名称作为
⼯具名称。
- 该装饰器将使⽤函数的⽂档字符串作为
⼯具的描述。
因此,函数名、类型提⽰和⽂档字符串都是传递给⼯具Schema的⼀部分,不可缺失。定义好的描述是使模型良好运⾏的重要部分
使用Pydantic类
我们可以先使用Pydantic类对工具的结构化输入参数和描述进行描述
这样我们就可以用结构化的类描述结构化的输入了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| from pydantic import BaseModel,Field
class AddInput(BaseModel): """两个整数相加求和""" a: int = Field(..., description="First Integer") b: int = Field(..., description="Second Integer")
class MulInput(BaseModel): """两数相乘求积""" a: int = Field(..., description="First Integer") b: int = Field(..., description="Second Integer")
from langchain.tools import tool
@tool(args_schema=AddInput) def add(a:int, b:int)->int: return a+b
@tool(args_schema=MulInput) def mul(a:int, b:int)->int: return a*b
|
使用Annotated类型
我们使用from typing_extensions import Annotated来指定工具函数的输入参数并添加描述
这样只需要声明一个函数即可
1 2 3 4 5 6 7 8 9 10
| from typing_extensions import Annotated from langchain.tools import tool
@tool def add( a:Annotated[int, ..., "First integer"], b: Annotated[int, ..., "Second integer"] ): """两数相加""" return a+b
|
这个函数的定义如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @classmethod def from_function( cls, func: Callable | None = None, coroutine: Callable[..., Awaitable[Any]] | None = None, name: str | None = None, description: str | None = None, return_direct: bool = False, args_schema: ArgsSchema | None = None, infer_schema: bool = True, *, response_format: Literal["content", "content_and_artifact"] = "content", parse_docstring: bool = False, error_on_invalid_docstring: bool = False, **kwargs: Any, ) -> StructuredTool:
|
关键参数说明
- func:要设置的工具函数
- coroutine:协程函数,要设置的异步工具函数
- name:工具名称。默认为函数名称
- description:工具描述。默认为函数文档字符串
- args_schema:工具输入参数的 schema。默认为 None
- response_format:工具响应格式。默认为
content
常规用法:直接使用函数创建工具
实际上基本上就仅仅是代替了@tool修饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| from langchain_core.tools.structured import StructuredTool
def multiply(a:int,b :int)->int: """ 两个整数相乘
Args: a: 第一个整数 b: 第二个整数 """ return a * b
mul_tool = StructuredTool.from_function(func=multiply)
print(mul_tool.invoke({"a": 2, "b": 3})) print(mul_tool.name) print(mul_tool.description) print(mul_tool.args)
|
输出如下
1 2 3 4 5 6 7 8 9 10 11
| D:\program_software\AnaConda\envs\langChainP13\python.exe D:\codes\code_pycharm\langChainTool\tool.py 6 multiply 两个整数相乘
Args: a: 第一个整数 b: 第二个整数 {'a': {'title': 'A', 'type': 'integer'}, 'b': {'title': 'B', 'type': 'integer'}}
Process finished with exit code 0
|
依赖Pydantic类,并进行配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| from pydantic import BaseModel,Field
class MulInput(BaseModel): """两数相乘求积""" a: int = Field(..., description="First Integer") b: int = Field(..., description="Second Integer")
def mul(a:int, b:int)->int: return a*b from langchain_core.tools import StructuredTool
mul_tool = StructuredTool.from_function( func=mul, name="Calculator", description="两数相乘求积", args_schema=MulInput )
print(mul_tool.invoke({"a": 2, "b": 3})) print(mul_tool.name) print(mul_tool.description) print(mul_tool.args)
|
输出如下
1 2 3 4 5 6 7
| D:\program_software\AnaConda\envs\langChainP13\python.exe D:\codes\code_pycharm\langChainTool\tool.py 6 Calculator 两数相乘求积 {'a': {'description': 'First Integer', 'title': 'A', 'type': 'integer'}, 'b': {'description': 'Second Integer', 'title': 'B', 'type': 'integer'}}
Process finished with exit code 0
|
配置额外返回输出原始内容
之前的工具配置都是只会返回用于构建AI消息的content,但是如果我们需要借助工具获取原始数据进行进一步数据加工的话,我们可以在创建工具时配置函数传参response_format为"content_and_artifact",即可在原本仅返回content的基础上返回artifact,即其它工件,通常数据类型为python字典或者列表。此时返回类型应该标注为元组Tuple[]
下面是一个简单的示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| from pydantic import BaseModel,Field from typing import Tuple,List
class MulInput(BaseModel): """两数相乘求积""" a: int = Field(..., description="First Integer") b: int = Field(..., description="Second Integer")
def mul(a:int, b:int)->Tuple[int,List[int]]: nums = [a,b] content = f"{nums}相乘的结果为{a*b}" return content,nums from langchain_core.tools import StructuredTool
mul_tool = StructuredTool.from_function( func=mul, name="Calculator", description="两数相乘求积", args_schema=MulInput, response_format="content_and_artifact" )
print(mul_tool.invoke({"a": 2, "b": 3})) print(mul_tool.name) print(mul_tool.description) print(mul_tool.args)
|
但是此时直接调用的输出只有content
1 2 3 4 5 6 7 8
| D:\program_software\AnaConda\envs\langChainP13\python.exe D:\codes\code_pycharm\langChainTool\tool.py [2, 3]相乘的结果为6 Calculator 两数相乘求积 {'a': {'description': 'First Integer', 'title': 'A', 'type': 'integer'}, 'b': {'description': 'Second Integer', 'title': 'B', 'type': 'integer'}}
Process finished with exit code 0
|
若要输出artifact的内容,则需要模拟大模型调用的传参
1 2 3 4 5 6
| print(mul_tool.invoke({ "name": "Calculator", "args": {"a": 2, "b": 3}, "id": "123", "type": "tool_call" }))
|
1
| content='[2, 3]相乘的结果为6' name='Calculator' tool_call_id='123' artifact=[2, 3]
|
绑定工具
创建完工具后就该给大模型示例绑定工具了。使用大模型实例的.bind_tool()方法即可,咱们着重了解下接口参数
1 2 3 4 5 6 7 8 9 10
| def bind_tools( self, tools: Sequence[dict[str, Any] | type | Callable | BaseTool], *, tool_choice: dict | str | bool | None = None, strict: bool | None = None, parallel_tool_calls: bool | None = None, response_format: _DictOrPydanticClass | None = None, **kwargs: Any, ) -> Runnable[LanguageModelInput, AIMessage]:
|
tools:工具列表
tool_choice:默认为None,要求模型要使用哪些工具
- 形为
<<tool_name>>的str,调用<tool_name>工具
'auto':自动选择工具(包括不使用
'none'或者False或者None:不调用工具
any或者required或者True:强制至少使用一个工具
- strict:默认为N
one
True:要求模型输出与工具定义中的Json Schema完全匹配,输入也将根据Schema进行验证
False:不验证输入或输出
None:不传参
parallel_tool_calls :是否允许并行工具的使用,默认允许,值为None
kwargs(Any):附加参数
返回值:一个Runnable实例,就跟前面的大模型实例是同一个类型
调用工具
前面代码不变,我们来调用一下大模型
1 2 3 4 5 6
| from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="deepseek-chat",openai_api_base="https://api.deepseek.com/v1") model_with_tool =model.bind_tools([mul_tool])
print(model_with_tool.invoke("3乘5等于多少"))
|

可以看到大模型返回的参数中有一段工具调用的内容,由于云端的大模型只知道要调用哪些工具,具体的执行只能交给api调用者执行,所以我们只能收到调用工具的指令,具体的调用还得自己完成,但是已经可以自动化了
强制执行
前面绑定工具列表时的tool_choice参数的某些选项能强制调用工具
工具属性
我们知道模型调用的返回值时AIMessage类型,但是如果这次返回的消息调用了工具,那它将具有一个tool_calls属性,可以调用一下并打印出来
1 2 3 4
| result = model_with_tool.invoke("3乘5等于多少") print(result.tool_calls)
|
前面可以看到,模型初次调用只告诉了我们要调用哪些工具,所以现在要调用工具实例获取ToolMessage加入到消息列表中,再通知大模型工具调用的结果。
特别的,我们可以借助result.tool_calls[index]["name"]字段来指定要调用的工具
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage,ToolMessage
model = ChatOpenAI(model="deepseek-chat",openai_api_base="https://api.deepseek.com/v1") model_with_tool =model.bind_tools([mul_tool])
messages = [ HumanMessage("3乘5等于多少") ] ai_msg = model_with_tool.invoke(messages)
messages.append(ai_msg)
for tool_call in ai_msg.tool_calls: selected_tool = { "Calculator":mul_tool } tool_msg = selected_tool[tool_call["name"]].invoke(tool_call) messages.append(tool_msg)
print(messages) result = model_with_tool.invoke(messages) print(result) print(result.content)
|
1 2 3 4 5
| D:\program_software\AnaConda\envs\langChainP13\python.exe D:\codes\code_pycharm\langChainTool\tool.py [HumanMessage(content='3乘5等于多少', additional_kwargs={}, response_metadata={}), AIMessage(content='我来帮您计算3乘5等于多少。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 28, 'prompt_tokens': 175, 'total_tokens': 203, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 128}, 'prompt_cache_hit_tokens': 128, 'prompt_cache_miss_tokens': 47}, 'model_provider': 'openai', 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_ffc7281d48_prod0820_fp8_kvcache', 'id': '5a7b936c-ca69-4491-a231-0c34147e5af7', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--58ac78eb-d13a-484f-9f5f-af5024ff82af-0', tool_calls=[{'name': 'Calculator', 'args': {'a': 3, 'b': 5}, 'id': 'call_00_TWuelsiwNZTxJS9ih1sxXRBI', 'type': 'tool_call'}], usage_metadata={'input_tokens': 175, 'output_tokens': 28, 'total_tokens': 203, 'input_token_details': {'cache_read': 128}, 'output_token_details': {}}), ToolMessage(content='[3, 5]相乘的结果为15', name='Calculator', tool_call_id='call_00_TWuelsiwNZTxJS9ih1sxXRBI', artifact=[3, 5])] content='3乘5等于15。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 6, 'prompt_tokens': 216, 'total_tokens': 222, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 192}, 'prompt_cache_hit_tokens': 192, 'prompt_cache_miss_tokens': 24}, 'model_provider': 'openai', 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_ffc7281d48_prod0820_fp8_kvcache', 'id': '6027b7d0-5a81-4bff-b5bb-7ac5ad47e405', 'finish_reason': 'stop', 'logprobs': None} id='lc_run--70c07587-8100-40a8-bc4c-b362a74ec83d-0' usage_metadata={'input_tokens': 216, 'output_tokens': 6, 'total_tokens': 222, 'input_token_details': {'cache_read': 192}, 'output_token_details': {}} 3乘5等于15。
|
可以看到最后它得出了乘法的答案
LangChain提供的工具
LangChain网站也提供了很多现成的工具,点这里跳转🔗
使用示例:TavilySearch
TavilySearch 类可以⽀持我们进⾏搜索,Tavily是⼀个专⻔为AI设计的搜索引擎,专为智能体检索与
推理需求量⾝打造的⼯具。
Tavily 不仅提供了⾼度可编程的API接⼝,还具备显著优于传统搜索引擎的上下⽂相关性理解能⼒。
能够以结构化、可解析的形式返回搜索结果,便于将检索到的信息直接⽤于后续的推理、⽣成或任务
执⾏流程
我们需要挂梯子去官网申请Tavily的API KEY,戳我去官网
申请好后我们将API key的值写入环境变量TAVILY_API_KEY
然后我们安装相关的工具包pip install -U langchain-tavily
然后我们编写代码进行简单的调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage from langchain_tavily import TavilySearch
tavily_tool = TavilySearch(max_results=4)
model = ChatOpenAI(model="deepseek-chat",openai_api_base="https://api.deepseek.com/v1") model_with_tool =model.bind_tools([tavily_tool])
messages = [ HumanMessage("中国湖南长沙的天气怎么样") ] ai_msg = model_with_tool.invoke(messages)
messages.append(ai_msg)
for tool_call in ai_msg.tool_calls: tool_msg = tavily_tool.invoke(tool_call) messages.append(tool_msg)
result = model_with_tool.invoke(messages) print(result.content)
|
结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| D:\program_software\AnaConda\envs\langChainP13\python.exe D:\codes\code_pycharm\langChainTool\tool.py 根据查询结果,我为您提供湖南长沙的天气情况:
**当前天气情况:** - 温度:6℃(阴天) - 湿度:49% - 风向风力:北风2级
**今日天气预报:** - 白天:10℃,多云 - 夜间:4℃,多云
**未来几天天气趋势:** - 明天(11月14日):晴,10℃~21℃,北风转西北风<3级 - 后天(11月15日):多云,6℃~23℃,北风<3级转5-6级 - 周日(11月16日):多云,5℃~16℃,北风4-5级转<3级
**生活指数建议:** - 穿衣指数:较舒适,建议穿着吸湿排汗的夏装,棉麻质地的短打扮 - 感冒指数:少发 - 运动指数:适宜 - 洗车指数:适宜
总体来说,长沙近期天气以多云到晴为主,温度适中,适合外出活动。建议您根据实际体感温度适当增减衣物。
Process finished with exit code 0
|