Langchain入门到精通0x09:Tool & Function Calling

大模型的边界能力

我们先看看下面一段代码,AI大模型执行的结果是什么呢?

ini 复制代码
# 通义千问大模型
model = get_lc_model_client()
output_parser = JsonOutputParser()

resp = model.invoke("今天是几月几号?")
print(resp)

AI居然不知道,是不是大跌眼镜呢? 我们尝试换一个模型(DeepSeek)试试:

ini 复制代码
model = get_ali_model_client()

果然,虽然答出了一个日期,但并不是真实今天的日期。之前我们在 为啥需要RAG 讲过大模型的时效性:训练是有成本的,不可能随时更新训练。所以可能有些最近的知识可能其并不知道。

这就是大模型的能力边界之一,解决方案除了RAG技术之外便是 Tool了。

Tool

Tool(工具) :这是 LangChain 中的一个对象 。它封装了一个具体的功能(如 Python 函数),并包含了该功能的名称、描述和参数模式(Schema)。开发者通过 @tool装饰器或 BaseTool类来创建工具,告诉模型"我这里有什么能力"。其核心流程如下:

  • 定义工具:开发者预先将函数(如 get_date())及其描述、参数格式告知LLM。

  • 模型规划 :当用户提问(如"今天是几月几号?"),LLM并不直接回答,而是分析意图,选择并规划需要调用的工具,并严格按照格式生成一个结构化的调用请求。

  • 外部执行 :你的程序解析 这个结构化请求,安全地在本地或远端执行真正的函数,获得结果

  • 整合回复:将执行结果返回给LLM,由LLM组织成自然语言回复给用户(如"今天是3月12日。")。

简单说,它让LLM担任"大脑"(理解和规划),而外部函数担任"手脚"(执行和获取),两者通过结构化的"神经接口"(Function Calling规范)协同工作。

bind_tools()

将一个或多个工具(函数),转换 成大模型能够理解和遵循的"调用规范"(通常是JSON Schema格式),并 "绑定" 到某个特定的对话模型实例上。绑定后,这个模型在思考时,就具备了调用这些工具的能力和知识。

  • 动态性与组合性bind_tools()的核心优势在于其动态性。你可以在运行时根据不同场景,为同一个模型绑定不同的工具集,实现高度的灵活性。

  • 底层协议 :它抽象了不同模型提供商(如OpenAI, Anthropic, DeepSeek)在工具调用上的细节差异,提供统一的接口。在OpenAI的上下文中,它本质上是在准备 tools参数。

ini 复制代码
# 大模型客户端绑定工具
tool_llm = model.bind_tools([get_date, open_browser])
resp = tool_llm.invoke("今天是几月几号?")

Agent

Tool 的选择和调用执行,都是通过 Agents 来完成的。

ini 复制代码
# 创建Agent
agent = create_agent(
    model=llm,  # 聊天模型
    tools=tools, # 工具列表
    system_prompt=system_prompt,
    checkpointer=memory  # 传入记忆组件
)

在大模型使用工具时使用bind_tools进行工具绑定,当我们工程中使用Agent时优先使用带tools的Agent。

方式一:@tool

代码

获取今天的具体日期的tool:

python 复制代码
# 注意:函数的描述必须写在函数体中的第一行
@tool
def get_date():
    # 文本描述相当于工具说明说,大模型正是依靠这段说明来选择对应工具
    """ 获取今天的具体日期 """
    # """ 获取今天的北京的天气 """
    return datetime.date.today().strftime("%Y-%m-%d")

获取浏览器,打开网站 的tool:

python 复制代码
mport webbrowser
@tool
def open_browser(url, browser_name=None):
    # """ 获取浏览器,打开网站 """
    """ 获取浏览器,打开网站可以做很多事情,包括查询天气,汽车限号等 """
    if browser_name:
        # 获取特定浏览器的控制器
        browser = webbrowser.get(browser_name)
    else:
        # 使用默认浏览器
        browser = webbrowser
    # 打开浏览器并导航到指定的URL
    browser.open(url)

使用方式:

ini 复制代码
agent = create_agent(
    model=llm,  # 聊天模型
    tools=[get_date, open_browser], # 工具列表
    system_prompt=system_prompt,
    checkpointer=memory  # 传入记忆组件
)

# 获取今天的日期
result = agent.invoke({"messages":[{"role":"user","content":"今天是几月几号?"}]})

注意事项

  1. 函数描述必须写在函数体中的第一行,否则AI识别不到

  2. 函数的描述必须正确,且与函数真实功能一致。否则会造成回答混乱

    • 将函数描述改为"获取今天的北京的天气"
python 复制代码
@tool
def get_date():
    """ 获取今天的北京的天气 """
    return datetime.date.today().strftime("%Y-%m-%d")

result = agent.invoke({"messages":[{"role":"user","content":"今天北京的天气怎么样?"}]}) 
   

我们发现问天气的时候,依然调用了时间函数。这就是函数描述错误引起的问题。

Log验证

前面也说了 Tool 的整体流程,我们再结合代码Debug来验证下。由打印结果可见:

  1. 用户发问。大模型自己发现搞不定

  2. 尝试寻找合适工具,找到并finesh。finesh_reason=tool_calls

  3. 找到的tool_calls信息:name=get_date

  4. 调用get_date得到信息ToolMessage(2026-03-12)

  5. AIMessage从ToolMessage拿到日期信息:2026-03-12,至此AI大模型已经知道这个问题的答案

果然,和上面的核心流程基本不差。

方式二:Tool()

除了@tool的方式外,还可以用Tool()的方式。

外部函数

python 复制代码
# 定义工具函数
def get_current_time(input: str = "") -> str:
    """获取当前时间"""
    current_datetime = datetime.now()
    formatted_time = current_datetime.strftime('%Y-%m-%d %H:%M:%S')
    result = f"当前时间:{formatted_time}。"
    print(result)
    return result


def recom_drink(input: str = "") -> str:
    """推荐附近的饮品店"""
    result = '''距离您500米内有如下饮料店:\n
    1、蜜雪冰城\n
    2、茶颜悦色\n
    另外距离您200米内有惠民便利店,里面应该有矿泉水或其他饮品'''
    return result


def open_calc(input: str = "") -> str:
    """打开计算器"""
    try:
        subprocess.Popen(['calc.exe'])
        return "计算器已打开"
    except Exception as e:
        return f"打开计算器失败: {str(e)}"


def open_browser(url: str) -> str:
    """打开浏览器访问指定网址"""
    try:
        webbrowser.open(url)
        return f"已打开浏览器访问 {url}"
    except Exception as e:
        return f"打开浏览器失败: {str(e)}"

Tool()

  • name:工具名,可以随意写。但为了实际区分,最好与函数名一致
  • func:函数名,不可写错!
  • description:函数描述。这里和@tool 写在函数首行的作用一致,这里函数首行就不必要写了
ini 复制代码
# 创建LangChain工具列表
# 方式二,自定义工具
tools = [
    # 通过Tool来描述声明工具 name 工具名称 func 函数体 description 函数的功能描述
    Tool(
        name="get_current_time", #可以随意,但是建议跟函数名称一致
        func=get_current_time,
        # 函数的功能描述,说明书
        description="当你想知道现在的时间时非常有用。"
    ),
    Tool(
        name="recom_drink",
        func=recom_drink,
        description="用户口渴,为其推荐附近的饮品店"
    ),
    Tool(
        name="open_calc",
        func=open_calc,
        description="打开本地计算机上的计算器。"
    ),
    Tool(
        name="open_browser",
        func=lambda url: open_browser(url),
        description="打开本地计算机上的网页浏览器,并接受网站的url作为参数。"
    )
]

Agent构建

ini 复制代码
# 这个时候函数工具选择和调用执行,都是通过Agent来完成的
# 创建Agent
agent = create_agent(
    model=llm,  # 聊天模型
    tools=tools, # 工具列表
    system_prompt=system_prompt,
    checkpointer=memory  # 传入记忆组件
)

方式三:BaseTool

就是将工具函数封装成一个类,这个类必须继承自BaseTool。那么这样设计是为什么呢? 其核心是为AI工具调用建立一套"强类型、结构化"的输入契约。这远不止是语法规范,而是构建可靠AI Agent的工程基石。

BaseTool

  • name:工具名称
  • description:工具描述
python 复制代码
class DateTool(BaseTool):
    """
    一个获取当前具体日期的简单工具。
    它是继承BaseTool类的最简示例。
    """
    # 1. 定义工具名称。这将是Agent在思考时提到的名字。
    name: str = "get_date"
    # 2. 定义工具描述。清晰准确的描述直接决定了Agent能否在正确场景下想起并使用它。
    description: str = "当需要知道今天的准确日期(年月日)时,使用此工具。"

args_schema(BaseModel)

args_schema 是一个强类型、可自描述的合约规范,专门用于约束和指导大模型如何生成调用该工具的参数。

args_schema告诉LangChain框架:"我这个工具的所有输入参数,其名称、类型、约束和描述,都完整地定义在这个Pydantic BaseModel里。" 这使得框架能自动完成以下工作:

  1. 生成准确的JSON Schema:这是与OpenAI等模型API进行"函数调用"(Tool Calling)通信的协议基础。
  2. 自动化验证:在工具执行前,框架会自动用schema验证模型生成的参数,无效输入会被拦截,防止工具函数崩溃。
  3. 工具发现的元数据:框架可以收集所有工具的schema,自动生成工具目录或用于路由。
ini 复制代码
# 3. 定义参数模式。即使此工具无需参数,也最好显式定义一个空的Schema,这是良好的实践。
args_schema: Type[BaseModel] = DateToolInput
ini 复制代码
# 使用Pydantic定义输入参数的模型。本例中无需输入,所以模型为空。
class DateToolInput(BaseModel):
    # 这个工具不需要任何输入参数。
    # 如果未来需要扩展,可以在这里添加字段,例如:
    # format: str = Field("YYYY-MM-DD", description="日期格式")
    query: Optional[str] = Field(
        default=None,
        description="查询日期的提示词,可为空"
    )

_run

_run函数是工具"能力" 的具体实现,是连接AI决策与现实世界接口的唯一执行终点。我们这里的获取日期的真正实现就是在这个函数里。

python 复制代码
# 4. 实现核心的 _run 方法。这里是所有业务逻辑存放的地方。
def _run(self, query: Optional[str] = None) -> str:
    """执行工具,返回当前日期字符串。"""
    # 使用datetime模块获取当前日期,并格式化为易读的字符串。
    current_date = datetime.date.today().strftime("%Y-%m-%d")
    return f"今天是:{current_date}"

# 5. (可选)实现 _arun 方法以支持异步调用。简单工具可暂不实现。
async def _arun(self, query: Optional[str] = None):
    raise NotImplementedError("此工具暂不支持异步调用。")

调用

ini 复制代码
# 实例化工具
date_tool = DateTool()
方式一(invoke)

直接使用date_tool。

ini 复制代码
# 方式一:invoke
result = date_tool.invoke("今天是几月几号?")
result = date_tool.invoke("")
print(result) # 输出:今天是:2026-03-13
方式二(run)

同样是直接使用date_tool。

ini 复制代码
# 方式二:run
result = date_tool.run("今天是几月几号?")
result = date_tool.run("")
print(result)
方式三(Agent)
ini 复制代码
# 方式三:Agent
# 获取模型
model = get_lc_model_client()
# 创建工具列表
tools = [date_tool]

# 使用ReAct提示模板,让Agent具备"思考-行动"的推理能力
prompt = "你是人工智能助手。需要帮助用户解决各种问题。"

# 大模型客户端绑定工具
# 创建Agent
agent = create_agent(
    model=model,  # 聊天模型
    tools=tools, # 工具列表
    system_prompt=prompt
)

# 调用示例
result = agent.invoke(
    {"messages": [{"role": "user", "content": "今天是几月几号?"}]},
)

Function Calling

FunctionCall(函数调用) :这是模型在推理过程中生成的一个结构化输出 。当模型决定要使用某个工具时,它不会直接执行代码,而是返回一个 JSON 对象,其中包含了要调用的工具名称(name)和具体的参数(arguments)。这是模型"思考"的结果。

  • 向模型描述一个工具:你必须提供一个符合特定JSON Schema格式的工具描述列表,包括工具名、描述和参数模式。

  • 模型如何回应表示想要调用工具 :模型不会在文本中直接说"请调用XXX工具",而是会在其返回的特定结构(如OpenAI的tool_calls数组)中,输出一个标准的JSON对象,包含要调用的工具名和参数。

前面讲到的bind_tools,当调用bind_tools(get_date)时,LangChain会自动将工具定义(args_schemadescription转换成底层模型(如GPT-4)所要求的Function Calling格式。

简单地说,Function Calling 是 "通信协议" ,而 LangChain Tool 是 "工程实现框架" 。Tool是开发者提供的能力定义 ,FunctionCall是模型发出的使用请求。在 LangChain 中,你通过 Tool来扩展 Agent 的能力,而 Agent 通过 Function Calling 来实际使用这些能力。

源码

github

相关推荐
格林威3 小时前
SSD 写入速度测试命令(Linux)(基于工业相机高速存储)
linux·运维·开发语言·人工智能·数码相机·计算机视觉·工业相机
Hilaku3 小时前
OpenClaw 跟病毒的区别是什么?
前端·javascript·人工智能
逻辑君3 小时前
认知神经科学研究报告【20260008】
人工智能·深度学习·神经网络·机器学习
GIS数据转换器3 小时前
延凡智慧水务系统:引领行业变革的智能引擎
大数据·人工智能·无人机·智慧城市
程序员老刘3 小时前
AI时代我们是否还需要学编程?丨《Flutter跨平台开发核心技巧与应用》新书上市啦
flutter·ai编程·客户端
行者无疆_ty4 小时前
小龙虾(OpenClaw)安装教程
人工智能·agent·openclaw·小龙虾
CSharp精选营4 小时前
代码过载时代:会写代码已经不牛了,会“删代码”才是真高手
ai编程·vibe coding
2601_949539454 小时前
家用新能源 SUV 核心技术科普:后排娱乐、空间工程与混动可靠性解析
大数据·网络·人工智能·算法·机器学习
北邮刘老师4 小时前
暗数据:智能体探索世界的下一步
人工智能·大模型·prompt·智能体·智能体互联网
ggabb4 小时前
世界人口血型分布及关联特点
人工智能