游乐游手机版
首页/AI教程/文章详情

LangChain从入门到精通:工具与函数调用详解

时间:2026-05-28 17:04
大模型的能力边界:时效性挑战与解决方案 先看一段代码,猜猜大模型会输出什么? 通义千问大模型model = get_lc_model_client()output_parser = JsonOutputParser()resp = model invoke( "今天是几月几号? ")print(re

大模型的能力边界:时效性挑战与解决方案

先看一段代码,猜猜大模型会输出什么?

# 通义千问大模型model = get_lc_model_client()output_parser = JsonOutputParser()resp = model.invoke("今天是几月几号?")print(resp)

结果令人意外——AI竟然不知道今天是几月几号。再换一个模型(DeepSeek)试试:

model = get_ali_model_client()

没错,它确实给了一个日期,但那并不是真实的今天。之前讨论“为什么需要RAG”时,我们提到过大模型的时效性局限:训练成本极高,模型不可能实时更新。因此,那些最新的事件、最新的知识,大模型很可能一无所知。

这就是大模型的一个能力边界。要突破这一限制,除了RAG技术,还有另一个关键手段——Tool(工具调用)。

Tool:大模型的能力延伸

Tool(工具)是LangChain中的一个核心概念。它封装了一个具体功能(比如一个Python函数),包含名称、描述和参数模式(Schema)。开发者通过@tool装饰器或继承BaseTool来创建工具,本质上是在告诉模型:“我这里有这些能力可以调用。” 整个工作流程如下:

  • 定义工具:开发者将函数(例如get_date())及其描述、参数格式提前告知LLM。
  • 模型规划:用户提问后,LLM并不直接给出答案,而是分析用户意图,选择合适的工具,并严格按照格式生成一个结构化的调用请求。
  • 外部执行:程序解析这个请求,安全地在本地或远端执行真实的函数,获取结果。
  • 整合回复:将执行结果返回给LLM,由它组织成自然语言回复用户。

简单来说,LLM扮演“大脑”(负责理解和规划),外部函数扮演“手脚”(负责执行和获取信息),两者通过结构化的“神经接口”(Function Calling规范)协同工作。

bind_tools():动态绑定工具

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

  • 动态性与组合性bind_tools()的最大优势在于动态性。你可以在运行时根据不同的场景,为同一个模型绑定不同的工具集,实现高度灵活。
  • 底层协议屏蔽:它抽象了不同模型提供商(OpenAI、Anthropic、DeepSeek)在工具调用上的细节差异,提供统一接口。在OpenAI的上下文中,它本质上是在准备tools参数。

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

Agent:工具调用的智能控制器

工具的选择和调用执行,最终是由Agent来完成的。

# 创建Agentagent = create_agent(model=llm,# 聊天模型tools=tools, # 工具列表system_prompt=system_prompt,checkpointer=memory# 传入记忆组件)

在大模型使用工具时,你可以用bind_tools进行工具绑定,但在工程实践中,推荐优先使用带tools参数的Agent,因为它提供了更完善的工具管理和执行流程。

方式一:@tool装饰器

代码示例

获取今天具体日期的自定义工具:

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

获取浏览器并打开指定网站的工具:

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

使用方式:

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. 函数的描述必须准确,且与函数实际功能保持一致。否则会导致回答混乱。

比如,如果把get_date的描述改为“获取今天的北京的天气”:

@tooldef get_date():""" 获取今天的北京的天气 """return datetime.date.today().strftime("%Y-%m-%d")result = agent.invoke({"messages":[{"role":"user","content":"今天北京的天气怎么样?"}]})

用户问天气,结果却调用了日期函数——这就是描述错误带来的问题。

Log验证

前面我们已经介绍了核心流程,现在结合代码Debug来验证一下。从打印结果可以看到:

  1. 用户提问后,大模型发现自己无法直接回答。
  2. 它尝试寻找合适的工具,找到后触发了tool_callsfinesh_reason=tool_calls
  3. tool_calls信息显示name=get_date
  4. 调用get_date返回ToolMessage(2026-03-12)
  5. AI拿到日期信息后,就能给出正确答案了。

果然,和我们前面讲解的核心流程基本一致。

方式二:Tool()类

除了@tool装饰器,还可以通过Tool()类来显式定义工具。

外部函数定义

# 定义工具函数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 resultdef recom_drink(input: str = "") -> str:"""推荐附近的饮品店"""result = '''距离您500米内有如下饮料店:n1、蜜雪冰城n2、茶颜悦色n另外距离您200米内有惠民便利店,里面应该有矿泉水或其他饮品'''return resultdef 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中写在函数首行的描述相同,但此处函数首行就不必再写了。

# 创建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构建

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

方式三:BaseTool继承

这种方式是将工具函数封装成一个类,必须继承自BaseTool。这样设计的目的是为了给AI工具调用建立一套“强类型、结构化”的输入契约。这绝不仅仅是语法规范,而是构建可靠AI Agent的工程基石。

BaseTool类定义

  • name:工具名称
  • description:工具描述

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进行“函数调用”通信的协议基础。
  2. 自动化验证:在工具执行前,框架自动用schema验证模型生成的参数,无效输入会被拦截,避免工具函数崩溃。
  3. 工具发现的元数据:框架可以收集所有工具的schema,自动生成工具目录或用于路由。

# 3. 定义参数模式。即使此工具无需参数,也最好显式定义一个空的Schema,这是良好的实践。args_schema: Type[BaseModel] = DateToolInput

# 使用Pydantic定义输入参数的模型。本例中无需输入,所以模型为空。class DateToolInput(BaseModel):# 这个工具不需要任何输入参数。# 如果未来需要扩展,可以在这里添加字段,例如:# format: str = Field("YYYY-MM-DD", description="日期格式")query: Optional[str] = Field(default=None,description="查询日期的提示词,可为空")

_run方法实现

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

# 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("此工具暂不支持异步调用。")

调用方式

# 实例化工具date_tool = DateTool()

方式一(invoke)

直接使用date_tool:

# 方式一:invokeresult = date_tool.invoke("今天是几月几号?")result = date_tool.invoke("")print(result) # 输出:今天是:2026-03-13

方式二(run)

同样是直接使用date_tool:

# 方式二:runresult = date_tool.run("今天是几月几号?")result = date_tool.run("")print(result)

方式三(Agent)

# 方式三:Agent# 获取模型model = get_lc_model_client()# 创建工具列表tools = [date_tool]# 使用ReAct提示模板,让Agent具备“思考-行动”的推理能力prompt = "你是人工智能助手。需要帮助用户解决各种问题。"# 大模型客户端绑定工具# 创建Agentagent = create_agent(model=model,# 聊天模型tools=tools, # 工具列表system_prompt=prompt)# 调用示例result = agent.invoke({"messages": [{"role": "user", "content": "今天是几月几号?"}]},)

带参数的demo示例

后续我们会在实战案例中进一步巩固tool的使用,其中会涉及带参数的场景,具体可参见源码 tools-get_weather_demo.py

自定义tool

这是开发者为了满足特定业务需求,通过继承BaseTool基类或使用@tool装饰器,自行创建的工具。它封装了专有的逻辑、数据或第三方服务,像上面举例的get_date/DateToolopen_browserrecom_drink都属于自定义工具。

  • 本质:是你为AI智能体量身定做的“专属装备”,是实现业务差异化和核心价值的关键。
  • 核心能力:连接一切——可以把任何函数、类方法、API、数据库查询、甚至遗留系统接口包装成一个工具。
  • 典型场景:查询内部知识库或CRM系统;调用公司内部的订单或天气API;执行特定的数据分析或业务流程。

预置tool

定义

这是LangChain官方或活跃社区预先封装好、可直接调用的工具集合。它们解决的是AI应用中最常见、最通用的需求,属于“开箱即用”的标准化组件。

  • 本质:经过良好测试、接口标准化的“功能黑盒”。只需关注配置(如API密钥),无需关心内部实现。
  • 典型代表WikipediaQueryRun(查询维基百科)、DuckDuckGoSearchRun(网络搜索)、ArxivQueryRun(学术论文搜索)、PythonREPLTool(安全沙箱中运行Python代码)等。

示例

ArxivQueryRun 为例,展示简单调用方法。

  1. 使用load_tools(["arxiv"])组装Agent:

# 组装Agentagent = create_agent( model=llm, tools=load_tools(["arxiv"]), #添加工具列表,绑定的论文查询的工具 system_prompt=system_prompt, checkpointer=memory# 传入记忆组件)

  1. 调用Agent查询论文:

# 调用Agent查询论文result = agent.invoke({"messages": [{"role": "user", "content": "请查询arxiv论文编号1605.08386的信息"}]},# 配置会话标识,用于区分不同用户config={"configurable": {"thread_id": "user_1"}}# 会话唯一标识,用于区分不同用户)

Function Calling:模型与工具的通信协议

Function Calling(函数调用)是模型在推理过程中生成的一个结构化输出。当模型决定要使用某个工具时,它不会直接执行代码,而是返回一个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是开发者提供的能力定义,Function Calling是模型发出的使用请求。在LangChain中,通过Tool来扩展Agent的能力,而Agent通过Function Calling来实际使用这些能力。

源码

github

来源:https://juejin.cn/post/7616201064984428554
上一篇AI制作PPT的5个高效方法提升商业演示成功率 下一篇造点AI图像视频创作平台提升内容效率300%
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Stable Diffusion WebUI本地模型下载配置与性能优化指南
AI教程 · 2026-07-04

Stable Diffusion WebUI本地模型下载配置与性能优化指南

StableDiffusionWebUI适合在个人电脑上运行本地绘图模型,关键在于准备显卡环境、正确下载模型、放入对应目录,并通过分辨率、采样器、显存参数等设置提升生成效率与稳定性。

Stable Diffusion WebUI插件安装配置教程:浏览器、编辑器或扩展市场
AI教程 · 2026-07-04

Stable Diffusion WebUI插件安装配置教程:浏览器、编辑器或扩展市场

StableDiffusionWebUI插件可增强模型管理、提示词、图像处理与工作流效率。安装前需确认版本、环境和来源,按内置扩展页、网址安装或本地导入完成配置,并做好备份与兼容性检查。

Stable Diffusion WebUI Docker一键部署:镜像拉取端口映射数据目录配置
AI教程 · 2026-07-04

Stable Diffusion WebUI Docker一键部署:镜像拉取端口映射数据目录配置

使用Docker部署StableDiffusionWebUI可降低环境配置难度,重点在于选择镜像、映射7860端口、挂载模型与输出目录,并提前确认显卡驱动、存储空间和访问权限。

Stable Diffusion WebUI API Key 获取与配置教程:账号注册与国内网络设置
AI教程 · 2026-07-04

Stable Diffusion WebUI API Key 获取与配置教程:账号注册与国内网络设置

围绕StableDiffusionWebUI的APIKey配置,说明账号注册、密钥获取、本地接口认证、国内网络访问设置、验证方法与安全注意事项,适合AI绘画工具初次部署和团队接入使用。

Stable Diffusion WebUI Linux服务器部署完整教程:从环境准备到后台运行
AI教程 · 2026-07-04

Stable Diffusion WebUI Linux服务器部署完整教程:从环境准备到后台运行

StableDiffusionWebUI在Linux服务器部署需先确认GPU、驱动、Python与依赖环境,再拉取项目、配置模型和启动参数。后台运行建议使用tmux、nohup或systemd,并做好访问鉴权、端口限制、资源监控与模型来源校验。