看到群里的小伙伴在Excel里使用DuckDB来获取数据,灵感迸发写了一篇“《Excel植入DuckDB,小宇宙爆发》”。
个人认为数据分析包含如下几个重要模块
因此我有了个更大胆的想法:
是不是可以用 “DuckDB/DuckLake + ECharts + LLM” 实现一个智能数据可视化产品?
DuckDB扮演数据获取、计算、LLM海马体的角色:
结合飞书/语雀这类文案产品就可以形成图文并茂的报表(DuckDB群里看到老朋友泽鹏已经这么干了,有九阳神功护体,干什么都快!!!), 再结合MCP/A2A的话, 还可以执行更复杂的任务, 例如数据分析后的决策/报告的生成等. 放在第二部分展开.
我另外还想提一下泽鹏开发的这个系统工程,已经开源了,详见:DuckCP演变之路
DuckCP打破数据孤岛,打通企业数据任督二脉,期待后续的演进。
接下来就让Gemini基于我前面的设想给个解决方案.
好的,使用 DuckDB、ECharts 和大型语言模型(LLM)来构建一个智能数据可视化产品是一个非常前沿且实用的想法。这个组合可以创造一个强大的“对话式数据分析机器人”,让非技术用户也能通过自然语言与数据进行交互和探索。
下面我将为您详细解读这个技术方案,包括其核心理念、系统架构、工作流程、关键功能、代码示例以及面临的挑战。
这个产品的核心是将用户的自然语言(“人话”)转换成机器可执行的指令和可视化的图表。
option 对象来定义图表的一切,非常灵活。LLM 的一个巨大优势就是能够生成结构化的 JSON 文本,这与 ECharts 的配置方式完美契合。ECharts 负责将从后端获取的数据和 LLM 生成的 option 配置渲染成漂亮的交互式图表。option 配置,包括标题、坐标轴、数据系列(series)等。注意, LLM的NL2SQL能力评测可参考文章:
一个典型的架构如下所示,它将前端与后端分离,通过 API 进行通信。
sequenceDiagram
participant User as 用户
participant Frontend as 前端 (浏览器, ECharts)
participant Backend as 后端 (Python/Node.js)
participant LLM as 大语言模型 (API)
participant DuckDB
participant DataSource as 数据源 (CSV/Parquet)
User->>Frontend: 1. 输入自然语言查询: "按产品类别统计总销售额"
Frontend->>Backend: 2. 发送API请求 (含查询文本和数据源信息)
activate Backend
Backend->>LLM: 3. 构造Prompt (含查询、数据表结构) 并请求
activate LLM
LLM-->>Backend: 4. 返回结构化JSON<br>{ "sql": "...", "echarts_option": {...} }
deactivate LLM
Backend->>DuckDB: 5. 使用返回的SQL查询数据
activate DuckDB
DuckDB->>DataSource: 读取数据文件
DataSource-->>DuckDB: 返回数据
DuckDB-->>Backend: 6. 返回查询结果 (Data)
deactivate DuckDB
Backend->>Frontend: 7. 返回API响应 (含查询结果和ECharts Option)
deactivate Backend
Frontend->>Frontend: 8. 使用ECharts渲染图表
Frontend-->>User: 9. 展示交互式图表
sales.csv)或选择一个已有的数据源。然后,在输入框中用自然语言提问。Table 'sales' has columns: [product_category (VARCHAR), sales_amount (DOUBLE), sales_date (DATE)]。sql 和 echarts_option 两个键的 JSON 对象。sql 字符串,并使用 DuckDB 在指定的数据源上执行该查询。option 一并返回给前端。setOption 方法,将数据和配置注入,即可渲染出最终的图表。这是一个简化的后端逻辑,展示了核心的编排过程。
from flask import Flask, request, jsonify
import duckdb
import openai # 假设使用OpenAI
import json
app = Flask(__name__)
# 假设LLM客户端已配置
# openai.api_key = "YOUR_API_KEY"
def get_table_schema(db_con, table_name='data'):
"""使用DuckDB获取表的结构信息"""
result = db_con.execute(f"DESCRIBE {table_name};").fetchall()
schema = "\n".join([f"- {col[0]} ({col[1]})"for col in result])
returnf"Table '{table_name}' has the following columns:\n{schema}"
def generate_visualization_payload(query, schema):
"""构造Prompt并调用LLM"""
prompt = f"""
You are an expert data analyst. Your task is to help a user visualize their data.
Given a user query and the data schema, you must generate a JSON object containing two keys: 'sql' and 'echarts_option'.
**Data Schema:**
{schema}
**User Query:**
"{query}"
**Instructions:**
1. The 'sql' key should contain a DuckDB-compatible SQL query to retrieve the data for visualization.
2. The 'echarts_option' key should contain a JSON object for ECharts to render the chart. The data field in the ECharts option should be left empty, as it will be populated later.
3. Choose the best chart type (bar, line, pie, etc.) based on the user's query.
**Output (JSON object only):**
"""
response = openai.ChatCompletion.create(
model="gpt-4-turbo", # 或者其他支持的JSON模式的模型
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"}
)
return json.loads(response.choices[0].message.content)
@app.route('/visualize', methods=['POST'])
def visualize_data():
user_query = request.json.get('query')
file_path = request.json.get('file_path') # 例如 'data/sales.parquet'
ifnot user_query ornot file_path:
return jsonify({"error": "Missing query or file_path"}), 400
try:
# 使用DuckDB连接数据
con = duckdb.connect(database=':memory:', read_only=False)
# DuckDB可以直接将文件注册为虚拟表
con.execute(f"CREATE OR REPLACE VIEW data AS SELECT * FROM '{file_path}';")
# 1. 获取表结构
schema_info = get_table_schema(con, 'data')
# 2. 调用LLM生成SQL和ECharts Option
payload = generate_visualization_payload(user_query, schema_info)
sql_query = payload.get('sql')
echarts_option = payload.get('echarts_option')
# 3. 执行SQL查询
data_result = con.execute(sql_query).pl().to_dicts() # Polars DataFrame to dicts
# 4. 将查询结果和ECharts Option返回给前端
# 注意:实际应用中,需要将data_result适配到echarts_option的数据格式中
return jsonify({
"data": data_result,
"echarts_option": echarts_option
})
except Exception as e:
return jsonify({"error": str(e)}), 500
finally:
if'con'in locals():
con.close()
if __name__ == '__main__':
app.run(debug=True)
综上所述,DuckDB + ECharts + LLM 的组合是一个极具潜力的技术栈,它能真正实现数据可视化的智能化和大众化。虽然挑战存在,但其带来的价值和创新的用户体验是无与伦伦比的。
好的,我们继续在上一个 DuckDB + ECharts + LLM 的基础上进行扩展,引入 MCP/A2A(Multi-Agent Collaboration Protocol / Agent-to-Agent Communication) 的概念。
引入多智能体协作框架,意味着我们将单一的、全能的 LLM 拆分成一个由多个 专业化的 AI 智能体(Agent) 组成的团队。每个智能体都有自己独特的角色和技能,它们通过协同工作来完成比单个 LLM 更复杂、更深入的任务,例如一份完整的数据分析报告或业务决策建议。
之前的模型,可以看作是一个强大的“瑞士军刀”式 LLM,它什么都能做一点。而引入 MCP/A2A 后,系统进化为一个“专家团队”。
这个团队可能包含以下几个核心角色:
option。graph TD
subgraph "用户接口"
User("用户")
end
subgraph "AI 智能体团队 (MCP/A2A Framework)"
Coordinator("项目经理<br>(Coordinator Agent)")
Analyst("数据分析师<br>(Analyst Agent)")
Visualizer("可视化设计师<br>(Visualization Agent)")
Reporter("报告撰写师<br>(Report Agent)")
end
subgraph "数据与工具层"
DuckDB("DuckDB")
DataSource("数据源")
ECharts("ECharts (前端)")
end
User -- "1 高层指令: '分析Q1销售额下降原因并写报告'" --> Coordinator
Coordinator -- "2 任务分解与分派" --> Analyst
Analyst -- "3 生成SQL并查询" --> DuckDB
DuckDB -- "4 从数据源获取数据" --> DataSource
DuckDB -- "5 返回数据结果" --> Analyst
Analyst -- "6 提炼数据洞察" --> Coordinator
Coordinator -- "7 请求可视化" --> Visualizer
Visualizer -- "8 生成ECharts Option" --> Coordinator
Coordinator -- "9 汇总所有材料并分派撰写任务" --> Reporter
Reporter -- "10 生成完整报告" --> Coordinator
Coordinator -- "11 交付最终报告" --> User
%% 连接可视化部分
subgraph "前端渲染"
style FronteEnd fill:#eee, stroke:#333, stroke-width:1px, stroke-dasharray: 5 5
Reporter -- "报告中的图表配置" --> ECharts
Analyst -- "报告中的数据" --> ECharts
end
用户指令:“分析第一季度销售额相比去年同期下降的原因,并生成一份图文并茂的分析报告。”
option JSON 对象,并返回给项目经理。option)的完整报告。总而言之,引入 MCP/A2A 框架,让智能数据可视化产品从一个“有问必答的工具”,进化为一个“能独立完成项目的自动化分析团队”。这不仅是功能的延伸,更是产品智能水平和实用价值的质的飞跃。