ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

LangChain实战:从Toolkit到Python Agent与SQL Agent的演进指南

2026/8/11 9:47:30 拓冰建站 浏览量
LangChain实战:从Toolkit到Python Agent与SQL Agent的演进指南 1. 项目概述从工具函数到智能体的演进之路最近在折腾LangChain发现很多朋友对它的Toolkit和Agent概念有点模糊尤其是从简单的工具函数调用到能自主决策的Python Agent再到能直接操作数据库的SQL Agent这中间的路径和实战细节网上资料要么太散要么直接丢给你一个看不懂的复杂例子。今天我就结合自己踩过的坑把这套“工具链”的实战进化史捋清楚。你完全可以把它看作一个能力逐步增强的“AI员工”培养手册最开始它只会你教的一个固定动作工具函数后来学会了根据你的指令从多个动作里选一个Python Agent最后甚至能直接去数据库里翻箱倒柜帮你找答案SQL Agent。无论你是想快速给现有应用加个智能对话功能还是想构建一个能自动分析数据的AI助手这套从简到繁的思路都能给你一个清晰的起点。2. 核心基石深入理解LangChain Toolkit在谈论Agent之前我们必须先夯实地基也就是Toolkit。你可以把它理解为一个“AI可用的工具箱”。但别误会这不是简单地把你的Python函数打个包就行。一个设计良好的Tool是Agent能够可靠工作的前提。2.1 Tool的本质与设计规范LangChain中的Tool本质上是一个将自然语言描述与可执行代码函数进行绑定的标准化接口。大模型LLM通过这个描述来理解“这个工具能干什么”并在需要时调用背后的函数。创建一个Tool远不止是写个tool装饰器那么简单。首先描述description是灵魂。一个模糊的描述会导致LLM误用或根本想不到用它。比如一个查询天气的函数糟糕的描述是“获取天气信息”。而好的描述应该是“根据提供的城市名称查询该城市当前的天气状况包括温度、天气现象晴、雨等、湿度和风速。输入应为单个字符串格式的城市名。”其次参数处理要健壮。LLM传来的参数可能是字符串、字典甚至是不完整的JSON。你的函数内部必须做好类型校验、默认值处理和异常捕获。我习惯在工具函数内部一开始就进行参数解析和清洗确保核心逻辑拿到的是干净、结构化的数据。from langchain.tools import tool from typing import Optional import requests tool def get_weather(city_name: str) - str: 根据城市名称查询实时天气。 参数: city_name (str): 城市的名称例如“北京”、“Shanghai”。 返回: str: 格式化的天气信息字符串包含温度、天气、湿度等。 # 1. 参数清洗与验证 if not city_name or not isinstance(city_name, str): return “请输入有效的城市名称。” city_name city_name.strip() # 2. 核心业务逻辑这里用模拟数据代替真实API调用 # 在实际项目中这里会调用如OpenWeatherMap的API weather_data { “temperature”: “22°C”, “conditions”: “晴”, “humidity”: “65%”, “wind_speed”: “10 km/h” } # 3. 格式化返回便于LLM理解和后续展示 return f“{city_name}的天气情况温度{weather_data[‘temperature’]}{weather_data[‘conditions’]}湿度{weather_data[‘humidity’]}风速{weather_data[‘wind_speed’]}。” # 测试工具 print(get_weather.invoke({“city_name”: “北京”}))注意Tool函数的返回值最好是结构清晰的字符串。虽然LLM能解析复杂JSON但清晰的文本更利于它生成流畅的自然语言回复。避免返回原生Python对象如字典、列表而不做任何处理。2.2 构建你的第一个工具箱Toolkit单个工具能力有限通常我们需要把相关工具组合成一个Toolkit供Agent选择。例如一个“数据查询工具箱”可能包含查询天气、查询股票价格、查询汇率。在LangChain中Toolkit就是一组Tool的集合。创建Toolkit的关键在于功能的内聚性。把毫不相干的工具塞进一个工具箱只会让Agent感到困惑。好的做法是按领域划分数据分析工具箱、文件操作工具箱、网络搜索工具箱等。from langchain.agents import create_toolkit # 假设我们已经定义了多个工具 weather_tool get_weather # 上面的天气工具 stock_tool get_stock_price # 假设的股票查询工具 currency_tool get_exchange_rate # 假设的汇率查询工具 # 将这些工具组合成一个工具箱 data_query_toolkit [weather_tool, stock_tool, currency_tool] # 在实际创建Agent时我们会直接传递这个工具列表这里有一个高级技巧为工具设计优先级或依赖关系。虽然LangChain的Agent会自己决定使用哪个工具但在某些场景下你可以通过提示词Prompt来隐式引导。例如在提示词中强调“当用户询问金钱相关问题时优先考虑使用汇率查询工具”。3. Python Agent实战让AI学会“思考”与“选择”有了工具箱我们就可以进入下一个阶段创建Python Agent。Agent与单纯工具调用的最大区别在于引入了“思考链”ReAct模式Reasoning Acting。Agent会根据你的问题自主决定是否需要使用工具、使用哪个工具、以及如何解读工具的返回结果。3.1 Agent的核心工作流与ReAct模式当你向一个配备了工具的Python Agent提问时它内部的工作流是这样的理解问题LLM解析你的输入。制定计划LLM判断是否需要使用工具来解决问题。如果需要它会“思考”应该选用哪个工具并生成调用该工具所需的参数。执行行动Agent框架调用被选中的工具并传入参数。观察结果工具执行完毕返回结果给Agent。反思与迭代LLM根据工具返回的结果判断问题是否已解决。如果未解决则重复步骤2-4可能选择其他工具或调整参数如果已解决则综合所有信息生成最终答案。这个“思考-行动-观察”的循环就是ReAct模式的核心。它让AI不再是一次性输出而是具备了多步推理和交互能力。3.2 使用create_agent函数构建智能体create_agent是构建Agent的一种高级、简洁的方式。它帮你封装了Agent执行器AgentExecutor的创建过程让你更关注工具和LLM本身。from langchain import hub from langchain.agents import create_agent, AgentExecutor from langchain.agents.format_scratchpad import format_log_to_str from langchain.agents.output_parsers import ReActSingleInputOutputParser from langchain.tools.render import render_text_description from langchain_openai import ChatOpenAI import os # 0. 设置你的LLM这里以OpenAI为例 os.environ[“OPENAI_API_KEY”] “your-api-key-here” llm ChatOpenAI(model“gpt-3.5-turbo”, temperature0) # 1. 准备工具列表使用之前定义的data_query_toolkit tools data_query_toolkit # 2. 获取一个预设的ReAct风格提示词模板 # LangChain Hub上有很多社区贡献的优质提示词 prompt hub.pull(“hwchase17/react”) # 3. 绑定工具描述到提示词中 # 这一步至关重要让LLM知道它有哪些工具可用 prompt prompt.partial( toolsrender_text_description(tools), # 将工具列表渲染成文本描述 tool_names“, “.join([t.name for t in tools]) # 提供工具名称列表 ) # 4. 定义Agent的运行逻辑 llm_with_stop llm.bind(stop[“\nObservation:”]) # 告诉LLM在哪里停止生成以等待工具执行结果 # 5. 构建Agent的推理链路 agent ( { “input”: lambda x: x[“input”], “agent_scratchpad”: lambda x: format_log_to_str(x[“intermediate_steps”]), # 格式化执行历史 } | prompt # 输入经过提示词模板 | llm_with_stop # 送入LLM生成思考过程 | ReActSingleInputOutputParser() # 解析LLM输出提取工具调用指令或最终答案 ) # 6. 创建Agent执行器 agent_executor AgentExecutor(agentagent, toolstools, verboseTrue, handle_parsing_errorsTrue) # 7. 运行Agent result agent_executor.invoke({“input”: “请问北京现在的天气怎么样同时100美元能换多少人民币”}) print(result[“output”])当你运行这段代码并将verbose设为True时你会在控制台看到完整的思考过程 Entering new AgentExecutor chain... 我需要回答两个问题北京的天气和美元兑人民币的汇率。我可以先用天气工具查询北京天气再用汇率工具查询美元兑人民币汇率。 Action: get_weather Action Input: {“city_name”: “北京”} Observation: 北京的天气情况温度22°C晴湿度65%风速10 km/h。 我现在有了天气信息。接下来需要汇率信息。 Action: get_exchange_rate Action Input: {“from_currency”: “USD”, “to_currency”: “CNY”, “amount”: 100} Observation: 100美元可兑换约718.5人民币。 现在我综合这两个信息来回答用户。 Finished chain. 北京目前天气晴朗温度22摄氏度湿度65%风速10公里/小时。另外根据当前汇率100美元大约可以兑换718.5元人民币。实操心得handle_parsing_errorsTrue这个参数非常有用。LLM有时生成的工具调用格式可能不规范设置这个参数能让执行器尝试自动修复而不是直接崩溃。这在生产环境中能显著提高系统的鲁棒性。3.3 提示词工程引导Agent更精准地工作Agent的表现严重依赖于提示词。上面例子中从Hub拉取的react提示词是个不错的起点但对于复杂任务你通常需要自定义。核心是在提示词中明确以下几点角色定义告诉AI它扮演什么角色例如“你是一个专业的数据分析助手”。工具说明书清晰列出每个工具的名称、描述、输入格式和输出示例。约束与规则规定它必须使用工具、不能编造信息、如何格式化输出等。思考格式明确要求它按照“Thought:”, “Action:”, “Action Input:”, “Observation:”的格式进行推理。一个自定义的提示词模板可能长这样你是一个智能助手可以调用以下工具来帮助用户 {tools} 请严格按照以下格式回应 Thought: 你需要思考现在应该做什么 Action: 需要调用的工具名称必须是[{tool_names}]中的一个 Action Input: 调用该工具所需的输入必须是有效的JSON格式 Observation: 工具返回的结果 当你得出最终答案时请以“Final Answer:”开头。 开始 用户问题{input} {agent_scratchpad}4. SQL Agent实战让AI直接与数据库对话如果说Python Agent是让AI调用通用API那么SQL Agent就是专门为数据库操作而生的“专家”。它允许你用自然语言查询数据库AI会自动生成SQL语句、执行、并解释结果。这对于不会SQL的业务人员或者需要快速进行数据探查的开发者来说是革命性的工具。4.1 为何需要专门的SQL Agent你可能会问用普通的Python Agent加一个“执行SQL”的工具不就行了吗理论上可以但实践中有诸多挑战数据库Schema复杂LLM需要理解表结构、字段类型、关联关系。SQL语法与安全生成的SQL必须语法正确且要防止SQL注入等安全问题。结果解释直接返回数据库查询结果如元组列表对用户不友好需要转换成自然语言。LangChain的SQL Agent通过create_sql_agent函数内置了一套专门处理这些问题的机制。它集成了SQLDatabase Toolkit这个工具箱里包含了描述表结构、查询示例、执行查询、检查查询结果等多个协同工作的工具。4.2 构建你的第一个SQL Agent让我们一步步构建一个连接SQLite数据库的Agent。from langchain_community.utilities import SQLDatabase from langchain_community.agent_toolkits import create_sql_agent from langchain_openai import ChatOpenAI import sqlite3 # 1. 创建或连接一个示例数据库 conn sqlite3.connect(‘example.db’) cursor conn.cursor() # 创建一个简单的员工表 cursor.execute(‘’’CREATE TABLE IF NOT EXISTS employees (id INTEGER PRIMARY KEY, name TEXT, department TEXT, salary REAL)’’’) # 插入一些示例数据 cursor.executemany(‘INSERT INTO employees (name, department, salary) VALUES (?, ?, ?)’, [(‘张三’, ‘技术部’, 15000), (‘李四’, ‘销售部’, 12000), (‘王五’, ‘技术部’, 18000), (‘赵六’, ‘人事部’, 9000)]) conn.commit() # 2. 创建SQLDatabase对象这是LangChain与数据库交互的抽象层 db SQLDatabase.from_uri(“sqlite:///example.db”) # 3. 初始化LLM llm ChatOpenAI(model“gpt-3.5-turbo”, temperature0) # 4. 创建SQL Agent agent_executor create_sql_agent( llmllm, dbdb, agent_type“openai-tools”, # 使用OpenAI函数调用格式的Agent更稳定 verboseTrue, handle_parsing_errorsTrue ) # 5. 用自然语言提问 result agent_executor.invoke( {“input”: “技术部有多少名员工他们的平均工资是多少”} ) print(result[“output”])运行后你会看到Agent的思考过程它先调用工具查看数据库中有哪些表sql_db_list_tables然后查看表结构sql_db_schema接着生成并执行SQLsql_db_query最后对结果进行总结。4.3 高级技巧与安全考量直接让AI生成并执行SQL听起来很强大但也非常危险。以下是我在实际项目中总结的必须遵守的准则严格的数据库连接权限永远不要给Agent一个具有DROP、DELETE、UPDATE权限的数据库账户。应该创建一个只读SELECT权限的专用账户。在SQLite中这意味着以只读模式打开连接或在生产数据库中严格限制权限。使用自定义提示词限制查询范围在create_sql_agent中你可以传入自定义的prompt参数。务必在提示词中强调“你只能执行SELECT查询严禁执行任何数据修改INSERT, UPDATE, DELETE, DROP等操作。” 虽然LLM大多会遵守但这是一个重要的安全层。结果行数限制避免Agent执行一个返回百万行数据的查询拖垮数据库。可以在SQLDatabase初始化时设置sample_rows_in_table_info参数限制它查看的样本行数也可以在提示词中要求“如果结果超过100行请只进行汇总分析”。处理复杂查询与错误对于多表JOIN或复杂子查询LLM可能会生成错误SQL。create_sql_agent的好处在于当执行出错时Agent会将错误信息作为Observation反馈给LLMLLM有机会修正SQL后重试。将handle_parsing_errors和max_iterations最大重试次数参数搭配使用可以提高成功率。agent_executor create_sql_agent( llmllm, dbdb, agent_type“openai-tools”, verboseTrue, handle_parsing_errorsTrue, max_iterations5, # 限制最大重试次数避免死循环 early_stopping_method“generate”, # 设置提前停止策略 agent_executor_kwargs{“handle_parsing_errors”: True} # 双重保险 )5. 常见问题排查与性能优化实录在实际开发和部署Agent的过程中你一定会遇到各种问题。下面是我整理的一些典型“坑”及其解决方案。5.1 Agent陷入循环或拒绝使用工具现象Agent一直在“思考”但就是不调用工具或者反复调用同一个工具无法得出最终答案。根因工具描述不清晰LLM无法准确理解工具用途。提示词约束过强或过弱可能没有强制要求它使用工具或者没有给出停止思考的明确指令。LLM温度temperature设置过高导致输出随机性太大无法稳定遵循指令。解决方案仔细打磨工具描述确保无歧义并包含输入输出示例。在提示词中明确写出“你必须使用提供的工具来回答问题。如果你认为工具无法解决请直接说‘我无法用现有工具回答这个问题’。”将LLM的temperature参数调低如设为0以获得更确定性的输出。5.2 SQL Agent生成错误或危险的SQL语句现象生成的SQL语法错误或者试图执行DELETE语句。根因Agent对数据库Schema理解不准确。提示词中安全约束不足。解决方案确保SQLDatabase对象能正确获取表结构信息。对于大型数据库可以使用custom_table_info参数手动提供关键表的精简Schema避免信息过载。实施强制安全策略这是最重要的。不要依赖LLM的自觉性。在应用层对Agent生成的SQL语句进行静态检查。可以使用简单的正则表达式或SQL解析库如sqlparse在执行前过滤掉所有非SELECT的关键字。import re import sqlparse def is_select_query(sql: str) - bool: “”“检查SQL是否为安全的SELECT查询”“” parsed sqlparse.parse(sql) if not parsed: return False first_token parsed[0].token_first(skip_cmTrue) return first_token and first_token.value.upper() ‘SELECT’ # 在执行SQL前进行拦截 if not is_select_query(generated_sql): raise ValueError(“只允许执行SELECT查询”)5.3 处理复杂、多轮对话的上下文现象在连续对话中Agent忘记了之前的对话历史导致每次回答都像重新开始。根因默认的Agent执行器是“无状态”的每次invoke都是独立的。解决方案你需要引入记忆Memory组件。LangChain提供了多种记忆后端如ConversationBufferMemory。关键是将记忆整合到Agent的输入输出循环中。from langchain.memory import ConversationBufferMemory from langchain.agents import AgentExecutor # 创建记忆体 memory ConversationBufferMemory(memory_key“chat_history”, return_messagesTrue) # 在创建提示词时加入记忆变量 prompt hub.pull(“hwchase17/react-chat”) prompt prompt.partial( toolsrender_text_description(tools), tool_names“, “.join([t.name for t in tools]), chat_history“{chat_history}” # 在提示词模板中预留位置 ) # 重新定义Agent的输入包含记忆 agent ( { “input”: lambda x: x[“input”], “chat_history”: lambda x: x.get(“chat_history”, “”), # 从输入中获取历史 “agent_scratchpad”: lambda x: format_log_to_str(x[“intermediate_steps”]), } | prompt | llm_with_stop | ReActSingleInputOutputParser() ) # 创建执行器时传入记忆 agent_executor AgentExecutor( agentagent, toolstools, verboseTrue, memorymemory, # 关键绑定记忆 handle_parsing_errorsTrue ) # 现在可以进行多轮对话了 result1 agent_executor.invoke({“input”: “北京天气如何”}) print(result1[“output”]) result2 agent_executor.invoke({“input”: “那上海呢”}) # Agent会记得之前聊过天气 print(result2[“output”])5.4 性能优化与成本控制现象Agent响应慢或者使用昂贵LLM如GPT-4时API调用成本激增。根因每次工具调用和反思都是一次LLM API请求复杂的任务会导致多次往返。解决方案工具设计聚合化如果一个复杂操作需要多次调用LLM考虑能否设计一个更强大的工具在工具内部完成复杂逻辑减少Agent的“思考-行动”轮次。使用更便宜的LLM进行规划可以采用“双LLM”策略。用一个快速、便宜的模型如GPT-3.5 Turbo负责规划和工具选择再用一个强大、昂贵的模型如GPT-4负责最终答案的润色和总结。这需要更复杂的架构设计。设置超时和最大迭代次数使用max_execution_time和max_iterations参数严格限制Agent的单次运行时长和思考步数防止因复杂或无法解决的问题而产生无限循环和巨额费用。缓存Caching对于重复性查询特别是SQL Agent中描述数据库Schema的步骤可以使用LangChain的缓存组件如SQLiteCache来缓存LLM的响应显著提升速度并降低成本。构建稳定、高效、安全的Agent系统是一个持续迭代的过程。从设计好一个单一功能的Tool开始到组装成能协同工作的Toolkit再到赋予其思考能力的Python Agent最后到领域专家SQL Agent每一步都考验着我们对问题拆解、工具抽象和提示词工程的理解。最重要的是始终把安全和控制放在第一位让AI在划定的边界内为我们创造价值。