AI数据助手:从文档问答到智能数据分析
前面 9 篇我们把 RAG 问答系统从零搭到了生产级。但一个真正的"AI 数据助手",不能只会翻文档回答问题。它应该能帮你做数据分析、生成报表、甚至从一堆数据里挖出你不知道的信息。
今天这篇,我们把 RAG 从"问答机器人"升级到"数据助手"。
大家好,我是黒漂技术佬。
一、RAG 只能做问答?格局小了
先对齐认知:当前大火的 AI 数据助手,本质上是在 RAG 基础之上叠加了新的能力:
传统 RAG(文档问答):
用户问 → 搜文档 → LLM 生成答案 → 返回文字
AI 数据助手(增强版):
用户问 → 理解意图 →
├── 搜文档(RAG)
├── 查数据库(Text-to-SQL)
├── 调API(Function Calling / Tool Use)
└── 汇总分析 → 生成结构化答案 + 图表
举个真实的场景:
用户:"上个月技术部的加班情况怎么样?"
传统 RAG 回答:从《考勤制度》里搜出加班规则(毫无意义)
AI 数据助手回答:
1. 通过 Text-to-SQL 查数据库 → 技术部30人,上月人均加班12.3小时
2. 通过 RAG 查找相关制度 → 加班费计算标准
3. 综合生成 → "技术部30人中,近两周加班总时长368小时,
其中王工累计42小时最高。按公司制度,加班费预计...(附图表)"
看出区别了吗?数据助手不只是"复读文档",而是能"理解数据、分析数据、呈现结论"。
二、核心技术一:Text-to-SQL ------ 让 AI 帮你查数据库
这是数据助手的第一个杀手级能力:用户用大白话问,AI 自动生成 SQL 去数据库查结果。
实现原理
python
def text_to_sql(user_question: str, db_schema: str) -> dict:
"""将自然语言问题转为 SQL 并执行"""
prompt = f"""
你是一个 SQL 专家。请根据用户问题,生成对应数据库查询 SQL。
【数据库 Schema】
{db_schema}
【注意事项】
1. 只生成 SELECT 语句,不要 INSERT/UPDATE/DELETE
2. 查询必须加 LIMIT 限制,防止返回海量数据
3. 涉及时间的查询注意时区
4. 表名和字段名用反引号包裹
【用户问题】
{user_question}
请生成 SQL:
"""
sql = llm.invoke(prompt)
# ⚠️ 安全检查:只允许 SELECT
if not sql.strip().upper().startswith("SELECT"):
return {"error": "只允许查询操作"}
# ⚠️ 安全检查:禁止危险关键词
dangerous = ["DROP", "ALTER", "TRUNCATE", "EXEC", "--", ";--"]
for keyword in dangerous:
if keyword in sql.upper():
return {"error": f"SQL 包含禁止的关键词: {keyword}"}
# 执行查询
try:
result = db.execute(sql, timeout=10) # 超时控制
return {"sql": sql, "data": result}
except Exception as e:
return {"error": str(e), "sql": sql}
为什么还需要 Schema 描述?
LLM 不知道你数据库里有什么表、什么字段。Schema 描述就是告诉它:
sql
【表:attendance(考勤记录)】
- employee_id: INT, 员工ID
- date: DATE, 日期
- overtime_hours: FLOAT, 加班时长(小时)
- department: VARCHAR, 部门
【表:employees(员工信息)】
- id: INT, 员工ID
- name: VARCHAR, 姓名
- department: VARCHAR, 部门
- hire_date: DATE, 入职日期
有了 Schema,LLM 就能把"技术部"映射到 WHERE department = '技术部'。
Text-to-SQL 的两个关键优化
优化 1:Few-shot 示例
给 Prompt 加几个示例,能显著提升 SQL 准确率:
python
examples = """
示例1:
用户:技术部有多少人?
SQL:SELECT COUNT(*) FROM employees WHERE department = '技术部'
示例2:
用户:上个月加班最多的是谁?
SQL:SELECT e.name, SUM(a.overtime_hours) as total_ot
FROM attendance a
JOIN employees e ON a.employee_id = e.id
WHERE a.date >= '2024-05-01' AND a.date < '2024-06-01'
GROUP BY e.name
ORDER BY total_ot DESC
LIMIT 5
"""
优化 2:SQL 纠错重试
LLM 生成的 SQL 可能语法错误(字段名写错、JOIN 条件漏了)。加一个纠错循环:
python
max_retries = 3
for attempt in range(max_retries):
sql = llm.invoke(prompt)
try:
result = db.execute(sql)
return result
except Exception as e:
prompt += f"\n上一版 SQL 报错:{e}\n请修正后重新生成 SQL:"
三、核心技术二:结构化输出 ------ 不只是聊天
传统 RAG 返回一段文本就完了。但数据助手常常需要返回结构化数据(表格、图表数据、JSON)。
让 LLM 输出 JSON
python
from langchain_core.output_parsers import JsonOutputParser
from pydantic import BaseModel
class OvertimeReport(BaseModel):
department: str
total_employees: int
total_overtime_hours: float
avg_overtime_per_person: float
top_overtime_person: str
top_overtime_hours: float
trend: str # "上升" / "下降" / "持平"
parser = JsonOutputParser(pydantic_object=OvertimeReport)
prompt = ChatPromptTemplate.from_template("""
根据以下数据,生成加班分析报告。
{data}
{format_instructions}
""")
chain = prompt | llm | parser
# 调用后得到的是 Python dict,可以直接渲染前端图表
report = chain.invoke({
"data": sql_result,
"format_instructions": parser.get_format_instructions()
})
# report = {
# "department": "技术部",
# "total_employees": 30,
# "total_overtime_hours": 368.5,
# "avg_overtime_per_person": 12.28,
# ...
# }
有了结构化输出,前端就能直接渲染折线图、柱状图、饼图------而不只是展示一段文字。
四、核心技术三:多轮对话 + 上下文记忆
数据助手不是"一问一答就结束"的工具。用户经常会在对话中逐步细化问题:
用户:技术部加班情况
AI:(返回加班总览表)
用户:王工的呢? ← "王工"是从上文继承的上下文
AI:(返回王工个人的加班明细)
用户:帮我把他的数据导出成 Excel ← "他"指的也是"王工"
AI:(生成 Excel 下载链接)
这需要上下文记忆:
python
from langchain.memory import ConversationBufferWindowMemory
# 只保留最近 5 轮对话的历史,防止上下文过长
memory = ConversationBufferWindowMemory(k=5, return_messages=True)
def chat_with_memory(user_input: str, user_id: str):
# 1. 获取历史消息
history = memory.load_memory_variables({})["history"]
# 2. 如果用户用了代词(他/她/它/这个/那个),用 LLM 做指代消解
if has_pronouns(user_input):
user_input = resolve_pronouns(user_input, history)
# "他的数据" → "王工的数据"
# 3. 正常的 RAG + Text-to-SQL 流程
answer = pipeline(user_input)
# 4. 保存本轮对话到记忆
memory.save_context({"input": user_input}, {"output": answer})
return answer
五、AI 数据助手的完整技术架构
把文档问答 + 数据库查询 + 对话记忆串起来,就是一个完整的 AI 数据助手:
用户输入
│
▼
┌─────────────────────┐
│ 意图识别(Router) │ ← 判断用户想干什么
└──────┬──────────────┘
│
┌───┼───────────┐
│ │ │
▼ ▼ ▼
查文档 查数据库 执行操作
(RAG) (Text2SQL) (Tool Call)
│ │ │
│ │ ┌──────┘
▼ ▼ ▼
┌──────────────┐
│ 结果聚合+分析 │ ← LLM综合所有结果
└──────┬───────┘
▼
┌──────────────┐
│ 结构化输出 │ ← JSON + 图表 + 下载链接
└──────────────┘
意图识别(Router)
用一个轻量的分类 Prompt 判断用户意图:
python
def classify_intent(question: str) -> str:
"""识别用户意图"""
prompt = f"""
判断用户意图,只输出一个词:
- "document":查文档、制度、流程
- "data":查数据、统计、分析
- "action":执行操作(导出、发送、下载)
用户:{question}
意图:
"""
return llm.invoke(prompt).strip()
然后根据意图走不同的处理链路。
💬 你的 AI 数据助手最想解决公司里的什么数据分析问题?是想查日报、看报表、还是做预测?评论区聊聊!