文章目录为什么需要 ToolTool 工作原理Tool 常用属性基本用法自定义工具LangChain 的 ‌工具Tools‌ 是其核心组件之一用于扩展大语言模型LLM的能力使其能够与外部系统、API 或自定义函数交互从而完成仅靠文本生成无法实现的任务如查询天气、执行计算、搜索数据库等。为什么需要 Tool虽然大模型具备强大的语言理解和生成能力但它本质上是静态的、不可交互的。比如不具备访问数据库、调用 API 的能力不能执行代码或文件操作无法实时访问互联网或动态数据等通过 Tool工具机制可以让模型具备“调用外部函数”的能力使其能够与外部系统、API 或自定义函数交互从而完成仅靠文本生成无法实现的任务。例如实时访问外部世界如天气、股票、网页等调用计算函数数学、单位换算查询数据库或搜索文档实现“多轮决策”流程如规划任务、搜索后总结一个完整的Agent至少要包含两个关键的部分模型是Agent的大脑负责推理、分析规划任务步骤工具是Agent的手脚负责执行任务与外界交互Tool 工作原理工具的工作流程如下定义工具指定工具的名称、描述和执行逻辑函数或类。注册工具将工具提供给代理或链代理根据任务描述选择工具。调用工具代理生成工具调用的指令包括输入参数工具执行并返回结果。处理结果代理或链将工具输出整合到工作流中生成最终响应。工具的核心依赖工具描述帮助代理理解工具的功能和适用场景。输入解析确保工具能正确处理代理提供的输入。输出格式工具返回的结果应与代理或链的期望兼容。Tool 常用属性属性类型描述namestr必选在提供给LLM或Agent的工具集中必须是唯一的。descriptionstr可选但建议描述工具的功能。LLM或Agent将使用此描述作为上下文使用它确定工具的使用args_schemaPydantic BaseModel可选但建议可用于提供更多信息例如few-shot示例或验证预期参数。return_directboolean仅对Agent相关。当为True时在调用给定工具后Agent将停止并将结果直接返回给用户。基本用法我们先通过一个案例快速回顾Agent定义的步骤以及Agent的工作原理。定义一个带有工具的Agent分为两步定义工具定义Agent绑定工具首先使用tool装饰器定义工具# 1.使用tool装饰器定义工具fromlangchain.toolsimporttooltooldefget_weather(location:str)-str: Get the weather in a given location. Args: location: city name or coordinates returnfCurrent weather in{location}is sunny接着定义Agent绑定工具fromlangchain.agentsimportcreate_agentfromlangchain_core.messagesimportHumanMessage# 2.创建智能体并绑定工具agentcreate_agent(modeldeepseek-chat,tools[get_weather])# 3.调用Agentresponseagent.invoke({messages:[HumanMessage(content杭州今天天气如何?)]},)formessageinresponse[messages]:message.pretty_print()执行结果如下Human Message杭州今天天气如何?Ai Message我来帮您查询杭州今天的天气情况。 Tool Calls:get_weather(call_00_FETE4MIR9p1Gr6uszgjcko6m)Call ID:call_00_FETE4MIR9p1Gr6uszgjcko6m Args:location:杭州Tool MessageName:get_weather Current weatherin杭州issunnyAi Message根据查询结果杭州今天的天气是晴朗的。天气很好适合外出活动流程图由此可见所谓的工具本质就是一个可调用的函数要想让Agent知道有哪些工具可调用该如何调用这些工具就必须把这个函数的详细信息发送给模型。包括函数名函数的作用函数的参数和返回值信息所以定义工具的时候关键就是把这些信息描述清楚即可。大模型会自动分析用户需求判断是否需要调用指定工具。如果模型认为需要调用工具如 MoveFileTool 返回的 message 会包含content : 通常为空因为模型选择调用工具而非生成自然语言回复。additional_kwargs : 包含工具调用的详细信息如果模型认为无需调用工具例如用户输入与工具无关返回的 message 会是普通文本回复自定义工具在LangChain中定义工具的过程被大大简化与定义普通函数几乎没什么差别只是在一些细节上需要注意。首先定义工具需要在函数上添加tool装饰器。例如我们定义一个计算平方根的数学工具# 定义工具fromlangchain.toolsimporttooltooldefsquare_root(x:float)-float:计算指定数字的平方根returnx**0.5智能体在工作时需要将函数的名称、输入、作用传递给大模型默认情况下这些信息的来源是工具名称函数名工具输入函数入参工具作用函数的注释当然我们可以通过tool装饰器来覆盖上述信息通过装饰器定义工具名称tool(square_root)deftool1(x:float)-float:Calculate the square root of a numberreturnx**0.5通过装饰器定义工具作用描述tool(square_root,descriptionCalculate the square root of a number)deftool1(x:float)-float:returnx**0.5通过装饰器定义工具入参约束如果要覆盖工具的入参信息则会复杂很多我们要借助于Pydantic或JSON约束。例如我们需要定义个查询天气的tool借助于Pydantic来约束入参。我们定义一个入参的模型在模型中添加入参描述信息# 例如一个查询天气的toolclassWeatherInput(BaseModel):查询天气的输入参数.location:strField(descriptionCity name or coordinates)units:Literal[celsius,fahrenheit]Field(defaultcelsius,descriptionTemperature unit preference)include_forecast:boolField(defaultFalse,descriptionInclude 5-day forecast)定义工具使用定义的模型来约束入参# 定义一个查询天气的tooltool(args_schemaWeatherInput)defget_weather(location:str,units:strcelsius,include_forecast:boolFalse)-str:Get current weather and optional forecast.temp22ifunitscelsiuselse72resultfCurrent weather in{location}:{temp}degrees{units[0].upper()}ifinclude_forecast:result\nNext 5 days: Sunnyreturnresult工具定义好之后调用方式与普通函数类似# 调用数学工具tool1.invoke({x:467})# 调用查询天气工具get_weather.invoke({location:杭州,include_forecast:True})注意 在LangChain中作为工具的函数有两个保留的参数名你的自定义参数不能与之重复他们是config用来传递运行时配置runtime用来传递运行时上下文当我们创建智能体时可以把定义好的工具传递给智能体将来模型就能得到工具信息并根据情况判断是否需要调用工具需要调用哪个工具了。fromlangchain.agentsimportcreate_agent# 创建智能体并添加工具agentcreate_agent(modeldeepseek-chat,tools[tool1,get_weather],system_prompt你是一个智能助手你使用工具来解决用户问题。)接下来调用智能体向其提问模型会自动根据用户问题判断是否需要调用工具该调用哪个工具该传递那些参数并且在调用工具之后根据工具执行结果给用户生成响应。# 调用智能体fortoken,metadatainagent.stream({messages:[HumanMessage(content467的平方根是多少?)]},stream_modemessages):print(token.content,end,flushTrue)fortoken,metadatainagent.stream({messages:[HumanMessage(content北京和杭州接下来几天天气如何?)]},stream_modemessages):print(token.content,end,flushTrue)如果采用stream模式的updates模式可以看到工具调用的具体步骤forchunkinagent.stream({messages:[HumanMessage(content467、529的平方根是多少?)]},stream_modeupdates):forstep,datainchunk.items():print(fstep:{step})print(fcontent:{data[messages][-1].content_blocks})print()输出如下step:model content:[{type:text,text:我来帮你计算这两个数的平方根。},{type:tool_call,id:call_00_oWChR8Xgo21mmWKW0SP9uOS9,name:square_root,args:{x:467}},{type:tool_call,id:call_01_UqzhGeRNcoSoidItA0gScaoY,name:square_root,args:{x:529}}]step:tools content:[{type:text,text:21.61018278497431}]step:tools content:[{type:text,text:23.0}]step:model content:[{type:text,text:计算结果如下\n\n- **467的平方根** ≈ 21.6102\n- **529的平方根** 23.0因为23 × 23 529所以529是完全平方数\n\n所以\n- √467 ≈ 21.6102\n- √529 23}]工作流程如图