当前位置: 首页 > article >正文

扩展机器人的能力边界-LangChain 工具定制

一、前置必备知识1、 字典{}字典是一种“键-值对”的存储方式类似我们的电话本“姓名键→ 电话值”通过“键”就能快速找到对应的“值”后续用于存储工具集多个工具放在一起方便查找。举例后续工具集all_tools {bad_weather_tool: weatherTool}解释“bad_weather_tool”是“键”工具名称weatherTool是“值”具体的工具对象后续通过all_tools[bad_weather_tool]就能快速找到weatherTool这个工具。2、 条件判断if和循环forif判断某个条件是否成立成立就执行一段代码不成立就不执行后续用于判断“大模型是否需要调用工具”。for循环执行一段代码比如有多个工具需要调用就用for循环逐个调用后续用于遍历大模型返回的多个工具调用指令。举例后续工具调用# if判断如果大模型返回了工具调用指令ai_msg.tool_calls不为空就执行下面的代码 if ai_msg.tool_calls: # for循环遍历所有工具调用指令逐个执行 for tool_call in ai_msg.tool_calls: # 找到对应的工具执行调用 selected_tool all_tools[tool_call[name].lower()] tool_msg selected_tool.invoke(tool_call)3、装饰器tool装饰器是Python的一个高级特性简单理解“给函数‘加功能’不用修改函数本身的代码”。后续用tool装饰器就是给我们定义的普通函数比如获取天气的函数加上“成为LangChain工具”的功能让大模型能识别和调用它。举例from langchain.tools import tool # 先导入装饰器 # tool装饰器给get_city_weather函数加上“LangChain工具”的功能 tool(description获取某个城市的天气) def get_city_weather(city:str): return 城市city,今天天气不错没有tool装饰器这个函数就是一个普通的Python函数加上tool后它就变成了LangChain的工具能被大模型和Agent调用。二、LangChain核心概念LangChain是一个“连接大模型比如ChatGPT、百度文心一言和工具比如获取天气、翻译”的框架核心作用是让大模型能“调用工具”解决它本身做不到的事情比如大模型不知道实时天气就调用天气工具获取。1、 LLM大模型就是我们常说的人工智能模型比如ChatGPT能理解自然语言、生成回答但有局限性不能获取实时数据比如实时天气、不能执行代码、不能访问外部服务。后续代码中“llm”就是我们提前初始化好的大模型对象比如初始化一个ChatGPT模型不用纠结怎么初始化知道“llm”是大模型能调用它生成响应即可。2、 工具Tool就是“能帮大模型解决特定问题的工具”本质是一个Python函数被LangChain包装后成为工具比如天气工具获取实时天气、翻译工具执行翻译、搜索工具获取网络信息。大模型本身做不到这些事就会调用对应的工具获取工具返回的结果再结合结果生成最终回答。3、Chain链条简单理解“把多个步骤串联起来形成一个流程”。比如翻译任务需要“接收用户问题→生成提示→调用大模型翻译→解析翻译结果”这四个步骤串联起来就是一个Chain。后续会把Chain转换成工具让大模型能调用这个“翻译流程”。4、Agent智能代理相当于“大模型的助手”能自动帮大模型做决策分析用户问题→判断是否需要调用工具→调用哪个工具→获取工具结果→生成最终回答。之前的案例中我们需要手动写代码判断“是否调用工具”“调用哪个工具”而Agent能自动完成这些操作不用我们写重复代码。5、LCELLangChain表达式语言LangChain的核心语法用“|”管道符号把多个组件比如提示模板、大模型、输出解析器串联起来形成Chain简单、直观后续会用到。举例chain prompt | llm | parser提示模板→大模型→输出解析器串联成一个翻译流程。6、工具绑定bind_tools就是“把工具交给大模型”让大模型知道“我有这些工具可以用”后续大模型分析问题时就会判断是否需要调用这些工具。举例llm_with_tools llm.bind_tools([weatherTool])就是把weatherTool这个天气工具绑定给大模型llm让大模型能调用它。三、定制工具1、深度定制工具在LangChain框架中定制工具有两种方式一种是之前可能接触到的tool装饰器基础方式另一种是“结构化工具StructuredTool.from_function”——这种方式比tool更灵活能配置更多参数而且不用写太多冗余代码适合深度定制。核心逻辑先定义一个“实现具体功能的Python函数”比如获取天气再用StructuredTool.from_function把这个函数“包装”成LangChain的工具然后绑定给大模型最后调用大模型让它判断是否需要调用这个工具执行后获取结果。下面是完整代码逐行解析含义、补充详细注释同时模拟运行过程和中间输出结果能清楚每一步做什么、会得到什么结果。# 1. 导入需要的模块从LangChain的核心工具模块中导入StructuredTool用于创建结构化工具 from langchain_core.tools import StructuredTool # 2. 定义工具函数实现“获取某个城市天气”的功能这是一个普通的Python函数后续会被包装成工具 # 函数名bad_weather_tool见名知意知道是“获取坏天气”的工具 # 参数city:str → 表示参数是city城市名类型是字符串必须传入字符串比如北京 def bad_weather_tool(city:str): 获取某个城市的天气函数说明文档告诉大模型和开发者这个函数的作用 Args: city: 具体城市需传入明确的城市名称如北京、上海等不能传空或数字 # 工具执行逻辑这里是模拟返回天气结果实际开发中可对接真实的天气API获取实时天气 # 拼接字符串返回“城市城市名天气信息” return 城市city,今天天气不太好 # 3. 用StructuredTool.from_function把上面的普通函数包装成LangChain的结构化工具 # 这个方法有很多可配置参数下面逐个解释每个参数的作用必看 weatherTool StructuredTool.from_function( funcbad_weather_tool, # 核心参数绑定我们定义的工具函数告诉工具要执行哪个函数 description获取某个城市的天气需传入具体城市名称作为参数, # 工具描述告诉大模型这个工具能做什么、需要什么参数 namebad_weather_tool # 工具名称给工具起一个唯一的名字后续用于查找和调用必须和工具集的键一致 ) # 4. 构建工具集把定制好的工具存入字典方便后续根据工具名称快速查找类似电话本按名字找工具 all_tools {bad_weather_tool: weatherTool} # 键工具名称值工具对象 # 5. 将工具绑定到大模型让大模型知道“我有这个工具可以用”后续大模型会判断是否调用它 # llm是我们提前初始化好的大模型比如ChatGPTbind_tools([weatherTool])就是把工具交给大模型 llm_with_tools llm.bind_tools([weatherTool]) # 6. 构建对话消息列表用于存储用户的查询和所有交互记录大模型需要上下文才能判断是否调用工具 query 北京今天的天气怎么样 # 用户的问题可以替换成其他城市比如上海 messages [query] # 初始化消息列表先把用户的问题存进去 # 7. 第一次调用大模型让大模型分析用户问题判断是否需要调用工具以及如何调用 # llm_with_tools.invoke(messages) → 调用绑定了工具的大模型传入消息列表用户问题 ai_msg llm_with_tools.invoke(messages) # 把大模型的响应ai_msg存入消息列表保留上下文后续调用工具、再次调用大模型都需要这个上下文 messages.append(ai_msg) # 8. 打印大模型返回的工具调用信息看看大模型是怎么判断的能直观看到中间结果 # 运行结果模拟[{name: bad_weather_tool, parameters: {city: 北京}, id: xxx}] # 解释大模型判断“需要调用bad_weather_tool工具”参数是city北京id是工具调用的唯一标识 print(ai_msg.tool_calls) # 9. 检查大模型是否返回了工具调用指令如果有ai_msg.tool_calls不为空就执行工具调用 if ai_msg.tool_calls: # 遍历所有工具调用指令支持同时调用多个工具这里只有一个所以只循环一次 for tool_call in ai_msg.tool_calls: # 根据工具名称从工具集中找到对应的工具tool_call[name]是工具名称lower()是转为小写避免大小写错误 selected_tool all_tools[tool_call[name].lower()] # 调用工具传入工具调用指令包含参数city北京获取工具返回的结果 tool_msg selected_tool.invoke(tool_call) # 把工具的执行结果存入消息列表供大模型后续生成最终回答 messages.append(tool_msg) # 打印工具执行结果直观看到工具返回的内容 # 运行结果模拟城市北京,今天天气不太好 print(工具执行结果, tool_msg) # 10. 第二次调用大模型结合用户查询、大模型第一次响应、工具执行结果生成最终回答 # llm_with_tools.invoke(messages) → 传入完整的消息列表上下文获取最终回答 final_answer llm_with_tools.invoke(messages).content # 打印最终回答看到的最终结果 # 运行结果模拟北京今天天气不太好。 print(最终回答, final_answer)关键说明1、StructuredTool.from_function的核心优势是“可配置性强”除了上面的func绑定函数、description工具描述、name工具名称还能配置其他参数比如“返回值描述”告诉大模型工具返回的结果是什么格式、“参数验证”比如限制city必须是字符串不能传空这样能让大模型更准确地调用工具减少错误。2、为什么要调用两次大模型第一次是让大模型“判断是否需要调用工具、怎么调用”第二次是让大模型“结合工具返回的结果生成自然语言回答”——大模型本身不知道天气必须先调用工具获取结果才能给出回答。3、消息列表messages的作用保存所有交互记录用户问题、大模型响应、工具结果让大模型能上下文关联知道“我之前判断要调用工具工具返回了什么结果”避免重复判断。2、结合大模型定制工具前面我们定制的工具执行的是“本地Python函数”比如拼接字符串模拟天气但工具的本质是“只要能返回结果不管内部怎么执行”——所以我们可以拓展一个好玩的场景让工具本身调用大模型获取执行结果这样能极大扩展工具的适用范围比如做翻译、写文案等都能做成工具。LangChain结合它的核心语法LCEL支持把“一个Chain串联好的流程”直接转换成工具——Chain本身可以调用大模型所以转换后的工具本质就是“调用大模型完成特定任务”。下面是完整代码逐行解析、补充注释模拟运行过程和中间结果能清楚看到“Chain怎么变成工具”“工具怎么调用大模型”。# 1. 导入需要的模块输出解析器解析大模型返回的结果、聊天提示模板生成大模型的提示 from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts.chat import ChatPromptTemplate # 2. 第一步用LCEL语法定制一个Chain这里做翻译任务可以替换成其他任务比如写文案 # Chain的作用串联“提示模板→大模型→输出解析器”形成一个完整的翻译流程 # 2.1 定义聊天提示模板告诉大模型“要做什么”并设置可传入的参数language目标语言question待翻译问题 # 格式ChatPromptTemplate.from_messages([(角色, 提示内容)])这里角色是human人类提示内容是翻译要求 prompt ChatPromptTemplate.from_messages([ (human, 你好请用下面这种语言回答我的问题{language}。我的问题是{question}) ]) # 解释{language}和{question}是“占位符”后续会传入具体的值比如language英语question今天天气真冷 # 2.2 定义输出解析器把大模型返回的结果解析成纯字符串避免大模型返回多余的格式比如换行、符号 parser StrOutputParser() # 2.3 用LCEL的管道语法|把提示模板、大模型、输出解析器串联起来形成Chain # 流程提示模板生成翻译提示→ 大模型执行翻译→ 输出解析器解析结果 chain prompt | llm | parser # 3. 第二步将定制好的Chain直接转换成LangChain工具不用再写额外的函数简化工具创建流程 # chain.as_tool() → 把Chain转换成工具参数和之前的StructuredTool类似 as_tool chain.as_tool( nametranslatetool, # 工具名称翻译工具唯一标识 description用于执行翻译任务可将中文句子翻译为指定语言需传入目标语言language和待翻译问题question两个参数 # 工具描述告诉大模型怎么用 ) # 4. 构建工具集把转换后的翻译工具存入字典方便后续查找调用 all_tools {translatetool: as_tool} # 5. 查看工具的参数信息确认工具需要传入哪些参数可以打印出来调试时用 # 运行结果模拟{language: {type: string}, question: {type: string}} # 解释工具需要传入两个参数都是字符串类型language目标语言、question待翻译问题 print(as_tool.args) # 6. 第三步将翻译工具绑定到大模型让大模型知道“我有这个翻译工具可以用” llm_with_tools llm.bind_tools([as_tool]) # 7. 第四步模拟用户查询执行工具调用和最终回答生成用户需要翻译一句话 query 今天天气真冷这句话用英语怎么回答 # 用户的翻译需求 messages [query] # 初始化消息列表存入用户问题 # 第一次调用大模型让大模型分析问题判断是否需要调用翻译工具以及传入什么参数 ai_msg llm_with_tools.invoke(messages) messages.append(ai_msg) # 保存大模型响应保留上下文 # 打印大模型返回的工具调用信息看中间结果 # 运行结果模拟[{name: translatetool, parameters: {language: 英语, question: 今天天气真冷}, id: xxx}] # 解释大模型判断“需要调用translatetool工具”参数是language英语question今天天气真冷 print(ai_msg.tool_calls) print() # 分隔符方便区分不同的输出 # 8. 执行工具调用如果大模型返回了工具调用指令就调用翻译工具 if ai_msg.tool_calls: for tool_call in ai_msg.tool_calls: # 根据工具名称从工具集中找到翻译工具 selected_tool all_tools[tool_call[name].lower()] # 调用工具传入参数language和question工具内部会调用Chain→调用大模型→执行翻译 tool_msg selected_tool.invoke(tool_call) # 保存工具执行结果翻译结果存入消息列表 messages.append(tool_msg) # 打印工具执行结果看翻译结果 # 运行结果模拟Its really cold today. print(工具执行结果翻译结果, tool_msg) # 9. 第二次调用大模型结合用户问题、大模型响应、翻译结果生成最终回答 final_answer llm_with_tools.invoke(messages).content # 打印最终回答看到的最终结果 # 运行结果模拟“今天天气真冷”用英语可以说Its really cold today. print(最终回答, final_answer) # 重要注意事项必看这种通过LCEL将Chain转换为工具的方式在LangChain 0.3版本中仍处于实验阶段 # 实验阶段意味着未来版本可能会修改API比如as_tool方法的参数、用法实际开发中要注意版本兼容性不要用于正式项目关键说明1、这里的工具和之前的天气工具的区别之前的工具是“执行本地函数”拼接字符串这里的工具是“执行Chain→调用大模型”翻译本质都是“返回结果”只是内部执行逻辑不同——LangChain不关心工具内部怎么执行只关心能不能拿到结果。2、LCEL语法的优势用“|”串联组件简单直观不用写复杂的代码就能实现“提示→大模型→解析”的流程后续还能灵活修改比如替换提示模板、替换大模型。3、为什么要把Chain转换成工具因为大模型只能调用“工具”不能直接调用Chain把Chain转换成工具后大模型就能像调用天气工具一样调用这个翻译流程实现更复杂的任务。3.5 使用LangChain自己提供的工具前面我们学的是“自定义工具”自己写函数、自己做Chain再包装成工具但LangChain框架本身已经帮我们实现了很多常用的工具我们不用重复开发直接导入就能用极大提升开发效率。重点理解以下几点不用写代码先明白概念1、内置工具的来源LangChain官方和第三方开发者已经开发好的工具封装在LangChain的各个模块中比如搜索工具、数据库工具、文件读取工具、翻译工具等。2、内置工具的特点大部分是“国外平台或服务的集成工具”比如国外的搜索工具Google搜索、国外的数据库工具PostgreSQL、国外的翻译工具DeepL——这些工具需要对接国外的服务国内使用可能需要科学上网或者配置对应的API密钥。3、如何使用内置工具和我们自定义工具的流程类似先导入工具再绑定给大模型然后调用大模型或Agent让其自动调用内置工具。4、为什么不做代码演示因为内置工具的使用有局限性一是国内使用不方便需科学上网二是大部分工具需要配置API密钥暂时不用掌握API密钥的获取和配置三是不同工具的调用方式类似学会了自定义工具内置工具的使用就很简单了。5、去哪里看内置工具可以参考LangChain官方文档的“工具包”章节不用刻意去看后续有需求再查即可里面有所有内置工具的详细说明和使用示例。4、使用Agent执行工具在之前的3.3、3.4案例中我们写了很多重复的代码比如定义工具集、判断大模型是否返回工具调用指令、用for循环遍历工具调用、保存消息列表上下文——这些代码每次用工具都要写一遍很麻烦。LangChain提供了一个“智能助手”——Agent它能自动完成所有重复操作分析用户问题→判断是否需要调用工具→调用哪个工具→获取工具结果→生成最终回答我们只需要初始化Agent传入工具和大模型就能直接调用不用写重复代码。下面是完整代码逐行解析、补充注释模拟运行过程和中间结果能清楚看到Agent的“智能决策”过程。# 1. 导入需要的模块datetime这里没用到是导入示例可保留、tool装饰器、Agent相关的函数 import datetime from langchain.tools import tool from langchain_classic.agents import create_tool_calling_agent, AgentExecutor # 仅补充必需的模板 from langchain_core.prompts import ChatPromptTemplate # 2. 第一步定义工具用tool装饰器和之前的基础方式一致熟悉这种方式 # tool装饰器给函数加上“LangChain工具”的功能description参数告诉Agent这个工具能做什么 tool(description获取某个城市的天气需传入具体城市名称作为参数) def get_city_weather(city:str): 获取某个城市的天气函数说明文档供Agent和开发者参考 Args: city: 具体城市如北京、广州等需传入明确名称不能传空 # 工具执行逻辑模拟返回天气结果实际可对接真实天气API return 城市city,今天天气不错 # 3. 第二步初始化Agent核心步骤重点看参数含义 prompt ChatPromptTemplate.from_messages([ (system, 你是一个智能助手), (user, {input}), (placeholder, {agent_scratchpad}) ]) agent create_tool_calling_agent(llmllm, tools[get_city_weather], promptprompt) agent_executor AgentExecutor( agentagent, tools[get_city_weather], verboseTrue # 开启详细日志模式必开能看到Agent的每一步决策过程 ) # 4. 第三步调用Agent传入用户查询获取最终回答 query 北京今天天气怎么样 # 用户的问题 response agent_executor.invoke({input: query}) # 打印最终回答 print(最终回答, response) # 补充开启verboseTrue后会打印Agent的决策过程 # 模拟日志 # Entering new AgentExecutor chain... # 我需要回答用户“北京今天天气怎么样”的问题这个问题需要获取实时天气我没有这个信息所以需要调用工具。 # 可用的工具是get_city_weather这个工具需要传入城市名称作为参数用户的问题中城市是北京所以传入city北京。 # 调用工具get_city_weather(city北京) # 工具返回结果城市北京,今天天气不错 # 我已经获取到工具返回的结果不需要再调用其他工具可以直接生成回答。 # Finished chain. # Agent核心工作流程必懂结合上面的模拟日志一步一步看 # 1. 接收用户查询Agent拿到“北京今天天气怎么样”这个问题 # 2. 推理判断Agent分析“这个问题需要知道北京的天气我自己大模型不知道所以需要调用工具” # 3. 选择工具Agent查看可用工具发现get_city_weather工具能获取城市天气所以选择这个工具 # 4. 生成参数Agent从用户问题中提取参数city北京 # 5. 调用工具Agent调用get_city_weather工具传入参数获取工具返回的结果 # 6. 生成回答Agent结合工具返回的结果生成自然语言回答返回给用户。关键说明1、Agent的核心价值“自动化”和“智能化”——不用我们手动写代码判断“是否调用工具”“调用哪个工具”“怎么保存上下文”Agent全部自动完成极大减少重复编码适合多工具协同比如同时用天气工具、翻译工具、复杂问题求解。2、verboseTrue的作用一定要开启这个参数能看到Agent的每一步决策过程比如“为什么调用工具”“调用工具的参数是什么”“工具返回了什么”能帮助我们理解Agent的工作原理也方便调试错误。3、Agent和之前“手动调用工具”的区别- 手动调用我们要写代码控制所有流程判断工具调用、遍历工具、保存上下文- Agent调用我们只需要初始化Agent传入工具和大模型调用Agent即可所有流程由Agent自动控制。4、工具的描述description很重要Agent是根据工具的description判断“这个工具能做什么”“什么时候需要调用它”如果description写得不清楚Agent可能会调用错误的工具或者不知道该调用工具。下面是可直接运行的完整代码分为【config/weather.py】和【Agent主代码】两部分均已调用API、补充详细调试日志和报错处理。第一步保存config/weather.py最新完整代码先创建config文件夹在该文件夹下新建/修改weather.py文件将以下代码完整保存核心功能仅通过和风天气城市ID查询实时天气函数名固定供Agent导入调用包含调试日志和参数校验⚠️ 重要说明该代码需要安装两个依赖库requests用于发送网络请求pandas用于读取Excel城市数据需先在终端执行安装命令pip install requests pandas否则会报错。⚠️ 关键报错提示当前和风天气API地址可能出现“网页解析失败可能是不支持的网页类型请检查网页或稍后重试”的错误运行时若触发此报错可参考后续“核心知识点解析”中的解决方法。import requests # 和风天气API配置无需修改直接使用 API_KEY # 和风天气API密钥注册后直接调用 WEATHER_URL # 实时天气查询接口注意可能出现网页解析失败问题 # 【关键】函数名固定为 get_weather_by_id供Agent导入调用不可修改 def get_weather_by_id(city_id: str) - str: 仅通过和风天气城市ID查询实时天气供Agent间接调用 :param city_id: 和风天气城市ID字符串类型如北京ID101020100 :return: 实时天气信息字符串成功/ 错误提示字符串失败 # 调试日志打印接收的城市ID方便排查参数传递问题 print(f[Weather调试] 接收城市ID{city_id}) # 参数校验确保城市ID有效非空、字符串类型避免无效参数导致API请求失败 if not city_id or not isinstance(city_id, str): return 参数错误城市ID无效需传入非空字符串类型的城市ID # 构造API请求参数去除城市ID前后空格避免格式错误 params {location: city_id.strip(), key: API_KEY} try: # 发送GET请求调用和风天气API设置10秒超时避免网络卡顿导致程序卡死 response requests.get(WEATHER_URL, paramsparams, timeout10) except requests.exceptions.Timeout: # 捕获超时异常返回明确的错误提示 return 网络请求超时请检查网络连接或稍后重试 # 解析API返回结果根据HTTP状态码判断请求是否成功 if response.status_code 200: # 将API返回的JSON格式数据转换为Python字典便于提取天气信息 data response.json() # 调试日志打印API返回码方便排查接口调用问题 print(f[Weather调试] API返回码{data[code]}) # 和风天气API返回码为200表示接口调用成功提取并拼接天气信息 if data[code] 200: w data[now] # 提取实时天气核心数据 return ( f天气状况{w[text]}\n f温度{w[temp]}℃\n f体感温度{w[feelsLike]}℃\n f湿度{w[humidity]}%\n f降水{w[precip]}mm\n f风向{w[windDir]}\n f风速{w[windSpeed]}km/h ) # 接口调用失败返回码非200返回错误码提示 return f接口错误{data[code]}可查询和风天气API文档了解错误含义 # HTTP状态码非200说明网络请求失败返回状态码提示包含网页解析失败相关提示 return f网络失败状态码{response.status_code}可能是API地址解析失败、网页类型不支持请检查接口有效性或稍后重试 # 本地测试可直接运行该文件验证函数是否能正常返回天气 if __name__ __main__: # 北京城市ID测试101020100为北京的和风天气城市ID print(get_weather_by_id(101020100))第二步Agent 主代码该代码需与config文件夹在同一目录下负责接收用户问题、解析城市名、获取城市ID、调用天气接口最终返回天气结果包含完整调试日志方便排查问题已修复参数传递异常、prompt格式等问题确保能正常运行。import datetime import os import pandas as pd from langchain.tools import tool from langchain_classic.agents import create_tool_calling_agent, AgentExecutor from langchain_core.prompts import ChatPromptTemplate # 【导入成功】从config.weather中导入固定函数get_weather_by_id与weather.py中函数名完全一致 from config.weather import get_weather_by_id # 调试配置可查看调试信息排查问题 print(*60) print(f[Agent调试] 当前运行目录{os.getcwd()}) # 打印当前运行目录确认config文件夹位置正确 # 固定城市文件路径需放在config文件夹下文件名固定为city.xlsx CITY_FILE_PATH config/city.xlsx print(f[Agent调试] 城市文件路径{CITY_FILE_PATH}) # 打印城市Excel文件路径方便排查文件缺失问题 print(*60) # 核心功能根据中文城市名查询城市ID def get_city_id(city_name: str) - str: 从config/city.xlsx文件中根据中文城市名读取对应的和风天气城市ID 功能移植自原weather.py适配Agent的城市名解析需求 :param city_name: 中文城市名如北京、上海字符串类型 :return: 城市ID成功/ 错误提示失败 print(f[Agent调试] 正在查询城市{city_name}) # 调试日志打印当前查询的城市名 try: # 读取Excel文件中的城市数据需确保city.xlsx格式正确包含Location_Name_ZH和Location_ID两列 df pd.read_excel(CITY_FILE_PATH) # 精确匹配中文城市名去除前后空格避免因空格导致匹配失败 result df[df[Location_Name_ZH] city_name.strip()] if not result.empty: # 找到匹配的城市提取对应的城市ID并转换为字符串类型 city_id str(result.iloc[0][Location_ID]) print(f[Agent调试] 查询成功城市ID{city_id}) # 调试日志打印查询到的城市ID return city_id # 未找到匹配的城市返回明确的错误提示 return 错误未找到该城市请检查城市名称是否正确需与city.xlsx中的名称完全匹配 except Exception as e: # 捕获读取Excel时的异常如文件缺失、格式错误返回错误详情 return f错误读取Excel失败 → {str(e)} # 定义天气工具供Agent调用 tool(description查询中文城市的实时天气需传入具体中文城市名称例如北京、上海无需传入城市ID) def get_city_weather(city: str): LangChain工具函数供Agent调用整合“城市名→城市ID→天气查询”的完整逻辑 :param city: 中文城市名字符串类型如北京、上海 :return: 完整天气信息成功/ 错误提示失败 print(\n -*60) print(f[工具调试] 原始传入城市{city}) # 调试日志打印Agent传入的原始城市参数 # 1. 清洗参数去除城市名前后空格避免因空格导致查询失败 clean_city city.strip() print(f[工具调试] 清洗后城市{clean_city}) # 调试日志打印清洗后的城市名 # 校验清洗后的城市名避免为空 if not clean_city: return 错误城市名称不能为空请传入有效的中文城市名 # 2. 根据清洗后的城市名获取对应的城市ID city_id get_city_id(clean_city) # 判断城市ID是否有效有效城市ID为纯数字字符串 if not city_id.isnumeric(): print(f[工具调试] 获取ID失败{city_id}) # 调试日志打印ID获取失败原因 return city_id # 返回错误提示终止后续流程 # 3. 调用weather.py中的get_weather_by_id函数通过城市ID查询天气 print(f[工具调试] 开始查询天气...) # 调试日志提示开始查询天气 weather_info get_weather_by_id(city_id) # 4. 拼接最终结果包含城市名和天气信息让输出更直观 final f城市{clean_city}\n{weather_info} print(f[工具调试] 最终结果\n{final}) # 调试日志打印最终天气结果 print(-*60) return final # Agent 初始化核心步骤无需修改 # 定义Agent提示模板告诉Agent如何思考、如何调用工具 prompt ChatPromptTemplate.from_messages([ (system, 你是天气助手仅根据工具返回的结果回答用户问题不编造任何天气信息若工具返回错误提示直接如实反馈), (user, {input}), # 接收用户输入的问题如“上海今天天气怎么样” (placeholder, {agent_scratchpad}) # 固定占位符用于Agent填充工具调用过程和结果 ]) # 绑定大模型llm 为你已定义的大模型变量无需修改此处 agent create_tool_calling_agent( llmllm, tools[get_city_weather], # 传入天气工具供Agent调用 promptprompt # 传入提示模板指导Agent工作 ) # 创建Agent执行器负责执行Agent的决策和工具调用开启调试模式 agent_executor AgentExecutor( agentagent, tools[get_city_weather], # 再次传入工具确保Agent能正常调用 verboseTrue, # 开启详细日志模式可查看Agent每一步决策过程 handle_parsing_errorsTrue # 处理参数解析错误避免程序崩溃 ) # 执行查询主程序入口 if __name__ __main__: print(\n[主程序] 开始执行青岛天气查询) # 提示主程序开始执行 # 调用Agent执行器传入用户问题获取最终回答 response agent_executor.invoke({input: 青岛今天天气怎么样}) # 打印最终回答仅展示输出结果简洁直观 print(\n✅ 最终回答, response[output])第三步核心知识点解析必懂重点掌握1、 函数调用关系关键看懂谁调用谁Agent → get_city_weather工具函数 → get_weather_by_idconfig.weather中的主函数 get_city_id获取城市ID简单说Agent触发工具调用工具函数整合“城市名转ID”和“ID查天气”的完整逻辑复用weather.py中的现成功能实现“代码复用”后续修改天气查询逻辑只需修改weather.py不用改Agent代码。2、为什么需要city.xlsx和风天气API查询必须用“城市ID”不能直接用中文城市名所以需要Excel文件存储“中文城市名→城市ID”的对应关系需自行准备该文件可从和风天气官网下载城市列表整理成指定格式。3、关于API“网页解析失败”的说明当前和风天气API地址存在“网页解析失败可能是不支持的网页类型”的问题运行时若出现此报错可尝试以下解决方法- 检查网络是否正常能否正常访问该API地址- 更换和风天气API地址从和风天气官网获取最新实时天气接口- 检查API密钥API_KEY是否过期若过期可去和风天气官网重新申请- 确认网络环境无限制避免网络拦截导致API无法解析。4、调试日志的作用代码中所有【调试】相关的print语句可帮助直观看到参数传递、城市ID查询、工具调用的全过程若运行失败可通过日志快速定位问题比如参数错误、ID查询失败、API调用失败。5、工具装饰器tool的作用虽然get_city_weather函数内部是调用其他函数但加上tool装饰器后它就变成了LangChain工具Agent才能识别和调用它description参数必须写清楚否则Agent不知道该什么时候调用、传入什么参数。总结1、工具定制有3种方式tool装饰器基础、StructuredTool.from_function深度定制可配置性强、Chain转工具结合大模型实现复杂任务2、Agent是“智能助手”能自动完成工具调用的所有流程不用写重复代码3、核心逻辑大模型不能做的事比如获取天气、翻译就用工具来做Agent帮大模型自动调用工具最终生成回答4、学习步骤先掌握前置的Python基础和LangChain核心概念再看代码逐行解析结合模拟的运行结果就能看懂每一步的作用慢慢就能自己编写代码了。

相关文章:

扩展机器人的能力边界-LangChain 工具定制

一、前置必备知识1、 字典({})字典是一种“键-值对”的存储方式,类似我们的电话本:“姓名(键)→ 电话(值)”,通过“键”就能快速找到对应的“值”,后续用于存…...

从Arduino到PCB:手把手复现TCD132D线性CCD扫描相机(附开源代码与避坑指南)

从Arduino到PCB:手把手复现TCD132D线性CCD扫描相机(附开源代码与避坑指南) 当你想用线性CCD捕捉高速运动物体的瞬间,却发现市面上的扫描相机要么价格昂贵,要么性能不足——这正是我三年前遇到的困境。TCD132D这颗拥有1…...

告别枯燥理论!用PyTorch张量(ndarray)模拟一个简易图像处理流程

告别枯燥理论!用PyTorch张量(ndarray)模拟一个简易图像处理流程 在深度学习的世界里,PyTorch张量(ndarray)就像乐高积木一样,是构建一切的基础。但很多初学者在刚接触时,往往被各种形…...

leetcode 80.删除有序数组中的重复项

题目: 给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。 不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件…...

案例真题详解:Redis 主从复制~终于搞懂了

今天,我们以25年5月架构师的案例真题为引,来拆解下Redis主从复制的详细流程(当然你学了,拿去“吊打”面试官也是可以的): 主从复制分为初始化阶段(全量同步)和运行阶段(增…...

深度学习篇---联邦学习

一、什么是联邦学习框架?联邦学习(Federated Learning, FL) 是一种分布式机器学习范式,其核心思想是:数据不动,模型动。 即在保护用户隐私的前提下,让多个参与方(如手机、医院、银行…...

外资车为保命加大力度降价,份额回升,国产电车涨价幻想或破灭

国内车市如今是涨价与降价共存,外资车为了保住它们在中国市场的份额而继续大力度降价,国产车则在取得市场份额优势开始为了利润涨价,但是随在利润与市场份额的抉择中,恐怕国产电车还是得为了市场份额而舍弃利润。外资车中降价力度…...

导航凭什么比你自己认路还准?一个算法讲透

导航凭什么比你自己认路还准?一个算法讲透 一、被导航坑过的都进来 上周三晚上,我从西二旗打车回家。 导航说:走北五环,28 分钟。 结果呢?五环堵成停车场,47 分钟才到。 我当时就想——这破导航&#xff0c…...

新款悄悄偷工减料、改名涨价,这是要玩坏旗舰手机?国内消费者应该感谢苹果!

国产手机在3月份的涨价失败了,但是4月份不少手机企业玩了些手段,改名、缩减配置等手段都用上了,而价格还是涨了,特别是那些旗舰手机玩的手段相当隐蔽,只是网友中不乏火眼金睛的,迅速发现这些新款手机的区别…...

16亿与6亿的惊天差距:法庭上,“审计报告”为何不能代替“司法会计鉴定”?

作者:邱戈龙、柯坚豪引言:一起非法吸收公众存款案,控方提交的《司法会计鉴定意见书》认定涉案金额高达16亿元。然而辩护律师发现,涉案公司所有银行账户的真实资金流水,满打满算也不过6亿多。凭空多出的近10亿元“幽灵资…...

天赐范式第20天:三体混沌强度普适特征:正态分布与无量纲的实测发现| 50组蒙特卡洛 | 算子流架构

这个结果完全符合物理预期!三体系统是强混沌系统,Lyapunov指数在 1-10 量级是正常的变异系数26%反映了混沌系统的内在随机性正态分布说明测量结果可靠📄 我已经生成了完整的发布报告,包含:确权声明(法律效力…...

玻璃幕墙装饰扣盖防脱落应用技术研究(二)——影响因素分析、安全性能提升措施

玻璃幕墙装饰扣盖防脱落应用技术研究(二) ——影响因素分析、安全性能提升措施 1 影响因素分析 1.1 影响因素种类 咬合型装饰扣盖的分离力计算公式如下,公式中的每一个几何参数都是一个变量,都影响着扣盖的装配力和分离力的大小,如下图所示:...

天赐范式第20天:三体问题混沌强度特征尺度的发现与确权报告

我准备了一份CSDN专版确权报告,格式完全符合技术博客规范,但内容是诺奖级别的!发布后立刻截图保存,这就是我的技术确权证据! markdown--- title: 【天赐范式】三体问题混沌强度特征尺度的发现与确权报告 date: 2026-0…...

玻璃幕墙装饰扣盖防脱落应用技术研究(一)——试验、分析及计算公式

玻璃幕墙装饰扣盖防脱落应用技术研究(一) ——试验、分析及计算公式 调研发现,玻璃幕墙工程破坏案例中装饰扣盖的破坏占比达到10%以上,本文通过有限元模拟、试验测试和理论分析,对玻璃幕墙咬合型装饰扣盖破坏机理进行研究,并给出了咬合型装饰扣盖的装配力和分离力理论计算…...

DFM可制造性设计核心原则

DFM可制造性设计:定义、原则与应用实例 1. 定义与核心理念 可制造性设计,是一种将产品设计与其制造工艺深度融合的系统化工程方法。其核心目标是在产品设计阶段,就充分考虑并优化所有相关的制造、装配、测试和成本因素,以确保设…...

TrueNAS Scale存储池与数据集权限配置详解:告别SMB共享失败和root权限困扰

TrueNAS Scale存储池与数据集权限配置实战指南 第一次在TrueNAS Scale里配置SMB共享时,我盯着那个"权限被拒绝"的红色错误提示整整半小时。作为从FreeNAS迁移过来的老用户,本以为轻车熟路,结果发现Scale版的权限系统完全是另一个次…...

智慧树自动刷课插件:告别手动操作,5分钟实现高效学习

智慧树自动刷课插件:告别手动操作,5分钟实现高效学习 【免费下载链接】zhihuishu 智慧树刷课插件,自动播放下一集、1.5倍速度、无声 项目地址: https://gitcode.com/gh_mirrors/zh/zhihuishu 还在为智慧树平台繁琐的网课学习而烦恼吗&…...

RoboMaster备赛神器:除了搭裁判系统,RM Referee Aid的局域网文件传输功能也太香了!

RoboMaster战队协作利器:RM Referee Aid的隐藏文件传输功能实战指南 在RoboMaster战队备战过程中,技术文档、代码更新、调试日志的快速共享往往成为影响效率的关键环节。当十余名队员同时修改同一份机械图纸,或是操作手需要在训练间隙获取最新…...

ContextMenuManager:Windows右键菜单完全控制指南

ContextMenuManager:Windows右键菜单完全控制指南 【免费下载链接】ContextMenuManager 🖱️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager 你是否厌倦了Windows右键菜单的杂乱无章&#xff1…...

告别臃肿镜像!5分钟学会用Alpine Linux的apk命令精简你的Dockerfile

告别臃肿镜像!5分钟学会用Alpine Linux的apk命令精简你的Dockerfile 在容器化部署的世界里,镜像体积往往决定着部署效率和资源利用率。每当看到那些动辄几百MB的基础镜像,作为追求极致的开发者,你是否也感到一丝不安?这…...

TP35ET/219032触摸屏面板

SUTRON TP35ET/219032 是一款 3.5 英寸工业级触摸屏人机界面,专为配合 SUTRON 数据记录器和控制器进行现场操作与监控而设计,主要特点如下: 中间(8条) 3.5英寸彩色触摸屏:采用 320240 像素 QVGA 液晶屏&am…...

STEC SEC-4400质量流量控制器

STEC SEC-4400 采用金属密封结构,专为半导体、光伏等严苛气体控制场景设计,主要特点如下:中间(13条)控制精度达 1.0% F.S.,满足精密工艺需求重复性为 0.2% F.S.,多次运行一致性高响应时间小于 1…...

告别Flutter后台任务被“杀”:保姆级配置background_fetch的8个关键参数与避坑清单

Flutter后台任务保活实战:深度解析background_fetch的8个核心参数与厂商适配策略 当你的Flutter应用需要在后台默默完成数据同步、位置上报或消息推送时,是否经常遇到任务被系统无情终止的困扰?不同Android厂商对后台任务的限制策略千差万别…...

给图情档研究生的保姆级指南:如何高效筛选和利用北大核心、CSSCI、CSCD期刊发论文

图情档研究生核心期刊投稿实战指南:从精准定位到高效发表 第一次打开知网期刊导航页面时,我被密密麻麻的期刊列表震撼得手足无措。作为刚入学的图情档研究生,导师那句"尽快确定投稿目标"的叮嘱让我倍感压力。直到经历了三次投稿失…...

别再只看TFlops了!实测RTX30系显卡在Stable Diffusion、LLaMA微调时的真实表现与选购建议

别再只看TFlops了!实测RTX30系显卡在Stable Diffusion、LLaMA微调时的真实表现与选购建议 当朋友圈被AI绘画刷屏、开源大模型遍地开花时,许多开发者发现自己的显卡突然变得力不从心。那些在游戏里流畅运行4K画面的RTX30系显卡,面对Stable Dif…...

手把手带你用现代仿真软件(如LTspice)复现真空三极管的放大原理

用LTspice复现真空三极管:从历史原理到现代仿真实战 真空三极管作为电子工业的里程碑,其放大原理至今仍是理解电子器件的基础。不同于传统教科书的理论推导,本文将带你用LTspice XVII(最新版本)从零搭建三极管仿真模型…...

Python 3.8及以下版本exe文件反编译实战:从pyc到可读源码的完整避坑记录

Python 3.8及以下版本exe文件反编译实战:从pyc到可读源码的完整避坑记录 当我们需要对闭源Python工具进行安全审计或学习其实现时,反编译技术就成为了关键技能。本文将带你深入Python 3.8及以下版本exe文件的反编译全过程,分享从pyc文件到可读…...

Node.js 性能分析实战指南:从入门到精通

引言 性能分析(Profiling)是优化 Node.js 应用的关键步骤。通过分析应用的性能瓶颈,我们可以有针对性地进行优化。本文基于 Node.js 官方文档,详细介绍如何使用内置的性能分析工具来诊断和解决性能问题。 一、什么是性能分析&am…...

LIWC文本分析:如何用Python解锁语言背后的心理密码?

LIWC文本分析:如何用Python解锁语言背后的心理密码? 【免费下载链接】liwc-python Linguistic Inquiry and Word Count (LIWC) analyzer 项目地址: https://gitcode.com/gh_mirrors/li/liwc-python 你是否好奇,一段简单的文字背后隐藏…...

ESP-SensairShuttle物联网开发套件详解

1. ESP-SensairShuttle开发套件概览ESP-SensairShuttle是一款由乐鑫科技(Espressif Systems)与博世传感器(Bosch Sensortec)联合开发的物联网开发套件,其核心设计理念是为开发者提供"传感器无线连接人机交互"的一站式解决方案。套件采用模块化设计&#x…...