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

相关推荐
ECH00O002 小时前
07-Embedding/嵌入:把文字变成数字的魔法
人工智能
旭日跑马踏云飞2 小时前
不需要账号、免登录使用ClaudeCode+国内模型
人工智能·阿里云·ai·腾讯云·ai编程
踩着两条虫2 小时前
AI 驱动的 Vue3 应用开发平台 深入探究(六):双向代码转换之DSL到Vue代码生成
前端·vue.js·ai编程
@Aurora.2 小时前
【AI赋能测试】—AI赋能web UI自动化测试
人工智能
ECH00O002 小时前
07-Prompt Engineering:与大模型对话的艺术
人工智能
猫头虎2 小时前
Docker 安装 OpenClaw 报错排查完全手册(续):如何解决pairing required,`EACCES: permission denied`Docker 拉取镜像提示 `denied
运维·docker·容器·开源·github·aigc·ai编程
YimWu2 小时前
Opencode 核心设计-Session会话机制
前端·agent·ai编程
chaors2 小时前
Langchain入门到精通0x06:RAG
人工智能·langchain·ai编程
molamolaxtq2 小时前
Minimind项目源码解析(3)Attention模块(核心)
人工智能