17.LangChain 1.0+ 第五篇:Tools 工具——让大模型从认识世界到改变世界

LangChain 1.0+ 第五篇:Tools 工具------让大模型从认识世界到改变世界

摘要:大模型只会生成文字,Tools 让它可以查询天气、调用 API、操作数据。本文讲透工具调用的完整流程、两种工具定义方式(手动 schema 与 @tool 装饰器),以及 tool_choice 强制使用工具的四种策略。


引子:只会聊天的大模型,如何"干实事"?

纯 LLM 的局限很明显:训练数据有截止时间、没有实时数据、没有外部系统访问权限 。问它"今天北京天气"它只能编;让它"帮我下单"它做不到。Tools(工具)就是破解之道:把能力以函数形式暴露给模型,模型决定何时调用、传什么参数,程序执行后把结果回传给模型,从而让模型"认识世界"变成"改变世界"。

承接 \[LangChain 1.0+ 入门:从只会调API到搭出AI应用] 与 \[LangChain 1.0+ 第四篇:Message 消息与提示词模板------让模型记住对话]。

一、工具调用的整体流程

结论:工具调用的本质是"模型不执行代码,只发调用请求,程序执行并把结果喂回模型"。

1. 两种调用方式

方式 说明
底层 API bind_tools() 绑定工具,返回 ToolCall 请求,程序手动执行
高级封装 create_agent() 创建智能体,自动完成"模型→工具→结果→模型"循环

2. 工具调用的四步流程(bind_tools 方式)

arduino 复制代码
① 工具绑定:model.bind_tools(tools) → 模型"认识"这些工具
② 模型生成调用请求:AIMessage 返回 tool_calls(含工具名、参数、ID)
③ 程序执行:按 tool_calls 逐个调用真实函数,得到结果
④ 结果回传:把 ToolMessage 加回消息列表,再次 invoke 模型,生成最终回答
python 复制代码
# ① 绑定工具
model_with_tools = model.bind_tools([multiply])
response = model_with_tools.invoke("3*4等于多少")

# ② 模型返回 tool_calls
# response.tool_calls → [{'name': 'multiply', 'args': {'a': 3, 'b': 4}, 'id': 'call_xxx'}]

# ③④ 执行并回传(完整循环见后文"应用案例")

3. 工具调用的关键消息链

模型返回工具调用后,必须按顺序把这些消息放回列表,再重新请求模型:

scss 复制代码
HumanMessage(用户提问)
 → AIMessage(tool_calls=[调用请求])      ← 模型决定调用
 → ToolMessage(content=执行结果, tool_call_id=对应ID)  ← 结果回传
 → AIMessage(最终回答)                    ← 模型综合后作答

易错点 :每个 ToolMessagetool_call_id 必须与对应 AIMessage.tool_calls[i]["id"] 一一对应,否则模型无法关联结果。

二、定义工具方式一:不使用 @tool(手动 schema)

结论:直接把 Python 函数 + 手动构造的 JSON Schema 传给 convert_to_openai_tool() 完成转换。

1. 定义普通函数

python 复制代码
def multiply(a: int, b: int) -> int:
    """将两个整数相乘。"""
    return a * b

2. 手动构造参数 Schema

python 复制代码
from langchain_core.tools import convert_to_openai_tool

multiply_schema = {
    "type": "function",
    "function": {
        "name": "multiply",
        "description": "将两个整数相乘",
        "parameters": {
            "type": "object",
            "properties": {
                "a": {"type": "integer", "description": "第一个整数"},
                "b": {"type": "integer", "description": "第二个整数"}
            },
            "required": ["a", "b"]
        }
    }
}
tool = convert_to_openai_tool(multiply_schema)

手动写 schema 的三大弊端

  1. 函数改了,schema 容易忘记同步改(职责分散);
  2. 手动维护,容易写错(类型、必填项);
  3. 参数一多,编写体验差。

所以生产环境强烈推荐下一种方式

三、定义工具方式二:@tool 装饰器(推荐)

结论:@tool 装饰器从函数签名与 docstring 自动生成 schema,描述越规范,模型调用越准确。

1. 基本用法

python 复制代码
from langchain_core.tools import tool

@tool
def multiply(a: int, b: int) -> int:
    """将两个整数相乘。"""
    return a * b

@tool 自动完成三件事:

  • 根据函数签名生成参数 schema(含类型、必填项);
  • 根据函数名生成工具名;
  • 根据 docstring 生成工具描述。

2. 描述如何影响模型选择

工具描述(description)决定模型"什么时候用、怎么用"这个工具

  • 描述中要写清楚:用途、使用时机、参数含义、边界条件;
  • docstring 写得好,模型才能正确填参;
  • 描述模糊 → 模型乱用或不用。

3. @tool 高级参数

参数 作用
name_or_callable 自定义工具名,如 @tool("天气查询")
description 显式指定描述(覆盖 docstring)
parse_docstring=True 从 docstring 提取参数说明(Google 风格 Args:
args_schema 用 Pydantic 类或 JSON Schema 精细控制参数
return_direct 工具结果直接返回用户(不经过模型总结),适合简单工具
response_format 工具响应格式
handle_tool_error 工具报错时返回预设提示(布尔或函数)

Google 风格 docstring 示例(配合 parse_docstring)

python 复制代码
@tool(parse_docstring=True)
def get_weather(city: str, date: str = "今天") -> str:
    """
    查询指定城市指定日期的天气情况。

    Args:
        city: 城市名称,如"北京"
        date: 日期,如"2026-08-20",默认为今天
    """
    return f"{city} {date} 天气晴朗"

4. args_schema:精细控制参数

方式一:Pydantic 类(推荐,校验+默认值)

python 复制代码
from pydantic import BaseModel, Field
from langchain_core.tools import tool

class AddExclamationInput(BaseModel):
    a: int = Field(description="加数1")
    b: int = Field(description="加数2")
    style: str = Field(description="感叹号风格", default="普通")

@tool(args_schema=AddExclamationInput)
def add_exclamation(a: int, b: int, style: str = "普通") -> str:
    """两个数相加,并按 style 风格加上感叹号。"""
    if style == "普通":
        return f"{a + b}!"
    return f"{a + b}!!!"

方式二:JSON Schema

python 复制代码
args_schema = {
    "type": "object",
    "properties": {
        "a": {"type": "integer", "description": "加数1"},
        "b": {"type": "integer", "description": "加数2"}
    },
    "required": ["a", "b"]
}
@tool(args_schema=args_schema)
def add(a: int, b: int) -> str:
    """两个整数相加。"""
    return str(a + b)

四、完整应用案例:从"问天气"到"算乘法"

python 复制代码
from langchain_core.tools import tool
from langchain_core.messages import AIMessage, ToolMessage

# 1. 定义两个工具
@tool
def multiply(a: int, b: int) -> int:
    """将两个整数相乘。"""
    return a * b

@tool
def get_weather(city: str, date: str = "今天") -> str:
    """查询指定城市指定日期的天气情况。"""
    return f"{city} {date} 天气晴朗"

# 2. 绑定工具
model_with_tools = model.bind_tools([multiply, get_weather])
messages = [HumanMessage(content="北京今天天气怎么样?3*4等于多少?")]

# 3. 模型生成调用请求
ai_msg = model_with_tools.invoke(messages)
messages.append(ai_msg)

# 4. 执行工具并回传结果
for tool_call in ai_msg.tool_calls:
    if tool_call["name"] == "multiply":
        result = multiply.invoke(tool_call["args"])
    elif tool_call["name"] == "get_weather":
        result = get_weather.invoke(tool_call["args"])
    # 关键:tool_call_id 一一对应
    messages.append(ToolMessage(content=str(result), tool_call_id=tool_call["id"]))

# 5. 再次调用模型,生成最终回答
final = model_with_tools.invoke(messages)
print(final.content)

一次提问可触发多个工具调用 (如同时查天气和算乘法),模型会并行返回多个 tool_calls,循环逐个执行回传即可。

五、强制使用工具:tool_choice

结论:tool_choice 控制模型是否必须调用工具,四种策略覆盖"不调用"到"指定工具"。

行为
"auto" 模型自主决定是否调用(默认)
"none" 强制不调用任何工具,只输出文本
"required"(或 "any" 强制调用工具(不指定具体哪个)
工具名(如 "multiply" 强制调用指定工具
python 复制代码
model.bind_tools([multiply, get_weather], tool_choice="required")
model.bind_tools([multiply], tool_choice="multiply")  # 指定工具
model.bind_tools([multiply], tool_choice="none")

六、实践要点总结

  1. 描述清晰:docstring 写清"用途、时机、参数、边界",模型才能正确选工具。
  2. 功能单一:一个工具只做一件事,职责单一最容易被模型正确调用。
  3. 失败处理 :三层防护------handle_tool_error 返回友好提示、模型侧提示词告知工具可能失败、必要时人工介入。
  4. 返回字符串:工具尽量返回字符串(模型好消费),复杂数据先序列化再返回。
  5. 同步/异步 :同步工具用 @tool,异步工具用 @tool 装饰 async def,Agent 自动适配。
  6. 生产优先 @tool + Pydantic args_schema:schema 自动生成且带校验,避免手动维护漂移。

下一篇解决大模型输出的"最后一公里":结构化输出,让模型返回程序可用的 JSON,而不是自由文本。

相关推荐
为你学会写情书2 小时前
NestJS + LangChain 集成指南:从库封装到手动构建
langchain
草莓熊Lotso2 小时前
【Linux网络加餐】手动部署:SSH 与 Web 服务实战 + 底层原理全解析
linux·运维·网络·人工智能·python·langchain·ssh
65岁退休Coder19 小时前
LangChain v1.3.4 笔记 - 08 MCP & 相关概念
后端·python·langchain
可遇_不可求20 小时前
合集:LangChain智能体开发(四)langchain-elasticsearch
elasticsearch·langchain
前端 贾公子1 天前
第08章:中间件(8)
langchain
睡觉时不困4421 天前
15.LangChain 1.0+ 第三篇:LangSmith——给 AI 应用装上监控台.发布清单
langchain
gis分享者1 天前
LangChain 和 LlamaIndex 有什么区别?各自适合什么场景?
人工智能·ai·langchain·场景·区别·llamaindex
北斗落凡尘2 天前
LangGraph 入门实战(12)--使用MCP
后端·python·langchain
赵广陆2 天前
企业实战:Web服务端搭建
前端·langchain·langgraph