【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 是连接指令和结果的关键纽带。
  • 从简单的问答模型,到可扩展的智能代理,工具调用是最重要的一步。
相关推荐
Irissgwe4 小时前
LangChain之核心组件(少样本提示词)
人工智能·langchain·llm·langgraph
lbb 小魔仙6 小时前
Python + LangChain 环境搭建完全指南:从零构建本地 RAG 知识库(附 Ollama 本地模型集成)
开发语言·python·langchain
风落无尘6 小时前
我用 LangChain 写了一个带“定速巡航”的向量化工具,发布到 PyPI 了!
人工智能·python·langchain
BU摆烂会噶6 小时前
【LangGraph】 流式处理入门
人工智能·python·langchain·人机交互
大模型真好玩7 小时前
LangChain DeepAgents 速通指南(八)—— DeepAgents流式输出详解
人工智能·langchain·agent
沪漂阿龙7 小时前
AI Agent爆火,但你真的懂LangChain吗?——大模型智能体开发完全指南
人工智能·langchain
庞轩px7 小时前
LangChain不是“套壳”——它解决了什么实际问题
langchain·大模型·agent·tool·ai应用开发
qq_283720057 小时前
LangChain 动态模型中间件实战使用技巧
中间件·langchain·middleware·wrap_model_call
JaydenAI9 小时前
拆解LangChain执行引擎[博文汇总-17篇]
langchain·langgraph·pregel