思路不复杂:把数据和你的描述发给大模型,让它返回一段可直接运行的绘图代码。Python 这边执行代码就能出图。

先搭基础框架

```python

# chart_agent.py

import json

import subprocess

import sys

from pathlib import Path

from llm_client import LLMClient, Message

SYSTEM_PROMPT = """你是一个数据可视化专家。根据用户的需求和数据,生成 Python 代码绘制图表。

规则:

1. 只使用 matplotlib 和 seaborn

2. 代码必须完整可运行,包含 import

3. 图表保存到指定路径

4. 中文标签需要设置中文字体

5. 返回格式:```python 代码块

6. 图表要美观:配色合理、标签清晰、图例完整"""

class ChartAgent:

    def __init__(self, client: LLMClient):

        self.client = client

    def generate_code(self, data_desc: str, chart_desc: str) -> str:

        """根据数据描述和图表需求生成代码"""

        prompt = f"""数据说明:

{data_desc}

绘图需求:

{chart_desc}

请生成 Python 代码,保存图表到 output_chart.png。"""

        messages = [

            Message(role="system", content=SYSTEM_PROMPT),

            Message(role="user", content=prompt),

        ]

        resp = self.client.chat(messages, temperature=0.2)

        return self._extract_code(resp.content)

    def _extract_code(self, text: str) -> str:

        """从回复中提取 Python 代码"""

        if "```python" in text:

            start = text.index("```python") + 10

            end = text.index("```", start)

            return text[start:end].strip()

        return text

    def run_code(self, code: str) -> str:

        """执行生成的代码,返回运行结果"""

        with open("_temp_chart.py", "w", encoding="utf-8") as f:

            f.write(code)

        result = subprocess.run(

            [sys.executable, "_temp_chart.py"],

            capture_output=True, text=True, timeout=30,

        )

        Path("_temp_chart.py").unlink(missing_ok=True)

        if result.returncode != 0:

            return f"运行失败: {result.stderr}"

        if Path("output_chart.png").exists():

            return "✅ 图表已生成: output_chart.png"

        return "代码执行完成,但未生成图表文件"

if __name__ == "__main__":

    client = LLMClient(

        api_key="your-api-key",

        base_url="https://api.deepseek.com",

        model="deepseek-chat",

    )

    agent = ChartAgent(client)

    data = """

    月份: 1月,2月,3月,4月,5月,6月

    销售额(万): 12, 18, 15, 22, 28, 35

    利润(万): 3, 5, 4, 7, 9, 12

    """

    chart = "画一个折线图,展示销售额和利润的趋势,要中文标签"

    code = agent.generate_code(data, chart)

    print(code)

    print(agent.run_code(code))

```

第一次运行,让它画个销售趋势图。模型返回的代码大致长这样:

```python

import matplotlib.pyplot as plt

import matplotlib

matplotlib.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']

matplotlib.rcParams['axes.unicode_minus'] = False

months = ['1月', '2月', '3月', '4月', '5月', '6月']

sales = [12, 18, 15, 22, 28, 35]

profit = [3, 5, 4, 7, 9, 12]

plt.figure(figsize=(10, 6))

plt.plot(months, sales, 'o-', label='销售额(万)', linewidth=2)

plt.plot(months, profit, 's--', label='利润(万)', linewidth=2)

plt.xlabel('月份')

plt.ylabel('金额(万)')

plt.title('上半年销售趋势')

plt.legend()

plt.grid(True, alpha=0.3)

plt.savefig('output_chart.png', dpi=150, bbox_inches='tight')

```

自动修正错误

生成的代码不一定一次跑通。字体找不到、列名不对、数据格式不匹配,常见问题加个自动修正。

```python

# chart_agent_v2.py

import traceback

class ChartAgentV2(ChartAgent):

    def run_with_fix(self, data_desc: str, chart_desc: str,

                     max_retries: int = 3) -> tuple[str, str]:

        """生成代码并自动修正,返回 (最终代码, 运行结果)"""

        code = self.generate_code(data_desc, chart_desc)

        for attempt in range(max_retries):

            # 写代码

            with open("_temp_chart.py", "w", encoding="utf-8") as f:

                f.write(code)

            # 执行

            result = subprocess.run(

                [sys.executable, "_temp_chart.py"],

                capture_output=True, text=True, timeout=30,

            )

            if result.returncode == 0 and Path("output_chart.png").exists():

                Path("_temp_chart.py").unlink(missing_ok=True)

                return code, "✅ 图表已生成"

            # 出错了,让模型修复

            error_msg = result.stderr or "图表文件未生成"

            fix_prompt = f"""生成的代码运行出错:

{code}

错误信息:

{error_msg}

请修复代码,只返回修正后的完整 Python 代码。"""

            messages = [

                Message(role="system", content=SYSTEM_PROMPT),

                Message(role="user", content=fix_prompt),

            ]

            resp = self.client.chat(messages, temperature=0.1)

            code = self._extract_code(resp.content)

            print(f"  第 {attempt+1} 次修正...")

        Path("_temp_chart.py").unlink(missing_ok=True)

        return code, f"❌ 修正 {max_retries} 次仍未通过"

```

支持多种图表类型

不同的数据适合不同的图表。在 prompt 里说清楚数据特征,让模型自己选。

```python

chart_types = [

    "画一个柱状图,对比各部门的业绩",

    "画一个饼图,展示各产品线的占比",

    "画一个散点图,分析广告投入和销售额的关系",

    "画一个热力图,显示各月份各产品的销量",

    "画一个箱线图,展示不同城市的薪资分布",

]

for desc in chart_types:

    print(f"\n--- {desc} ---")

    code, result = agent.run_with_fix(data_desc, desc)

    print(result)

```

如果数据是 CSV 文件

大多数时候数据在 CSV 文件里,不是手写的。加一个 CSV 读取接口。

```python

# csv_chart.py

import pandas as pd

from chart_agent_v2 import ChartAgentV2

from llm_client import LLMClient

def csv_to_description(filepath: str, max_rows: int = 5) -> str:

    """读取 CSV 前几行,生成数据描述"""

    df = pd.read_csv(filepath)

    info = f"""文件: {filepath}

列名: {', '.join(df.columns)}

行数: {len(df)}

数据类型:

{df.dtypes.to_string()}

前 {max_rows} 行数据:

{df.head(max_rows).to_string(index=False)}

"""

    return info

def main():

    client = LLMClient(

        api_key="your-api-key",

        base_url="https://api.deepseek.com",

        model="deepseek-chat",

    )

    agent = ChartAgentV2(client)

    csv_path = input("CSV 文件路径: ").strip()

    data_desc = csv_to_description(csv_path)

    print(f"\n已加载: {csv_path}")

    print(f"列: {data_desc.split(chr(10))[1]}")

    chart_desc = input("想要什么图?(例如: 按月份画销售额折线图): ").strip()

    print("\n生成中...")

    code, result = agent.run_with_fix(data_desc, chart_desc)

    print(f"\n{result}")

    print(f"\n生成的代码:\n{code}")

if __name__ == "__main__":

    main()

```

使用:

```bash

python csv_chart.py

CSV 文件路径: sales_data.csv

想要什么图?按月份画销售额折线图,不同产品用不同颜色

```

批量生成图表

数据分析报告经常要一次生成几十张图,一个个问太慢。写个批量模式。

```python

# batch_chart.py

import json

from pathlib import Path

from chart_agent_v2 import ChartAgentV2

from llm_client import LLMClient

def batch_generate(config_file: str):

    """从配置文件批量生成图表"""

    with open(config_file, encoding="utf-8") as f:

        tasks = json.load(f)

    client = LLMClient(

        api_key="your-api-key",

        base_url="https://api.deepseek.com",

        model="deepseek-chat",

    )

    agent = ChartAgentV2(client)

    output_dir = Path("charts_output")

    output_dir.mkdir(exist_ok=True)

    for i, task in enumerate(tasks, 1):

        print(f"\n[{i}/{len(tasks)}] {task['name']}")

        data_desc = f"文件: {task['file']}\n需求: {task['description']}"

        chart_desc = task['chart']

        code, result = agent.run_with_fix(data_desc, chart_desc)

        print(f"  {result}")

        # 保存图表和代码

        chart_file = output_dir / f"{task['name']}.png"

        if Path("output_chart.png").exists():

            Path("output_chart.png").rename(chart_file)

        code_file = output_dir / f"{task['name']}.py"

        code_file.write_text(code, encoding="utf-8")

if __name__ == "__main__":

    batch_generate("chart_tasks.json")

```

配置文件 `chart_tasks.json`:

```json

[

    {

        "name": "月度销售趋势",

        "file": "sales_2026.csv",

        "description": "2026年各月销售额和利润",

        "chart": "双折线图展示销售趋势"

    },

    {

        "name": "部门业绩对比",

        "file": "dept_performance.csv",

        "description": "各部门Q1和Q2业绩",

        "chart": "分组柱状图对比Q1和Q2"

    },

    {

        "name": "产品占比",

        "file": "product_sales.csv",

        "description": "各产品销售额占比",

        "chart": "饼图展示占比,标出百分比"

    }

]

```

几个实际踩过的坑

1. 中文字体问题。matplotlib 默认不支持中文,生成的代码必须显式设置中文字体。SimHei(黑体)在 Windows 上可用,macOS 用 Arial Unicode MS,Linux 要装文泉驿。系统不同,字体路径不同。比较稳的做法是用 font_manager 查找:

```python

import matplotlib.font_manager as fm

fonts = [f.name for f in fm.fontManager.ttflist if 'Hei' in f.name or 'CJK' in f.name]

```

2. 数据格式不一致。CSV 里的列名可能有空格、中文、特殊字符。生成的代码直接用列名当变量名可能报错。在数据描述里把列名用反引号括起来,让模型生成的代码也加上。

3. 图表太丑。默认的 matplotlib 配色和样式比较旧。可以在 system prompt 里要求使用 seaborn 样式。seaborn 的默认配色比 matplotlib 好看很多。

4. 生成的代码有安全隐患。大模型可能生成执行系统命令的代码(比如 os.system)。建议在沙箱里执行,或者至少审查生成的代码再运行。上面用了 subprocess 隔离,但也不是完全安全。

5. 模型对图表类型的理解有限。说"画个好看的图",不同模型理解不一样。最好明确说"折线图" "柱状图" "散点图",而不是"分析图" "趋势图"这类模糊描述。

6. 大数据集时间长。几千行的 CSV 传给模型,要消耗大量 token。建议只传前几行作为结构示意,完整数据让生成的代码自己去读 CSV 文件。上面 csv_chart.py 就是这么做的。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐