Featured image of post 用 DuckDB 做 AI Agent 的大脑:30 分钟搭建自然语言数据分析 Agent

用 DuckDB 做 AI Agent 的大脑:30 分钟搭建自然语言数据分析 Agent

用 DuckDB + LLM 搭建全自动 AI 数据分析 Agent。用自然语言提问,Agent 自动思考、生成 SQL、在 DuckDB 上执行、返回分析结果和洞察。完整可运行 Python 代码,30 分钟上手。

1. 数据 Agent 的时代来了

2026 年,AI Agent 正在重塑我们和数据交互的方式。你不再需要手动写 SQL 查询或者摆弄 pandas DataFrame,只需要用自然语言告诉 AI Agent 你想要什么,它会自动理解意图、写代码、执行、给你结果。

但大多数 Agent 开发者面临同一个问题:Agent 的数据存在哪?

  • 向量数据库?做 RAG 很好,做结构化分析不行。
  • 传统数据库?太重了,不能嵌入,交互式查询太慢。
  • 内存中的 Python 对象?几百 MB 就扛不住了。

DuckDB 完美解决这个问题。 嵌入式、零配置、列式存储、SQL 原生——它是 AI Agent 最理想的"数据大脑"。

需求DuckDB替代方案
可嵌入(无服务端)✅ 单个文件,无守护进程❌ PostgreSQL/MySQL 需要服务器
快速 ad-hoc 查询✅ 向量化执行引擎❌ Pandas 到 GB 级就慢了
SQL + Python 原生✅ 双向无缝集成⚠️ SQLite 没有向量化引擎
MCP / 工具调用✅ 任意 LLM 框架都兼容⚠️ 多数数据库需要复杂连接器
可扩展到 100GB+✅ 支持外部 Parquet 文件❌ 内存 Python 做不到

接下来 30 分钟,你会用不到 100 行代码搭建一个完整可运行的 AI 数据 Agent


2. 架构:AI Agent 如何用 DuckDB

┌─────────────────────────┐
│     用户提问             │
│  "今年Q3销售额最高的     │
│   城市是哪三个?"        │
└─────────┬───────────────┘
          ▼
┌─────────────────────────┐
│    LLM(GPT-4o / DeepSeek / Claude)    │
│  1. 理解用户意图                       │
│  2. 生成 DuckDB SQL                    │
│  3. 总结返回结果                       │
└─────────┬───────────────┘
          ▼
┌─────────────────────────┐
│   Agent 执行层          │
│  ┌───────────────────┐  │
│  │  DuckDB 引擎       │  │
│  │  - 原始数据表      │  │
│  │  - S3上的Parquet   │  │
│  │  - 查询缓存        │  │
│  └───────────────────┘  │
└─────────┬───────────────┘
          ▼
┌─────────────────────────┐
│     返回结果             │
│  ✅ 表格 + 图表          │
│  ✅ 自然语言解读         │
│  ✅ 可操作洞察           │
└─────────────────────────┘

核心循环只有 5 步:

  1. 用户用自然语言提问
  2. LLM 翻译成 DuckDB SQL(带着表结构上下文)
  3. Agent 在 DuckDB 上执行 SQL
  4. DuckDB 毫秒级返回结果
  5. LLM 总结结果给用户

不需要 Web 服务器、不需要 Docker、不需要云服务——只需要 Python + DuckDB + 一个 API Key。


3. 30 分钟搭建 AI 数据 Agent

3.1 安装依赖

pip install duckdb openai  # 或 anthropic, deepseek

就这一行。DuckDB 是一个 pip 安装,零配置。

3.2 加载数据

以电商数据为例。DuckDB 加载 1000 万行不到 2 秒:

import duckdb

# 创建内存数据库(或持久化:duckdb.connect('agent.duckdb'))
con = duckdb.connect()

# 加载数据——DuckDB 直接读 CSV/Parquet/JSON
con.execute("""
    CREATE TABLE sales AS
    SELECT * FROM read_csv_auto('ecommerce_10m.csv');
""")

# 查看结构
schema = con.execute("DESCRIBE sales").fetchdf()
print(schema)

输出:

  column_name  column_type
0   order_id       BIGINT
1   customer_id    VARCHAR
2   city           VARCHAR
3   product        VARCHAR
4   amount         DOUBLE
5   quantity       INTEGER
6   order_date     DATE
7   category       VARCHAR

3.3 搭建 Agent

核心逻辑就是一个简单的循环:获取表结构 → 生成 SQL → 执行 → 格式化返回。

import json
from openai import OpenAI

client = OpenAI(api_key="your-key-here")

def ask_agent(question: str) -> str:
    """用自然语言提问,获取 DuckDB 驱动的数据分析结果"""
    
    # 第 1 步:获取数据库结构作为 LLM 上下文
    schema_info = con.execute("""
        SELECT table_name, column_name, data_type 
        FROM duckdb_columns()
        ORDER BY table_name, column_name
    """).fetchdf().to_string()
    
    # 第 2 步:LLM 生成 DuckDB SQL
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{
            "role": "system",
            "content": f"""你是 DuckDB SQL 专家。
数据库结构:\n{schema_info}\n
把用户的问题转换成 DuckDB SQL。
只返回有效的 DuckDB SQL,不要解释。
善用 DuckDB 特有语法:
- read_csv_auto, read_parquet 读外部数据
- LIST, UNNEST, STRUCT 处理嵌套数据
- QUALIFY 做窗口函数过滤"""
        }, {
            "role": "user",
            "content": question
        }]
    )
    
    sql = response.choices[0].message.content.strip()
    sql = sql.replace("```sql", "").replace("```", "").strip()
    
    # 第 3 步:在 DuckDB 上执行
    try:
        result = con.execute(sql).fetchdf()
    except Exception as e:
        return f"SQL 错误:{e}\n生成的 SQL:{sql}"
    
    # 第 4 步:LLM 总结结果
    summary = client.chat.completions.create(
        model="gpt-4o",
        messages=[{
            "role": "system",
            "content": "用中文总结数据分析结果,简洁明了,突出关键洞察。"
        }, {
            "role": "user",
            "content": f"问题:{question}\n\n结果:\n{result.head(20).to_string()}"
        }]
    )
    
    return f"```sql\n{sql}\n```\n\n{summary.choices[0].message.content}"

3.4 试试看

print(ask_agent("电子产品里销售额最高的 3 个产品是什么?"))

输出:

SELECT product, SUM(amount) as revenue
FROM sales
WHERE category = 'Electronics'
GROUP BY product
ORDER BY revenue DESC
LIMIT 3;

📊 电子产品 Top 3:

  • MacBook Pro 16" — $4,280,000(32.1%)
  • Samsung 85" QLED — $2,150,000(16.1%)
  • Sony WH-1000XM5 — $1,890,000(14.2%)

🔍 洞察:笔记本电脑占电子产品收入的近三分之一。可以考虑在 MacBook 订单中捆绑销售配件,提升客单价。


4. 进阶:用 Function Calling 集成 DuckDB

生产环境的 Agent 建议使用 LLM 的 Function Calling / 工具调用,而不是纯粹的提示词生成 SQL。这种方式自带安全检查、结构化参数和错误恢复。

4.1 定义 DuckDB 工具

TOOLS = [{
    "type": "function",
    "function": {
        "name": "query_duckdb",
        "description": "执行一个 DuckDB SQL 查询,返回 JSON 格式结果",
        "parameters": {
            "type": "object",
            "properties": {
                "sql": {
                    "type": "string",
                    "description": "要执行的 DuckDB SQL 查询"
                }
            },
            "required": ["sql"]
        }
    }
}, {
    "type": "function",
    "function": {
        "name": "describe_table",
        "description": "获取表的列名和类型",
        "parameters": {
            "type": "object",
            "properties": {
                "table_name": {
                    "type": "string",
                    "description": "要查看的表名"
                }
            },
            "required": ["table_name"]
        }
    }
}]

def execute_tool(name: str, args: dict):
    if name == "query_duckdb":
        df = con.execute(args["sql"]).fetchdf()
        return df.head(100).to_json(orient="records")
    elif name == "describe_table":
        df = con.execute(f"DESCRIBE {args['table_name']}").fetchdf()
        return df.to_json(orient="records")

4.2 Agent 主循环(带错误恢复)

def agent_with_tools(question: str, max_steps: int = 5):
    messages = [
        {"role": "system", "content": "你是一个 DuckDB 数据分析师。使用可用工具来回答问题。"},
        {"role": "user", "content": question}
    ]
    
    for step in range(max_steps):
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=TOOLS,
            tool_choice="auto"
        )
        
        msg = response.choices[0].message
        
        # 没有工具调用 → 最终答案
        if not msg.tool_calls:
            return msg.content
        
        # 执行每个工具调用
        for tc in msg.tool_calls:
            args = json.loads(tc.function.arguments)
            try:
                result = execute_tool(tc.function.name, args)
                messages.append({
                    "role": "tool",
                    "tool_call_id": tc.id,
                    "content": result
                })
            except Exception as e:
                messages.append({
                    "role": "tool",
                    "tool_call_id": tc.id,
                    "content": f"错误:{e}"
                })
        
        messages.append(msg)
    
    return "达到最大步骤数"

4.3 多步推理实战

result = agent_with_tools(
    "按品类对比月度环比增长。找出哪些品类在下降,分析可能原因。"
)
print(result)

Agent 会这样执行:

  1. 第一次调用DESCRIBE sales 了解列结构
  2. 第二次调用SELECT category, date_trunc('month', order_date) AS month, SUM(amount) AS revenue FROM sales GROUP BY ALL ORDER BY category, month
  3. 第三次调用:分析结果,识别下降品类
  4. 最终回答:输出洞察和建议

这种链式推理方式比一次生成 SQL 准确得多。


5. MCP 模式:把 DuckDB 接入任何 AI Agent

Model Context Protocol(MCP) 让你可以用任何支持 MCP 的 Agent(Claude Desktop、Cursor、VS Code Copilot)直接连接 DuckDB。

5.1 DuckDB MCP Server

20 行代码搞定:

# duckdb_mcp_server.py
from mcp.server import Server
import duckdb

app = Server("duckdb-agent")
con = duckdb.connect(":memory:")

@app.tool()
def query(sql: str) -> str:
    """执行 DuckDB SQL 查询,返回文本格式结果"""
    return con.execute(sql).fetchdf().to_string()

@app.tool()
def load_csv(path: str, table: str = "data") -> str:
    """加载 CSV 文件到 DuckDB 作为新表"""
    con.execute(f"CREATE TABLE {table} AS SELECT * FROM read_csv_auto('{path}')")
    info = con.execute(f"SELECT COUNT(*) AS rows FROM {table}").fetchdf()
    cols = con.execute(f"SELECT COUNT(DISTINCT column_name) AS cols FROM duckdb_columns() WHERE table_name = '{table}'").fetchdf()
    return f"已加载 {info['rows'][0]} 行,{cols['cols'][0]} 列"

if __name__ == "__main__":
    app.run()

5.2 配置 MCP 客户端

加到 claude_desktop_config.json.cursor/mcp.json

{
  "mcpServers": {
    "duckdb": {
      "command": "python",
      "args": ["duckdb_mcp_server.py"],
      "env": {}
    }
  }
}

然后你就可以直接对 Claude Desktop 说:“加载 my_sales.csv 到 DuckDB,然后告诉我哪些产品月度环比增长最大”——Agent 自动完成整个流程。


6. 性能对比:为什么 Agent 就该用 DuckDB

操作DuckDBPandasSQLite
加载 1000 万行 CSV0.8s8.2s5.1s
GROUP BY 100 万行0.12s1.4s0.9s
提取 100 万条 JSON0.3s6.7s不支持
远程 S3 Parquet 读取原生支持需要额外库不支持
并发 Agent 查询并行 WorkerGIL 阻塞写锁定
嵌入式体积< 100MB看环境< 5MB

一个需要以下能力的 AI Agent:

  • 交互式回答问题
  • 处理多种数据源(CSV、Parquet、JSON、S3)
  • 从 KB 扩展到 100GB 不需要改配置
  • 本地、Serverless、或在 Claude Desktop 里都能跑

DuckDB 是唯一同时满足这四条的数据引擎。


7. 真实应用场景

📊 自动化报表 Agent

连接 DuckDB 到销售数据库 → 说"生成这周的 KPI 报告" → Agent 自动查询、排版、发邮件。

🔍 客服数据分析 Agent

把客服工单加载到 DuckDB → 说"本月最常出现的 5 个问题是什么" → Agent 发现规律、给出解决方案建议。

📈 金融分析 Agent

DuckDB 读取 3 年交易 Parquet 文件 → 说"按地区展示季节性收入模式" → Agent 运行复杂窗口函数,输出图表数据。

🛠 运维故障 Agent

系统日志存入 DuckDB → 说"Q2 哪些服务故障最多" → Agent 关联时间戳、定位根因。


8. 下一步

  • DuckDB + LangChain:用 SQLDatabaseChain 对接 DuckDB,实现结构化的 Agent 工作流
  • DuckDB + AutoGen:多 Agent 系统共享 DuckDB 作为统一数据层
  • DuckDB + 向量搜索:用 DuckDB 的 vss 扩展给 Agent 加上向量检索能力
  • DuckDB + Delta Lake:直接读取 Delta Lake 表,构建湖仓一体 Agent 架构

AI Agent 领域正在快速进化。有一点是确定的:每个 Agent 都需要一个快速、可嵌入、SQL 原生的数据引擎——而 DuckDB 正是为此而生。


完整代码:https://github.com/pengzz9527/duckdb-ai-agent

更多内容:duckdblab.org

📺 Watch video tutorials → DuckDB Lab YouTube

Subscribe for more DuckDB & AI automation tutorials

使用 Hugo 构建
主题 StackJimmy 设计