使用LangChain进行AI应用构建-快速上手,定义模型和调用工具部分

使用LangChain进行AI应用构建-1.快速上手部分

一.前言

读者在进行学习之前,需要了解Python并自行解决魔法上网问题,最好是有其他语言的基础比如C/C++或JAVA。本文主要演⽰如何使⽤ LangChain 接⼊⼤模型组件,以及与⼤模型进⾏简单对话的能力。 掌握了基本⽤法后,将会引出 LangChain 相关前置概念,以便后续深⼊学习 LangChain 标准化组件。注意:Python版本为3.13版本

二.快速上手

2.1申请api-key

这里建议使用openai官方提供的apikey,官网https://platform.openai.com/

按照如下流程进行操作:


将 API Key 在⾃⼰本地保存好,后续接⼊ ChatGPT 时需要使⽤。

2.2添加api-key到环境变量中

在系统环境变量中新建一个名为OPENAI_API_KEY的环境变量,然后内容填入你上面创建的Key即可:

将 API Key 配置在环境变量中主要是为了保证其隐私性。由于 api key ⽐较隐私,为避免在程序中暴露,可提前将各个 api key 配置在环境变量中,这样在程序中就可以通过获取对应环境变量拿到 api key,保证了 api key 的隐私。

2.3定义大模型并进行简单的对话

1.安装openai包

bash 复制代码
pip install -U langchain-openai

2.一个简单的对话请求样例

python 复制代码
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_openai import ChatOpenAI
#定义OPENAI模型,如不填写api-key则默认从系统环境变量中获取key值
model = ChatOpenAI(model="gpt-4o-mini")

#定义系统消息与用户消息
messages = [
    SystemMessage(content="请帮我进行翻译,由中文翻译为英文!"),
    HumanMessage(content="你好啊,请问你是谁?")
]

#调用模型
result = model.invoke(messages)

print(result)

输出结果如下(此过程需要开启系统代理):

bash 复制代码
C:\code\LangChain\.venv\Scripts\python.exe C:\code\LangChain\test.py 
content='Hello! Who are you?' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 6, 'prompt_tokens': 31, 'total_tokens': 37, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_560af6e559', 'id': 'chatcmpl-CcTQXr0JVMrZzyTlkSLzW8kWtGaCY', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None} id='run--8fca99af-fa66-4c0f-a555-c187301c32a1-0' usage_metadata={'input_tokens': 31, 'output_tokens': 6, 'total_tokens': 37, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}

进程已结束,退出代码为 0

这里额外提一嘴,如果想要使用国内的api-key平台也不是不可以,接口大致都是一样的,但是实验时会因为模型导致实验结果与本文示例中实验模型输出的结果不一致,比如我们这里以硅基流动的api调用进行示例:

首先安装三方提供的硅基流动的软件包:

bash 复制代码
pip install -U langchain-siliconflow

然后配置环境变量,如上OPENAI的示例,但是这里变量名需要更改为:SILICONFLOW_API_KEY

接下来进行简单的对话:

python 复制代码
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_openai import ChatOpenAI
from langchain_siliconflow import ChatSiliconFlow
#定义硅基流动模型,默认从系统环境变量中获取key值
model = ChatSiliconFlow(model="deepseek-ai/DeepSeek-V3.1-Terminus",base_url="https://api.siliconflow.cn/v1")
##定义系统消息与用户消息
messages = [
    SystemMessage(content="可以帮我对下面这段话进行翻译吗,谢谢"),
    HumanMessage(content="你好啊,请问你是谁?")
]

result = model.invoke(messages)
print(result)

这时你去看它的输出结果是有问题的:

bash 复制代码
C:\code\LangChain\.venv\Scripts\python.exe C:\code\LangChain\test.py 
content='你好!我是DeepSeek,由深度求索公司创造的AI助手。很高兴认识你!😊\n\n我是一个纯文本模型,可以帮你解答各种问题、协助处理文字工作、进行对话交流等。虽然我不支持多模态识别,但我可以处理你上传的图像、txt、pdf、ppt、word、excel等文件,从中读取文字信息来帮助你。\n\n有什么我可以帮你的吗?无论是学习、工作还是生活上的问题,我都很乐意协助你!' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 99, 'prompt_tokens': 23, 'total_tokens': 122, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 0, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': None}, 'model_name': 'deepseek-ai/DeepSeek-V3.1-Terminus', 'system_fingerprint': '', 'id': '019a8c218310221d62262118c800df05', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None} id='run--6cf28266-1aff-4135-9ed4-bd48f8399710-0' usage_metadata={'input_tokens': 23, 'output_tokens': 99, 'total_tokens': 122, 'input_token_details': {}, 'output_token_details': {'reasoning': 0}}

进程已结束,退出代码为 0

它好像并没有理会我们给他提示的系统消息,这有可能是因为有些模型(特别是某些定制版本或特定架构的模型)可能没有完全实现标准的 ChatML 格式,导致对 system 角色的处理不一致。(当然博主也不确定是不是这个原因,我也是个入门者) 所以如果非要使用国内的api,如果出现上面结果不符合预期的情况,最暴力的方式就是合并系统信息与用户信息。方便起见我们还是使用openai官方的gpt-4o-mini模型。

不过并不是完全不能用,因为硅基流动平台兼容openai的请求格式,下面我们换种方式就能够成功了。详情请见3.1。

3.输出解析

如果我们不想要它json报文中的其他内容,只想要content部分的内容可以这样操作:

python 复制代码
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
#定义OPENAI模型,如不填写api-key则默认从系统环境变量中获取key值
model = ChatOpenAI(model="gpt-4o-mini")

#定义系统消息与用户消息
messages = [
    SystemMessage(content="请帮我进行翻译,由中文翻译为英文!"),
    HumanMessage(content="你好啊,请问你是谁?")
]

#调用模型
result = model.invoke(messages)
# print(result)

#定义解析器
parser = StrOutputParser()
print(parser.invoke(result))

这时输出结果就只有content部分了:

bash 复制代码
C:\code\LangChain\.venv\Scripts\python.exe C:\code\LangChain\test.py 
Hello, who are you?

进程已结束,退出代码为 0

注意一个点,通过上述步骤,⽆论是调⽤⼤模型,还是输出解析,我们发现,每次都调⽤了⼀个invoke() ⽅法,最终才会得到我们想要的结果。

对于 LangChain,它给我们提供了链式执⾏的能⼒,即我们只需要定义各个"组件",将它们"链起来",⼀次性执⾏即可得到最终效果。

示例代码如下:

python 复制代码
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
#定义OPENAI模型,如不填写api-key则默认从系统环境变量中获取key值
model = ChatOpenAI(model="gpt-4o-mini")

#定义系统消息与用户消息
messages = [
    SystemMessage(content="请帮我进行翻译,由中文翻译为英文!"),
    HumanMessage(content="你好啊,请问你是谁?")
]

#调用模型
# result = model.invoke(messages)
# print(result)

#定义解析器
parser = StrOutputParser()
# print(parser.invoke(result))

chain = model | parser
print(chain.invoke(messages))

此时便会得到和上面一样的结果。

2.4了解LangChain相关概念

1.Runnable 接口

Runnable 接⼝是使⽤ LangChain Components(组件)的基础。

概念说明:

Components(组件):⽤来帮助当我们在构建应⽤程序时,提供了⼀系列的核⼼构建块,例如语⾔模型、输出解析器、检索器、编译的 LangGraph 图等。

Runnable 定义了⼀个标准接口,允许 Runnable 组件:

  • Invoked(调⽤): 单个输⼊转换为输出。
  • Batched(批处理): 多个输⼊被有效地转换为输出。
  • Streamed(流式传输): 输出在⽣成时进⾏流式传输。
  • Inspected(检查): 可以访问有关 Runnable 的输⼊、输出和配置的原理图信息。
  • spected(检查): 可以访问有关 uable 的输⼊、输出和配置的原理图信息。
  • Composed(组合): 可以组合多个 Runnable,以使⽤ LCEL 协同⼯作以创建复杂的管道

因此,在上面的示例代码中,我们定义的语⾔模型(model)、输出解析器(StrOutputParser)都是Runnable 接⼝的实例,他们都使⽤了 Invoked(调⽤)的能力。

2.LangChain Expression Language

LangChain Expression Language(LCEL):采⽤声明性⽅法,从现有 Runnable 对象构建新的 Runnable 对象。

通过 LCEL 构建出的新的 Runnable 对象,被称为 RunnableSequence ,表⽰可运⾏序列。RunnableSequence 就是⼀种 链 (参考步骤6)。通过调试就能发现,chain 的类型就是RunnableSequence 。如下所示例:

重要的是, RunnableSequence 也是 Runnable 接⼝的实例 ,它实现了完整的 Runnable 接⼝,因此它可以⽤与任何其他 Runnable 相同的姿势使⽤。

可以看到,LCEL 其实是⼀种编排解决⽅案 ,它使 LangChain 能够以优化的⽅式处理链的运⾏时执⾏ 。任何两个 Runnable 实例都可以"链"在⼀起成序列。上⼀个可运⾏对象的 .invoke() 调⽤的输出 作为**输⼊**传递给下⼀个可运⾏对象。⽅法就是使⽤ | (管道/运算符):

python 复制代码
chain = model | parser

它通过两个 Runnable 对象去创建⼀个 RunnableSequence 。实际上 LangChain 重载了 | 运算符,使⽤ | 运算符就相当于:

python 复制代码
from langchain_core.runnables import RunnableSequence
chain = RunnableSequence(first=model, last=parser)

除此之外,LangChain还参考了Linux下进程间通信的函数pipe,可以通过Runnable的方法pipe去生成一个RunnableSequence对象,这也相当于 | 运算符:

python 复制代码
chain = model.pipe(parser)

三.核心聊天模型能力

3.1定义聊天模型

⼤语⾔模型 (LLM) 在各种与语⾔相关的任务(例如⽂本⽣成、翻译、摘要、问答等)中表现出⾊。现代 LLM 通常通过聊天模型 接⼝访问,该接⼝将消息列表 作为输⼊,并返回消息作为输出 ,⽽不是使⽤纯⽂本。

这⾥需要注意 LLM 与 LangChain 中 聊天模型 的关系:

  • 在 LangChain 的官⽅⽂档中,认为 LLM ⼤多数是纯⽂本补全模型 。这些纯⽂本模型封装的 API 接受⼀个字符串提⽰作为输⼊,并输出⼀个字符串补全结果(实际上 LLM 还包括多模态输⼊ )。
    OpenAI 的 GPT-5 就是作为 LLM 来实现的。
  • LangChain 中的 聊天模型 通常由 LLM 提供⽀持,但经过专⻔调整以⽤于对话。关键在于,它们不是接受单个字符串作为输⼊,⽽是接受聊天消息列表,并返回⼀条 AI 消息作为输出。

3.1.1通过API定义聊天模型

方式一:ChatOpenAI

ChatOpenAI 定义聊天模型在快速上⼿模块中已经涉及。

class langchain_openai.chat_models.base.ChatOpenAI 是 LangChain 为 OpenAI 的聊天模型(如 gpt-5 , gpt-5-mini )提供的具体实现类。

其继承了 class langchain_openai.chat_models.base.BaseChatOpenAI ,且BaseChatOpenAI 实现了标准的 Runnable 接⼝。

参数名 参数描述
model 要使用的 OpenAI 模型的名称
temperature 采样温度,温度值越高,AI 回答越天马行空;温度越低,回答越保守靠谱。
max_tokens 要生成的最大令牌数
timeout 请求超时时间
max_retries 最大重试次数
openai_api_key / api_key OpenAI API 密钥。如果未传入,将从环境变量中读取 OPENAI_API_KEY 。
base_url API 请求的基本 URL。
organization OpenAI 组织 ID。如果未传入,将从 env var OPENAI_ORG_ID 中读取。
...

temperature

对于温度字段,OpenAI的模型一般支持0-2的温度值 ,我们假设现在有一个问题,让ai帮助我们续写一段故事:一只小猫正在___?。不超过10个字,当温度值越高,ai回答的结果越具有想象力:

温度为0时回答结果:

python 复制代码
追逐一只老鼠(可能会有所不同,但5次测试下4次ai回答的都是追老鼠)

而温度为2时就比较富有想象力了:

python 复制代码
晒着太阳,满脸的享受

Token(令牌)简单说明

tokens并不是简单的等于一个字母或一个汉字或一个词,而是有着如下简单的对应关系:

说明项 描述
基本定义 Token 是文本在自然语言处理(NLP)中的基本单位,不等同于一个词或一个字。
英文文本估算 1 个 token ≈ 4 个字符 ≈ 0.75 个单词
中文文本估算 1 个汉字 ≈ 1.5 -- 2 个 token
资源消耗说明 中文文本通常比英文更耗费 token(资源密集)。

所以一个很遗憾的事实是,使用中文回更加的费token。

兼容OpenAI的大模型

对于deepseek,因为它是兼容OpenAI的,所以也可以直接使用这套接口,但是有部分参数需要进行特殊说明:

  • base_url :出于与 OpenAI 兼容考虑,要将 base_url 设置为 https://api.deepseek.com/v1 来使⽤,但注意,此处 v1 与模型版本⽆关。(此为deepseek官方文档中给出的说法
  • openai_api_key :需要单独申请 DeepSeek 的 API Key,然后重新进⾏环境变量配置。或者你直接作为参数传入也行。DeepSeek API Key 申请地址:https://platform.deepseek.com/api_keys

我们可以来使用OpenAI的这套接口来使用deepseek提供的api-key,参照官方给出的样例:

python 复制代码
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
model = ChatOpenAI(
    model="deepseek-chat",
    api_key="你自己的ds api-key",
    base_url="https://api.deepseek.com"
)

#定义系统消息与用户消息
messages = [
    SystemMessage(content="帮我将下面这段话翻译为英文"),
    HumanMessage(content="可以介绍下Python中的qrcode三方库吗?")
]

#定义解析器
parser = StrOutputParser()

chain = model | parser
print(chain.invoke(messages))

可以看到是能够正常输出结果的:

deepseek的模型温度支持0-1.5,可以在官方文档中查找到,官方还给出了适宜的使用场景:

哎,此时博主发现,硅基流动平台也是兼容OpenAI访问接口的。那我们来尝试下:

python 复制代码
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
model = ChatOpenAI(
    model="deepseek-ai/DeepSeek-V3.1-Terminus",
    api_key="你自己的硅基流动api-key",
    base_url="https://api.siliconflow.cn/v1"
)

#定义系统消息与用户消息
messages = [
    SystemMessage(content="帮我将下面这段话翻译为英文"),
    HumanMessage(content="可以介绍下Python中的qrcode三方库吗?")
]

#定义解析器
parser = StrOutputParser()

chain = model | parser
print(chain.invoke(messages))

ok啊,也是没有问题的:

方式二的前置说明:invoke调用

介绍 ⽅式2 之前,需要先来了解⼀下关于 Runnable 接⼝中的 .invoke() 调⽤。该⽅法是将单个输⼊转换为对应的输出。例如对于聊天模型来说,就是根据⽤⼾的问题输⼊,输出相应的答案。
invoke() 方法定义:

python 复制代码
abstractmethod invoke(
	input: Input,
	config: RunnableConfig | None = None,
	**kwargs: Any,
) → Output

请求参数:

  • input :输⼊⼀个 Runnable 实例
  • config (默认空):⽤于 Runnable 的配置。(类型为字典类型,下面使用的时候就明白了)

返回值:

  • 返回⼀个 Runnable 实例

class langchain_core.runnables.config.RunnableConfig 常⽤参数说明

参数名 参数描述
configurable 通过 .configurable_fields() 在此 Runnable 或子 Runnable 上配置的属性运行时间值。
run_id 针对此调用运行的跟踪器的唯一标识符。如果未提供,将生成新的 UUID。
run_name 此调用的跟踪器运行的名称。默认为类的名称。
metadata 此次调用和任何子调用的元数据。键是字符串,值是 JSON。类型:dict[str, Any]
......

具体使用看方法二中的示例即可。

方式二:init_chat_model

上⾯的 ChatOpenAI ⽤于明确创建 OpenAI 聊天模型的实例。⽽ init_chat_model() 是⼀个⼯⼚函数,它可以初始化多种⽀持的聊天模型(如 OpenAI、Anthropic、FireworksAI 等),不仅仅是 OpenAI 的聊天模型。
init_chat_model() 函数定义

python 复制代码
langchain.chat_models.base.init_chat_model(
    model: str,
	*,
	model_provider: str | None = None,
	configurable_fields: Literal[None] = None,
	config_prefix: str | None = None,
	** kwargs: Any,
) → BaseChatModel

init_chat_model() 常⽤参数说明

参数名 参数描述
model 要使用的模型的名称
model_provider 模型提供方。支持的 model_provider 值和相应的集成包有: • openai -> langchain-openaianthropic -> langchain-anthropicgoogle_genai -> langchain-google-genaiollama -> langchain-ollamadeepseek -> langchain-deepseek • ... 如果未指定,将尝试从模型推断 model_provider。
configurable_fields 设置哪些模型参数是可配置的。若配置为: • None: 没有可配置的字段。 • any: 所有字段都是可配置的,类似 api_keybase_url 等可以在运行时更改。 • Union[List[str], Tuple[str, ...]]: 指定的字段是可配置的。
config_prefix 配置为非空字符串,则模型将在运行时通过查找 config["configurable"]["config_prefix"].params 字段设置配置项。 配置为空字符串,那么模型将可以通过 config["configurable"]["param"] 字段设置配置项。
temperature 采样温度,温度值越高,AI回答越天马行空;温度越低,回答越保守靠谱。
max_tokens 要生成的最大令牌数
timeout 请求超时时间
max_retries 最大重试次数
openai_api_key / api_key OpenAI API密钥。如果未传入,将从环境变量中读取 OPENAI_API_KEY
base_url API请求的基本URL。
...

可以看到很多的参数是重复的,重复的部分下面不再多说。
init_chat_model() 返回值说明

函数返回⼀个与指定的 model_namemodel_provider 相对应的 BaseChatModel (如 ChatOpenAI , ChatAnthropic 等)。注意要是模型可配置,则返回⼀个聊天模型模拟器,该模拟器在传⼊配置后,于运⾏时才会初始化底层模型。

方式二的三种示例使用

在此之前记得先安装deepseek的三方包。

python 复制代码
pip install -U langchain-deepseek

基本使用

python 复制代码
from langchain.chat_models import init_chat_model
gpt_model = init_chat_model(model="gpt-4o-mini",model_provider="openai")
ds_model = init_chat_model(model="deepseek-chat",model_provider="deepseek")

print(f"gpt_model:{gpt_model.invoke("你是谁").content}")
print(f"ds_model:{ds_model.invoke("你是谁").content}")
python 复制代码
gpt_model:我是一个人工智能助手,专门用来回答问题和提供信息。有什么我可以帮助你的吗?
ds_model:你好!我是DeepSeek,由深度求索公司创造的AI助手!😊

我是一个纯文本模型,虽然不支持多模态识别功能,但我有文件上传功能,可以帮你处理图像、txt、pdf、ppt、word、excel等文件,并从中读取文字信息进行分析处理。我完全免费使用,拥有128K的上下文长度,还支持联网搜索功能(需要你在Web/App中手动点开联网搜索按键)。

你可以通过官方应用商店下载我的App来使用我。我很乐意帮助你解答问题、协助工作学习,或者只是聊聊天!有什么我可以帮你的吗?✨

可配置模型

我们发现,上面两种调用方式仅在model与model_provider上有区别,我们能不能仅使用一个BaseChatModel对象去调用两个不同的模型,可以参考如下代码:

python 复制代码
from langchain.chat_models import init_chat_model
model = init_chat_model(temperature=1.5)
result = model.invoke(
    input="你是谁",
    config={
        "configurable":{
            "model" : "gpt-4o-mini",
            "model_provider" : "openai"
        }
    }
)
print(result.content)
result = model.invoke(
    input="你是谁",
    config={
        "configurable":{
            "model" : "deepseek-chat",
            "model_provider" : "deepseek"
        }
    }
)
print(result.content)

回答内容和基本使用那部分差不多,这里就不贴出来了。
可配置模型(带上默认值)

我们上面实现了仅使用一个BaseChatMode去调用两个不同的模型,不过我们的init_chat_model此时没有默认模型,如果有默认模型呢,这个模型因为需要被修改,对应model与model_provider字段需要被修改,所以我们需要在init_chat_model中进行配置:

python 复制代码
from langchain.chat_models import init_chat_model

model = init_chat_model(
    model="deepseek-chat",
    model_provider="deepseek",
    temperature=1.5,
    configurable_fields=["model","model_provider"],
    config_prefix="pre"
)
print(model.invoke("你是谁").content)
result = model.invoke(
    input="你是谁",
    config={
        "configurable":{
            "pre_model" : "gpt-4o-mini",
            "pre_model_provider" : "openai"
        }
    }
)
print(result.content)
python 复制代码
你好!我是DeepSeek,由深度求索公司创造的AI助手!😊

我是一个纯文本模型,虽然不支持多模态识别功能,但我有文件上传功能,可以帮你处理图像、txt、pdf、ppt、word、excel等各种格式的文件,从中读取文字信息进行分析处理。我完全免费使用,拥有128K的上下文长度,还支持联网搜索功能(需要你在Web/App中手动开启)。

你可以通过官方应用商店下载我的App,也可以通过网页版与我对话。我很乐意帮助你解答问题、处理文档、进行创作或任何其他我能协助的事情!

有什么我可以帮你的吗?无论是学习、工作还是生活上的问题,我都很愿意为你提供帮助!✨
我是一个人工智能助手,旨在提供信息和解答问题。如果有任何你想了解的内容,欢迎随时问我!

3.1.2通过本地部署的 LLM 定义聊天模型(仅以Ollama为例子)

本地部署很简单,只需要安装ollama的包即可:

python 复制代码
pip install -U langchain_ollama
ChatOllama 常用初始化参数说明
参数名 参数描述
model 要使用的 Ollama 模型的名称
temperature 采样温度,温度值越高,AI 回答越天马行空;温度越低,回答越保守靠谱。
timeout 请求超时时间
base_url API 请求的基本 URL。
num_ctx 设置用于生成下一个令牌的上下文窗口的大小。(默认值:2048)
num_gpu 要使用的 GPU 数量。在 macOS 上,默认为 1 表示启用金属支持,默认为 0 表示禁用。
......

调用方式大差不差,因为默认ollama服务是在本地的端口上启动的,所以我们以ip+端口号的方式填写url即可:

python 复制代码
from langchain_ollama import ChatOllama

model = ChatOllama(model="deepseek-r1:1.5b",base_url="http://127.0.0.1:11434")
print(model.invoke("你是谁?").content)
python 复制代码
C:\code\LangChain\.venv\Scripts\python.exe C:\code\LangChain\ollamatest.py 
您好!我是由中国的深度求索(DeepSeek)公司开发的智能助手DeepSeek-R1。如您有任何任何问题,我会尽我所能为您提供帮助。

进程已结束,退出代码为 0

3.2聊天模型-调用工具

平时一般我们使用的都是大语言模型,我说一句话你说一句话。但是有的时候,我们需要LLM去主动调用一些工具来回答我们的问题,比如让他查询下数据库找符合条件的数据进行总结。或者是搞一个聊天机器人让他能够结合聊天记录进行回答,那这时我们就需要通过LangChain提供的一些方式来创建我们自定义的工具让LLM去调用。

3.2.1常见的三种定义工具方法

一共有三种定义方式,最直观的方式如下:

python 复制代码
from langchain_core.tools import tool

@tool
def add(a : int,b : int,c : int) -> int:
    '''
    三数相加
    Args:
        a: 第一个参数
        b: 第二个参数
        c: 第三个参数
    '''
    return a + b + c

print(add.invoke({"a" : 10,"b" : 20,"c" : 30}))
python 复制代码
C:\code\LangChain\.venv\Scripts\python.exe C:\code\LangChain\ollamatest.py 
60

进程已结束,退出代码为 0

需要注意的是工具函数必须加上如下三点内容:

  1. 函数名称
  2. 字符串文档提示
  3. 类型注解

少一样都会抱错,为什么。我们可以看一个JSON Schema的例子:

什么是 Schema ?

想象以不同的⽅式,在 JSON 中表⽰有关⼀个⼈的信息:

xml 复制代码
{
	"name": "张⼩红",
	"birthday": "1732年2⽉22⽇",
	"address": "陕西省西安市雁塔区"
}
html 复制代码
{
	"surname": "王",
	"given_name": "刚",
	"birthday": "1732-02-22",
	"address": {
		"district": "萧⼭区",
		"city": "杭州市",
		"province": "浙江省"
		"country": "中国"
	}
}

我如果想要一个人的记录时,显然要第二种的更为详细。人是可以一眼看出来那个更详细的,但是程序不可以,此时就需要JSON Schema做校验,来确保程序给我的信息即为第二种格式而不是第一种格式,以上面为例子我们可以得到如下的一个JSON Schema:

html 复制代码
{
    "type": "object",
    "properties": {
        "surname": {
            "type": "string"
        },
        "given_name": {
            "type": "string"
        },
        "birthday": {
            "type": "string",
            "format": "date"
        },
        "address": {
            "type": "object",
            "properties": {
                "district": {
                    "type": "string"
                },
                "city": {
                    "type": "string"
                },
                "province": {
                    "type": "string"
                },
                "country": {
                    "type": "string"
                }
            }
        }
    }
}

程序按照这样的一个JSON Schema即可准确无误的生成上面我们指定的第二种格式的个人信息。

对于LangChain这里的Schema也是同理,毕竟最终这个工具是要给LLM调用的,那么就需要让LLM知道这个函数在干什么,该怎么用,相当于去给LLM写提示词。
关于工具文档的编写格式

有了以上概念铺垫,对于⼯具schema,它将从函数名、类型提⽰和⽂档字符串中获取相关属性,以此来声明⼀个⼯具,包括其名称、描述、输⼊参数、输出类型等等。这⾥需要说明的是,若是简单定义⼯具,如上述⽰例,⼯具 schema 需要解析 Google ⻛格的⽂档字符串去获取【参数描述】。

什么是 Google ⻛格的⽂档字符串?Google ⻛格是 Python ⽂档字符串的⼀种写作规范。它并⾮ Python 语⾔官⽅强制要求,⽽是由 Google 为其内部 Python 项⽬制定的规范,后来因为其极⾼的可读性和简洁性⽽在整个 Python 社区中变得⾮常流⾏。它使⽤ Args: , Returns: 等关键字,参数描述简洁明了,如下所⽰:

python 复制代码
def fetch_data(url, retries=3):
    """从给定的URL获取数据。
    Args:
        url (str): 要从中获取数据的URL。
        retries (int, optional): 失败时重试的次数。默认为3。
    Returns:
        dict: 从URL解析的JSON响应。
    """
# ... 函数实现 ...

第二种定义工具的方式是依赖 Pydantic 类

若使⽤ @tool 定义⼯具时,没有提供⽂档字符串,则会报错,我们上面已经说过了,但可以通过Pydantic将注解文档与函数体分离:

python 复制代码
from langchain_core.tools import tool
from pydantic import BaseModel, Field

class myAddInput(BaseModel):
    '''三数相加'''
    a: int = Field(...,description="第一个参数")
    b: int = Field(...,description="第二个参数")
    c: int = Field(...,description="第三个参数")

@tool(args_schema=myAddInput)
def add(a : int,b : int,c : int) -> int:
    return a + b + c

print(add.invoke({"a" : 10,"b" : 20,"c" : 30}))

结果与第一种示例方式一样。

第三种方式是依赖 Annotated

在 LangChain 中,可以依赖 Annotated 和⽂档字符串传递给⼯具 Schema 。如下所⽰:

python 复制代码
from langchain_core.tools import tool
from typing_extensions import Annotated

@tool
def add(
    a: Annotated[int,...,"第一个参数"],
    b: Annotated[int,...,"第二个参数"],
    c: Annotated[int,...,"第三个参数"],
) -> int:
    '''三数之和'''
    return a + b + c

print(add.invoke({"a" : 10,"b" : 20,"c" : 30}))

结果与第一种示例方式一样。

3.2.2 使用 StructuredTool 类提供的函数定义工具

class langchain_core.tools.structured.StructuredTool 类⽤来初始化⼯具,其中from_function 类⽅法通过给定的函数来创建并返回⼀个⼯具。 from_function 类⽅法定义如下:

python 复制代码
classmethod from_function(
	func: Callable | None = None,
	coroutine: Callable[[...], Awaitable[Any]] | None = None,
	name: str | None = None,
	description: str | None = None,
	return_direct: bool = False,
	args_schema: type[BaseModel] | dict[str, Any] | 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"。
    如果配置为 "content" ,则⼯具的输出为 ToolMessage 的 content 属性。
    对于 HumanMessage 、 AIMessage 已经⻅过,分别表⽰ ⽤⼾消息 和 AI消息响应 ,对于 ToolMessage ,它表⽰对应⼯具⻆⾊所发出的消息。
    如果配置为 "content_and_artifact" ,则输出应是与 ToolMessage 的 content 属性
    与 artifact 属性相对应的⼆元组。(⽤法见下⾯的⽰例)

最简单的使用方式就是把想要绑定的函数直接传给它然后使用其返回的对象进行调用:

python 复制代码
from langchain_core.tools import StructuredTool

def add(a : int,b : int) -> int:
    '''两数相加

    Args:
        a (int): 第一个参数
        b (int): 第二个参数
    Returns:
        int: 相加结果
    '''
    return a + b

myFunc = StructuredTool.from_function(add)

print(myFunc.invoke({"a" : 100,"b" : 200}))

当然如果你不想把注释什么的都写到函数里面,也可以借助Pydantic类 ,同时借助使⽤description 参数,替代⽂档字符串中对于⼯具描述的 schema 属性。

python 复制代码
from langchain_core.tools import StructuredTool
from pydantic import BaseModel, Field

def add(a : int,b : int) -> int:
    return a + b

class AddInput(BaseModel):
    a : int = Field(description="第一个整型参数")
    b : int = Field(description="第二个整型参数")

myFunc = StructuredTool.from_function(
    func=add,
    name="AddFunc",
    description="两数相加",
    args_schema=AddInput
)

print(myFunc.invoke({"a" : 100,"b" : 200}))

有的时候,如果模型的调用不符合我们的预期,我们一般会去查看其调用的过程。大多数api传给大模型的结果一般是json格式的,大模型通过解析json字符串来总结出最终的content返回给我们结果,我们模仿下大模型调用的姿势更改下上面的代码:

python 复制代码
from langchain_core.tools import StructuredTool
from pydantic import BaseModel, Field
from typing import Tuple, List


def add(a : int,b : int) -> Tuple[str,List[int]]:
    content = f"两数相加{a}和{b}的结果是{a + b}"
    artifact = [a,b]
    return content,artifact

class AddInput(BaseModel):
    a : int = Field(description="第一个整型参数")
    b : int = Field(description="第二个整型参数")

myFunc = StructuredTool.from_function(
    func=add,
    name="AddFunc",
    description="两数相加",
    args_schema=AddInput,
)

print(myFunc.invoke({"a" : 100,"b" : 200}))

返回结果是这样的:

python 复制代码
C:\code\LangChain\.venv\Scripts\python.exe C:\code\LangChain\test3.py 
('两数相加100和200的结果是300', [100, 200])

进程已结束,退出代码为 0

这是在LangChain里面常规情况下LLM处理完毕后看到的真实全部信息,第一个位置是给用户看的,第二个位置是给大模型看的。就好比一个软件出错,用户看到的是软件给的错误信息content,而开发人员看到的是软件底层给出的错误日志artifact

但是对于大模型而言,这样的结果终究只是一个字符串,人可以看懂但是LLM看不懂。那么在定义工具时就需要加上额外的选项来让LangChain解析结果来告诉LLM,这个选项就是response_format

response_format

如果希望我们的⼯具区分消息内容(content)和其他⼯件(artifact),让⼤模型读取 content ,⽽⼀些⽤来构造 content 的原始数据保存下来,若后续有⼀些记录、分析的步骤,就可以派上用场了,这就是 artifact 。 artifact 通常需要使用字典 Dict 或列表 List 保存。

我们需要在定义⼯具时指定 response_format="content_and_artifact" 参数,并确保我们返回⼀个元组 (content, artifact)

上⾯的代码中,我们将需要相加的数据当作原始数据,作为样例。(说明: @tool 也⽀持response_format 参数)

如果我们直接使⽤⼯具参数调⽤⼯具,将只返回输出的 content 部分,所以使用工具时也需要模仿大模型调用的姿势来进行调用:

python 复制代码
from langchain_core.tools import StructuredTool
from pydantic import BaseModel, Field
from typing import Tuple, List


def add(a : int,b : int) -> Tuple[str,List[int]]:
    content = f"两数相加{a}和{b}的结果是{a + b}"
    artifact = [a,b]
    return content,artifact

class AddInput(BaseModel):
    a : int = Field(description="第一个整型参数")
    b : int = Field(description="第二个整型参数")

myFunc = StructuredTool.from_function(
    func=add,
    name="AddFunc",
    description="两数相加",
    args_schema=AddInput,
    response_format="content_and_artifact"
)

print(myFunc.invoke(
    {
        "name" : "AddFunc",
        "args" : {"a" : 100,"b" : 200},
        "id" : "1",# 必须,与⼯具调用关联的标识符,将⼯具调用请求与⼯具调用结果相关联。(常见起作用与并发场景)
        "type" : "tool_call"# 必须
    }
))

返回结果如下:

python 复制代码
C:\code\LangChain\.venv\Scripts\python.exe C:\code\LangChain\test3.py 
content='两数相加100和200的结果是300' name='AddFunc' tool_call_id='1' artifact=[100, 200]

进程已结束,退出代码为 0

3.2.3 绑定工具

bind_tools

绑定工具很简单,使⽤聊天模型的 .bind_tools() ⽅法。如下所⽰:

python 复制代码
import os
import qrcode
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.tools import tool
from langchain_deepseek import ChatDeepSeek

@tool
def createQrCode(tarStr : str,outputPath : str) -> str:
    '''生成二维码的工具函数

    Args:
        tarStr (str):用户给的目标文字
        outputPath (str):用户期望二维码输出的实际本机路径
    Returns:
        str:成功返回用户传入的outputPath,失败返回错误信息
    '''
     # 1. 提取文件夹路径
    output_dir = os.path.dirname(outputPath)
    if os.path.exists(output_dir):
        img = qrcode.make(tarStr)
        img.save(outputPath)
        return outputPath
    return "请检查所给的输出路径是否存在"

@tool
def add(a : int,b : int) -> int:
    '''求两数之和的工具函数

    Args:
        a (int):第一个参数
        b (int):第二个参数
    Returns:
        int:计算所得结果
    '''
    return a + b

model = ChatDeepSeek(model="deepseek-chat")

tools = [createQrCode,add]
model_with_tools = model.bind_tools(tools=tools)#绑定后返回一个新model,新的model带有绑定好的工具

bind_tools() 方法定义:

python 复制代码
bind_tools(
	tools: Sequence[dict[str, Any] | type | Callable | BaseTool],
	*,
	tool_choice: dict | str | Literal['auto', 'none', 'required', 'any'] | bool
	| None = None,
	strict: bool | None = None,
	parallel_tool_calls: bool | None = None,
	**kwargs: Any,
) → Runnable[PromptValue | str | Sequence[BaseMessage | list[str] |
tuple[str, str] | str | dict[str, Any]], BaseMessage]

请求参数:

  • tools :绑定到此聊天模型的工具定义列表。支持的类型为:字典、pydantic.BaseModel 类、Python 函数和 BaseTool(如 @tool 装饰器创建的类)。
  • tool_choice (默认空):要求模型调用哪个工具。可以设置为:
    • 形式为 '<<tool_name>>'str:调用 <tool_name> 工具。
    • 'auto':自动选择工具(包括无工具)。
    • 'none':不调用工具。
    • 'any''required'True:强制调用至少一个工具。
    • FalseNone:无效果,默认 OpenAI 的行为。
  • strict (默认空):
    • 如果为 True,则保证模型输出与工具定义中提供的 JSON Schema 完全匹配。输入也将根据提供的 Schema 进行验证。
    • 如果为 False,则不会验证输入,也不会验证模型输出。
    • 如果为 None ,则不会将 strict 参数传递给模型。
  • parallel_tool_calls :默认为 None,允许并行工具使用。设置为 False 以禁用并行工具。
  • kwargs(Any) :任何附加参数都直接传递给 bind()

返回值:

  • 返回一个 Runnable 实例。
    • 该实例支持多种格式输入:
      • 原始提示 PromptValue
      • 字符串:"上海天气如何?"
      • 消息或消息列表:[HumanMessage(content="...")]
    • 该实例的输出:
      • 包含工具调用信息的 AIMessage

3.2.4调用工具

ok了,上面我们工具也定义好了,然后调用工具如果按照前面说的逻辑,那么我们直接invoke就可以了,但是实际上是不行的,我们invoke下就知道了,比如我基于绑定工具部分的代码添加如下代码发起调用()😦这里注意一个点,如果你的问题语义让LLM判断符合度与工具提示文本schema不是那么高的话,它不一定会去调用工具,但是你可以在给model绑定工具时设置一个tool_choice="any"让大模型至少去调用一个工具

python 复制代码
print(model_with_tools.invoke("2+300的结果是"))

这里我们发现原本应该有内容的部分content为空了,我把结果分片展示出来如下:

不要关心别的地方,就看我标红的地方。首先我们前面不是说了模仿大模型调用工具的姿势才能看到内容吗,注意看这里的tool_calls,说明经过大模型判断它主动根据我们的问题调用了我们提供的工具。但是我们不想让它只是这种形式给我们返回啊,我们想要的是LLM根据工具的调用结果进行总结然后告诉我们比如2加300的结果是302这样的回答。

还记得我们最开始快速上手部分搞了一个Message列表传给model了吗,实际上,这里想要解决这个问题,按照逻辑就需要传ToolMessage对象给LLM才能让他根据工具调用结果为我们总结答案:

但是实际上这里返回的消息类型并不是ToolMessage,而是AIMessage,这个也需要传给LLM:

输出说明:

  • AIMessage :来自 AI 的消息。从聊天模型返回,作为对提示(输入)的响应。
    • content:消息的内容。
    • additional_kwargs:与消息关联的其他有效负载数据。对于来自 AI 的消息,可能包括模型提供程序编码的工具调用。
    • response_metadata :响应元数据。例如:响应标头、logprobs、令牌计数、模型名称。

结论 :从输出结果看来,AI 给出的响应是进行工具的调用!!

到这里可以发现,我们仅仅只是成功调用了工具,但是聊天模型并没有给我们返回我们真正需要的答案。此时就需要:

  1. 传递上下文 :将工具输出传递给聊天模型,包括 HumanMessageAIMessage (工具调用)。
  2. 获取最终结果 :聊天模型根据以上消息输入,将最终结果 AIMessage 返回。

为什么要发送 ToolMessage 呢?

之前我们讲过,聊天模型通常不是接受单个字符串作为输入,而是接受聊天消息(XxxMessage)列表 。因此在这里我们需要将工具的返回,构造为 ToolMessage,再传输给聊天模型!!!

便捷操作:

如果我们使用 @tool 装饰器创建的工具,使用 tool.invoke(tool_calls)自动返回 一个 ToolMessage

那么我们基于上面部分的代码将最后一行print部分修改为如下逻辑:

python 复制代码
messages = [
    HumanMessage("2+300的结果是")
]

ai_message = model_with_tools.invoke(messages)
messages.append(ai_message)

#检查LLM是否实际调用工具的逻辑并生成ToolMessage列表
for tool in ai_message.tool_calls:
    # 根据⼯具名选择对应工具函数(不区分大小写)
    selected_tool = {"createqrcode" : createQrCode,"add" : add}[tool["name"].lower()]
    # 执行工具调用,返回 ToolMessage
    tool_message = selected_tool.invoke(tool)
    # 将 ToolMessage 加⼊消息
    messages.append(tool_message)

print(model_with_tools.invoke(messages))

注意啊,必须把AiMessage也传给模型,也就是让模型衔接上下文,

我们可以通过下文理解消息队列的传递逻辑:

  • 如果没有 ai_message:模型看到的历史是 [用户:2+300, 工具结果:302]。这不符合 OpenAI 协议的逻辑顺序,会导致模型无法正确关联。
  • 有了 ai_message:模型看到的历史是 [用户:2+300, AI:我想算add(2,300), 工具结果:302]。模型立刻明白:"哦,计算已经完成了,结果是 302",然后它会组织语言回复你。

此时我们就可以看到如下结果,content此时就不为空了:

当然你的模型回答的结果可能与这不一致,反正意思大差不差就是了。当然我们也可以一条语句中问两个或多个问题,让ai帮我们去调用我们上面写的两个工具,我们把HumanMessage改为如下内容:

python 复制代码
messages = [
    HumanMessage("将'https://www.runoob.com/python3/python3-tutorial.html'基于这段单引号的文本"
                 "生成一个二维码图片到'C:\\Users\\15890\\Desktop\\qrcode.png'这个单引号括住的本机路径下面吗。除此之外2+300的结果是")
]

但是此时会有一个问题,无论我们给模型的输出路径是否正确,他都是貌似只调用了一个工具这是因为(我们以输入的路径是错误的情况举例子):

  • 你提出的需求是双重的:生成二维码 + 做加法。

  • 模型的反应:虽然你希望它一次性调用两个工具,但有些模型在第一次 invoke 时,可能只识别到了 createQrCode 的调用,或者在生成的 ai_message 中只包含了一个工具请求。

  • 报错信息的干扰:你的 createQrCode 函数中有个逻辑:if os.path.exists(outputPath):。如果你提供的路径 C:\Users\15890\Desktop\qrcode.png 还没有这个文件,os.path.exists 会返回 False(因为它既检查目录也检查文件)。

  • 结果:工具返回了 "请检查所给的输出路径是否存在"。

  • 模型的连锁反应:模型看到工具报错了,它在第二次回复时(即你 print 的那个结果),试图停下来解释路径问题,同时它发现"加法"还没做,于是又发出了一个 tool_calls 请求去调用 add。

那么我们想要的最终结果便是让ai调用完毕工具之后再给我们答复,那么就需要循环遍历,具体修改后的完整代码如下:

python 复制代码
import qrcode
from langchain_core.messages import HumanMessage, SystemMessage, ToolMessage
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI

@tool
def createQrCode(tarStr : str,outputPath : str) -> str:
    '''生成二维码的工具函数

    Args:
        tarStr (str):用户给的目标文字
        outputPath (str):用户期望二维码输出的实际本机路径
    Returns:
        str:成功返回用户传入的outputPath,失败返回错误信息
    '''
    # 1. 提取文件夹路径
    output_dir = os.path.dirname(outputPath)
    if os.path.exists(output_dir):
        img = qrcode.make(tarStr)
        img.save(outputPath)
        return outputPath
    return "请检查所给的输出路径是否存在"

@tool
def add(a : int,b : int) -> int:
    '''求两数之和的工具函数

    Args:
        a (int):第一个参数
        b (int):第二个参数
    Returns:
        int:计算所得结果
    '''
    return a + b

model = ChatOpenAI(
    model="deepseek-ai/DeepSeek-V3.1-Terminus",
    api_key="你自己的api-key",
    base_url="https://api.siliconflow.cn/v1"
)

tools = [createQrCode,add]
model_with_tools = model.bind_tools(tools=tools)#绑定后返回一个新model,新的model带有绑定好的工具

messages = [
    HumanMessage("将'https://www.runoob.com/python3/python3-tutorial.html'基于这段单引号的文本"
                 "生成一个二维码图片到'C:\\Users\\15890\\Desktop\\qrcode.png'这个单引号括住的本机路径下面吗。除此之外2+300的结果是")
]

while True:
    ai_message = model_with_tools.invoke(messages)
    #当大模型不再有工具调用行为时,退出循环
    if not ai_message.tool_calls:
        break
    #将ai回答的结果加入到上下文中,确保上下文完整
    messages.append(ai_message)
    # 检查LLM是否实际调用工具的逻辑并生成ToolMessage列表
    for tool in ai_message.tool_calls:
        # 根据⼯具名选择对应工具函数(不区分大小写)
        selected_tool = {"createqrcode": createQrCode, "add": add}[tool["name"].lower()]
        # 执行工具调用,返回 ToolMessage
        tool_message = selected_tool.invoke(tool)
        # 将 ToolMessage 加⼊消息
        messages.append(tool_message)

print(model_with_tools.invoke(messages))

此时就没有问题了:

也可以看下content简化后的结果(这里我又重新调了一次):

python 复制代码
C:\code\LangChain\.venv\Scripts\python.exe C:\code\LangChain\test4.py 
任务完成!✅

1. **二维码生成**:已成功将 URL 'https://www.runoob.com/python3/python3-tutorial.html' 生成二维码图片,保存在 'C:\Users\15890\Desktop\qrcode.png'

2. **数学计算**:2 + 300 = **302**

进程已结束,退出代码为 0

哎注意一个点啊,如果你把while循环里面的if与下面的添加ai消息的代码切换了,那么它最后是不给你任何回答的,这是为什么?
我们来分析下切换后的流程:(我让哈吉米(gemini)3总结的)

  • 第一轮 invoke:AI 返回两个工具请求。
  • append:messages 列表里有了这两个请求。
  • if 判断:因为有工具调用,不跳出。
  • 执行工具:两个工具的结果被 append 到 messages 后面。
  • 回到 while 开头:
  • 再次 invoke:AI 看到工具结果,生成了最终文字答案。
  • append:这个最终文字答案被塞进了 messages 列表。
  • if 判断:重点来了!因为这个最终答案没有 tool_calls,if 条件成立,直接 break 结束了循环!
  • 导致"没回答"的关键原因: 当你跳出循环后,你执行了print(model_with_tools.invoke(messages))。 但请注意,此时 messages 列表的最后一条消息已经是 AI 刚才给出的最终答案了。
  • 比喻: 你问 AI:"2+2 等于几?" AI 思考后把"等于 4"写在了纸上(append 到了 messages 里)。 你看到纸上写了"等于 4",于是你立刻收起纸笔(break 循环)。 然后你又把这张写着"等于 4"的纸拍在 AI 脸上说:"再给我算一遍!" AI 的反应:它看着最后一行已经写好的"等于 4",会觉得莫名其妙,可能回一个空格,或者一段无关痛痒的话。

为什么我们现在的逻辑能跑通 ? 是因为你现在的逻辑在 break 的那一刻,messages 里的最后一条消息是工具的结果,而不是AI 的回答,所以你在循环外的那次 invoke 实际上是"补上了最后临门一脚",让 AI 把工具结果翻译成了人话。

3.2.5 LangChain集成好的三方工具

可以在下面找到LangChain已经为我们集成好的一些三方工具:
https://docs.langchain.com/oss/python/integrations/tools

当然,最方便的还是给ai集成一个网页搜索工具,这里我们挑一个来用,即TavilySearch

网页搜索工具-TavilySearch

TavilySearch 类可以⽀持我们进⾏搜索,Tavily 是⼀个专⻔为 AI 设计的搜索引擎,专为智能体检索与推理需求量⾝打造的⼯具。

Tavily 不仅提供了⾼度可编程的 API 接⼝,还具备显著优于传统搜索引擎的上下⽂相关性理解能⼒。能够以结构化、可解析的形式返回搜索结果,便于将检索到的信息直接⽤于后续的推理、⽣成或任务执⾏流程。

Tavily官⽹:https://www.tavily.com/,需魔法使⽤。登录完成后,新建 API Keys,然后将获取到的api-key添加到我们电脑的环境变量中键值为-TAVILY_API_KEY(当然你也可以在代码中添加)即可开始使用。

使用之前需要先安装包:

python 复制代码
pip install -U langchain-tavily

那我们给上面调用工具部分再添加一个网页搜索工具,然后替换下问的问题,修改后的代码如下(只给出修改过的代码相较于调用工具部分):

python 复制代码
...
tavilySearchTool = TavilySearch(max_result=4)# max_results 返回的最大搜索结果,有兴趣的读者可以查ai问问相关参数
...
tools = [createQrCode,add,tavilySearchTool]
...
messages = [
    HumanMessage("将'https://www.runoob.com/python3/python3-tutorial.html'基于这段单引号的文本"
                 "生成一个二维码图片到'C:\\Users\\15890\\Desktop\\qrcode.png'这个单引号括住的本机路径下面吗。"
                 "除此之外2+300的结果是。"
                 "最后一个请求,你可以帮我查询下北京市明天的温度吗")
]
...
selected_tool = {"createqrcode": createQrCode, "add": add,"tavily_search" : tavilySearchTool}[tool["name"].lower()]
...

可以看到它三个任务都帮我执行了:

python 复制代码
C:\code\LangChain\.venv\Scripts\python.exe C:\code\LangChain\test4.py 
我已经帮您完成了所有三个请求:

1. **二维码生成**:成功为"https://www.runoob.com/python3/python3-tutorial.html"生成了二维码,保存在您指定的路径"C:\Users\15890\Desktop\qrcode.png"

2. **数学计算**:2+300的结果是**302**

3. **北京市明天温度查询**:
   - 根据最新的天气预报,**北京市明天的温度为-5℃到-12℃**
   - 天气状况:晴天
   - 风力:小于3级
   - 空气质量:良好
   - 建议:天气寒冷,建议穿着厚羽绒服等隆冬服装,注意防寒保暖

需要注意的是,北京目前正经历一轮寒潮天气,气温较低,明天将是此次低温天气的谷底时期之一。

进程已结束,退出代码为 0
相关推荐
中科天工2 小时前
智装升级:工业4.0时代的高效包装革命
大数据·人工智能·智能
爱喝可乐的老王2 小时前
机器学习监督学习模型----KNN
人工智能·算法·机器学习
丝斯20112 小时前
AI学习笔记整理(54)——大模型之Agent 智能体开发前沿技术
人工智能·笔记·学习
优爱蛋白2 小时前
基于活性探针策略的Bromodomain蛋白质功能研究
人工智能·健康医疗
晨非辰2 小时前
C++波澜壮阔40年|类和对象篇:拷贝构造与赋值重载的演进与实现
运维·开发语言·c++·人工智能·后端·python·深度学习
网络安全研发随想2 小时前
AI Code编辑器到底是怎么做出来的?
人工智能·编辑器
2501_941837262 小时前
龙虾性别分类与未定义类别识别模型训练 tood_r50_fpn_ms-2x_coco实现详解_1
人工智能·分类·数据挖掘
彩虹糖_haha2 小时前
多线程并发处理模式详解
人工智能·计算机视觉
Alter12302 小时前
海南椰子鸡和宁夏滩羊的拼多多“漂流”:透视地域特产的数字进化论
大数据·人工智能