数据问答和可视化分析肯定是企业管理应用的日常,今天带大家看怎么实现这个功能。
先看先最终效果吧:
关于标题的不愉快
首先呢,我确实使用 LangChain 的 SqlAgent 实现了数据问答和可视化分析。
其次呢,LangChain 的逻辑和内置提示模板不是那么好用,偶尔会死循环。Token 用的也是飞快,如果真的这样上线,执行一次分析可能要几毛钱的价格。
下面是我开发用的 OpenAI 账号的用量,平时写写代码用量都很少。
这两天写这篇文档的代码时候用了下 LangChain SqlAgent,流量(Token)使用飙升,虽然只有几美金,但是我这两天每天也只是调用了十几次而已(还没达到理想效果)。
关于架构
我之前以为的架构 SqlAgent 这块的是这样:
实际上他是这样,中间会有不停的试错纠错重试:
这也是我为什么之前建议过别人能不用就不用 LangChain,除了 Hello World 这样的 Demo 东西,后面 LangChain 还是有一些问题的。
总结下就是:
深入研究学习 LangChain 不如直接调用 OpenAI 来实现(重构)应用。
废话不说,看看这个数据问答开发过程吧。
需求
有两个小的数据分析需求。
分析邮件
第一个是咱们之前关于邮件的:
• 统计下有谁给我发了邮件,
• 邮件的主题分类都是那些,比如
分析开发任务
第二个是关于开发任务的,假设你是某个部门负责人、比如开发团队负责人,你是不是时不时的想看看团队的开发情况?
• 这周有多少个开发任务,
• 有多少任务延期了了,多少任务准时完成了,
• 各个部门完成的任务情况,
• 任务的打分、耗时等分布。
比如这样?
或者这样:
LangChain 的 SQL 支持
首先,企业数据通常存储在 SQL 数据库中,如果要做数据分析,肯定离不开 SQL 和数据库。
LangChain 提供了 SqlChain 和 Agent 来实现基于自然语言提示构建和运行 SQL 查询。
底层实现是通过 SQLAlchemy 这个 ORM 库,SQLAlchemy 支持的主要 SQL 方言的兼容(例如 MySQL、PostgreSQL、Oracle SQL、Databricks、SQLite)。
基于这些能力,我们可以试下下面的应用:
• 自然语言的数据库查询
• 根据数据库的数据回答问题聊天机器人
• 自然语言的数据分析,和构建自定义仪表板
实现
引入相关包
import sys
from langchain.agents import create_sql_agent, AgentType
from langchain.agents.agent_toolkits import SQLDatabaseToolkit
from langchain.chains import create_sql_query_chain
from langchain.chat_models import ChatOpenAI
from langchain.sql_database import SQLDatabase
from langchain.llms.openai import OpenAI
from langchain.agents import AgentExecutor
创建数据库字段
# sqlite
db = SQLDatabase.from_uri("sqlite:////Users/.../emails.db")
# postgres
db = SQLDatabase.from_uri("postgresql://user:password@localhost:5432/postgres?connect_timeout=10")
创建 Agent
llm = OpenAI(temperature=0, verbose=True)
toolkit = SQLDatabaseToolkit(db=db, llm=OpenAI(temperature=0))
agent = create_sql_agent(
llm=llm,
toolkit=toolkit,
verbose=True,
agent_type=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
)
描述表结构
response = agent.run("描述下邮件表")
print(response)
我根据日志画了一下 LangChain 的执行过程(多次调用 LLM 的过程):
下面是打印的详细输出,可以看到 LangChain 的整个思考过程。
> Entering new AgentExecutor chain...
Action: sql_db_list_tables
Action Input:
Observation: emails
Thought: I should query the schema of the emails table
Action: sql_db_schema
Action Input: emails
Observation:
CREATE TABLE emails (
id BIGINT,
from_email VARCHAR(255),
from_name VARCHAR(255),
subject VARCHAR(1000),
date_beijing TIMESTAMP(0) WITHOUT TIME ZONE,
text TEXT
)
/*
3 rows from emails table:
id from_email from_name subject date_beijing text
8129 info@techtarget.com ComputerWeekly.com Inside Group-IB’s playbook 2023-04-11 19:39:57 None
8156 update@grafana.com Grafana Labs Team You're invited: Grafana Terraform Provider 2023-04-12 10:01:23 None
*/
Thought: I now know the final answer
Final Answer: The emails table contains the following columns: id, from_email, from_name, subject, date_beijing, and text.
> Finished chain.
The emails table contains the following columns: id, from_email, from_name, subject, date_beijing, and text.
客户端的返回时:
The emails table contains the following columns: id, from_email, from_name, subject, date_beijing, and text.
查询邮件,用 Markdown 展示
提问如下:
查看最近3个月有多少封的邮件。返回给我发送邮件最多的5个人,用 Markdown 表格展示 姓名,邮件地址,总邮件数,最后发送时间,
LangChain 执行过程如下,依然看图:
看下完整的输出:
> Entering new AgentExecutor chain...
Action: sql_db_list_tables
Action Input:
Observation: emails, tasks
Thought: I should query the schema of the emails table
Action: sql_db_schema
Action Input: emails
Observation:
CREATE TABLE emails (
id BIGINT,
from_email VARCHAR(255),
from_name VARCHAR(255),
subject VARCHAR(1000),
date_beijing TIMESTAMP(0) WITHOUT TIME ZONE,
text TEXT
)
/*
3 rows from emails table:
id from_email from_name subject date_beijing text
8129 info@zmp.techtarget.com ComputerWeekly.com Inside Group-IB’s cyber security playbook 2023-04-11 19:39:57 None
8156 update@grafana.com Grafana Labs Team You're invited: Mastering the Grafana Terraform Provider 2023-04-12 10:01:23 None
*/
Thought: I should query the emails table to get the data I need
Action: sql_db_query
Action Input: SELECT from_name, from_email, COUNT(*) as total_emails, MAX(date_beijing) as last_sent_date FROM emails WHERE date_beijing > NOW() - INTERVAL '3 MONTHS' GROUP BY from_name, from_email ORDER BY total_emails DESC LIMIT 5
Observation: [('ComputerWeekly.com', 'info@zmp.techtarget.com', 20, datetime.datetime(2023, 8, 17, 18, 31, 50)), ('Grafana Labs', 'cloud-success@grafana.
Thought: I now know the final answer
Final Answer:
| 姓名 | 邮件地址 | 总邮件数 | 最后发送时间 |
| --- | --- | --- | --- |
| ComputerWeekly.com | info@techtarget.com | 20 | 2023-08-17 18:31:50 |
| Grafana Labs | cloud@grafana.com | 13 | 2023-08-22 14:57:27 |
| Google Cloud | googlecloud@google.com | 9 | 2023-08-25 14:47:25 |
| LeanIX Connect Summit | summit@connect.com | 8 | 2023-08-24 14:05:50 |
| Grafana Labs Team | update@grafana.com | 6 | 2023-07-11 12:31:15 |
> Finished chain.
谁给我发的邮件最多?
上面其实还好,当我问下下面这个问题的时候, LangChain Agent 炸了!
response = agent.run("查看最近一个月的邮件。看谁给我发的邮件最多?")
print(response)
LangChain 会告诉你:Agent stopped due to iteration limit or time limit.
没错这么简单的问题,他因为轮训次数太多超出限制停止尝试了,
输出大概是这样的:
> Entering new AgentExecutor chain...
Action: sql_db_list_tables
Action Input:
Observation: emails
Thought: I should query the schema of the emails table
...
此处省略 100 行
...
Thought:
> Finished chain.
Agent stopped due to iteration limit or time limit.
问题的原因无非就是生成的 SQL 错误等等,然后不停修复不停重试。
这跟 LLM 有关(GPT4可能会好很多,我用的 GPT3),同样的问题有时会成功,比如一次成功的:
> Finished chain.
ComputerWeekly.com 发了最多的邮件,共发了 22 封。
这个问题不纠结,我们继续(后面还会出现这种类似的死循环过程,我就不赘述了)。
可视化
下面我们来解决可视化的问题,当然可视化也有两类方案:
• 一个是后端 Python 生成图片返回前端, 比如 Matplotlib;
• 一个是返回 Json 数据给前端显示,比如 ECharts。
我们尝试使用前端展示的方案,毕竟这样的交互性强一点。
提示
我们现在还是使用 LangChain,那么此处的关键就是编写 Prompt,能让 AI 做的都让 AI 做,比如:直接生成 ECharts 的配置。
prompt = '''
根据我的问题,执行数据查询,并选择合适的可视化方案(线图、饼图、柱状图等),生成 echarts 的配置,
问题:%s 。
数据为: %s
请按照以下格式回复我:
:::
{
"charts":{<echarts的配置>}
}
:::
'''
可视化最近一周任务
提问:最近一周不同部门每天完成的任务数
返回的配置:
{
"charts": {
"option": {
"title": {
"text": "最近一周不同部门每天完成的任务数"
}
...
}
}
可视化效果如下:
集成到我们的前端里面:
总结
上面是简单粗暴的 AI 数据应用实现,下一期以应用架构的四维,融合大语言模型的能力来实现一个可用的数据交互功能。
--- END ---

