作者:飞哥(一个喜欢讲故事的全栈开发者,擅长把复杂的代码翻译成“人话”)
难度:⭐⭐⭐⭐⭐
关键词:Agent, LangChain, Tool Use, Text-to-SQL, ReAct

大家好,我是飞哥!👋

之前的 RAG 主要是让 AI “”📖(检索文档)。
今天我们要让 AI “”🛠️(使用工具)。

想象一下,你问 AI:“上个月销售额是多少?” 🤔

  • 普通 LLM:“抱歉,我不知道你们公司的销售数据。”(因为它只读过预训练的数据)🤷‍♂️
  • RAG:可能会去搜文档,但如果没有现成的报表文档,它也歇菜。📚
  • Agent (智能体):“好的,我先去数据库查一下 sales 表… 查到了,上个月销售额是 170 万。”🕵️‍♂️

Agent 的本质,就是 大模型 + 工具 (Tools) + 规划 (Planning)。今天我们就来造一个能写 SQL 的 Agent!🤖


📦 准备工作

1. 安装 Python 库

我们需要 LangChain 全家桶:

pip install langchain langchain-openai langchain-community python-dotenv

2. 准备 API Key (.env 文件) 🔑

为了安全起见,我们不要把 Key 直接写在代码里,而是放在一个 .env 文件中。

  1. 在你的代码目录(和 agent_demo.py 同级)下,创建一个名为 .env 的文件。
  2. 在文件中填入你的 API Key:
# DeepSeek API Key
DEEPSEEK_API_KEY=sk-your-key-here

注意

  1. LangChain 通常默认读取 OPENAI_API_KEY,但我们在代码里显式指定了读取 DEEPSEEK_API_KEY,所以请务必使用这个变量名。
  2. 如果你将代码上传到 GitHub,记得把 .env 加入 .gitignore,防止 Key 泄露!

第一步:理解 ReAct 模式 🧠

1. 什么是 ReAct?

不是 React.js 前端框架哦!它是 Reasoning (推理) + Acting (行动)

Agent 的思考过程像极了人类解决问题

  1. 思考 (Thought):用户问销售额 -> 我应该去查数据库。🤔
  2. 行动 (Action):调用 run_sql_query 工具。🏃
  3. 观察 (Observation):工具返回了数字 1,700,000。👀
  4. 思考 (Thought):拿到了数据,我可以回答用户了。💡
  5. 回答 (Final Answer):上个月销售额是 170 万。✅

2. 流程图解 📊

为了让大家更直观地理解,我画了一张 Agent 工作的时序图:

🗄️ Database (SQLite) 🛠️ Tool (execute_sql) 🤖 Agent (大脑) 👤 用户 🗄️ Database (SQLite) 🛠️ Tool (execute_sql) 🤖 Agent (大脑) 👤 用户 🧠 思考 (Thought) 🧠 思考 (Thought) 🗣️ 提问: "2023年10月卖得最好的产品?" 分析意图: 需要查询 sales 表 筛选日期并按销量排序 🎬 行动 (Action): execute_sql(...) 执行 SQL 查询 返回结果: [('iPhone 15', 1300000.0)] 👀 观察 (Observation): 结果数据 分析结果: 拿到数据了,是 iPhone 15 💡 最终答案 (Final Answer): iPhone 15...

第二步:避坑指南 —— Agent 开发的 3 个真相 🚧

很多同学跑通了 Demo 就觉得万事大吉,一上生产环境就翻车。飞哥给你透个底:

1. 幻觉问题 (Hallucination) 😵

  • 现象:AI 可能会编造一个不存在的字段名(比如把 amount 猜成 price),导致 SQL 报错。
  • 解法:Prompt 里一定要把 Schema 写得非常清楚!甚至可以加一个 check_sql 工具,让 AI 先查 Schema 再写 SQL。

2. 权限失控 (Security) 🚨

  • 现象:用户问“帮我删库跑路”,AI 傻乎乎地执行了 DROP TABLE
  • 解法只给读权限 (Read-Only)!千万别给 Agent 连 root 账号。工具里最好加一层校验:if "DROP" in sql: return "禁止危险操作"

3. 循环死锁 (Loop) 🔄

  • 现象:AI 觉得自己没查到,反复重试,直到 Token 耗尽。
  • 解法:设置 max_iterations=5(最大重试次数),到了次数强制停止。

第三步:代码实战 (Agent Demo) 🧩

为了让大家直接能跑起来,飞哥把创建数据库定义工具运行 Agent 的代码合在了一起。而且,我们这次直接加上记忆功能,让它能跟你多轮对话!

创建一个 agent_demo.py 文件,复制以下代码:

import sqlite3
from langchain.tools import tool
from langchain_openai import ChatOpenAI
from langchain.agents import create_react_agent, AgentExecutor
from langchain import hub
from langchain.memory import ConversationBufferMemory
from langchain_core.prompts import PromptTemplate
import os
from dotenv import load_dotenv

# 加载 .env 文件
load_dotenv()

# --- 1. 准备环境 (模拟数据库) ---
# 为了演示,我们先创建一个本地 SQLite 数据库,并塞点数据进去
def setup_dummy_db():
    print("🛠️ 正在初始化测试数据库...")
    conn = sqlite3.connect("company.db")
    cursor = conn.cursor()
    # 建表
    cursor.execute(
        "CREATE TABLE IF NOT EXISTS sales (id INTEGER PRIMARY KEY, amount REAL, date TEXT, product TEXT)"
    )
    # 塞数据 (如果表是空的)
    cursor.execute("SELECT count(*) FROM sales")
    if cursor.fetchone()[0] == 0:
        data = [
            (1, 500000, "2023-10-01", "iPhone 15"),
            (2, 300000, "2023-10-05", "MacBook Pro"),
            (3, 100000, "2023-10-10", "AirPods"),
            (4, 800000, "2023-10-15", "iPhone 15"),
        ]
        cursor.executemany("INSERT INTO sales VALUES (?, ?, ?, ?)", data)
        conn.commit()
    conn.close()
    print("✅ 数据库准备就绪!表名: sales")


setup_dummy_db()


# --- 2. 定义工具 (Tools) ---
# 这是 Agent 的“手”,用来执行 SQL
@tool
def execute_sql(sql_query: str) -> str:
    """
    当需要查询数据库信息时使用此工具。
    输入是一个合法的 SQL 查询语句。
    """
    try:
        conn = sqlite3.connect("company.db")
        cursor = conn.cursor()
        cursor.execute(sql_query)
        result = cursor.fetchall()
        conn.close()
        return str(result)
    except Exception as e:
        return f"SQL执行出错: {e}"


# 告诉 AI 数据库里有哪些表,这也是很重要的 Prompt
db_schema = """
表名: sales
字段: id (int), amount (float), date (text), product (text)
"""

# --- 3. 组装 Agent ---
print("🤖 正在初始化 Agent...")

# (1) 初始化大模型
# 💡 推荐使用 DeepSeek-Coder,写代码逻辑能力极强,且性价比高
llm = ChatOpenAI(
    model="deepseek-coder",
    temperature=0,
    base_url="https://api.deepseek.com",
    api_key=os.getenv("DEEPSEEK_API_KEY"),  # 从环境变量读取 DEEPSEEK_API_KEY
)

# (2) 准备工具列表
tools = [execute_sql]

# (3) 定义支持对话历史的 Prompt 模板
# 注意:我们手动定义模板,以便加入 {chat_history}
template = """Answer the following questions as best you can. You have access to the following tools:

{tools}

Instructions:
{instructions}

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Begin!

Previous conversation history:
{chat_history}

Question: {input}
Thought:{agent_scratchpad}"""

prompt = PromptTemplate.from_template(template)

# (4) 把 Schema 信息注入 Prompt
instructions = f"""
数据库结构如下:{db_schema}

重要提示:
1. 当你找到答案时,必须以 'Final Answer:' 开头输出最终结果。
2. 不要只输出思考过程,最后一定要有 Final Answer。
"""
prompt = prompt.partial(instructions=instructions)

# (5) 添加记忆功能
memory = ConversationBufferMemory(memory_key="chat_history")

# (6) 创建 Agent
agent = create_react_agent(llm, tools, prompt)

# (7) 创建执行器
# max_iterations=5: 防止 AI 陷入死循环
# handle_parsing_errors=True: 如果 AI 输出格式错了,自动纠正它
agent_executor = AgentExecutor(
    agent=agent, 
    tools=tools, 
    verbose=True, 
    max_iterations=5, 
    handle_parsing_errors=True,
    memory=memory  # 注入记忆
)

# --- 4. 运行 (多轮对话) ---
print("\n💬 进入多轮对话模式。输入 'exit' 或 'quit' 退出。")
while True:
    try:
        user_input = input("\n🗣️ 用户提问: ")
        if not user_input or user_input.lower() in ["exit", "quit"]:
            print("👋 再见!")
            break
        
        result = agent_executor.invoke({"input": user_input})
        print(f"\n💡 最终答案: {result['output']}")
    except EOFError:
        break
    except KeyboardInterrupt:
        break

第四步:运行与观察 👀

在终端运行:

python agent_demo.py

你会看到 Agent 精彩的内心独白 (Verbose Output),而且它能记住你说的话!

Round 1:

🗣️ 用户提问: 帮我查一下 2023年10月 卖得最好的产品是哪个?

> Entering new AgentExecutor chain...
Thought: 用户想知道 2023年10月 销量最好的产品...
Action: execute_sql
Action Input: SELECT ...
Observation: [('iPhone 15', 1300000.0)]
Final Answer: 2023年10月卖得最好的产品是 iPhone 15,销售额为 130 万。

Round 2:

🗣️ 用户提问: 那它的销售额是多少?

> Entering new AgentExecutor chain...
Thought: 用户问“它”的销售额。根据历史记录,“它”指 iPhone 15。我之前已经查到了,但我可以再确认一下...
Final Answer: 它的销售额是 1,300,000.0。

看!它不仅会写 SQL,还能结合上下文理解你的意图!这就是记忆 (Memory) 的力量。✨


总结 📚

  1. Agent = LLM + Tools + Logic。🧩
  2. ReAct 是最经典的 Agent 思考模式(先想后做)。🧠
  3. Memory 让 Agent 拥有了“短期记忆”,能够处理连续的对话。💾

💡 飞哥作业
尝试给 Agent 增加一个新的工具,比如 send_email(content),让它查完数据后直接把报表发到你的邮箱!

创作不易,记得👇关注飞哥👇 ,点赞、收藏哦~~,下篇见👋

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐