【AI原生开发实战】3.2 LangChain工具调用开发实战

张开发
2026/4/12 22:18:00 15 分钟阅读

分享文章

【AI原生开发实战】3.2 LangChain工具调用开发实战
学习目标读完本章后你将掌握理解LangChain Agent体系的抽象层次与设计哲学掌握tool装饰器的实现原理与Pydantic参数Schema的设计思路能够区分并选择ReAct Agent、OpenAI Functions Agent、Conversational Agent掌握生产级Agent开发中的错误处理、异步执行、流式输出等核心实践了解LangChain Agent的调试技巧与常见问题排查能够独立构建一个完整的工具调用Agent系统一、LangChain Agent体系概述1.1 为什么选择LangChain从手写到框架在上一章中我们详细讲解了ReAct模式的核心原理理解了思考-行动-观察循环的工作机制。当我们尝试用原生代码实现一个完整的ReAct Agent时会发现需要处理诸多细节手动实现ReAct Agent需要关注的维度 ┌────────────────────────────────────────────────────────────────┐ │ 1. Prompt工程 ──→ 如何设计格式、如何引导LLM遵循格式 │ │ 2. 工具解析 ──→ 如何解析LLM的tool_calls输出 │ │ 3. 循环控制 ──→ 何时终止循环、如何防止无限循环 │ │ 4. 错误处理 ──→ 工具执行失败怎么办、如何重试 │ │ 5. 状态管理 ──→ 如何维护对话历史、如何传递中间结果 │ │ 6. 类型安全 ──→ 如何确保参数类型正确、如何进行参数验证 │ │ 7. 日志调试 ──→ 如何追踪执行过程、如何定位问题 │ │ 8. 流式输出 ──→ 如何实时展示思考过程、如何实现打字机效果 │ └────────────────────────────────────────────────────────────────┘LangChain的价值在于将这些通用基础设施封装为可复用的抽象让开发者专注于业务逻辑本身。正如Web开发从手写HTTP服务器演进到Django/Flask框架AI Agent开发也从手写ReAct循环演进到LangChain这样的高层抽象。框架不会取代开发者的创造力而是将重复性的工作自动化让你有更多精力关注真正重要的问题。1.2 LangChain Agent架构分层设计思想LangChain的Agent体系采用分层设计每一层都有明确的职责边界┌─────────────────────────────────────────────────────────────────┐ │ LangChain Agent 架构 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 应用层Application │ │ │ │ ReAct Agent │ OpenAI Fn Agent │ Conversational │ │ │ └─────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 编排层Orchestration │ │ │ │ LangGraph │ 状态机 │ 条件分支 │ Checkpointing │ │ │ └─────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 工具层Tools │ │ │ │ tool装饰器 │ Pydantic Schema │ 异步工具 │ 工具包 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘各层的职责分工层级职责关键词应用层定义Agent类型与交互模式ReAct、Functions、Conversational编排层管理Agent的执行流程与状态LangGraph、循环、条件分支工具层封装可调用工具的接口tool、Schema、异步这种分层设计的好处是关注点分离Separation of Concerns。当你需要修改Agent的交互模式时只需改应用层当你需要优化执行流程时只需关注编排层当你需要添加新工具时只需在工具层扩展。这种模块化设计使得系统易于维护和扩展。二、工具定义从基础到进阶2.1 tool装饰器设计原理声明式工具定义LangChain使用tool装饰器实现声明式工具定义。这种设计背后的思想是“告诉框架工具能做什么而不是教它如何实现”fromlangchain_core.toolsimporttooltooldefget_weather(location:str)-str:获取指定位置的天气信息。 Args: location: 城市名称如北京、Tokyo Returns: 天气信息字符串 returnf{location}今天晴朗气温25°C适合出行装饰器做了什么当我们给函数加上tool装饰器时LangChain会自动提取函数签名解析参数类型、默认值提取文档字符串使用docstring作为工具描述生成JSON Schema将Python类型映射为JSON Schema格式创建Tool对象生成符合LangChain规范的Tool实例# tool装饰器自动生成的元数据print(get_weather.name)# get_weatherprint(get_weather.description)# 获取指定位置的天气信息...print(get_weather.args)# {location: {type: string, ...}}为什么使用装饰器而非配置字典装饰器模式的优势在于类型安全与代码一致性。当你修改函数签名时工具的Schema会自动更新无需手动同步配置。如果函数参数类型写错Python的类型检查会直接报错而不是等到运行时才发现问题。2.2 Pydantic参数Schema结构化参数验证当工具需要更复杂的参数结构时LangChain推荐使用Pydantic模型定义SchemafrompydanticimportBaseModel,Fieldfromlangchain_core.toolsimporttoolclassWeatherInput(BaseModel):天气查询输入参数location:strField(description城市名称)unit:strField(defaultcelsius,description温度单位celsius或fahrenheit)include_forecast:boolField(defaultFalse,description是否包含预报)tool(args_schemaWeatherInput)defget_weather(location:str,unit:strcelsius,include_forecast:boolFalse)-str:获取指定位置的天气信息# 实现逻辑...passPydantic Schema的价值特性作用为什么重要类型注解明确参数类型LLM能更准确地构造参数Field描述为每个字段提供语义说明这是LLM理解参数含义的主要依据默认值指定可选参数的默认值减少LLM出错的概率枚举约束限制参数取值范围确保LLM生成的参数合法运行时验证自动校验参数合法性在执行前捕获错误描述字段的设计原则描述description是LLM理解参数的关键。一条好的描述应该说明语义解释这个参数代表什么给出示例帮助LLM理解正确的取值指明约束说明取值范围的限制例如对于股票代码参数symbol:strField(description股票代码支持美股如AAPL、GOOGL和港股如00700、09988)2.3 异步工具实现提升性能在生产环境中工具往往涉及网络请求如调用外部API。为了避免阻塞执行LangChain支持异步工具定义fromlangchain_core.toolsimporttoolimportasynciotoolasyncdeffetch_news(topic:str)-str:异步获取指定主题的最新新闻asyncwithaiohttp.ClientSession()assession:asyncwithsession.get(fhttps://api.news.com/search?topic{topic})asresp:dataawaitresp.json()returnf关于{topic}的最新新闻{data[headlines]}异步设计的考量I/O密集型场景网络请求、文件读写等I/O操作适合异步并发能力多个独立的异步工具可以并行执行用户体验流式输出时异步可以保证响应的实时性三、主流Agent类型开发3.1 ReAct Agent经典范式的框架实现LangChain对ReAct模式提供了原生支持其核心组件包括┌────────────────────────────────────────────────────────────────┐ │ ReAct Agent 架构 │ ├────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ │ │ │ System Prompt│ ──→ ReAct格式模板Thought/Action/Observation│ │ └──────┬───────┘ │ │ ↓ │ │ ┌──────────────┐ │ │ │ ReAct Agent │ ←── 工具定义tools │ │ └──────┬───────┘ │ │ ↓ │ │ ┌──────────────┐ │ │ │ AgentExecutor│ ←── 循环控制max_iterations │ │ └──────────────┘ │ │ │ └────────────────────────────────────────────────────────────────┘fromlangchain.agentsimportAgentExecutor,create_react_agent# 创建ReAct Agentagentcreate_react_agent(llm,tools)# 创建执行器agent_executorAgentExecutor.from_agent_and_tools(agentagent,toolstools,verboseTrue,# 输出思考过程max_iterations10,# 最大迭代次数handle_parsing_errorsTrue# 处理解析错误)# 执行resultagent_executor.invoke({input:苹果股价多少})ReAct Agent的执行流程LLM根据用户输入和可用工具决定是否需要调用工具如果需要输出符合ReAct格式的ActionAgentExecutor执行工具将结果作为Observation返回LLM根据Observation决定下一步继续调用工具或输出Final Answer循环直到LLM输出Final Answer或达到最大迭代次数3.2 OpenAI Functions Agent原生API的框架封装OpenAI Functions Agent是专门为OpenAI的Function Calling功能设计的Agent类型fromlangchain.agentsimportcreate_openai_functions_agentfromlangchain_core.messagesimportSystemMessage# 系统提示词system_message你是一个专业的金融分析师助手。 你擅长 1. 查询股票价格和市场数据 2. 进行财务计算和分析 3. 撰写投资研究报告 请始终使用提供的工具来获取最新数据不要编造数字。agentcreate_openai_functions_agent(llm,tools,system_messageSystemMessage(contentsystem_message))与ReAct Agent的区别维度ReAct AgentOpenAI Functions Agent思考格式显式Thought/Action分离隐式推理输出格式文本格式解析结构化JSON输出工具选择LLM自由选择基于Function Calling兼容性通用专用于支持Function Calling的模型选择建议如果你的LLM原生支持Function Calling如GPT-4、Claude推荐使用OpenAI Functions Agent如果是其他模型或需要更精细的控制使用ReAct Agent。3.3 Conversational Agent带记忆的对话AgentConversational Agent在ReAct基础上增加了对话记忆功能适合多轮对话场景fromlangchain.agentsimportcreate_conversational_react_agentfromlangchain.memoryimportConversationBufferMemory# 创建记忆组件memoryConversationBufferMemory(memory_keychat_history,return_messagesTrue)# 创建对话Agentagentcreate_conversational_react_agent(llm,tools,memorymemory)# 执行器agent_executorAgentExecutor.from_agent_and_tools(agentagent,toolstools,memorymemory,verboseTrue)# 多轮对话agent_executor.invoke({input:我叫小明})agent_executor.invoke({input:我的名字是什么})# 可以回答Conversational Agent的工作原理┌─────────────────────────────────────────────────────────────────┐ │ Conversational Agent 记忆机制 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ User: 我叫小明 │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 记忆组件存储{role: user, content: 我叫小明} │ │ │ └─────────────────────────────────────────────────────────┘ │ │ ↓ │ │ Agent处理请求调用工具如果有 │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 记忆组件追加{role: assistant, content: 好的...} │ │ │ └─────────────────────────────────────────────────────────┘ │ │ ↓ │ │ User: 我的名字是什么 │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 完整历史上下文[用户:我叫小明, 助手:好的..., 用户:...] │ │ │ └─────────────────────────────────────────────────────────┘ │ │ ↓ │ │ Agent基于完整上下文回答你叫小明 │ │ │ └────────────────────────────────────────────────────────────────┘3.4 Agent类型对比与选择Agent类型核心特点适用场景局限性ReAct Agent显式推理链复杂问答、需要可解释性的场景Token消耗较高OpenAI Functions Agent结构化输出支持Function Calling的模型依赖特定APIConversational Agent对话记忆多轮对话、聊天场景记忆管理复杂选择决策树需要构建Agent ↓ 是 ↓ ↓ 多轮对话场景 ──→ 是 ──→ Conversational Agent ↓ 否 模型支持Function Calling ──→ 是 ──→ OpenAI Functions Agent ↓ 否 ReAct Agent四、生产级开发实践4.1 错误处理与重试策略让Agent更健壮生产环境中的工具可能因网络问题、超时等原因执行失败。LangChain提供了优雅的错误处理机制fromtenacityimportretry,stop_after_attempt,wait_exponentialfromlangchain_core.toolsimporttooltoolretry(stopstop_after_attempt(3),# 最多重试3次waitwait_exponential(multiplier1,min2,max10)# 指数退避)defreliable_api_call(query:str)-str:带重试机制的API调用responsecall_external_api(query)ifnotresponse:raiseValueError(API返回为空)returnresponse重试策略的设计原则策略适用场景注意事项立即重试偶发性瞬时故障不适用于持续性问题固定间隔可预测的恢复时间可能导致请求堆积指数退避网络相关的不稳定问题需要设置最大等待时间4.2 流式输出与回调机制实时展示思考过程用户体验对于Agent系统至关重要。流式输出让用户能够实时看到Agent的思考过程# 流式执行Agentforchunkinagent_executor.stream({input:苹果股价多少}):ifactionsinchunk:# 展示Agent的思考print(f思考:{chunk[actions][0][log]})elifstepsinchunk:# 展示工具执行结果print(f执行:{chunk[steps][0][observation]})elifoutputinchunk:# 最终回答print(f回答:{chunk[output]})回调机制通过CallbackHandler可以拦截Agent执行的关键事件fromlangchain_core.callbacksimportBaseCallbackHandlerclassToolExecutionLogger(BaseCallbackHandler):defon_tool_start(self,serialized,input_str,**kwargs):print(f 开始执行:{serialized[name]})defon_tool_end(self,output,**kwargs):print(f✅ 完成结果:{output[:100]}...)4.3 异步执行优化提升系统吞吐量对于涉及多个独立工具调用的场景异步执行可以显著提升效率importasynciofromlangchain_core.toolsimporttool,AsyncTool# 定义异步工具classAsyncWeatherTool:nameget_weatherdescription异步获取天气信息asyncdefinvoke(self,location:str)-str:asyncwithaiohttp.ClientSession()assession:asyncwithsession.get(fhttps://api.weather.com/{location})asresp:dataawaitresp.json()returnf{location}天气{data[temp]}°C# 使用异步Agentagent_executorAgentExecutor.from_agent_and_tools(agentagent,tools[AsyncWeatherTool()],async_executionTrue)resultawaitagent_executor.ainvoke({input:北京天气})五、实战案例解析智能投研助手5.1 系统架构设计一个典型的投研助手Agent需要整合多种工具┌─────────────────────────────────────────────────────────────────┐ │ 智能投研助手架构 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 用户查询 │ │ 分析苹果和谷歌的投资价值 │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ Agent Core │ │ │ │ • 理解投资分析意图 │ │ │ │ • 规划工具调用顺序 │ │ │ │ • 整合多源信息 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ 股票查询 │ │ 财务指标 │ │ 计算器 │ │ 报告生成 │ │ │ │ Tool │ │ Tool │ │ Tool │ │ Tool │ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ ↓ │ │ 分析报告输出 │ │ │ └─────────────────────────────────────────────────────────────────┘5.2 工具设计原则在设计投研助手工具集时我们遵循以下原则工具粒度适中单个工具职责单一如获取股票价格、“获取财务指标”避免万能工具什么都做保持工具的可复用性避免过于细碎的原子工具如获取某只股票的某一指标结果格式标准化# 好的设计返回结构化、可解析的结果tooldefget_stock_price(symbol:str)-str:获取股票当前价格returnf{symbol}: ${price}({change})# 问题返回不可解析的非结构化文本tooldefget_stock_price(symbol:str)-str:returnf根据最新数据{symbol}当前交易价格为${price}较昨日上涨{change}...描述提供上下文tooldefcalculate_investment(amount:float,price:float)-str:计算投资数量和预期收益。 Args: amount: 投资金额美元 price: 当前股价美元 Returns: 可购买的股数和计算依据 sharesamount/pricereturnf投资${amount}可购买{shares:.2f}股单价${price}六、常见问题与调试技巧6.1 LLM不调用工具症状LLM直接回答问题而不是调用工具获取信息。可能原因Prompt描述不够清晰LLM认为不需要调用工具工具描述不够详细LLM不理解工具的用途问题太简单LLM认为基于自身知识可以回答解决方案在系统提示词中明确要求必须使用工具获取实时数据改进工具描述强调当你需要…时请使用此工具在工具描述中提供使用场景示例6.2 LLM调用错误的工具症状LLM调用了不相关或错误的工具。可能原因工具名称不够清晰工具描述之间存在歧义工具顺序影响LLM选择解决方案使用更精确的工具名称动词名词改进描述清晰区分功能相似的工具调整工具顺序将更常用的工具放在前面6.3 循环不终止症状Agent陷入无限循环不断重复调用工具。可能原因工具返回结果不足以回答问题LLM无法正确识别Final Answer格式问题需要多次工具调用但没有上限控制解决方案设置max_iterations参数在Prompt中明确终止条件检查工具返回结果的质量6.4 调试建议开启verbose模式agent_executorAgentExecutor.from_agent_and_tools(agentagent,toolstools,verboseTrue# 打印完整的思考过程)使用流式输出观察forchunkinagent_executor.stream({input:你的问题}):print(chunk)检查中间步骤# 在Agent执行前打印配置print(fAgent类型:{type(agent)})print(f工具数量:{len(tools)})print(f工具列表:{[t.namefortintools]})小结LangChain为AI Agent开发提供了成熟的抽象层让开发者能够专注于业务逻辑而非基础设施。本章我们深入学习了核心要点回顾框架价值LangChain通过分层抽象应用层/编排层/工具层简化了Agent开发工具定义tool装饰器实现了声明式工具定义Pydantic Schema提供了结构化参数验证Agent类型ReAct Agent、OpenAI Functions Agent、Conversational Agent各有适用场景生产实践错误处理、流式输出、异步执行是构建可靠系统的关键调试技巧verbose模式、流式输出、中间步骤检查是定位问题的重要手段下一步在理解了LangChain Agent的开发模式后你可以进一步探索LangGraph更灵活的Agent编排框架多Agent协作多个Agent如何分工合作Agent评估如何量化Agent的性能和质量习题概念理解解释题解释tool装饰器背后的设计思想。为什么LangChain选择装饰器而非配置文件来定义工具对比题对比ReAct Agent与OpenAI Functions Agent的实现机制差异。在什么场景下你会选择其中一种而非另一种设计题为一个智能订餐助手设计工具集。需要考虑哪些类型的工具工具之间可能存在什么依赖关系实践应用实现题使用LangChain实现一个包含记忆功能的对话Agent能够记住用户的偏好如我喜欢中餐。优化题分析以下场景可能导致的问题工具执行超时但Agent没有重试机制工具返回大量数据导致Token溢出调试题假设你的Agent总是调用错误的工具来回答今天天气如何。请设计一个调试流程来定位并解决这个问题。参考文献LangChain Documentation[EB/OL]. https://python.langchain.com/docs/concepts/agents/, 2024.Harrison Chase. LangChain Agents[EB/OL]. https://github.com/langchain-ai/langchain, 2024.OpenAI. Function calling[EB/OL]. https://platform.openai.com/docs/guides/function-calling, 2024.Pydantic. Data validation using Python type hints[EB/OL]. https://docs.pydantic.dev/, 2024.Yao S, Zhao J, Yu D, et al. ReAct: Synergizing Reasoning and Acting in Language Models[J]. arXiv preprint arXiv:2210.03629, 2022.

更多文章