LangChain工具调用实战:用@tool装饰器给AI Agent装上第一个工具

从「会说」到「会做」:工具是Agent的关键一步
大语言模型再聪明,它的本质仍然只是一个文本生成器。它不知道现在几点,不能查天气,更无法帮你订机票——因为它只能根据训练数据生成语言,无法与真实世界交互。
从技术原理来看,大语言模型(LLM)的核心工作机制是基于Transformer架构的自回归文本生成——它根据已有的上下文token序列,预测下一个最可能出现的token。Transformer架构由Vaswani等人在2017年论文《Attention Is All You Need》中提出,其核心创新是自注意力(Self-Attention)机制,允许模型在处理序列时同时关注所有位置的信息,而非像RNN那样逐步处理。更具体地说,自注意力机制通过将输入序列映射为Query、Key、Value三组向量,计算每对位置之间的注意力权重,从而实现全局信息的聚合。多头注意力(Multi-Head Attention)则将这一过程并行执行多次,让模型能从不同的表示子空间捕捉信息。现代LLM通常包含数十到上百层这样的注意力层,参数量从数十亿到数千亿不等。在推理阶段,为了加速自回归生成,通常采用KV Cache技术——缓存已计算过的Key和Value矩阵,避免每生成一个新token时重复计算整个序列的注意力,这也是为什么LLM推理时GPU显存消耗会随上下文长度线性增长的原因。
自回归生成意味着模型每次只预测一个token,然后将该token加入已有序列,再预测下一个——这就是为什么LLM的回答是逐字生成的。Token是文本的最小处理单元,中文中通常是1-2个字符对应一个token,英文中一个常见单词通常是1个token,而不常见的词可能被拆分为多个子词(subword)。目前主流的分词算法包括BPE(Byte Pair Encoding)、WordPiece和SentencePiece等,它们通过统计语料中的字符组合频率来确定词表。这种逐步生成的机制虽然能产生流畅文本,但也决定了模型本质上只是在做概率预测,而非真正「理解」或「执行」任何操作。
这意味着模型的所有「知识」都冻结在训练时的参数权重中,它无法感知当前时间、访问实时数据或执行任何计算之外的动作。这种局限性被称为「知识截断」(knowledge cutoff)问题。要让模型突破这一边界,目前业界有两种主要方案:一是通过检索增强生成(RAG,Retrieval-Augmented Generation)在推理时从外部知识库检索相关文档并注入上下文,让模型基于最新信息生成回答;二是通过工具调用让模型主动执行外部操作获取实时数据。RAG更适合知识密集型的问答场景(如企业知识库查询),而工具调用则适合需要执行操作的场景(如计算、API调用、数据库查询)。在实际的Agent系统中,两者往往是互补的——Agent可能同时拥有RAG检索工具和各种操作性工具,根据任务需要灵活调用。
这正是「工具(Tools)」存在的意义。在B站UP主小柯的AI Agent系列课程第三讲中,作者明确指出:工具是让Agent从「只会说」变成「会做事」的关键一步。一旦给大模型装上工具,它就能通过调用外部API、执行代码、查询数据库来获取实时信息,真正把事情办成。
本文基于该课程内容,梳理LangChain中工具调用的完整流程与最简实现方式,帮助初学者快速上手Agent开发。LangChain由Harrison Chase于2022年10月创建,是目前最流行的LLM应用开发框架之一。其核心设计理念是通过模块化组件(模型、提示模板、工具、记忆、链、Agent等)的组合来构建复杂的AI应用。LangChain的架构分为几个层次:langchain-core提供基础抽象和接口定义;langchain-community包含第三方集成;各模型提供商有独立的包(如langchain-openai)。值得注意的是,LangChain生态在2024年经历了重要演进——LangGraph作为独立库被推出,专门用于构建有状态、可控的Agent工作流。与传统的Agent循环不同,LangGraph采用图(Graph)的抽象来定义Agent的执行流程,每个节点代表一个处理步骤(如调用LLM、执行工具、人工审核),边则定义了状态转移条件。这使得开发者可以精确控制Agent的行为路径,实现条件分支、循环、并行执行等复杂编排,同时支持检查点(checkpoint)和回放功能,极大提升了生产环境中Agent的可调试性和可靠性。与之竞争的框架包括LlamaIndex(侧重RAG)、AutoGen(侧重多Agent对话)、CrewAI(侧重Agent角色协作)、以及Anthropic推出的Claude Agent SDK等。
LangChain工具调用的完整流程
要理解工具调用,首先要看清整个链路。以一个简单问题「25乘以4加10等于多少」为例,工具调用会经历四个步骤:
四步走通工具调用链路
-
用户提问:用户向Agent发起请求,例如询问一个数学计算问题。
-
模型返回结构化请求:大模型分析后并不直接回答文本,而是返回一个结构化的工具调用请求。相当于模型说:「我需要用计算器这个工具,参数是 25×4+10」。这里的关键技术基础是Function Calling协议——当模型判断需要使用工具时,它输出的不是自然语言,而是一个符合JSON Schema规范的结构化对象,包含工具名称和参数。Function Calling最早由OpenAI在2023年6月随GPT-3.5/GPT-4 API一同发布,标志着LLM从纯文本生成向结构化输出的重要转变。在此之前,开发者需要通过复杂的提示工程让模型输出特定格式的文本再手动解析,容易出错且不稳定。Function Calling将工具调用变成了模型的原生能力——模型经过专门微调,能够在适当时机生成符合规范的JSON调用请求,而非自然语言。这一能力需要在模型训练阶段通过大量工具调用数据进行有监督微调(SFT)和RLHF对齐,使模型学会何时应该调用工具、如何正确填写参数。
值得关注的是,Function Calling协议在不同厂商之间的实现并不完全统一。OpenAI最初称之为Function Calling,后来演进为Tool Use;Anthropic的Claude使用类似但格式略有不同的协议。为了解决这种碎片化问题,Anthropic在2024年底提出了MCP(Model Context Protocol,模型上下文协议),试图建立一个开放的、统一的标准,让工具提供者只需实现一次MCP服务端,就能被所有支持MCP的模型和框架调用。MCP采用客户端-服务端架构,定义了工具发现、参数传递、结果返回的标准化流程,类似于USB接口统一了硬件连接标准。LangChain也已经支持MCP协议的集成,这意味着开发者可以同时使用原生@tool定义的工具和通过MCP协议接入的外部工具。
JSON Schema是一种用于描述JSON数据结构的元数据标准,精确定义了每个字段的类型、是否必填、取值范围等约束。各大模型提供商(OpenAI、Anthropic、DeepSeek等)都实现了类似的协议,使得框架可以统一解析模型的工具调用意图。
-
框架自动执行:LangChain框架自动执行这个工具调用,运行对应的计算函数,拿到结果 110。在这一步中,框架起到了「代理人」的角色——它解析模型输出中的工具调用请求,找到对应的工具函数,验证参数类型,执行函数,并将结果格式化后返回。这种解耦设计意味着模型完全不知道工具的具体实现——它只知道工具的名称、描述和参数Schema,具体执行逻辑完全由框架侧管理。
-
组织自然语言回答:把工具返回的结果交还给大模型,模型再组织成自然语言回答用户。

整个过程对用户来说是无感的——用户只看到了提问和回答,中间的工具调用由Agent自动完成。这种「模型决策 + 框架执行」的分工,正是Agent架构的精髓所在。
值得一提的是,这一模式背后对应的是学术界的ReAct(Reasoning + Acting)范式,由Yao等人在2022年提出。ReAct范式源自Shunyu Yao等人发表的论文《ReAct: Synergizing Reasoning and Acting in Language Models》。在此之前,LLM的推理(Chain-of-Thought)和行动(工具调用)是分别研究的两个方向。Chain-of-Thought(CoT)由Wei等人在2022年提出,通过在提示中加入「让我们一步步思考」这样的引导,让模型显式展示中间推理步骤,从而提高复杂任务的准确率。ReAct的贡献在于证明了将推理和行动交织在一起——让模型生成思维链来规划行动,同时通过行动获取外部信息来辅助推理——能显著提升任务完成率。其核心思想是让LLM交替进行推理(Thought)和行动(Action):模型先思考当前需要做什么,然后选择合适的工具执行,观察结果后再决定下一步。具体的循环格式为:Thought(思考当前状态)→ Action(选择并调用工具)→ Observation(观察工具返回结果)→ 重复或给出最终答案。LangChain的Agent实现正是基于这一循环——模型产生工具调用请求→框架执行工具→结果返回模型→模型决定是否需要继续调用工具或直接回答用户。这种循环可以多次迭代,直到任务完成。
在ReAct之后,学术界又提出了多种Agent推理范式的改进:Reflexion引入了自我反思机制,让Agent能从失败中学习;LATS(Language Agent Tree Search)将蒙特卡洛树搜索引入Agent规划;而Plan-and-Execute模式则将规划和执行分开——先让模型生成完整计划,再逐步执行每个步骤,适合需要全局规划的复杂任务。
用 @tool 装饰器定义工具
在LangChain中,定义工具最简洁的方式是使用 @tool 装饰器。下面是课程给出的计算器工具示例:
from langchain_core.tools import tool
@tool
def calculator(expression: str) -> str:
"""计算数学表达式,比如 2+3 或 10*0.5"""
try:
# 注意:生产环境中应使用 ast.literal_eval 或专用数学解析器
# eval 仅用于演示,实际项目需防范代码注入风险
result = eval(expression)
return f"计算结果:{result}"
except Exception as e:
return f"计算错误:{e}"

@tool装饰器背后做了三件事
Python装饰器(decorator)本身是一种设计模式,允许在不修改原函数代码的情况下为函数添加额外功能。它本质上是一个高阶函数:接收原始函数作为输入,返回一个增强后的对象。在Python中,@tool 语法糖等价于 calculator = tool(calculator)——即将原函数传入tool函数处理后重新赋值。在LangChain中,@tool 装饰器会将普通Python函数包装成一个 StructuredTool 对象,该对象不仅保留了原函数的执行逻辑,还自动生成了符合LLM工具调用协议的元数据。
当@tool装饰器将普通函数转化为StructuredTool对象时,实际上创建了一个包含丰富元数据的类实例。StructuredTool继承自BaseTool基类,包含name(工具名)、description(描述)、args_schema(基于Pydantic模型自动生成的参数验证Schema)、以及_run方法(实际执行逻辑)。Pydantic是Python生态中最流行的数据验证库,它利用Python类型注解(Type Hints,PEP 484定义)在运行时进行数据校验和序列化。Pydantic v2使用Rust编写的核心验证引擎,性能比v1提升了5-50倍。LangChain利用Pydantic将函数签名转换为JSON Schema,这个Schema随后被注入到发送给LLM的请求中,作为模型理解如何正确调用该工具的规范说明。
除了@tool装饰器,LangChain还提供了其他定义工具的方式:直接继承BaseTool类(适合需要复杂初始化逻辑或异步执行的场景)、使用StructuredTool.from_function()工厂方法、以及通过Pydantic模型精确定义参数Schema。对于需要严格参数验证的生产场景,推荐使用带有详细Pydantic模型的定义方式。
具体来说,@tool 装饰器自动完成了三项关键工作:
- 生成输入参数定义:把函数的类型标注(如
expression: str)自动转换成 JSON Schema 格式,让大模型知道调用这个工具时需要传入什么参数。例如,expression: str会被转换为{"type": "object", "properties": {"expression": {"type": "string"}}, "required": ["expression"]}。如果参数有默认值,则该参数不会被标记为required。 - 提取工具描述:把函数的文档字符串(docstring)提取为工具的自然语言描述。大模型正是靠这段描述来判断「什么时候该用这个工具」,因此文档字符串写得越清晰,模型调用越准确。好的工具描述应该包含:工具的用途、适用场景、输入格式的示例、以及不适用的情况。这本质上是一种面向LLM的「API文档编写」。
- 生成唯一工具名称:默认使用函数名作为工具名称(如
calculator)。
这里有一个值得注意的安全细节:示例中使用了 eval 来执行表达式,但作者特别提醒——生产环境绝不能直接用 eval。Python的 eval() 函数能够执行任意Python表达式,这意味着恶意用户可以通过构造特殊输入(如 __import__('os').system('rm -rf /') )来执行危险的系统命令,这就是代码注入攻击。在Agent场景中风险更高,因为用户输入经过LLM转化后传入工具,攻击面更难预测——攻击者可以通过精心设计的自然语言提问,诱导LLM生成恶意的工具调用参数,这种攻击方式被称为间接提示注入(Indirect Prompt Injection)。
安全的替代方案包括:ast.literal_eval(仅解析Python字面值,如数字、字符串、列表等)、sympy 库(专业的符号数学计算库,只解析数学表达式)、numexpr 库(安全的数值表达式求值,支持高性能数组运算),以及基于沙箱的代码执行环境(如Docker容器、gVisor、或云端的无服务器函数)。对于Agent系统而言,还应该实施工具调用的权限分级——将工具分为只读工具(如查询天气)和写入工具(如发送邮件、删除文件),对写入工具增加人工确认环节(Human-in-the-Loop),防止Agent在判断失误时造成不可逆的后果。
把工具接入 Agent
定义好工具后,下一步是初始化大模型并创建Agent。课程以DeepSeek模型为例:
from langchain.chat_models import init_chat_model
llm = init_chat_model(
model="deepseek-chat", # 替换为你使用的模型
base_url="https://api.deepseek.com",
api_key="替换为你的API Key"
)

DeepSeek是由中国AI公司深度求索开发的大语言模型系列,其DeepSeek-V2/V3采用了MoE(Mixture of Experts,混合专家)架构,在保持强大性能的同时大幅降低了推理成本。init_chat_model 是LangChain提供的统一模型初始化接口,它根据模型名称自动选择对应的模型类(如ChatOpenAI、ChatAnthropic等),简化了多模型切换的代码改动。base_url 参数允许指向任何兼容OpenAI API格式的端点,这意味着你也可以指向本地部署的开源模型(通过vLLM、Ollama等推理框架暴露的API),实现完全本地化的Agent系统。
接着创建Agent,并通过 tools 参数把工具传入:
from langchain.agents import create_agent
agent = create_agent(
model=llm,
tools=[calculator],
prompt="你是一个智能助手,可以使用计算器工具来回答数学计算问题"
)
result = agent.invoke({
"messages": [{"role": "user", "content": "请计算 25*4+10 等于多少"}]
})
print(result["messages"][-1].content)

只要把定义好的工具通过 tools 参数传给 create_agent,Agent 就能在需要时自动判断并调用它,无需开发者手动干预调用时机。这里的「自动判断」依赖于模型在推理阶段对工具描述信息的理解——LangChain会在发送给模型的系统提示中注入所有可用工具的名称、描述和参数Schema,模型据此决定是否需要调用工具以及调用哪一个。在底层API层面,这些工具信息通过请求体中的 tools 字段传递给模型API,模型在响应中通过 tool_calls 字段返回调用请求。
当工具数量较多时(超过10-20个),模型的选择准确率可能下降,此时可以采用工具路由策略:先用一个轻量模型或嵌入相似度计算(将用户查询和工具描述都转换为向量,计算余弦相似度来筛选最相关的工具)筛选候选工具,再让主模型从缩小后的范围中精确选择。这种分层决策机制在生产级Agent系统中非常常见,也是构建拥有大量工具的通用Agent时需要考虑的架构问题。OpenAI等厂商的经验表明,在单次请求中提供5-10个工具时模型表现最佳,超过20个工具后性能会明显下降。
工具命名的注意事项
作者在课程末尾还提到一个容易踩坑的细节:工具名称建议使用蛇形命名法(snake_case),比如用 web_search 而不是 Web Search。原因在于,部分模型供应商对包含空格或特殊字符的名称会直接报错。这是因为工具名称在Function Calling协议中通常需要作为JSON对象的键名或函数标识符使用,而JSON规范和大多数编程语言的标识符命名规则都不支持空格和特殊字符。此外,蛇形命名法在Python生态中是函数命名的标准惯例(PEP 8规范),保持一致性也有利于代码的可读性和维护性。
如果需要自定义工具名称或描述,可以给 @tool 装饰器传入参数:
from langchain_core.tools import tool
@tool("calculator", description="执行数学运算,如加减乘除")
def calc(expression: str) -> str:
"""计算数学表达式"""
...
这种写法在需要更精确控制工具元信息时非常有用,尤其是当函数名不够直观或需要为模型提供更详细的调用指引时。一个进阶技巧是在description中使用Few-shot示例来提高模型调用的准确性,例如:description="执行数学运算。示例输入:'2+3'、'(10*5)/2'。注意:不支持变量赋值或复杂编程表达式"。
小结与展望
回顾本讲的核心要点:
- 工具调用让大模型能够通过调用外部函数来获取信息、执行操作,突破了「只能生成文本」的天然局限。
@tool装饰器是LangChain中最简洁的工具定义方式,自动处理类型标注、文档描述和工具命名三件事。- 把定义好的工具通过
tools参数传给create_agent,Agent就能在需要时自动调用。 - 安全与命名规范不容忽视:避免在生产环境使用
eval,工具名称应采用蛇形命名法。
这一讲展示的是单工具、单参数的最简场景。而在真实项目中,工具往往需要多个参数、复杂输入,甚至需要Agent同时协调调用多个工具(即并行工具调用,parallel function calling——模型在一次响应中同时发出多个工具调用请求,框架并发执行后统一返回结果,大幅减少交互轮次)。更复杂的场景还包括工具调用链(一个工具的输出作为另一个工具的输入,也称为sequential tool calling)、工具调用失败后的重试与降级策略(如指数退避重试、降级到备选工具、或向用户请求澄清)、以及多Agent协作中的工具权限管理(不同Agent拥有不同的工具访问权限,通过权限矩阵控制工具调用的边界)。
从行业趋势来看,Agent工具生态正在快速标准化和丰富化。MCP协议的普及使得工具可以像「插件」一样即插即用;各大平台(如GPTs Store、Coze、Dify)也在构建自己的工具市场。未来的Agent开发将越来越像「搭积木」——开发者更多精力会放在编排逻辑和业务理解上,而非底层工具实现。
这些进阶话题,将在后续课程中继续展开。
对于想入门Agent开发的读者来说,理解「模型决策、框架执行」这一分工模型,是掌握后续复杂编排的基础。这一范式的核心洞察在于:LLM擅长理解意图和做出决策,但不擅长精确执行;而传统代码擅长精确执行但不擅长理解模糊指令——Agent架构正是将两者的优势结合起来。
相关推荐

AI对话中模型名称消失怎么办?原因分析与解决方案
AI对话应用中模型名称标识突然消失,影响用户判断回答质量和使用成本。本文深入分析模型标识消失的可能原因,包括UI改版、前端Bug和A/B测试,并提供实用解决建议。

Rainbow DQN没有新想法:值函数强化学习算法演进全解析
从表格Q-learning到DQN再到Rainbow,详解值函数强化学习算法的演进逻辑。通过失败驱动的视角,理解Double DQN、优先经验回放、对决网络、多步回报、C51等六项关键改进如何逐步修补前代算法的痛点,最终汇聚为Rainbow。

OpenAI暂停RL训练:模型能力增长过快,安全对齐跟不上了?
Sam Altman宣布OpenAI暂停强化学习训练,称模型能力增长极其迅速已超越安全对齐进度。本文深度解读这一决定背后的技术原因、行业影响及AI安全治理启示。