【多模态】使用LLM生成code+E2B输出图表内容
画图工具调用,实现一些画图函数,LLM生成对应的参数来调用,这种方式需要实现丰富的图表函数直接输出画图的结果表示(html、mermaid等),再存储为图片格式LLM输出取数画图的代码,执行代码得到图表结果。
·
【多模态】使用LLM生成code+E2B输出图表内容
前言
LLM只能输出纯文本的内容,在分析完数据后,如果要进一步的用图表来展示,可以有这些方式:
- 画图工具调用,实现一些画图函数,LLM生成对应的参数来调用,这种方式需要实现丰富的图表函数
- 直接输出画图的结果表示(html、mermaid等),再存储为图片格式
- LLM输出取数画图的代码,执行代码得到图表结果
1 准备工作
- 需要E2B申请一个api_key,免费版即可,如果不是with的方式create沙箱,最后调用完需要kill沙箱
- 需要在阿里云的百炼平台申请LLM的api_key
- pip install e2b_code_interpreter
申请了之后可以运行代码测试看是否成功
from langchain_community.tools import E2BDataAnalysisTool
from langchain.agents import AgentType, initialize_agent
from langchain_openai import ChatOpenAI
import os
import re
from e2b_code_interpreter import Sandbox
from langchain_community.chat_models.tongyi import ChatTongyi
E2B_API_KEY = 'e2b_xxxxx'
ALI_API_KEY = 'sk_xxxx'
qwen_flash = ChatTongyi(
model="qwen-flash",
api_key=ALI_API_KEY, temperature=0
)
deepseek_v3 = ChatOpenAI(model="deepseek-v3", temperature=0, api_key=ALI_API_KEY,
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1")
os.environ["E2B_API_KEY"] = E2B_API_KEY
from e2b_code_interpreter import Sandbox
with Sandbox.create(api_key=E2B_API_KEY) as sandbox:
sandbox.run_code("x = 1")
execution = sandbox.run_code("x+=1; x")
print(execution.text)
# outputs 2
2 图表绘制
2.1 数据上传
- 可以使用kaggle的测试数据
import kagglehub
# Download latest version
path = kagglehub.dataset_download("nishanthsalian/socioeconomic-country-profiles")
print("Path to dataset files:", path)
- 可以本地准备一份已经分析完的比较简单明了的数据,仅用于画图
- 需要把数据描述告诉LLM
data_desc_template = """
- It's in the `{data_online_path}` file
- The CSV file is using , as the delimiter
- It has the following columns (examples included):
- country: "Argentina", "Australia"
- Region: "SouthAmerica", "Oceania"
- Surface area (km2): for example, 2780400
- Population in thousands (2017): for example, 44271
- Population density (per km2, 2017): for example, 16.2
- Sex ratio (m per 100 f, 2017): for example, 95.9
- GDP: Gross domestic product (million current US$): for example, 632343
- GDP growth rate (annual %, const. 2005 prices): for example, 2.4
- GDP per capita (current US$): for example, 14564.5
- Economy: Agriculture (% of GVA): for example, 10.0
- Economy: Industry (% of GVA): for example, 28.1
- Economy: Services and other activity (% of GVA): for example, 61.9
- Employment: Agriculture (% of employed): for example, 4.8
- Employment: Industry (% of employed): for example, 20.6
- Employment: Services (% of employed): for example, 74.7
- Unemployment (% of labour force): for example, 8.5
- Employment: Female (% of employed): for example, 43.7
- Employment: Male (% of employed): for example, 56.3
- Labour force participation (female %): for example, 48.5
- Labour force participation (male %): for example, 71.1
- International trade: Imports (million US$): for example, 59253
- International trade: Exports (million US$): for example, 57802
- International trade: Balance (million US$): for example, -1451
- Education: Government expenditure (% of GDP): for example, 5.3
- Health: Total expenditure (% of GDP): for example, 8.1
- Health: Government expenditure (% of total health expenditure): for example, 69.2
- Health: Private expenditure (% of total health expenditure): for example, 30.8
- Health: Out-of-pocket expenditure (% of total health expenditure): for example, 20.2
- Health: External health expenditure (% of total health expenditure): for example, 0.2
- Education: Primary gross enrollment ratio (f/m per 100 pop): for example, 111.5/107.6
- Education: Secondary gross enrollment ratio (f/m per 100 pop): for example, 104.7/98.9
- Education: Tertiary gross enrollment ratio (f/m per 100 pop): for example, 90.5/72.3
- Education: Mean years of schooling (female): for example, 10.4
- Education: Mean years of schooling (male): for example, 9.7
- Urban population (% of total population): for example, 91.7
- Population growth rate (annual %): for example, 0.9
- Fertility rate (births per woman): for example, 2.3
- Infant mortality rate (per 1,000 live births): for example, 8.9
- Life expectancy at birth, female (years): for example, 79.7
- Life expectancy at birth, male (years): for example, 72.9
- Life expectancy at birth, total (years): for example, 76.4
- Military expenditure (% of GDP): for example, 0.9
- Population, female: for example, 22572521
- Population, male: for example, 21472290
- Tax revenue (% of GDP): for example, 11.0
- Taxes on income, profits and capital gains (% of revenue): for example, 12.9
- Urban population (% of total population): for example, 91.7"""
- 把数据从本地上传到E2B的沙箱环境中
def upload_dataset(code_interpreter, local_path):
print("Uploading dataset to Code Interpreter sandbox...")
dataset_path = local_path
if not os.path.exists(dataset_path):
raise FileNotFoundError("Dataset file not found")
try:
with open(dataset_path, "rb") as f:
remote_path = code_interpreter.files.write(dataset_path,f)
if not remote_path:
raise ValueError("Failed to upload dataset")
print("Uploaded at", remote_path)
return remote_path
except Exception as error:
print("Error during file upload:", error)
raise error
2.2 设置输出图表绘制的prompt
- 除了生成代码的prompt,还可以设置一个检查代码正确性的
task_template = """You're a Python data scientist. You are given tasks to complete and you run Python code to solve them.
Information about the csv dataset:
{data_desc}
Generally, you follow these rules:
- ALWAYS FORMAT YOUR RESPONSE IN MARKDOWN
- ALWAYS RESPOND ONLY WITH CODE IN CODE BLOCK LIKE THIS:
```python'
[your code here]
```'
- the Python code runs in jupyter notebook.
- all code must be generated and executed in a single cell.
- display visualizations using matplotlib or any other visualization library directly in the notebook. don't worry about saving the visualizations to a file.
- you also have access to the filesystem and can read/write files.
- you can install any pip package (if it exists) if you need to be running `!pip install [package]`. The usual packages for data analysis are already preinstalled though.
- you can run any Python code you want, everything is running in a secure sandbox environment
Your task is:
{task}
Now please generate the Python code to perform the task:
"""
code_check_template = """You are a code analysis tool. You are given a piece of Python code. You need to check if the code has any syntax errors or other issues that would prevent it from running successfully. If the code has no issues, respond with "GOOD". If there are issues, respond with "BAD".
Here is the code:{code}
"""
def chat_with_llm(e2b_code_interpreter, user_message, max_retries=1):
print(f"\n{'='*50}\nUser message: {user_message}\n{'='*50}")
for attempt in range(max_retries + 1):
messages = [
{"role": "user", "content": user_message},
]
if attempt>0:
response_message = deepseek_v3.invoke(messages)
else:
response_message = qwen_flash.invoke(messages)
print(f"Model response: {response_message.content}\n{'='*50}")
python_code = match_code_blocks(response_message.content)
if python_code != "":
# 先进行语法校验
code_check_result = qwen_flash.invoke([{"role": "user", "content": code_check_template.format(code=python_code)}])
if code_check_result.content == "GOOD":
code_interpreter_results = code_interpret(e2b_code_interpreter, python_code)
return code_interpreter_results,python_code
else:
print(f"Code syntax check failed, retrying... (attempt {attempt+1})")
else:
print(f"Failed to match any Python code in model's response {response_message}, retrying... (attempt {attempt+1})")
print("All attempts failed.")
return []
def chat_with_llm_rerun(e2b_code_interpreter, user_message,pre_code,pre_error, max_retries=1):
print(f"\n{'='*50}\nUser message: {user_message}\n{'='*50}")
new_user_message = f"""
{user_message}
The previous code you generated is:
{pre_code}
And it resulted in the following error:
{pre_error}
You need to carefully review the error message and fix the code accordingly. Give me the updated code. No comments or explanations.
"""
for attempt in range(max_retries + 1):
messages = [
{"role": "user", "content": new_user_message},
]
if attempt>0:
response_message = deepseek_v3.invoke(messages)
else:
response_message = qwen_flash.invoke(messages)
print(f"Model response: {response_message.content}\n{'='*50}")
python_code = match_code_blocks(response_message.content)
if python_code != "":
# 先进行语法校验
code_check_result = qwen_flash.invoke([{"role": "user", "content": code_check_template.format(code=python_code)}])
if code_check_result.content == "GOOD":
code_interpreter_results = code_interpret(e2b_code_interpreter, python_code)
return code_interpreter_results,python_code
else:
print(f"Code syntax check failed, retrying... (attempt {attempt+1})")
else:
print(f"Failed to match any Python code in model's response {response_message}, retrying... (attempt {attempt+1})")
print("All attempts failed.")
return []
2.3 沙箱运行code_interpret
- 如果代码运行失败会有报错信息,可以把报错信息也取出来,再让LLM重试修复代码
def code_interpret(e2b_code_interpreter, code):
print("Running code interpreter...")
exec = e2b_code_interpreter.run_code(
code,
on_stderr=lambda stderr: print("[Code Interpreter]", stderr),
on_stdout=lambda stdout: print("[Code Interpreter]", stdout)
)
if exec.error:
print("[Code Interpreter ERROR]", exec.error)
return [-1, exec.error]
else:
return [0, exec.results]
pattern = re.compile(
r"```python\n(.*?)\n```", re.DOTALL
) # Match everything in between ```python and ```
def match_code_blocks(llm_response):
match = pattern.search(llm_response)
if match:
code = match.group(1)
print(code)
return code
return ""
task = "Make a chart showing linear regression of the relationship between GDP per capita and life expectancy from the data."+\
" Filter out any missing values or values in wrong format."
with Sandbox.create(api_key=E2B_API_KEY) as code_interpreter:
# Upload the dataset to the code interpreter sandbox
remote_path = upload_dataset(code_interpreter, "data.csv")
data_desc = data_desc_template.format(data_online_path=remote_path.path)
task_prompt = task_template.format(data_desc=data_desc, task=task)
code_results,python_code = chat_with_llm(
code_interpreter,
task_prompt
)
if code_results[0]==0: # 执行成功
first_result = code_results[1][0]
else:
print('代码执行失败,重试一次')
code_results,python_code = chat_with_llm_rerun(
code_interpreter,
task_prompt,
python_code,
code_results[1].traceback,
max_retries=2
)
if code_results[0]==0: # 执行成功
first_result = code_results[1][0]
else:
raise Exception("No code interpreter results")
from PIL import Image
import io
import base64
# This will render the image if you're running this in a notebook environment.
# If you're running it as a script, you can save the image to a file using the Pillow library.
# save code_results[1][0]
# image = Image.fromarray(code_results[1][0])
if len(first_result.png)>0:
image_base64 = first_result.png
image = Image.open(io.BytesIO(base64.b64decode(image_base64)))
image.save("output_image.png")
# This will render the image if you're running this in a notebook environment.
# If you're running it as a script, you can save the image to a file using the Pillow library.
first_result
复杂一些的也可以画出来,例如
task = "Make a chart showing the top 5 countries by GDP in each region. Each region should have a different color."
with Sandbox.create(api_key=E2B_API_KEY) as code_interpreter:
# Upload the dataset to the code interpreter sandbox
remote_path = upload_dataset(code_interpreter, "data.csv")
data_desc = data_desc_template.format(data_online_path=remote_path.path)
task_prompt = task_template.format(data_desc=data_desc, task=task)
code_results,python_code = chat_with_llm(
code_interpreter,
task_prompt
)
if code_results[0]==0: # 执行成功
first_result = code_results[1][0]
else:
print('代码执行失败,重试一次')
code_results,python_code = chat_with_llm_rerun(
code_interpreter,
task_prompt,
python_code,
code_results[1].traceback,
max_retries=2
)
if code_results[0]==0: # 执行成功
first_result = code_results[1][0]
else:
raise Exception("No code interpreter results")
# This will render the image if you're running this in a notebook environment.
# If you're running it as a script, you can save the image to a file using the Pillow library.
first_result
更多推荐
所有评论(0)