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

RAG 每日一技(十八):手写SQL-RAG太累?LangChain的SQL智能体(Agent)前来救驾!

前情回顾

在上一篇文章中,我们亲手实现了一个Text-to-SQL的完整流程。这是一个很棒的学习经历,但它也暴露了我们“手工作坊”模式的脆弱性。想象一下,一个稍有歧义的用户问题,一个微不足道的SQL语法错误,整个系统就会因为一次执行失败而彻底崩溃。它没有第二次机会,也缺乏应对复杂情况的智慧。

我们需要一个更强大的存在。它不仅仅是一个被动的“代码生成器”,更应该是一个主动的“数据分析师”。它需要能够思考,能够选择工具,甚至能够在犯错后自我纠正

这个更强大的存在,就是智能体(Agent)

从“链”到“智能体”:一次智能的飞跃

在我们之前的学习中,我们接触了LangChain的“链(Chain)”。“链”的工作模式是固定且线性的,就像一条预设好的流水线,每一步都严格按照顺序执行。

而“智能体(Agent)”则完全不同。它是一个动态的、循环的执行单元。它的核心是一个由LLM驱动的推理引擎。它的工作遵循一个“思考-行动-观察”的循环:

思考(Thought):根据用户目标和当前情况,分析下一步该做什么。
行动(Action):从它拥有的“工具箱”中选择一个工具,并决定如何使用它。
观察(Observation):查看工具执行后的结果,这个结果会成为下一轮“思考”的输入。

这个循环会一直持续,直到它认为已经达成了用户的最终目标为止。

SQL智能体:你的专属数据库分析师

LangChain为我们提供了一个专门用于数据库交互的、预置好的专家——SQL智能体(SQL Agent)

我们可以把它想象成一个虚拟的数据库分析师。它的“工具箱”里装满了与数据库交互所需的各种工具,比如检查所有表名的工具、查询某个表结构的工具、执行SQL语句的工具等等。

它最强大的能力,就是自我纠错。如果它生成的SQL语句在执行时报错了,它不会直接放弃。它会“观察”到数据库返回的错误信息,然后在下一轮“思考”中分析错误原因(“哦,原来employees表里没有name字段,应该是employee_name”),然后生成一个修正后的SQL语句,再次尝试。

上手实战:释放SQL智能体的威力

让我们看看,用LangChain构建一个如此强大的Agent需要多少代码。你会惊讶于它的简洁。

首先,确保相关库已安装:

pip install langchain langchain-openai langchain_community sqlalchemy
准备阶段:连接数据库

我们将使用和上一篇完全相同的数据库。但这次,我们用LangChain推荐的方式来连接它。

from langchain_community.utilities import SQLDatabase
from langchain_openai import ChatOpenAI
from langchain_community.agent_toolkits import create_sql_agent
import sqlite3
import os# 设置你的OpenAI API Key
# os.environ["OPENAI_API_KEY"] = "sk-..."# --- 准备数据库 ---
# 创建并填充数据库,与上一篇的代码完全相同
conn = sqlite3.connect('company.db')
cursor = conn.cursor()
cursor.execute("DROP TABLE IF EXISTS employees;")
cursor.execute("DROP TABLE IF EXISTS departments;")
cursor.execute('''
CREATE TABLE departments (department_id INTEGER PRIMARY KEY,department_name TEXT NOT NULL
);
''')
cursor.execute('''
CREATE TABLE employees (employee_id INTEGER PRIMARY KEY,employee_name TEXT NOT NULL,department_id INTEGER,salary INTEGER,FOREIGN KEY (department_id) REFERENCES departments(department_id)
);
''')
cursor.execute("INSERT INTO departments VALUES (1, '技术部'), (2, '市场部');")
cursor.execute("INSERT INTO employees VALUES (101, '张三', 1, 8000), (102, '李四', 1, 9500), (103, '王五', 2, 6000);")
conn.commit()
conn.close()# 使用LangChain的SQLDatabase类来连接数据库
# 这会为Agent提供一个标准化的数据库接口
db = SQLDatabase.from_uri("sqlite:///company.db")
print("数据库连接成功,方言:", db.dialect)
创建并运行智能体

接下来就是最核心的部分了。我们只需要调用一个“工厂函数”,就能瞬间创造出我们的SQL智能体。

# 初始化LLM
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)# 使用工厂函数create_sql_agent创建智能体
# 它会自动将LLM和数据库工具包(toolkit)组合起来
agent_executor = create_sql_agent(llm, db=db, agent_type="openai-tools", verbose=True)# 让我们来问一个比昨天更复杂的问题
question = "技术部有多少名员工?他们的平均工资是多少?"
response = agent_executor.invoke({"input": question})print("\n--- 最终回答 ---")
print(response["output"])
深入智能体的“内心世界”

由于我们设置了 verbose=True,在运行代码时,你将看到一段非常精彩的输出,它完整地展示了Agent的“思考-行动-观察”链条。

你会看到类似这样的输出:

> Entering new AgentExecutor chain...Thought: The user is asking for the number of employees and the average salary for the '技术部' department.
I need to query the `employees` and `departments` tables.
I will first find the `department_id` for '技术部'.
Then I will use this `department_id` to filter the `employees` table to count the number of employees and calculate the average salary.Action: sql_db_query
Action Input: SELECT department_id FROM departments WHERE department_name = '技术部'
Observation: [(1,)]
Thought: The department_id for '技术部' is 1.
Now I can query the `employees` table to get the count of employees and their average salary for this department.Action: sql_db_query
Action Input: SELECT COUNT(employee_id), AVG(salary) FROM employees WHERE department_id = 1
Observation: [(2, 8750.0)]
Thought: I have the final answer. The count of employees is 2 and the average salary is 8750.0. I will now formulate the final response to the user.> Finished chain.--- 最终回答 ---
技术部有2名员工,他们的平均工资是8750.0。

这段日志完美地展示了Agent的智能:它将一个复杂问题,自主地分解成了两个步骤,并且像一个真正的数据分析师一样,一步步地推理,最终得出了正确的答案。这就是Agent的魅力!

总结与预告

今日小结:
今天我们完成了从“链”到“智能体”的认知飞跃。我们了解到,智能体(Agent)是一个由LLM驱动的、能够进行“思考-行动-观察”循环的动态推理引擎。LangChain的SQL智能体封装了强大的数据库交互工具和自我纠错能力,让我们无需编写复杂的Prompt和错误处理逻辑,就能构建出极其鲁棒的数据库问答机器人。

我们已经分别征服了非结构化的“文本世界”和结构化的“表格世界”。但是,真实世界的数据,往往是两者的混合体。

想象一下,一份公司的PDF年报,里面既有大段大段的管理层分析(非结构化文本),又嵌有密密麻麻的财务数据表(结构化表格)。对于这样的“半结构化”文档,我们该怎么办?

明天预告:RAG 每日一技(十九):当文本遇上表格,如何处理“半结构化”的复杂文档?

明天,我们将挑战这个极具现实意义的难题。我们将探索如何构建一个“多模态”的RAG系统,它能够智能地判断用户的问题是关于文本还是表格,并将问题路由给不同的处理引擎,最终实现对复杂混合文档的深度理解和问答。

http://www.dtcms.com/a/341483.html

相关文章:

  • 动态规划面试真题解析
  • Linux网络服务(三)——DNS域名解析服务
  • 学习中需不需要划线、做笔记
  • 2-1.利用框架构建一个easy的web应用
  • CISP-PTE之路--09文
  • 拓扑排序判断环 P1347 排序题解
  • LeetCode 刷题【47. 全排列 II】
  • k8s笔记01
  • WIFI国家码修改信道方法_高通平台
  • 如何将数据从 iPhone 转移到 vivo?
  • 基于Python的反诈知识科普平台 Python+Django+Vue.js
  • 道路车道线分割数据集左车道右车道中线labelme格式3494张4类别
  • 工业电脑选得好生产效率节节高稳定可靠之选
  • Pycharm-002 Pycharm 编译器运行器不显示,日志不打印
  • MySQL 事务(重点)
  • GThinker多模态大模型:线索引导式反思的突破
  • Oracle官方文档翻译《Database Concepts 23ai》第2章-容器数据库与可插入数据库
  • Qwen Image edit的ComfyUI工作流搭建
  • vue中动态设置class类名和style样式
  • Javascript面试题及详细答案150道之(121-135)
  • 医学影像分析中的持续学习:近期进展与未来展望综述|文献速递-深度学习人工智能医疗图像
  • 42-Python基础语法-2
  • Lecture 5 GPUs课程笔记
  • Leetcode 深度优先搜索 (9)
  • Kafka-Kraft
  • leetcode3 无重复字符的最长子串
  • vue2封装日期选择组件--有时间选择版本
  • 创建Vue项目的不同方式及项目规范化配置
  • Playwright 与 Scrapy 的实际应用场景能力分析
  • 深入理解 Spring 的 @ControllerAdvice