欢迎光临
我们一直在努力

LangChain基础入门3(小白必看)

LangChain 工具调用实战:从 @tool 定义到 bind_tools 与 tool_choice

大模型本身只能生成文字,不能直接访问天气接口、数据库或业务系统。工具(Tool)就是把一个外部函数描述给模型,让模型在需要时提出调用请求,再由程序真正执行函数并把结果交回模型。

完整链路可以概括为:

定义 Python 函数

转换为模型可理解的工具 Schema

model.bind_tools(tools)

模型返回 tool_calls

程序执行工具

ToolMessage 回传模型

模型生成最终回答

一、工具有两种调用方式

1. 直接调用工具

from langchain_core.tools import tool

@tool
def get_weather(city: str) > str:
"""获取指定城市的天气信息"""
return f"{city} 晴天,温度 15°C"

result = get_weather.invoke({"city": "北京"})
print(result)

这里的 get_weather 已经不是普通函数,而是一个 LangChain StructuredTool。调用 .invoke() 时传入字典,字典的键名要和工具参数名一致。

2. 基于模型调用工具

model_with_tools = model.bind_tools([get_weather])
response = model_with_tools.invoke("北京天气如何?")

if response.tool_calls:
print("AI 想调用工具:", response.tool_calls)
else:
print("AI 直接回答:", response.content)

bind_tools() 的作用不是执行工具,而是把工具的名称、描述和参数 Schema 发送给模型。模型根据用户问题决定:直接回答,还是生成 tool_calls。

二、不使用 @tool 时,工具描述是怎样生成的?

LangChain 最终需要把 Python 函数转换为模型能理解的 JSON Schema。可以直接查看转换结果:

from langchain_core.utils.function_calling import convert_to_openai_tool

def get_weather(city: str = "北京"):
"""
查询城市的天气

Args:
city: 具体的城市
"""
return f"{city}天气晴朗"

print(convert_to_openai_tool(get_weather))

转换后的描述大致包含:

{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询城市的天气",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "具体的城市"}
}
}
}
}

模型并不会读取 Python 函数体来理解工具,它主要依赖这份结构化描述。因此工具名称、函数注释、参数类型和默认值都很重要。

三、工具描述的四个关键部分

1. 工具名称

默认使用 Python 函数名:

def get_weather(city: str):
...

模型看到的工具名通常就是 get_weather。名称应简洁、明确,使用动词更容易理解,例如 search_news、query_order。

也可以自定义名称:

@tool(parse_docstring=True, name_or_callable="getWeather")
def get_weather(city: str):
"""获取城市天气"""
return f"{city}天气晴朗"

实际开发通常建议直接使用清晰的函数名,避免函数名和工具名不一致导致调试困难。

2. description

工具必须有清楚的描述。使用 @tool 时,如果没有显式提供 description,函数一般需要写 docstring:

@tool
def get_weather(city: str):
"""查询指定城市当日天气"""
return f"{city}天气晴朗"

描述应该说明“工具做什么”,不要只写“工具函数”。模型会依据描述判断什么时候该调用它。

3. 参数说明

推荐使用 Google 风格 docstring,并开启 parse_docstring=True:

@tool(parse_docstring=True)
def get_weather(city: str) > str:
"""
查询指定城市当日天气

Args:
city: 城市名称,例如北京、上海
"""
return f"{city}天气晴朗"

参数注释会进入 Schema,模型就能知道 city 应该传什么。

4. 类型和默认值

def get_weather(city: str, days: int = 1):
...

str、int、bool 等类型会被转换为 JSON Schema 类型。设置默认值后,该参数通常不会出现在 required 中,模型可以省略它。

def get_weather(city: str = "北京"):
...

没有默认值的参数通常是必填参数:

def get_weather(date: str, city: str = "北京"):
...

此时 date 必须提供,city 可以使用默认值。

四、使用 @tool 装饰器

@tool 会把普通函数包装成 LangChain 工具:

from langchain.tools import tool

@tool(parse_docstring=True)
def get_news(domain: str) > str:
"""
查询指定领域的热点新闻

Args:
domain: 新闻领域
"""
return f"{domain}领域暂无新闻"

装饰前:

get_news 是普通 Python 函数

装饰后:

get_news 具有 name、description、args_schema 和 invoke() 等工具能力

五、使用 Pydantic 定义 args_schema

工具参数复杂时,可以使用 Pydantic 模型集中定义校验规则:

from pydantic import BaseModel, Field
from typing import Literal
from langchain.tools import tool

class WeatherInput(BaseModel):
city: str = Field(default="北京", description="具体城市")
unit: Literal["celsius", "fahrenheit"] = "celsius"
include_forecast: bool = Field(
default=False,
description="是否包含未来五天预报",
)

@tool(args_schema=WeatherInput)
def get_weather(
city: str,
unit: str = "celsius",
include_forecast: bool = False,
) > str:
"""获取城市天气"""
return f"{city}天气晴朗"

这里:

  • Field(description=…) 给模型提供参数含义;
  • Literal[…] 限制参数只能取指定值;
  • 默认值让参数变成可选;
  • args_schema 把这套规则绑定到工具上。

六、从 Message 流转看完整工具调用

模型不会直接运行 Python 函数。程序必须负责中间的执行步骤:

from langchain.messages import HumanMessage

@tool
def get_weather(city: str) > str:
"""获取天气"""
return f"{city}天气晴朗"

model_with_tools = model.bind_tools([get_weather])
messages = [HumanMessage("今天北京天气如何?")]

# 第一次调用:模型只提出工具请求
response = model_with_tools.invoke(messages)
messages.append(response)

for tool_call in response.tool_calls:
if tool_call["name"] == "get_weather":
tool_response = get_weather.invoke(tool_call)
messages.append(tool_response)

# 第二次调用:模型读取 ToolMessage,组织最终回答
final_response = model_with_tools.invoke(messages)
print(final_response.content)

运行顺序是:

1. HumanMessage 提出问题
2. model_with_tools 返回带 tool_calls 的 AIMessage
3. 程序读取 tool_calls
4. get_weather.invoke(tool_call) 执行真实函数
5. 生成 ToolMessage 并追加到 messages
6. 再次调用模型
7. 模型根据工具结果返回自然语言答案

这也是大模型和 Agent 的一个重要区别:单独的大模型通常只会提出工具调用请求,Agent 或程序代码负责真正执行工具并继续循环。

七、多工具调用

可以一次绑定多个工具:

@tool(parse_docstring=True)
def get_weather(city: str) > str:
"""获取当日天气"""
return f"{city}当天晴朗"

@tool(parse_docstring=True)
def get_news() > str:
"""获取当日新闻"""
return "今天有新的科技新闻"

model_with_tools = model.bind_tools([get_weather, get_news])
response = model_with_tools.invoke(
"今天杭州天气如何?今天有什么新闻?"
)

模型可能返回一个或多个 tool_calls。程序应遍历全部调用,并根据 tool_call["name"] 分发给对应工具:

for tool_call in response.tool_calls:
if tool_call["name"] == "get_weather":
messages.append(get_weather.invoke(tool_call))
elif tool_call["name"] == "get_news":
messages.append(get_news.invoke(tool_call))

工具名称必须和绑定时的名称一致。代码中如果把 get_weather 错写成 get_weacher_and_forecast,就不会进入正确的分支。

八、tool_choice:控制模型是否调用工具

1. tool_choice="none"

禁止调用工具:

model_with_tools = model.bind_tools(
[get_weather],
tool_choice="none",
)

即使用户问天气,模型也只能直接生成文本答案。

2. tool_choice="auto"

由模型自行判断:

model_with_tools = model.bind_tools(
[get_weather],
tool_choice="auto",
)

用户问天气时可能调用工具;用户问“2 + 3 = ?”时可能直接回答。它是最常见、最自然的模式。

3. tool_choice="required"

要求模型必须调用工具:

model_with_tools = model.bind_tools(
[get_weather],
tool_choice="required",
)

即使用户问一个简单数学问题,模型也会被要求从工具集合中选一个调用。工具本身是否适合这个问题,需要开发者谨慎设计。

4. 强制调用指定工具

绑定多个工具时,可以强制指定一个:

model_with_tools = model.bind_tools(
[get_weather1, get_weather2],
tool_choice="get_weather2",
)

此时模型必须调用名为 get_weather2 的工具。

九、工具设计的实践建议

1. 工具描述要面向模型

不要只写:

"""工具函数"""

应该写清楚:

"""
查询指定城市当天的天气。

Args:
city: 城市名称,例如北京或上海。
"""

2. 参数尽量少而明确

工具参数越多,模型越容易填错。可以把复杂参数放进 Pydantic Schema,并给枚举字段和默认值。

3. 工具返回结果要便于模型阅读

推荐返回结构清晰的字符串或字典,而不是大段无关日志。

4. 工具执行必须有异常处理

真实 API 可能超时、鉴权失败或返回空数据。生产环境应把异常转换为模型可理解的工具错误消息,并决定是否重试。

5. 不要把 API Key 写进工具代码

使用 .env 和 load_dotenv(override=True) 加载密钥,并通过环境变量读取。

十、总结

@tool:把普通函数包装成 LangChain 工具
docstring:提供工具和参数说明
args_schema:用 Pydantic 约束复杂参数
bind_tools:把工具描述交给模型,不会自动执行工具
tool_calls:模型提出的工具调用请求
ToolMessage:程序执行工具后返回给模型的结果
tool_choice:控制不调用、自动调用、必须调用或指定调用

真正完整的工具调用不是一次 API 请求,而是一个消息循环:模型提出请求,程序执行工具,工具结果回到模型,模型再生成最终答案。理解这条链路后,就能进一步使用 LangGraph 构建带工具的 Agent。

赞(0)
未经允许不得转载:171主机测评 » LangChain基础入门3(小白必看)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址