AI数据助手:从文档问答到智能数据分析
📅 2026/7/4 2:55:18
👁️ 阅读次数
📝 编程学习
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 去数据库查结果。
实现原理
deftext_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)# ⚠️ 安全检查:只允许 SELECTifnotsql.strip().upper().startswith("SELECT"):return{"error":"只允许查询操作"}# ⚠️ 安全检查:禁止危险关键词dangerous=["DROP","ALTER","TRUNCATE","EXEC","--",";--"]forkeywordindangerous:ifkeywordinsql.upper():return{"error":f"SQL 包含禁止的关键词:{keyword}"}# 执行查询try:result=db.execute(sql,timeout=10)# 超时控制return{"sql":sql,"data":result}exceptExceptionase:return{"error":str(e),"sql":sql}为什么还需要 Schema 描述?
LLM 不知道你数据库里有什么表、什么字段。Schema 描述就是告诉它:
【表: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 准确率:
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 条件漏了)。加一个纠错循环:
max_retries=3forattemptinrange(max_retries):sql=llm.invoke(prompt)try:result=db.execute(sql)returnresultexceptExceptionase:prompt+=f"\n上一版 SQL 报错:{e}\n请修正后重新生成 SQL:"三、核心技术二:结构化输出 —— 不只是聊天
传统 RAG 返回一段文本就完了。但数据助手常常需要返回结构化数据(表格、图表数据、JSON)。
让 LLM 输出 JSON
fromlangchain_core.output_parsersimportJsonOutputParserfrompydanticimportBaseModelclassOvertimeReport(BaseModel):department:strtotal_employees:inttotal_overtime_hours:floatavg_overtime_per_person:floattop_overtime_person:strtop_overtime_hours:floattrend: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 下载链接)这需要上下文记忆:
fromlangchain.memoryimportConversationBufferWindowMemory# 只保留最近 5 轮对话的历史,防止上下文过长memory=ConversationBufferWindowMemory(k=5,return_messages=True)defchat_with_memory(user_input:str,user_id:str):# 1. 获取历史消息history=memory.load_memory_variables({})["history"]# 2. 如果用户用了代词(他/她/它/这个/那个),用 LLM 做指代消解ifhas_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})returnanswer五、AI 数据助手的完整技术架构
把文档问答 + 数据库查询 + 对话记忆串起来,就是一个完整的 AI 数据助手:
用户输入 │ ▼ ┌─────────────────────┐ │ 意图识别(Router) │ ← 判断用户想干什么 └──────┬──────────────┘ │ ┌───┼───────────┐ │ │ │ ▼ ▼ ▼ 查文档 查数据库 执行操作 (RAG) (Text2SQL) (Tool Call) │ │ │ │ │ ┌──────┘ ▼ ▼ ▼ ┌──────────────┐ │ 结果聚合+分析 │ ← LLM综合所有结果 └──────┬───────┘ ▼ ┌──────────────┐ │ 结构化输出 │ ← JSON + 图表 + 下载链接 └──────────────┘意图识别(Router)
用一个轻量的分类 Prompt 判断用户意图:
defclassify_intent(question:str)->str:"""识别用户意图"""prompt=f""" 判断用户意图,只输出一个词: - "document":查文档、制度、流程 - "data":查数据、统计、分析 - "action":执行操作(导出、发送、下载) 用户:{question}意图: """returnllm.invoke(prompt).strip()然后根据意图走不同的处理链路。
💬 你的 AI 数据助手最想解决公司里的什么数据分析问题?是想查日报、看报表、还是做预测?评论区聊聊!
编程学习
技术分享
实战经验