ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

手把手构建AI智能体:基于LangChain实现工具调用实战

手把手构建AI智能体:基于LangChain实现工具调用实战 你好我是专注于AI应用开发的技术博主。在上一篇中我们探讨了Agent和Tool Use的基本概念让AI助手从“只会说”进化到“知道能做什么”。今天我们将进入实战环节手把手教你如何为模型装上“手”让它真正具备调用外部工具的能力完成从查询时间到复杂计算等一系列任务。无论你是想入门AI应用开发还是希望为自己的项目增加智能体能力这篇从环境搭建到代码实现的完整指南都将为你提供清晰的路径。1. 核心概念回顾与本文目标在深入代码之前我们快速回顾并明确几个关键概念这有助于理解后续的每一步操作。Agent智能体一个能够感知环境、进行决策并执行动作以达成目标的系统。在我们的上下文中通常指以大语言模型LLM为“大脑”能够规划和使用工具的程序。Tool Use / Function Calling工具调用/函数调用这是赋予LLM行动力的关键机制。它允许LLM根据用户请求识别出需要调用哪个预定义的工具函数并生成符合要求的调用参数通常为JSON格式然后由系统执行该函数并将结果返回给LLM最终由LLM整合信息回复用户。JSON Schema一种描述JSON数据结构的标准。在工具调用中我们使用JSON Schema来严格定义每个工具所需的输入参数名称、类型、描述、是否必填等。LLM根据这个“说明书”来生成格式正确的调用参数。本文目标我们将构建一个简单的AI助手它能够理解用户需求并动态调用两个核心工具get_current_time获取当前时间和calculator执行数学计算。通过这个案例你将掌握工具调用的完整流程定义工具、构建Agent逻辑、处理模型响应并执行工具。2. 环境准备与项目初始化我们将使用Python作为开发语言并利用OpenAI的API兼容其函数调用格式以及LangChain框架来简化流程。LangChain提供了优秀的Agent和Tool抽象让开发更高效。2.1 基础环境要求操作系统Windows 10/11, macOS 或 Linux (Ubuntu 20.04)Python版本3.8 或更高版本 (推荐 3.9)包管理工具pip2.2 创建项目与安装依赖首先创建一个新的项目目录并初始化虚拟环境这能有效隔离项目依赖。# 创建项目目录并进入 mkdir ai_assistant_agent cd ai_assistant_agent # 创建虚拟环境 (以venv为例) python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate激活虚拟环境后命令行提示符前通常会显示(venv)。接下来安装核心依赖库。# 安装依赖 pip install openai langchain langchain-openai python-dotenvopenai/langchain-openai: OpenAI官方SDK及LangChain集成用于调用GPT模型。langchain: 核心框架提供Agent、Tool、Chain等高级抽象。python-dotenv: 用于从.env文件加载环境变量如API密钥避免硬编码。2.3 配置API密钥为了调用OpenAI的模型你需要一个有效的API Key。请前往 OpenAI平台 注册并获取。在项目根目录下创建一个名为.env的文件用于安全存储密钥。# .env 文件内容 OPENAI_API_KEY你的实际API密钥重要安全提示务必在.gitignore文件中添加.env切勿将此文件提交到版本控制系统。2.4 项目结构预览完成后的简易项目结构如下ai_assistant_agent/ ├── .env # 环境变量文件保密 ├── .gitignore # Git忽略文件 ├── requirements.txt # 依赖列表可通过 pip freeze requirements.txt 生成 ├── tools/ # 工具模块目录 │ ├── __init__.py │ └── custom_tools.py # 自定义工具实现 └── main.py # 主程序入口3. 核心工具Tool的定义与实现工具的本质是一个Python函数辅以清晰的元数据描述名称、描述、参数schema以便LLM理解和使用它。3.1 创建工具模块在项目根目录下创建tools文件夹和custom_tools.py文件。# tools/custom_tools.py import json from datetime import datetime from typing import Type, Optional from pydantic import BaseModel, Field from langchain.tools import BaseTool # --- 工具1获取当前时间 --- class GetCurrentTimeInput(BaseModel): 获取当前时间的输入参数。此工具无需输入但为保持结构统一我们定义一个空模型。 # 这是一个无参数的工具所以模型内部是空的。 pass class GetCurrentTimeTool(BaseTool): name: str get_current_time description: str 当用户询问当前时间、现在几点、今天日期时使用此工具。 args_schema: Type[BaseModel] GetCurrentTimeInput def _run(self) - str: 执行获取当前时间的逻辑。 now datetime.now() # 格式化为易读的字符串 return now.strftime(%Y年%m月%d日 %H时%M分%S秒) async def _arun(self) - str: 异步执行本例中简单同步实现。 return self._run() # --- 工具2计算器 --- class CalculatorInput(BaseModel): 计算器的输入参数模型。 expression: str Field( ..., description一个合法的数学表达式例如3 5 * (2 - 1) 或 sin(45) log(100)。支持加减乘除(-*/)、括号和常见数学函数。, ) class CalculatorTool(BaseTool): name: str calculator description: str 用于执行数学计算。当用户提出涉及算术、数学表达式求解的问题时使用此工具。 args_schema: Type[BaseModel] CalculatorInput def _run(self, expression: str) - str: 执行计算。 警告直接使用eval有安全风险仅用于演示。 生产环境应使用更安全的表达式求值库如 asteval。 try: # 注意eval 在生产环境中非常危险可能执行任意代码。 # 此处仅为演示实际项目务必替换 result eval(expression, {__builtins__: None}, {}) return f表达式 {expression} 的计算结果是{result} except Exception as e: return f计算表达式 {expression} 时出错{e} async def _arun(self, expression: str) - str: return self._run(expression)代码解析与关键点Pydantic模型 (BaseModel)用于严格定义工具的输入参数。这直接对应了JSON Schema确保了LLM生成的参数类型正确。BaseTool类来自LangChain是构建工具的基类。我们需要定义name、description和args_schema并实现_run方法。description的重要性这是LLM决定是否调用该工具的主要依据。描述必须清晰、准确涵盖工具的用途和典型使用场景。安全警告CalculatorTool中使用了eval这在实际项目中是极其危险的因为它会执行字符串中的任何Python代码。这里仅作演示后文会讨论安全替代方案。3.2 理解JSON Schema的生成LangChain会在底层自动将args_schema即Pydantic模型转换为JSON Schema。例如CalculatorInput会被转换为类似下面的结构并发送给LLM{ type: object, properties: { expression: { type: string, description: 一个合法的数学表达式例如3 5 * (2 - 1) 或 sin(45) log(100)。支持加减乘除(-*/)、括号和常见数学函数。 } }, required: [expression] }LLM正是根据这个Schema来生成格式正确的{expression: 3 5 * 2}参数。4. 构建智能体Agent与主流程我们将使用LangChain的create_openai_tools_agent来构建一个Agent。这个Agent的核心是一个LLM和一系列Tools。4.1 编写主程序在项目根目录创建main.py。# main.py import os from dotenv import load_dotenv from langchain import hub from langchain.agents import AgentExecutor, create_openai_tools_agent from langchain_openai import ChatOpenAI from tools.custom_tools import GetCurrentTimeTool, CalculatorTool # 1. 加载环境变量 load_dotenv() # 2. 初始化LLM (使用GPT-3.5-turbo成本较低且支持工具调用) llm ChatOpenAI( modelgpt-3.5-turbo-1106, # 或 gpt-4-turbo-preview确保模型支持工具调用 temperature0, # 设置为0使输出更确定适合工具调用场景 api_keyos.getenv(OPENAI_API_KEY) ) # 3. 实例化工具列表 tools [GetCurrentTimeTool(), CalculatorTool()] # 4. 获取Agent的Prompt模板 # LangChain Hub上预置了优秀的Agent提示词模板我们直接拉取。 prompt hub.pull(hwchase17/openai-tools-agent) # 你可以打印prompt.template查看其内容它指导LLM如何思考和使用工具。 # 5. 创建Agent agent create_openai_tools_agent(llm, tools, prompt) # 6. 创建Agent执行器 agent_executor AgentExecutor( agentagent, toolstools, verboseTrue, # 设置为True可以看到Agent的思考过程调试非常有用 handle_parsing_errorsTrue, # 优雅处理解析错误 max_iterations5, # 限制最大迭代次数防止死循环 ) # 7. 交互循环 def main(): print(AI助手已启动我可以帮你查询时间和进行数学计算。输入‘退出’或‘quit’结束对话。) while True: try: user_input input(\n你: ) if user_input.lower() in [退出, quit, exit]: print(助手: 再见) break if not user_input.strip(): continue # 调用Agent执行器 response agent_executor.invoke({input: user_input}) print(f助手: {response[output]}) except KeyboardInterrupt: print(\n\n程序被中断。) break except Exception as e: print(f发生错误: {e}) if __name__ __main__: main()4.2 关键组件解析ChatOpenAI这是LangChain对OpenAI聊天模型的封装。temperature0使得模型输出更稳定减少随机性这对于需要精确触发工具调用的场景很重要。hub.pull(“hwchase17/openai-tools-agent”)从LangChain Hub拉取一个社区维护的、专门为OpenAI工具调用优化的提示词模板。这省去了我们自己编写复杂Prompt的麻烦。该Prompt会指示模型以特定的JSON格式tool_calls返回工具调用请求。create_openai_tools_agent此函数将LLM、工具列表和Prompt组合起来形成一个具备工具调用决策能力的Agent对象。AgentExecutor这是驱动Agent运行的引擎。它负责将用户输入和对话历史传递给Agent。解析Agent的输出可能是最终答案也可能是工具调用请求。如果解析到工具调用则执行对应的工具。将工具执行结果作为新的上下文再次传递给Agent进行下一轮思考。循环此过程直到Agent返回最终答案或达到最大迭代次数。verboseTrue会打印出详细的思考链Chain of Thought是调试和理解Agent行为的利器。5. 运行与效果验证现在让我们启动助手并看看它的实际表现。确保你在虚拟环境中且.env文件已正确配置API密钥。在终端运行程序python main.py你将看到类似以下的交互过程verboseTrue时的输出AI助手已启动我可以帮你查询时间和进行数学计算。输入‘退出’或‘quit’结束对话。 你: 现在几点了 进入新的Agent执行链... 我可以用获取当前时间的工具来回答这个问题。 动作get_current_time 动作输入{} 观察2024年5月20日 15时30分25秒 思考我已经获得了当前时间可以回答用户了。 最终答案现在是2024年5月20日 15点30分25秒。 助手: 现在是2024年5月20日 15点30分25秒。 你: 请计算一下 (15 7) * 3 的值是多少 进入新的Agent执行链... 用户要求计算一个数学表达式。我应该使用计算器工具。 动作calculator 动作输入{expression: (15 7) * 3} 观察表达式 (15 7) * 3 的计算结果是66 思考我已经得到了计算结果可以回复用户了。 最终答案(15 7) * 3 的计算结果是 66。 助手: (15 7) * 3 的计算结果是 66。 你: 先告诉我时间然后计算时间的小时数加上分钟数是多少。 进入新的Agent执行链... 用户有两个请求先获取时间然后进行一个计算。我需要按顺序使用工具。 动作get_current_time 动作输入{} 观察2024年5月20日 15时31分10秒 思考我得到了时间“2024年5月20日 15时31分10秒”。接下来需要计算小时数15加上分钟数31。 动作calculator 动作输入{expression: 15 31} 观察表达式 15 31 的计算结果是46 思考我完成了两个步骤现在可以给出最终答案。 最终答案当前时间是15点31分10秒。小时数15加上分钟数31等于46。 助手: 当前时间是15点31分10秒。小时数15加上分钟数31等于46。效果分析Agent成功理解了用户的自然语言指令。它能正确选择工具get_current_time或calculator。它能从观察结果中提取信息并用于后续的思考或计算如第三个例子。verbose日志清晰展示了Agent的“思考-行动-观察”循环这正是其智能的体现。6. 进阶话题安全、扩展与最佳实践一个基础的Agent跑起来了但要用于实际项目我们还需要考虑更多。6.1 计算器工具的安全强化如前所述eval是危险的。我们可以使用更安全的库如asteval或numexpr。# 安全计算器工具示例 (需安装 pip install asteval) from asteval import Interpreter class SafeCalculatorTool(BaseTool): name “safe_calculator” description “用于安全地执行数学计算。支持数字、基本运算符(-*/)、括号和常见数学函数如sin, cos, sqrt等。” args_schema CalculatorInput # 复用之前的输入模型 def _run(self, expression: str) - str: try: aeval Interpreter() result aeval(expression) if aeval.error: return f“计算错误: {aeval.error}” return f“表达式 {expression} 的计算结果是{result}” except Exception as e: return f“计算表达式 {expression} 时出错{e}”asteval提供了一个受限的求值环境大幅提升了安全性。6.2 扩展更多工具你可以遵循相同的模式添加任意工具例如网络搜索工具集成SerpAPI或DuckDuckGo搜索。数据库查询工具连接数据库执行SQL查询需严格控制权限。文件操作工具读写特定目录下的文件。API调用工具调用天气预报、股票、翻译等第三方API。关键原则每个工具的描述(description)必须精准输入模型(args_schema)必须严谨。6.3 错误处理与Agent稳定性handle_parsing_errors当模型返回的JSON格式无法解析时此参数允许执行器尝试修复或给出友好提示。max_iterations必须设置。防止Agent陷入“工具调用-观察-再调用”的死循环。max_execution_time可以设置最大执行时间避免长时间挂起。结构化输出对于复杂任务可以要求模型以更结构化的格式如JSON输出最终答案便于后续程序处理。6.4 提示词工程优化虽然我们使用了Hub上的模板但在复杂场景下你可能需要自定义Prompt。系统消息在Prompt中明确Agent的角色、能力和限制。少样本示例在Prompt中提供几个“用户提问-Agent思考并调用工具”的示例可以显著提升模型使用工具的准确性。格式强调反复强调输出格式必须是特定的JSON。7. 常见问题与排查思路在开发过程中你可能会遇到以下问题问题现象可能原因排查与解决思路错误OpenAI API key not provided1..env文件未创建或路径不对。2. 环境变量未正确加载。3. 虚拟环境未激活。1. 检查.env文件是否在项目根目录且名称正确。2. 在main.py开头打印os.getenv(“OPENAI_API_KEY”)前几位确认是否加载成功。3. 确认终端提示符前有(venv)。Agent不调用工具直接回答问题1. 工具description描述不清模型无法匹配。2. Prompt模板不适合。3. 模型温度(temperature)过高导致输出随机。1. 优化工具描述确保涵盖用户可能的所有问法。2. 尝试不同的Prompt模板或自定义Prompt。3. 将temperature设为0。启用verboseTrue查看模型原始思考。错误...is not valid JSON1. 模型生成的工具调用参数格式错误。2. Pydantic模型定义与Schema不匹配。1. 检查verbose输出看模型生成的“动作输入”是否合规。2. 确保args_schema中的字段类型如str,int定义正确。简化复杂的嵌套模型。工具被错误调用或多次调用1. Agent对任务理解有偏差。2. 工具描述有歧义或重叠。1. 在Prompt中提供更明确的任务指令和示例。2. 仔细区分不同工具的description避免功能重叠。设置max_iterations防止循环。计算器执行eval报安全错误表达式包含非法字符或函数。立即停止使用eval按照6.1节替换为asteval等安全库。8. 总结与展望至此我们已经完成了一个具备工具调用能力的AI助手从零到一的构建。回顾整个流程定义工具使用Pydantic模型和BaseTool类清晰定义功能、描述和输入规范。构建Agent利用LangChain框架将LLM、工具集和Prompt模板组合成智能体。执行与交互通过AgentExecutor驱动思考-行动循环并处理用户交互。安全与优化替换危险函数、增加错误处理、优化提示词使系统更健壮。这个简单的“查时间算算术”助手已经展示了AI Agent的核心范式。你可以以此为基石接入更强大的工具如搜索引擎、知识库、业务系统API构建出能真正处理复杂任务的智能助理。未来的学习方向可以聚焦于记忆为Agent添加对话历史记忆使其能进行多轮复杂对话。规划让Agent能够分解复杂任务制定多步执行计划Plan-and-Execute。多智能体协作创建多个各司其职的Agent让它们通过协作解决更宏大的问题。与RAG结合让Agent在调用工具的同时也能从你提供的专属文档库中检索信息实现知识增强。动手尝试修改代码添加一个新工具比如一个查询城市天气的模拟工具是巩固学习的最佳方式。开发过程中多利用verboseTrue模式观察Agent的思考链这能帮你精准定位问题并理解其工作原理。
返回列表