LangChain 中 “附加 OpenAI 函数” 和 “附加 OpenAI 工具”
要理解 LangChain 中“附加 OpenAI 函数”和“附加 OpenAI 工具”,核心是先抓住一个大前提:OpenAI 模型(如 GPT-3.5/4)本身支持“函数调用能力”——模型能根据用户需求,自主决定是否调用你定义的外部功能(比如查天气、算股票、调用API),而不是只输出自然语言。
LangChain 的这两个操作,本质是把 OpenAI 的“原生函数调用能力”和 LangChain 的“链式流程(Expression Language)”结合起来,让你更方便地让模型调用外部功能。下面分两部分拆解,结合文档和通俗例子讲清楚:
一、先搞懂:为什么需要“附加函数/工具”?
模型本身有局限:比如 GPT-4 不知道“2025年9月30日北京的实时天气”(没有实时数据)、算不了“123456789×987654321”(复杂计算容易错)、没法直接调用你的本地数据库。
这时候就需要让模型“借力”——调用外部的函数/工具来完成这些事。而“附加”的动作,就是告诉模型:“你可以调用这些函数/工具,它们的作用是XXX,参数要这么传”。
二、附加 OpenAI 函数:给模型绑定“原生函数定义”
1. 是什么?
“附加 OpenAI 函数”,是指把 符合 OpenAI 函数格式要求的“纯函数定义”(比如“查天气”的函数逻辑、参数说明),通过 bind
方法绑定到 OpenAI 模型上。
这里的“函数”是 OpenAI 原生支持的格式(不是随便写的 Python 函数),必须包含 3 个核心信息:
name
:函数名(模型调用时会用这个名字);description
:函数的作用(模型靠这个判断“是否需要调用这个函数”,比如“获取指定城市的实时天气”);parameters
:函数需要的参数(比如“城市名”“日期”,还要说明参数类型、是否必填)。
2. 为什么用?
让模型知道“有哪些函数可以调用”,并能自主判断:“用户的问题是否需要调用这个函数,以及该传什么参数”。
比如用户问“北京今天天气怎么样”,模型看到绑定的“查天气”函数,就会输出一个“函数调用指令”(而不是直接编天气),告诉你“需要调用 get_current_weather
函数,参数是 city="北京"
”。
3. 怎么用?(结合文档例子)
文档里用了“查天气”的例子,我们拆解步骤:
步骤1:定义符合 OpenAI 格式的函数
# 1. 定义一个“查天气”的函数(符合 OpenAI 格式)
get_weather_function = {"name": "get_current_weather", # 函数名"description": "获取指定城市的实时天气", # 函数作用(模型靠这个判断是否调用)"parameters": {"type": "object","properties": {"city": {"type": "string","description": "城市名称,比如北京、上海", # 参数说明(模型靠这个传对参数)},"unit": {"type": "string","enum": ["celsius", "fahrenheit"],"description": "温度单位,默认摄氏度",},},"required": ["city"], # 必填参数},
}
步骤2:把函数“附加”到 OpenAI 模型上
用 model.bind(functions=[...])
把函数绑定到模型——这就是“附加”的核心动作:
from langchain_openai import ChatOpenAI# 2. 初始化 OpenAI 模型,并附加上面的函数
model = ChatOpenAI(model="gpt-3.5-turbo")
model_with_function = model.bind(functions=[get_weather_function])
步骤3:让模型判断是否调用函数
# 3. 给模型发用户问题,模型会判断是否调用函数
response = model_with_function.invoke("北京今天的天气怎么样?")
print(response)
输出结果(模型会返回“函数调用指令”):
content=None # 模型不直接回答,而是调用函数
additional_kwargs={"function_call": {"name": "get_current_weather", # 要调用的函数名"arguments": '{"city":"北京","unit":"celsius"}' # 要传的参数}
}
这就是“附加 OpenAI 函数”的效果:模型没有编天气,而是输出了“调用查天气函数”的指令,接下来你只需要执行这个函数(比如调用天气API),再把结果返回给模型,模型就能整理成自然语言回答了。
三、附加 OpenAI 工具:给模型绑定“LangChain 封装的工具”
1. 是什么?
“附加 OpenAI 工具”,是指把 LangChain 格式的 Tool(工具) 绑定到 OpenAI 模型上。
这里的“工具”和上面的“函数”是什么关系?
→ LangChain 的 Tool 是对“原生函数”的上层封装:它不仅包含了“函数的定义(name/description/parameters)”,还包含了“函数的具体执行逻辑”(比如“查天气”工具里,已经写好了调用天气API的代码)。
简单说:函数
是“说明书”,工具
是“说明书+能直接用的机器”。
2. 为什么用?
LangChain 的 Tool 已经帮你做好了“函数执行逻辑”的封装,不需要你自己写“调用天气API”的代码;同时,Tool 能更好地融入 LangChain 的生态(比如和其他组件串联成链条、支持事件流等)。
比如上面的“查天气”,如果用 Tool,你不需要自己写 API 调用代码,直接用 LangChain 现成的天气 Tool,或者自己封装一个包含执行逻辑的 Tool 即可。
3. 怎么用?(结合文档逻辑扩展)
步骤1:定义 LangChain 格式的 Tool(包含执行逻辑)
用 @tool
装饰器封装一个 Tool,里面既包含“函数描述”,也包含“执行逻辑”:
from langchain_core.tools import tool
import requests # 假设用 requests 调用天气API# 1. 定义一个“查天气”的 Tool(包含执行逻辑)
@tool
def get_current_weather(city: str, unit: str = "celsius"):"""获取指定城市的实时天气。 # Tool 的描述(对应函数的 description)Args:city: 城市名称,比如北京、上海(对应函数的 parameters.city)unit: 温度单位,可选 "celsius"(摄氏度)或 "fahrenheit"(华氏度)"""# 下面是 Tool 的核心:具体执行逻辑(调用天气API)api_key = "你的天气API密钥"url = f"https://api.weatherapi.com/v1/current.json?key={api_key}&q={city}&aqi=no"response = requests.get(url).json()temp = response["current"]["temp_c"] if unit == "celsius" else response["current"]["temp_f"]return f"{city}当前温度:{temp}°{unit}"
步骤2:把 Tool 转换为 OpenAI 函数格式,再附加到模型
LangChain 提供了 format_tool_to_openai_function
工具,能把 LangChain 的 Tool 自动转换成 OpenAI 原生的函数格式,然后用 bind
附加:
from langchain_core.utils.function_calling import format_tool_to_openai_function# 2. 把 Tool 转成 OpenAI 函数格式
openai_weather_function = format_tool_to_openai_function(get_current_weather)# 3. 附加到模型上
model_with_tool = model.bind(functions=[openai_weather_function])
步骤3:模型调用 Tool(先判断,再执行)
# 3. 第一步:模型判断需要调用 Tool,输出调用指令
response = model_with_tool.invoke("北京今天天气怎么样?")
print(response) # 输出和之前类似的“函数调用指令”# 4. 第二步:执行 Tool(用 LangChain 的 Tool 直接调用,不需要自己写逻辑)
from langchain_core.functions import FunctionMessage# 提取模型输出的函数调用信息
function_call = response.additional_kwargs["function_call"]
function_name = function_call["name"]
function_args = eval(function_call["arguments"]) # 解析参数# 执行 Tool(直接调用 Tool 对象)
tool_result = get_current_weather(** function_args)# 5. 第三步:把 Tool 结果返回给模型,让模型整理成自然语言
final_response = model.invoke([("user", "北京今天天气怎么样?"),("function", function_name, tool_result) # 把 Tool 结果传给模型
])
print(final_response.content) # 输出:北京当前温度:22°C,天气晴朗...
四、核心总结:附加函数 vs 附加工具
对比维度 | 附加 OpenAI 函数 | 附加 OpenAI 工具 |
---|---|---|
本质 | 给模型传“函数说明书”(name/description/parameters) | 给模型传“说明书+能执行的机器”(包含函数定义+执行逻辑) |
执行逻辑 | 只给定义,需要你自己写“调用函数”的代码 | 封装了执行逻辑,直接调用 Tool 即可 |
依赖 | 依赖 OpenAI 原生函数格式 | 依赖 LangChain 的 Tool 格式 |
适用场景 | 简单场景,自己能写执行逻辑 | 复杂场景,想复用 LangChain 生态,减少重复代码 |
简单说:
- 如果你只需要让模型“知道有这个函数”,自己写执行逻辑,用“附加函数”;
- 如果你想直接用“能跑的工具”,融入 LangChain 链条,用“附加工具”。
五、文档里的关键逻辑再强调
文档里的 binding
(绑定)动作,不管是绑定 stop 词、函数还是工具,核心都是 通过 bind
方法,把参数/配置“提前传给模型”,让模型在调用时能使用这些配置。
比如:
model.bind(stop="SOLUTION")
:提前告诉模型“遇到 SOLUTION 就停止”;model.bind(functions=[...])
:提前告诉模型“你可以调用这些函数/工具”。
这样做的好处是,你可以把“模型配置”和“后续调用”分开,让链条更清晰、更易复用。