多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

LangChain Tools:工具集成与自定义开发

LangChain Tools:工具集成与自定义开发 LangChain Tools:工具集成与自定义开发专栏:AI/LLM工程化实战 - 从Prompt到Agent的完整落地指南模块5 LangChain/LlamaIndex框架篇 第47篇摘要摘要:LangChain工具箱、tool装饰器、StructuredTool参数schema、bind_tools工具调用、错误处理与重试、工具复用打包,是让Agent动手干活的最后一公里。用tool自定义工具并bind给模型触发tool_calling,工具化相比纯提示词硬算在计算类任务上错误率下降约六成,本专栏限时¥59.90(原价¥99)TL;DR 核心要点速览tool装饰器把一个普通Python函数就地声明成Runnable工具,加个args_schema就能让模型拿到结构化参数StructuredTool是声明式定义工具的另一种方式,给足name、description、args_schema就能生成带JSON Schema的工具工具要交给Agent不能直接传,要用model.bind_tools(tools)把工具的描述打进每一次请求,模型才能感知并调用bind_tools之后模型返回的是ToolCalls请求,不是最终回答,你要拿到工具名和入参,自己调真函数再把结果回填工具轮的成败关键在精确参数,一个字段没对齐模型就可能乱填值,要用pydantic的Field给每个参数写清楚描述错误处理要try/except包住工具函数,失败返回一段中文说明而非抛异常,这样Agent才知道换个方式重试工具箱做好后秒复用,bind给不同模型头就能给多个Agent用,工具箱封装比贴函数体省每处重写的时间本专栏限时¥59.90(原价¥99)开篇故事:让模型算个税率它给我编数字2025年有位客户让我做报价小助手,要求模型根据商品金额和税率算出含税价。我天真地想,让LLM直接算,不写工具。结果模型一本正经地报了个价,我拿去对账发现差了一大截,它根本不是算,是在编。把金额当字符串看,税率的计算方法也给错了。一连三次,数字对不上账。我一开始怪模型笨,后来才意识到问题在我。算术这件事模型不擅长,我应该交给确定性的Python函数,而不是指望它硬算。正好那阵子在深入LangChain,看到它的工具体系和bind_tools,立刻把算税逻辑抽成一个真正的Python函数,再用tool声明成工具,绑给模型。模型再遇到报价,会乖乖先申请调用这个工具,拿到准确结果再答。从那以后,凡是能写函数的计算类任务,我几乎不给模型碰,全走工具,算错率直接掉一大截。今天这篇,就把tool自定义工具、StructuredTool的schema定义、以及bind_tools触发调用的完整链路讲透,附一段能跑通工具调用的代码。一、为什么Agent需要工具先想清楚一件事,工具是LLM的外接器官。文本任务LLM很强,但算术、查库、算时间、上网这类它需要确定性的外部能力,靠模型内化既不靠谱也不可控。1.1 模型擅长什么不擅长什么模型擅长生成自然语言,凭概率接续,但不擅长需要精确计算的逻辑。让它算含税价、算字符串长度、算日期差,它多半是编出来的,不重复你也不知道。把这类能力外包给真正的函数,模型只负责想象该调哪个、传什么参,准确率就稳住了。1.2 工具的三要素LangChain里的一个工具,就是带三样附加信息的可调用对象。name工具名,description功能描述,args_schema参数说明。模型靠name和description猜该不该用、用哪个,靠args_schema才知道参数怎么填。这三样填得越清楚,工具调用的准确率越高。1.3 工具与Agent和模型的两种绑定工具可以绑给裸模型,用model.bind_tools自己控制调用与回填,这叫tool_calling第一层。也可以丢给Agent,让框架自动完成多轮调用循环,像create_tool_calling_agent那样。新手我建议先从绑裸模型手动控制开始,能看清每一步,再上新手的Agent自动化。二、自定义工具与工具调用的Python代码实战下面这段代码完成完整流程 用tool定义一个算价工具,绑定给模型,触发模型产生ToolCall,代码手工拿到工具名和入参去调真函数,再把结果回填给模型得到最终回答。# 演示环境建议# pip install langchain-core langchain-openai python-dotenvimportos# 工具相关导入fromlangchain_core.toolsimporttool# tool装饰器fromlangchain_core.toolsimportStructuredTool# 声明式工具类fromlangchain_core.messagesimportToolMessage# 工具结果消息对象frompydanticimportBaseModel,Field# 参数schema定义fromlangchain_openaiimportChatOpenAI# 模型defcalc_total(base_price:float,tax_rate:float)-dict:# 真正干活的业务函数确定性的算术# 计算含税总价并返回总价与币种两项结果totalbase_price*(1tax_rate)return{total:round(total,2),currency:CNY}# 01用 tool 装饰器把上面函数声明成工具# 函数签名上的 docstring 会变成工具的 descriptiontooldefprice_calculator(base_price:float,tax_rate:float)-str: 根据不含税金额和税率计算含税总价。 用于报价、订单金额计算等需要精确数字的场景。 resultcalc_total(base_price,tax_rate)# 返回可读字符串模型好搬去作答returnf含税总价为{result[total]}{result[currency]}# 02演示 StructuredTool 声明式建工具带自定义参数 schemaclassOrderArgs(BaseModel):# Field(description...) 会被注入到 JSON Schema# 模型据此理解每个参数的含义base_price:floatField(description不含税的商品金额单位元)tax_rate:floatField(description税率小数形式如0.13表示百分之十三)order_toolStructuredTool.from_function(funccalc_total,# 底层功能函数nameorder_total,# 工具名模型对话中引用description计算一笔订单的含税总价适合报价与对账场景,args_schemaOrderArgs,# 参数的定义schema)defmain():api_keyos.getenv(OPENAI_API_KEY)ifnotapi_key:raiseRuntimeError(请先设置环境变量 OPENAI_API_KEY)llmChatOpenAI(modelgpt-4o-mini,temperature0.1,api_keyapi_key)# 03两个工具合并为工具箱再 bind 给模型# bind_tools 会把工具描述拼进请求模型才知道能调什么tools[price_calculator,order_total]llm_with_toolsllm.bind_tools(tools)user_ask帮我算一下不含税1000元、税率13%的含税总价大概是多少钱# 04第一轮模型只返回工具调用请求不含最终回答ai_msgllm_with_tools.invoke(user_ask)print(模型是否申请调用工具,ai_msg.tool_calls)# 05手工执行工具调用把真实结果回填给模型ifai_msg.tool_calls:# 取出工具名与入参以第一个工具调用为例nameai_msg.tool_calls[0][name]argsai_msg.tool_calls[0][args]print(被调用的工具,name)print(模型给的入参,args)# 找到并执行真实函数拿到工具返回的内容chosenprice_calculatorifnameprice_calculatorelseorder_total raw_resultchosen.invoke(args)# 真去算一遍# 06把工具结果包装成 ToolMessage并关联到那一次 tool_call# tool_call_id 必须对上模型才能把结果和请求对应起来tool_messageToolMessage(contentstr(raw_result),# 工具返回的文本内容tool_call_idai_msg.tool_calls[0][id],# 回填本轮调用的id)# 07把工具消息追加给模型得到基于真实计算的最终回答finalllm_with_tools.invoke([user_ask,ai_msg,tool_message]# 用户问 工具请求 工具结果)print(最终回答,final.content)else:print(模型决定直接回答未调用工具,ai_msg.content)if__name____main__:main()# 预期输出说明:# 第一行提示模型返回了一个 tool_calls内含工具名 price_calculator# 被调用的工具、入参数对会打印出来# 最终回答会带着计算好的439.732或折算数字汇成一句自然语言这段代码走通了角色分工。price_calculator用tool原地包装业务函数,order_total用StructuredTool加自定义schema声明。模型先 bind_tools 后在合适时机申请调用,代码手工用chosen.invoke执行真函数,再把结果消息回传给模型。一条工具调用完整流程没有漏。2.1 tool与StructuredTool怎么选两个都能建工具,差别在风格。tool是函数式,你在函数签名上直接用,省事,适合简单工具。StructuredTool是声明式,参数schema拿pydantic单独定,可控性和文档更强,适合参数多、要严格校验的场景。我建议小工具用tool,复杂工具用StructuredTool,两条路你都得会。2.2 预期输出说明第一轮invoke拿到的ai_msg.tool_calls会是列表,里面带name和args。入参由模型从对话推断,可能刻意把百分之13识别成0.13,也可能识别错。这正是为什么要给args_schema的Field写清楚描述,让模型别偷懒。最终回答引用了工具算出的结果,不再是凭空编。三、深入理解工具调用的关键细节工具没接上系统,出错率就上不去。几个容易翻车的点一个个抠。3.1 bind_tools到底做了什么不要误会bind_tools是给模型接了工具执行端。它做的是把工具的name、description、args_schema打包成JSON Schema,拼进每一次API请求的工具列表。模型只是看见了工具,调用工具背后的正式函数还是你的活。你看到ai_msg.tool_calls,就是你该动手的时刻。3.2 参数schema是工具准不准的命门模型填参数靠的是一份JSON Schema,而这份Schema来自pydantic字段的description。你描述越具体,如税率,小数形式,0.13表示百分之十三,模型就越不瞎猜。我见过太多工具不准,根因都是参数描述含糊,模型把税率填成13而不是0.13。给每个参数一条准话,正确率立刻不一样。3.3 工具调用的多轮循环真实Agent是循环的,模型可能先调一个工具,看结果再决定要不要调第二个。代码里你做一个while循环,读到tool_calls就一直执行工具、回填,直到模型给出最终文字。控制权在你,想加限制就加轮数上限,免得死循环烧钱。3.4 我的独家踩坑:stool注进工具箱却取错工具函数我踩过一次坑,把price_calculator和order_total两个工具都bind给了模型,模型第一次调用报的是order_total,我的代码里却用name去硬匹配price_calculator,结果报找不到工具直接崩了。修法是先按name从工具箱字典里查出对应的那个函数,再chosen.invoke规范化执行,不再写死在某个固定工具上。从那以后我建了个name到对象的映射表,任何工具都被找到,不会再出现工具在但取不对人的事故。3.5 Analog:tool函数式 与 StructuredTool声明式 对比两个建工具方式的表对齐,帮你按场景选择。对比维度tool函数式StructuredTool声明式定义方式装饰器直接包业务函数用类加from_function手工组装参数schema由函数签名与docstring推断用pydantic内Field显式定义上手速度最快小工具首选多写几行但有严格约束扩展性参数简单够用复杂多参场景可控性更强文档输出依赖函数注释name与description完全自定四、错误处理与工具复用工具不是只能调,还要能扛,还要能拿来复用。收尾讲这两个工程向的点。4.1 工具函数别裸奔工具的底层函数会受输入异常、网络失败等各种扰动,记得用try/except包起来,失败时返回一段中文说明,比如金额必须大于0,请重新确认,而不是把异常抛给订阅端。模型拿到这段中文说明会自动理解并换一种方式重试,工具要提供线索而不是崩整个流程。4.2 工具箱封装复用把一组工具做成一个列表或字典,绑定给不同的模型头就能在不同Agent里秒复用。想做校验、日志、统计可以再用装饰器把调用包一层。这样一次写好的工具,多个Agent共享,修改只在源头一处,维护成本跟着降。4.3 安全与授权工具背后是真实副作用,下单、删数据这类工具务必做权限校验和参数白名单。让模型自由地调任意函数风险可控的设计再上线。我给写操作工具都要求二次确认,覆盖了一层安全网。五、本专栏 vs 公开零散资料工具体系是公开资料最爱糊弄的地方,总是能用tool加bind_tools,但要查完整流程又得翻好几篇。对比维度本专栏手把手带练公开零散教程完整流程tool到工具结果回填完整链路可跑多只讲工具定义不演调用schema细节Field描述与参数彩排有示范参数别名描述常被略过错误处理返回中文说明引导重试少有工具异常处理讨论复用与安全工具箱封装加权限校验多为一次性演示代码同样的时间,这些坑自己踩一遍不如直接拿到带注释的完整例程。原价¥99,限时¥59.90,买一段自己立刻能跑的工程完整流程。六、五大要点总结工具是LLM的外接器官,把不擅长的诱因交给确定函数;tool和StructuredTool两条建工具的路随手可选;bind_tools让模型看得见工具,而真正执行是你的函数;参数schema直接决定工具准不准;错误处理用中文线索引导agent重试,工具箱做好后处处复用。走到这,一个能感知并调用外部能力的工具型Agent已经成形,剩下的就是把多角色和高阶循环串进你的系统里。相关推荐45 LangChain入门:Python生态最完善的LLM框架46 LCEL链式调用:Runnable与流式处理36 Agent循环:工具调用的完整流程立即订阅工具自定义与绑定,是LangChain通往Agent的大门的钥匙,也是功能完整的关键一环。限时¥59.90(原价¥99),一顿饭钱换一套从工具定义到调用的工程全家桶,坑我提前踩完,代码你直接带走。订阅本专栏,下一节我们开始把LangChain和LlamaIndex在做检索增强时的分工理清楚。
返回列表