【LangChain 1.2 实战(六)】 工具调用 (Function Calling)

一、上一章回顾

模型初始化通过 init_llm.py 使用 LangChain 的 init_chat_model 初始化了 DeepSeek 模型,并成功进行了简单问答。代码:

python 复制代码
from langchain.chat_models import init_chat_model
from app.config import DEEPSEEK_API_KEY, DEEPSEEK_BASE_URL, MODEL_NAME

# 使用 init_chat_model 创建 DeepSeek 聊天模型实例
# 模型标识格式为 "provider:model_name",DeepSeek 兼容 OpenAI 接口,故 provider 写 "openai"
deepseek_llm = init_chat_model(
    model=f"openai:{MODEL_NAME}",      # 例如 "openai:deepseek-chat"
    openai_api_key=DEEPSEEK_API_KEY,
    openai_api_base=DEEPSEEK_BASE_URL,
    temperature=0,
)

并使用验证了一下调用模型的效果

python 复制代码
from init_llm import deepseek_llm

response = deepseek_llm.invoke("你好,请介绍一下自己")
print(response.content)

如果问一个需要实时数据的问题,比如"北京现在天气如何?",模型会怎么回答?


二、示例一:不调用工具的直接问答

2.1 代码 (no_tool_demo.py)

python 复制代码
"""
示例一:不调用工具 -- 直接问答
"""
from init_llm import deepseek_llm

question = "北京的天气如何?"
response = deepseek_llm.invoke(question)
print("模型回复:", response.content)

2.2 实际运行结果

复制代码
模型回复: 北京的天气会随季节变化较大,以下是当前和未来几天的概况(截至2023年10月更新,实际天气请以最新预报为准)

2.3 现象分析

模型很诚实,但也很无奈。因为它的知识截止于训练数据,而天气是随时变化的。如果强行回答,就可能产生"幻觉"------胡编一个结果。

这揭示了纯语言模型的根本局限:

  • 知识陈旧,无法获取最新信息;
  • 无法与外部世界交互(查数据库、调 API、执行操作)。

那么,能不能让模型自己去"查"一下呢?这就是我们今天要学的 工具调用


三、工具调用核心概念

工具调用 ,也叫 Function Calling,是一种让模型能够"请求"外部函数执行的机制。流程如下:

用户提问

→ 模型判断需要调用工具

→ 返回"工具调用指令"

→ 我们的代码执行该工具

→ 将结果返回给模型

→ 模型结合结果生成自然语言回复

关键角色:

  • 工具 (Tool):一个带有描述的实际函数(如查天气)。
  • 绑定 (Bind):让模型知道有哪些工具可用。
  • 工具调用指令 (Tool Call):模型输出的结构化要求("请执行函数X,参数为Y")。
  • 工具消息 (ToolMessage):把执行结果包装后送回模型。

四、示例二:带工具调用的智能问答

现在我们要让模型能真实地查询天气

4.1 定义工具函数

工具就是一个普通的 Python 函数,但我们用 @tool 装饰器标记它。函数的文档字符串至关重要,模型会根据它来理解工具的功能和使用方法。

python 复制代码
from langchain_core.tools import tool

@tool
def get_weather(location: str) -> str:
    """查询指定城市的实时天气情况。参数 location:城市名称,例如 '北京'。"""
    weather_data = {
        "北京": "晴,温度 22°C,湿度 40%",
        "上海": "多云,温度 25°C,湿度 70%",
        "深圳": "阵雨,温度 28°C,湿度 85%",
    }
    return weather_data.get(location, f"未找到 {location} 的天气信息")

4.2 完整代码:绑定工具并多步交互

python 复制代码
"""
示例二:完整的工具调用 (Function Calling) 示例
使用 LangChain + DeepSeek API
前提:已通过 init_llm.py 初始化模型
"""
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage, ToolMessage
from init_llm import deepseek_llm    # 使用已配置好的模型

# ========== 1. 定义工具 ==========
@tool
def get_weather(location: str) -> str:
    """查询指定城市的实时天气情况。参数 location:城市名称,例如 '北京'。"""
    weather_data = {
        "北京": "晴,温度 22°C,湿度 40%",
        "上海": "多云,温度 25°C,湿度 70%",
        "深圳": "阵雨,温度 28°C,湿度 85%",
    }
    return weather_data.get(location, f"未找到 {location} 的天气信息")

# ========== 2. 绑定工具到模型 ==========
model_with_tools = deepseek_llm.bind_tools([get_weather])

# ========== 3. 用户提问,模型返回工具调用指令 ==========
user_question = "北京的天气如何?"
response = model_with_tools.invoke(user_question)

print("工具调用指令:", response.tool_calls)

# ========== 4. 真正执行工具,获取结果 ==========
tool_messages = []
if response.tool_calls:
    for tool_call in response.tool_calls:
        tool_name = tool_call["name"]
        tool_args = tool_call["args"]

        if tool_name == "get_weather":
            result = get_weather.invoke(tool_args)   # 此处真正执行函数
            print(f"工具执行结果:{result}")

            # 将结果包装为 ToolMessage(必须携带 tool_call_id)
            tool_messages.append(
                ToolMessage(content=result, tool_call_id=tool_call["id"])
            )

# ========== 5. 将工具结果返回模型,生成最终回答 ==========
if tool_messages:
    final_response = deepseek_llm.invoke(
        [HumanMessage(content=user_question), response] + tool_messages
    )
    print("\n最终回答:", final_response.content)
else:
    # 如果模型没调用工具,直接输出原回复
    print("\n最终回答:", response.content)

4.3 运行结果

复制代码
工具调用指令: [{'name': 'get_weather', 'args': {'location': '北京'}, 'id': 'call_abc123'}]
工具执行结果:晴,温度 22°C,湿度 40%

最终回答: 北京今天的天气是晴天,温度22°C,湿度40%,非常适合外出。

4.4 逐步解析

步骤 消息类型 发生了什么 谁在执行
① 用户提问 【HumanMessage】 "北京的天气如何?" 用户
② 模型决策 【AIMessage】 模型发现绑定了 get_weather 工具,判断需要调用,返回指令:get_weather(location="北京") 模型
③ 执行工具 我们的代码解析指令,真正调用 get_weather("北京"),得到结果字符串 我们的程序
④ 结果返回 【ToolMessage】 将天气结果包装成 ToolMessage,连同原问题一起再次发给模型 我们的程序
⑤ 最终生成 【AIMessage】 模型阅读天气结果,生成流畅自然的中文回答 模型

要点:

  • tool_calls 是模型输出的结构化指令,不是真正的函数调用。我们必须在代码中手动执行函数。
  • ToolMessage 必须附带与对应的 tool_call 相同的 id,这样模型才能将指令和结果正确关联。
  • LangChain 的 .bind_tools() 自动将工具的签名和描述转换为模型能理解的格式。

五、两段代码全面对比

对比维度 示例一(不调用工具) 示例二(调用工具)
信息准确性 可能过时或直接说"不知道" 基于真实数据,准确
模型角色 仅被动回答 主动判断需调用哪个工具,再组织答案
与外界交互 可连接数据库、API、文件系统等
交互轮次 单轮 至少两轮(模型→工具→模型)
代码复杂度 极简 需处理工具调用指令、工具执行和 ToolMessage
输出示例 "我无法获取实时天气" "北京今天晴,22°C,湿度40%"
适用场景 常识问答、文本生成 实时查询、数据操作、自动化任务

核心差异一句话

不调用工具,模型是"知识有限的作家";

调用工具,模型是"能动手获取知识的智能代理"。


六、总结

通过两段代码的对比,明白为什么需要工具调用 ,以及如何实现它

关键收获:

  • 模型通过 tool_calls 输出结构化的调用指令,而非直接执行。
  • 我们编写代码执行工具,并用 ToolMessage 将结果送回。
  • tool_call_id 是连接指令和结果的关键纽带。
  • 从简单的问答模型,到可扩展的智能代理,工具调用是最重要的一步。
相关推荐
索西引擎14 小时前
【LangChain 1.0】 接入 Ollama:在本地跑通 DeepSeek-R1 的完整指南
langchain
染指111017 小时前
12.LangChain框架4-输出解释器
人工智能·langchain·rag
索西引擎19 小时前
【LangChain 1.0】环境搭建指南:从 conda 到 uv 的现代化 Python 工程实践
python·langchain·conda
云姜.19 小时前
Langchain快速上手编程-Runnable 与 LCEL
java·开发语言·langchain
Coder小相20 小时前
LangChain1.0第四篇 - 统一接口多厂商模型适配
人工智能·langchain·agent
PeterLi21 小时前
LangChain v1.x 最新官方完整教程(六大核心组件全解析+生产级代码示例)
langchain·agent
Irissgwe1 天前
十、LangGraph能力详解(1)LangGraph介绍及核心概念
python·ai·langchain·ai编程·工作流·langgraph
索西引擎1 天前
【LangChain 1.0】接入 DeepSeek API:从 API Key 申请到流式响应的完整实践
android·java·langchain
索西引擎1 天前
【LangChain 1.0】 语义搜索实战:从 PDF 文档到向量知识库的完整 RAG 链路
langchain·pdf