ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

LangChain 入门系列五:Tools创建及错误处理

LangChain 入门系列五:Tools创建及错误处理 LangChain 入门系列五Tools定义及使用前言Tools创建google风格方式Pydantic方式Json Schema方式调用Tools方式model调用ToolsAgent调用工具工具调用错误处理总结前言上一篇介绍了LangChain框架的Agent定义、调用方式本节介绍Tools的定义及Agent如何调用工具。在LangChain中工具Tools是具有明确定义输入和输出的可调用函数模型则根据对话上下文决定何时调用工具以及传递哪些参数。Tools创建LangChain框架提供的工具是一个被 tool 装饰器修饰的函数。fromlangchain_core.toolsimporttooltooldefcalculate(a:float,b:float,operation:str)-float:工具函数计算两个数字的运算结果print(f调用 calculate 工具第一个数字{a},第二个数字{b},运算类型{operation})result0matchoperation:caseadd:resultabcasesubtract:resulta-bcasemultiply:resulta*bcasedivide:ifb!0:resulta/belse:raisedivide by zeroreturnresult说明tool装饰器创建工具默认的名称与函数名相同也可以通过“tool(“XXXX”)”指定新名称。tool定义工具时可以在函数内进行注释可以使用python注释无需参数和返回值说明如上述示例代码未对参数和返回值做说明在tool定义工具时可传入description参数传入description参数后它会完全替代函数的文档字符串成为工具的唯一描述。不要使用config或runtime作为参数名这些是LangChain内部保留的特殊参数字段tool(calculator,descriptionPerforms arithmetic calculations. Use this for any math problems.)defcalc(expression:str)-str:Evaluate mathematical expressions.returnstr(eval(expression))另外为了增加可读性和可维护性针对入参结构、函数入参及返回值注释方式工具函数LangChain框架提供了三种常用的工具创建方式在实际的开发常推荐使用前两种方式创建工具。遵循google风格定义工具入参Pydantic模型定义工具入参使用Json Schema定义工具入参下面将详细介绍如何用这三种入参结构创建工具google风格方式代码示例fromlangchain.agentsimportcreate_agentfromlangchain_core.toolsimporttoolfrominit_modelimportdeepseek#google分格创建工具tool(description查询员工信息)defget_employee_info(employee_id:str)-str: 根据员工ID查询工具信息 Args employee_id: str Returns: mock_employee_data{emp00011:{name:zhansan,department:技术部},emp00022:{name:wangwu,department:人事部}}ifmock_employee_data.get(employee_id)isnotNone:returnf查到员工信息是{mock_employee_data[employee_id]}else:returnf{employee_id}不存agentcreate_agent(modeldeepseek,tools[get_employee_info],system_prompt你是一个员工管理助手帮助获取员工信息)responseagent.invoke({messages:[{role:user,content:请查询emp00011员工的信息}]})print(response[messages][-1].content)说明使用“Google风格”对工具进行注解即使用 Args:、Returns:、Raises:等关键字这种方式可读性强。Agent通过工具的这些注释来理解工具的用途和调用时机因此清晰、准确的文档字符串是工具能被正确调用的前提。注释一定要遵循规范否则会报错具体可以参考下文LangGraph GraphLoadErrorArg Returns in docstring not found in function signature 完整踩坑修复方案Pydantic方式Pydantic 模型可以对参数的格式和验证规则进行设置帮助大模型更准确地理解如何调用工具对于参数复杂、有特定约束条件的业务场景非常适合。fromlangchain.agentsimportcreate_agentfrompydanticimportBaseModel,Field,field_validatorfromtypingimportLiteral,Optionalfromlangchain_core.toolsimporttoolimportjsonfrominit_modelimportdeepseek# 1.定义复杂的工单查询参数模型classTicketQueryInput(BaseModel):工单查询输入参数 - 支持多种筛选条件ticket_id:Optional[str]Field(defaultNone,description工单ID)assigner:Optional[str]Field(defaultNone,description负责人姓名)status:Optional[Literal[open,in_progress,resolved,closed]]Field(defaultNone,description工单状态: open(待处理), in_progress(处理中), resolved(已解决), closed(已关闭))priority:Optional[Literal[low,medium,high,urgent]]Field(defaultNone,description优先级: low(低), medium(中), high(高), urgent(紧急))field_validator(ticket_id)defconvert_ticket_id_to_upper(cls,v:Optional[str])-Optional[str]:将工单ID转换为大写returnv.upper()ifvelseNone# 2. 使用tool装饰器定义工具并通过args_schema指定参数模型tool(args_schemaTicketQueryInput)defquery_tickets(ticket_id:Optional[str]None,assigner:Optional[str]None,status:Optional[str]None,priority:Optional[str]None,)-str: 查询工单系统支持多条件筛选。 此工具用于从工单管理系统中检索符合特定条件的工单信息。 至少需要提供一个筛选条件否则返回最近创建的工单。 try:# 模拟一个简单的工单数据库mock_tickets_db[{ticket_id:TK2025012001,assigner:张三,title:登录页面加载缓慢,status:open,priority:low},{ticket_id:TK2025012002,assigner:李四,title:用户头像上传失败,status:open,priority:medium},{ticket_id:TK2025011901,assigner:张三,title:支付成功通知未发送,status:resolved,priority:high},{ticket_id:TK2025011902,assigner:马六,title:订单查询接口返回空值,status:closed,priority:high},]filtered_ticketsmock_tickets_dbifticket_id:filtered_tickets[tfortinfiltered_ticketsift[ticket_id]ticket_id]ifassigner:filtered_tickets[tfortinfiltered_ticketsift[assigner]assigner]ifstatus:filtered_tickets[tfortinfiltered_ticketsift[status]status]ifpriority:filtered_tickets[tfortinfiltered_ticketsift[priority]priority]ifnotfiltered_tickets:return未找到符合条件的工单# 格式化返回结果result{total_count:len(filtered_tickets),tickets:filtered_tickets}returnjson.dumps(result,ensure_asciiFalse,indent2)exceptExceptionase:returnf查询工单时发生错误:{str(e)}# 3.创建智能体agentcreate_agent(modeldeepseek_llm,tools[query_tickets],system_prompt你是一个助手你可以查询工单系统的工单信息。,)# 4. 测试智能体# 4.1 测试基本功能# response1 agent.invoke({messages: [{role: user, content: 请帮我查一下TK2025012001工单的详细信息}]})# print(response1 content, response1[messages][-1].content)# 4.2 测试 Pydantic 参数验证response5agent.invoke({messages:[{role:user,content:请帮我查一下tk2025012001工单的详细信息}]})print(response5)print(response5 content,response5[messages][-1].content)以上代码注意如下问题pydantic方式定义工具通过 tool(args_schemaPydanticModelCls)将这个 Pydantic 模型与工具函数关联。PydanticModelCls 需要继承自 BaseModel的类使用类型提示如 str, int和 Field函数来声明每个字段的名称、类型、默认值和描述。每个字段的 description参数描述含义要准确且明确它帮助大模型能准确理解参数含义。Pydantic 的类型系统进行参数验证在大模型需要调用工具前Pydantic 会自动验证参数的类型和有效性。在 convert_ticket_id_to_upper方法中的 cls代表的是 这个 Pydantic 模型类本身在这里也就是 TicketQueryInput这个类。field_validator装饰器将对应方法标记为类方法类方法的第一个参数约定俗成地命名为 cls它指向类而不是类的实例。这样在验证逻辑中如要访问类的其他属性或方法就可以通过 cls 来操作。作。Json Schema方式JSON Schema 提供了极大的灵活性特别适合参数结构需要动态生成的场景。fromlangchain.agentsimportcreate_agentfromlangchain_core.toolsimporttoolimportjsonfrominit_modelimportdeepseek#1. 直接使用JSON Schema字典定义复杂的查询参数book_query_schema{type:object,properties:{title_keyword:{type:string,description:图书标题关键词支持模糊匹配},author:{type:string,description:图书作者姓名},category:{type:string,enum:[技术,文学,历史,科学,经济学,传记],description:图书分类}},required:[],# 至少需要提供标题关键词、作者或分类中的一个条件所以这里为空}#2. 使用tool装饰器定义工具并通过args_schema指定JSON Schematool(args_schemabook_query_schema)defquery_books(title_keyword:strNone,author:strNone,category:strNone)-str: 根据多种条件查询企业图书库中的图书信息。 此工具用于从企业图书管理系统中检索图书。 至少需要提供标题关键词、作者或分类中的一个条件。 try:# 模拟一个简单的图书数据库mock_books_db[{book_id:BK1001,title:人工智能导论,author:张明,category:技术},{book_id:BK1002,title:机器学习实战,author:李华,category:技术},{book_id:BK1003,title:中国近代史,author:王伟,category:历史},{book_id:BK1004,title:红楼梦,author:曹雪芹,category:文学},{book_id:BK1005,title:经济学原理,author:刘强,category:经济学},{book_id:BK1006,title:文学导论,author:张明,category:文学},{book_id:BK1007,title:Python编程基础,author:王丽,category:技术}]filtered_booksmock_books_db# 根据条件过滤图书iftitle_keyword:filtered_books[bookforbookinfiltered_booksiftitle_keyword.lower()inbook[title].lower()]ifauthor:filtered_books[bookforbookinfiltered_booksifbook[author]author]ifcategory:filtered_books[bookforbookinfiltered_booksifbook[category]category]ifnotfiltered_books:return未找到符合条件的图书。# 格式化返回结果result{total_count:len(filtered_books),books:filtered_books}returnjson.dumps(result,ensure_asciiFalse,indent2)exceptExceptionase:returnf查询图书时发生错误:{str(e)}#3.创建智能体agentcreate_agent(modeldeepseek,tools[query_books],system_prompt你是一个企业图书管理员可以帮助员工查询图书信息。,)#4.测试智能体response1agent.invoke({messages:[{role:user,content:请帮我查一下历史类图书}]})print( 测试1按图书种类精确查询 )print(response1[messages][-1].content)调用Tools方式LangChain框架中model和agent都可以直接调用toolsmodel调用Tools模型调用工具fromlangchain_core.messagesimportHumanMessagefromlangchain_core.toolsimporttoolfrominit_modelimportdeepseek# 1.创建工具tooldefget_weather(local:str)-str:获取天气信息returnf{local}天气非常晴朗# 2.给模型绑定工具model_with_toolsdeepseek.bind_tools([get_weather])# 3.准备messagesmessages[]humanMessageHumanMessage(content杭州今天的天气)messages.append(humanMessage)# 4.模型不会真正执行调用工具只是知道要调用工具responsemodel_with_tools.invoke(messages)messages.append(response)# 5.获取工具调用信息ifresponse.tool_calls:fortool_callinresponse.tool_calls:# 打印工具调用信息iftool_call[name]get_weather:# 手动调用工具tool_resultget_weather.invoke(tool_call)messages.append(tool_result)# 6.模型会根据工具调用结果生成最终回复print(messages:,messages)final_responsemodel_with_tools.invoke(messages)print(type(final_response))print(final_response)Agent调用工具[...agentcreate_agent(modeldeepseek,tools[get_weather],# 调用工具放在tools参数中system_prompt你是一个天气查询助手)...]工具调用错误处理调用工具错误处理是指当智能体调用外部工具如API、数据库查询等遇到异常时系统能够优雅地捕获、处理这些错误并向大模型返回有意义的错误信息而不是让未处理的异常直接中断整个流程。在LangChain框架中当Agent调用工具异常时使用wrap_tool_call中间件来灵活处理调用工具发生的异常错误如下importrequestsfromlangchain.agentsimportcreate_agentfromlangchain.agents.middlewareimportwrap_tool_call,ModelRequestfromlangchain_core.messagesimportToolMessagefromlangchain_core.toolsimporttoolfrominit_modelimportdeepseekwrap_tool_calldefhandler_tool_errors(request:ModelRequest,handler):使用自定义消息处理工具执行错误try:returnhandler(request)exceptExceptionase:# 向量模型返回自定义错误消息returnToolMessage(contentf调用工具错误请检查输入参数并重试。({str(e)}),tool_call_idrequest.tool_call[id])tooldefget_employee_info(employee_id:str)-str: 根据员工ID查询工具信息 Args employee_id: str Returns: mock_employee_data{emp00011:{name:zhansan,department:技术部},emp00022:{name:wangwu,department:人事部}}ifmock_employee_data.get(employee_id)isnotNone:returnf查到员工信息是{mock_employee_data[employee_id]}else:returnf{employee_id}不存tool(description查询天气)defget_weather_info(city_id:str)-str: 根据城市ID查询天气情况 Args: city_id: str Returns: try:responserequests.get(fhttps://baidu.weather/{city_id},timeout1)returnresponse.contentexceptrequests.exceptions.RequestExceptionase:print(f查询天气数据失败:{str(e)})raiseException(f查询天气数据失败:{str(e)})agentcreate_agent(modeldeepseek,tools[get_employee_info,get_weather_info],middleware[handler_tool_errors],system_prompt你是一个公司员工管理助手)responseagent.invoke({messages:[{role:user,content:你帮我查询一下张三信息和cityIdbj001城市的天气}]})#print(response)formsginresponse[messages]:msg.pretty_print()当工具调用失败时Agent将返回带有自定义错误消息的ToolMessageToolMessage必须传递content工具执行的结果或错误信息和tool_call_id所调工具唯一标识符将此消息与引发此次调用的特定 AIMessage中的工具调用关联起来两个核心参数。returnToolMessage(contentf调用工具错误请检查输入参数并重试。({str(e)}),tool_call_idrequest.tool_call[id])以上中间件的执行过程如下用户提问 → LLM分析并决定调用工具 → LangChain准备工具调用参数 → 错误处理中间件介入 → 实际工具执行 → 结果/错误返回总结本次主要分享了如下内容1、LangChain框架中工具时一个被tool装饰器修饰的函数2、在LangChain框架中提供三种核心的工具创建方式帮助工具有更好的可读性和可维护性遵循google风格、Pydantic 模型定义入参结构、json schema3、介绍了model和agent调用工具的方式4、介绍了工具调用异常处理方式当工具调用出现异常时通过友好处理可以帮助Agent继续执行不会中断。下篇文章将介绍Agent的结构化输出方式如果文章对你有帮助欢迎点赞➕关注往期文章链接LangChain 入门系列三大模型结构化输出三种实现方案LangChain 入门系列四agent初体验-创建与调用
返回列表