图表理解——让 AI 看懂你的业务报表
图表理解——让 AI 看懂你的业务报表
适读人群:做数据报告自动化、BI 增强的开发者 | 阅读时长:约 14 分钟 | 核心价值:搞清楚图表理解的真实价值场景,避开伪需求,掌握工程实现
有个朋友在做电商数据中台,他们的 BI 部门每周要出几十份报告,里面大量是趋势图、对比图、饼图。
他找我说,想做个功能:把这些图丢给 Vision AI,让 AI 自动写报告解读文字。
我问他:这些图的数据有没有原始数据?
他说:有,都在数据库里。
我说:那你为什么要用 Vision?直接用数据生成解读不香吗?
他愣了一下。
这就是图表理解场景里最典型的伪需求:明明有原始数据,却非要绕个弯用 Vision 模型去"看"图。
这篇文章讲清楚两件事:什么场景用 Vision 做图表理解是真需求,什么场景是伪需求;以及真需求场景下怎么实现。
先区分真需求和伪需求
伪需求:有数据源却用 Vision
如果你有图表的原始数据(数据库、API、Excel 文件),直接用数据做分析,不要走 Vision 这条路。
原因很简单:
- Vision 读图是近似值,精度不如原始数据
- 处理成本高(Vision API 贵)
- 处理速度慢(Vision 推理慢)
- 可能引入幻觉(模型有时候会"看错"数值)
如果你有数据,直接把数据序列化后给 LLM 做文字分析,效果更好、成本更低、准确率更高。
真需求:只有图没有数据
以下场景,Vision 图表理解才是真需求:
场景一:外部来源的截图报告
竞品报告、行业分析报告、咨询公司的 PDF 报告,里面的图表你拿不到数据,只有图片。你需要从图里提取关键数据和趋势用于二次分析。
场景二:历史报表的数字化
公司有大量历史纸质报表或扫描件,需要把图表里的数据提取出来建立数字化档案。
场景三:用户上传的图表
你的产品允许用户上传任意图片,包括他们截取的各种来源的图表,需要 AI 理解并解读。
场景四:实时截屏监控
自动截取某些数据展示页面(新闻、社媒数据等),用 Vision 提取关键数据点。
工程实现
基础:图表数据提取
import anthropic
import base64
import json
import re
from pathlib import Path
def extract_chart_data(image_path: str, chart_context: str = '') -> dict:
"""
从图表图片中提取数据
chart_context: 图表的背景信息,比如"这是某公司Q3电商销售数据"
返回结构化的图表数据
"""
client = anthropic.Anthropic()
with open(image_path, 'rb') as f:
image_data = base64.b64encode(f.read()).decode()
# 判断图片格式
suffix = Path(image_path).suffix.lower()
media_type_map = {'.jpg': 'image/jpeg', '.jpeg': 'image/jpeg',
'.png': 'image/png', '.gif': 'image/gif', '.webp': 'image/webp'}
media_type = media_type_map.get(suffix, 'image/jpeg')
context_prompt = f"图表背景:{chart_context}\n\n" if chart_context else ""
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=2000,
messages=[{
"role": "user",
"content": [
{
"type": "image",
"source": {"type": "base64", "media_type": media_type, "data": image_data}
},
{
"type": "text",
"text": f"""{context_prompt}请分析这张图表,提取以下信息:
1. 图表类型(折线图/柱状图/饼图/散点图/其他)
2. 图表标题(如有)
3. X轴和Y轴的含义及单位(如适用)
4. 图例信息(如有多个系列)
5. 所有可读取的数据点(尽量精确)
6. 时间范围(如适用)
请用 JSON 格式输出,结构如下:
{{
"chart_type": "图表类型",
"title": "图表标题或null",
"x_axis": {{"label": "X轴含义", "unit": "单位或null"}},
"y_axis": {{"label": "Y轴含义", "unit": "单位或null"}},
"time_range": "时间范围或null",
"series": [
{{
"name": "系列名称",
"data": [
{{"label": "X轴值", "value": 数值, "note": "特殊标注或null"}}
]
}}
],
"annotations": ["图表上的标注文字列表"],
"confidence": "high/medium/low" // 数据读取的置信度
}}
只返回 JSON,不要其他内容。"""
}
]
}]
)
response_text = response.content[0].text.strip()
try:
# 提取 JSON
json_match = re.search(r'\{.*\}', response_text, re.DOTALL)
if json_match:
return json.loads(json_match.group())
except json.JSONDecodeError:
pass
return {'error': '解析失败', 'raw': response_text}
def generate_chart_insight(chart_data: dict, business_context: str = '') -> str:
"""
基于提取的图表数据生成业务洞察
注意:这一步用的是结构化数据,不是再次用 Vision
数据提取和洞察生成分两步,效果和可控性都更好
"""
client = anthropic.Anthropic()
context_prompt = f"业务背景:{business_context}\n\n" if business_context else ""
# 把图表数据转换为自然语言描述
data_description = json.dumps(chart_data, ensure_ascii=False, indent=2)
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=1000,
messages=[{
"role": "user",
"content": f"""{context_prompt}以下是从图表中提取的数据:
{data_description}
请生成一段专业的图表解读,要求:
1. 描述主要趋势(上升/下降/波动)
2. 指出峰值和低谷,以及出现的时间节点
3. 如果有多个系列,对比分析各系列的差异
4. 提出 1-2 个值得关注的异常点或有趣规律
5. 用业务语言而不是统计语言描述(不要说"标准差"这类词)
输出 200-400 字的解读文字。"""
}]
)
return response.content[0].text批量处理报告里的图表
import fitz # PyMuPDF
import io
from PIL import Image
def extract_charts_from_pdf(pdf_path: str, output_dir: str) -> list[dict]:
"""
从 PDF 报告中提取所有图表
"""
import os
os.makedirs(output_dir, exist_ok=True)
doc = fitz.open(pdf_path)
charts = []
for page_num in range(len(doc)):
page = doc[page_num]
# 提取页面上的图片
image_list = page.get_images()
for img_idx, img_info in enumerate(image_list):
xref = img_info[0]
base_image = doc.extract_image(xref)
width = base_image['width']
height = base_image['height']
# 过滤:只处理足够大的图(排除小图标)
# 图表通常比较大,宽高比接近 4:3 或 16:9
if width < 300 or height < 200:
continue
aspect_ratio = width / height
if aspect_ratio < 0.5 or aspect_ratio > 4:
continue # 比例异常,可能不是图表
image_bytes = base_image["image"]
image_path = f"{output_dir}/page{page_num+1}_img{img_idx}.jpg"
# 转换并保存
img = Image.open(io.BytesIO(image_bytes))
img.convert('RGB').save(image_path, 'JPEG', quality=90)
charts.append({
'page': page_num + 1,
'image_index': img_idx,
'image_path': image_path,
'size': (width, height),
})
doc.close()
return charts
def analyze_report_charts(
pdf_path: str,
output_dir: str,
report_context: str = ''
) -> list[dict]:
"""
分析报告中的所有图表,生成洞察
"""
# 提取图表
charts = extract_charts_from_pdf(pdf_path, f"{output_dir}/charts")
print(f"发现 {len(charts)} 张图表")
results = []
for i, chart in enumerate(charts):
print(f"分析第 {i+1}/{len(charts)} 张图表...")
# 提取数据
chart_data = extract_chart_data(
chart['image_path'],
chart_context=report_context
)
# 如果置信度低,标记需要人工确认
if chart_data.get('confidence') == 'low':
chart['needs_review'] = True
# 生成洞察
insight = ''
if 'error' not in chart_data and chart_data.get('series'):
insight = generate_chart_insight(chart_data, report_context)
results.append({
**chart,
'chart_data': chart_data,
'insight': insight
})
return results图表对比分析
这是真正有价值的场景之一:把多张图放在一起,做跨图对比分析。
def compare_charts(
chart_data_list: list[dict],
comparison_question: str
) -> str:
"""
对多张图表的数据做对比分析
chart_data_list: 已提取的多张图表数据
comparison_question: 对比分析的问题,如"这三个季度的销售趋势有什么差异?"
"""
client = anthropic.Anthropic()
# 把所有图表数据组合
charts_summary = []
for i, chart_data in enumerate(chart_data_list):
charts_summary.append(f"图表 {i+1}:{json.dumps(chart_data, ensure_ascii=False)}")
combined_data = '\n\n'.join(charts_summary)
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=1500,
messages=[{
"role": "user",
"content": f"""以下是多张图表的数据:
{combined_data}
请回答:{comparison_question}
分析要求:
1. 横向对比各图表的数据
2. 找出共同趋势和差异点
3. 给出具体的数据支撑
4. 提出有商业价值的结论"""
}]
)
return response.content[0].text准确率的真实情况
用 Vision 读图表不是万能的,这里给一个诚实的准确率评估。
折线图:准确率最高,通常在 85%-95%。关键点(最高值、最低值、转折点)识别准确,但精确数值可能有 5%-10% 的误差。
柱状图:80%-90%,对于差异明显的柱子效果好,当多根柱子高度相近时容易出错。
饼图:75%-85%,百分比读取通常比较准,但当有很多小块时会遗漏。
复合图表(双Y轴、组合图):60%-75%,复杂图表容易混淆对应关系。
低置信度的信号:
- 图片模糊、像素低
- 颜色对比度低(如浅色系图表)
- 数据点太密集(折线图有几百个点)
- 图表使用了非标准的可视化方式
对于低置信度的情况,要么标记需要人工确认,要么直接跳过,不要把不确定的数字当作可靠的结果。
一个真实项目的效果
那个朋友最后我帮他分析了真正的需求场景。
他们的 BI 报告里有两类内容:
- 自家数据生成的图表:有原始数据 → 不用 Vision,直接用数据 + LLM 生成解读文字
- 竞品分析报告(竞对公司、行业报告):只有图,没有数据 → 用 Vision 提取数据
分清楚这两类后,方案变成了:
- 自有数据:数据直接送给 LLM 生成解读,准确率 100%,成本极低
- 竞品报告:Vision 提取 + 置信度标注,低置信度的人工复核
效果:自有数据部分完全自动化,竞品报告部分节省了大约 60% 的人工时间。
这比一开始"全部用 Vision"的方案,效果更好,成本更低,架构更简单。
好的工程决策往往是"不要在不必要的地方用复杂方案"。Vision 图表理解是有价值的技术,但价值在于处理你拿不到原始数据的图表,而不是炫技。
