【LangChain开发】LangChain 聊天模型——调用工具

🚀 欢迎来到我的CSDN博客:Optimistic _ chen

一名热爱技术与分享的全栈开发者,在这里记录成长,专注分享编程技术与实战经验,助力你的技术成长之路,与你共同进步!


🚀我的专栏推荐

专栏 内容特色 适合人群
🔥C语言从入门到精通 系统讲解基础语法、指针、内存管理、项目实战 零基础新手、考研党、复习
🔥Java基础语法 系统解释了基础语法、类与对象、继承 Java初学者
🔥Java核心技术 面向对象、集合框架、多线程、网络编程、新特性解析 有一定语法基础的开发者
🔥Java EE 进阶实战 Servlet、JSP、SpringBoot、MyBatis、项目案例拆解 想快速入门Java Web开发的同学
🔥Java数据结构与算法 图解数据结构、LeetCode刷题解析、大厂面试算法题 面试备战、算法爱好者、计算机专业学生
🔥Redis系列 从数据类型到核心特性解析 项目必备

🚀我的承诺:

✅ 文章配套代码:每篇技术文章都提供完整的可运行代码示例

✅ 持续更新:专栏内容定期更新,紧跟技术趋势

✅ 答疑交流:欢迎在文章评论区留言讨论,我会及时回复(支持互粉)


🚀 关注我,解锁更多技术干货!
⏳ 每天进步一点点,未来惊艳所有人!✍️ 持续更新中,记得⭐收藏关注⭐不迷路 ✨

📌 标签:#技术博客#编程学习#Java#C语言#算法#程序员

文章目录

聊天模型调用工具

上篇博客说了LangChain解决LLM三大痛点问题的方法之一就是⼯具调⽤------让⼤语⾔模型(LLM)具备与外部世界交互的能⼒。

调用工具后:

  • 扩展边界能力,模型借助工具完成任务
  • 保证消息实时性:通过调用搜索工具获得最新消息
  • 处理复杂任务:将一个复杂请求分解成多个步骤,依次调用不同的工具协同完成

创建工具

  我们可以使用别人封装好的工具,同时也可以使用我们自定义的工具。自定义的工具就需要@Tool装饰器创建工具。@tool 装饰器是⾃定义⼯具的最简单⽅法。

简单来说:⼯具通过@tool 加Python函数 实现

  • 装饰器默认使⽤函数名称作为⼯具名称
  • 装饰器将使⽤函数的**⽂档字符串作为⼯具的描述**

因此,函数名、类型提⽰和⽂档字符串都是传递给⼯具Schema 的⼀部分,不可缺失。定义好的描述是使模型良好运⾏的重要部分。

特别注意JSON Schema:一套专门用来约束、校验 JSON 数据格式的标准规范,用 JSON 本身写一套规则,用来规定一段合法 JSON 必须满足什么条件。

使用@tool注解(自动生成schema)

工具Schema:大模型工具调用(Function Calling)专用的结构化描述模板,基于 JSON Schema 规范,用来告诉 AI:这个工具叫什么、干什么、需要传哪些参数、参数是什么类型、哪些必填,强制 AI 生成合法参数供后端调用接口 / 函数。

python 复制代码
from langchain_core.tools import tool
@tool
def add(a:int,b:int)->int:
   """加法运算 
   参数:
   a:加数
   b:被加数
   """
   return a+b
print(add.invoke({"a":2,"b":3}))
print(add.name)
print(add.description)
print(add.args)

底层依赖 Pydantic 自动生成 JSON Schema

Pydantic Base Model(复杂结构化参数写法)

python 复制代码
from pydantic import BaseModel, Field
class AddInput(BaseModel):
    """Add two integers."""
    a: int = Field(..., description="加数")
    b: int = Field(..., description="被加数")

from langchain_core.tools import tool
@tool(args_schema=AddInput)
def add(a: int, b: int) -> int:
    # 未提供描述
    return a + b

print(add.invoke({"a":2,"b":3}))

Annotated+@tool(轻量化带校验、描述)

python 复制代码
from typing import Annotated
from pydantic import Field
from langchain_core.tools import tool

@tool
def add(
    a: Annotated[int, Field(description="第一个加数,必须大于0", gt=0)],
    b: Annotated[int, Field(description="第二个加数,必须大于0", gt=0)]
) -> int:
    """两数相加计算工具,仅支持正整数加法"""
    return a + b

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

使⽤StructuredTool类提供的函数创建⼯具

StructuredTool类⽤来初始化⼯具 ,其中from_function 类⽅法通过给定的函数来创建并返回⼀个⼯具

python 复制代码
from langchain_core.tools import StructuredTool

def add(a:int,b:int)->int:
    """ 两数相加"""
    return a+b;
calculator_tool=StructuredTool.from_function(func=add)
print(calculator_tool.invoke({"a": 2, "b": 3}))

因为LLM的理解大部分来自文本描述,所以工具的输出content必须是结构良好、简洁的文本,方便LLM执行下一步指令。

在链(chain)中,⼯具调⽤之后的其他组件或函数,可能需要⼯具的原始且结构化数据(即artifact )来执⾏特定操作。这些数据可能是庞⼤的、且⾮⽂本的。这些数据不适合直接塞给模型。因此,artifact 其实是为了给链中后续的组件或函数使⽤的,不被⼤模型所直接使⽤

绑定工具

为了实际将这些工具绑定到聊天模型,可以使用聊天模型的.bind_tools()方法

它的源码如下:

工具调用

.bind_tools()方法返回了一个Runnable实例,因此我们可以使用该是咧,调用.invoke()方法,完成工具调用。

python 复制代码
from langchain_core.tools import StructuredTool
from langchain.chat_models import init_chat_model

model=init_chat_model("deepseek-v4-pro",model_provider="deepseek")
def add(a:int,b:int)->int:
    """ 两数相加"""
    return a+b;
calculator_tool=StructuredTool.from_function(func=add)

#绑定工具
tools=[add]
model_with_tools=model.bind_tools(tools)

#调用工具
result=model_with_tools.invoke("2加5等于多少?")
print(result)

输出内容是AIMessage,从输出结果看AI给出的响应是进行工具的调用(这里只是调用了工具,没有答案)

记住:⼯具调⽤的⼀个关键原则是,模型根据输⼊的相关性决定何时使⽤⼯具。模型并不总是需要调⽤⼯具

将工具输出传给聊天模型

前面我们仅仅只是调用了工具,但是聊天模型并没有给我们预期中的答案。因此我们需要:

  1. 将工具输出传递给聊天模型,包括HumanMessage(用户输出)、AIMessage(工具调用)、ToolMessage(工具输出)
  2. 聊天模型根据以上信息,将最终结果AIMessage返回

因为聊天模型输入是接收聊天消息列表,所以我们需要把工具的返回构造成ToolMessage,再传给聊天模型。

python 复制代码
from typing import Annotated
from langchain_core.tools import tool
from langchain.chat_models import init_chat_model
from pydantic import Field
from langchain_core.messages import HumanMessage

# ------------------ 1. 初始化大语言模型 ------------------
# 使用 DeepSeek 的 v4-pro 模型(需确保已配置 API Key 等环境变量)
model = init_chat_model("deepseek-v4-pro", model_provider="deepseek")

# ------------------ 2. 定义工具(函数) ------------------
# @tool 装饰器将普通 Python 函数转换为 LangChain 可识别的工具,
# 并自动根据函数签名和 Pydantic Field 生成 JSON Schema 供 LLM 理解。
@tool
def add(a: Annotated[int, Field(description="加数")],
        b: Annotated[int, Field(description="被加数")]
        ) -> int:
    """两数相加"""
    return a + b

# ------------------ 3. 将工具绑定到模型 ------------------
# 绑定后,模型在调用时会自动在请求中携带工具的定义(function calling)。
tools = [add]
model_with_tools = model.bind_tools(tools)

# ------------------ 4. 第一次调用:让模型决定是否使用工具 ------------------
# 构建初始用户消息
messages = [HumanMessage("5加3等于多少?")]

# 调用绑定了工具的模型,模型会分析意图并可能返回 tool_calls
ai_message = model_with_tools.invoke(messages)
# 将模型的响应(包含 tool_calls 决策)追加到对话历史中
messages.append(ai_message)

# ------------------ 5. 本地执行工具 ------------------
# 遍历模型返回的所有工具调用请求(可能同时请求多个)
for tool_call in ai_message.tool_calls:
    # 根据工具名称选择对应的 Python 函数(这里仅演示单一 add 工具)
    # 注意:实际生产环境应使用字典映射或注册表,避免硬编码
    selected_tool = {"add": add}[tool_call["name"].lower()]

    # 执行工具:invoke 会自动提取 tool_call 中的 args 作为参数,
    # 并返回 ToolMessage 对象(包含执行结果和 tool_call_id)
    tool_message = selected_tool.invoke(tool_call)
    # 将工具执行结果追加到消息历史,以便模型获取计算结果
    messages.append(tool_message)

# 打印此时的完整消息列表(用于调试),包含 Human、AI(带工具调用)、Tool 三条消息
print(messages)

# ------------------ 6. 第二次调用:基于工具结果生成最终回复 ------------------
# 注意:这里使用原始的 model(未绑定工具),因为我们只需让模型根据工具结果
# 生成自然语言回答,而不希望它再次尝试调用新工具。
result = model.invoke(messages)
# 打印最终回复内容
print(result)

从输出结果来看,我们调用了两次聊天模型:

  • 第一次 :只是将HumanMessage发送给聊天模型进行处理,结果返回了,结果返回了包含⼯具调⽤的AIMessage,并没有返回我们想要的结果。然后我们执⾏⼯具,得到ToolMessage
  • 第二次 :将HumanMessage +AIMessage +ToolMessage 消息记录发送给聊天模型进行处理,结果返回了包含结果的AIMessage

整个流程:

  1. 第一次请求发送: HumanMessage("5加3等于多少?") ,附带: tools=add函数的JSON Schema定义
  2. 第一次响应返回AIMessage(不含文本内容,有tool_calls),tool_calls = {name: "add", args: {a: 5, b: 3}, id: "call_xxx"}
  3. 第二次请求(注意:这次用的是 model.invoke,没有绑定工具;发送: 完整消息历史 HumanMessage, AIMessage(tool_calls), ToolMessage,注意:此时 没 有 传 递 tools 参数
  4. 第二次响应返回: AIMessage (content="5加3等于8。")

LangChain提供的⼯具

⼯具也不是全部都需要我们⾃⼰⼿搓,其实LangChain官⽅也已经给我们提供了很多现成的⼯具(Tool)和⼯具包(Toolkit)。写好的⼯具⼀般都是为了使⽤LangChain中集成的三⽅组件或⼯具⽽创造的,有搜索、数据库、⽹⻚浏览器等相关的⼯具。

官方工具

这里使用一个搜索工具类Tavily来示范:

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

点击Tvaily官网获取API Key,然后配置到系统环境变量中

python 复制代码
from langchain.chat_models import init_chat_model
from langchain_core.messages import HumanMessage
from langchain_tavily import TavilySearch


model = init_chat_model("deepseek-v4-pro", model_provider="deepseek")

#绑定⼯具

tool =TavilySearch(max_results=4)
model_with_tools = model.bind_tools([tool])
#添加AIMessage到消息中去
messages = [
    HumanMessage("中国西安今天的天⽓怎么样?")
]
ai_msg = model_with_tools.invoke(messages)
messages.append(ai_msg)
for tool_call in ai_msg.tool_calls:
    # 执⾏⼯具调⽤,返回ToolMessage
    tool_msg=tool.invoke(tool_call)
    messages.append(tool_msg)

result=model_with_tools.invoke(messages)
print(result.content)

完结撒花!🎉

如果这篇博客对你有帮助,不妨点个赞支持一下吧!👍
你的鼓励是我创作的最大动力~

想获取更多干货? 欢迎关注我的专栏 → optimistic_chen

📌 收藏本文,下次需要时不迷路!

我们下期再见!💫 持续更新中......


悄悄说:点击主页有更多精彩内容哦~ 😊