feat: add chart visualization tools which support png/html output
This commit is contained in:
@@ -197,3 +197,6 @@ cython_debug/
|
||||
|
||||
# OSX
|
||||
.DS_Store
|
||||
|
||||
# node
|
||||
node_modules
|
||||
|
||||
+6
-1
@@ -8,6 +8,7 @@ from app.tool import Terminate, ToolCollection
|
||||
from app.tool.browser_use_tool import BrowserUseTool
|
||||
from app.tool.python_execute import PythonExecute
|
||||
from app.tool.str_replace_editor import StrReplaceEditor
|
||||
from app.tool.chart_visualization.chart_visualization import ChartVisualization
|
||||
|
||||
|
||||
class Manus(BrowserAgent):
|
||||
@@ -33,7 +34,11 @@ class Manus(BrowserAgent):
|
||||
# Add general-purpose tools to the tool collection
|
||||
available_tools: ToolCollection = Field(
|
||||
default_factory=lambda: ToolCollection(
|
||||
PythonExecute(), BrowserUseTool(), StrReplaceEditor(), Terminate()
|
||||
PythonExecute(),
|
||||
ChartVisualization(),
|
||||
BrowserUseTool(),
|
||||
StrReplaceEditor(),
|
||||
Terminate(),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
|
||||
|
||||
# Chart Visualization Tool
|
||||
|
||||
The chart visualization tool generates data processing code through Python and ultimately invokes [@visactor/vmind](https://github.com/VisActor/VMind) to obtain chart specifications. Chart rendering is implemented using [@visactor/vchart](https://github.com/VisActor/VChart).
|
||||
|
||||
## Installation
|
||||
|
||||
1. Install Node.js >= 18
|
||||
|
||||
```bash
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
|
||||
# After installation, restart the terminal and install the latest Node.js LTS version:
|
||||
nvm install --lts
|
||||
```
|
||||
|
||||
2. Install dependencies
|
||||
|
||||
```bash
|
||||
cd app/tool/chart_visualization
|
||||
npm install
|
||||
```
|
||||
|
||||
## Tool Parameters
|
||||
```typescript
|
||||
{
|
||||
// Generates Python code for data processing to produce a CSV file
|
||||
code: string;
|
||||
// Parses user intent to generate chart description
|
||||
chart_description: string;
|
||||
// Final output type (png/html). HTML supports VChart rendering and interaction
|
||||
output_type: 'png' | 'html'
|
||||
}
|
||||
```
|
||||
|
||||
## Output
|
||||
The final results will be saved locally in `png` or `html` format for subsequent use by agents.
|
||||
|
||||
## VMind Configuration
|
||||
|
||||
### LLM
|
||||
|
||||
VMind requires LLM invocation for intelligent chart generation. By default, it uses the `config.llm["default"]` configuration.
|
||||
|
||||
### Generation Settings
|
||||
|
||||
Main configurations include chart dimensions, theme, and generation method:
|
||||
### Generation Method
|
||||
Default: png. Currently supports automatic selection of `output_type` by LLM based on context.
|
||||
|
||||
### Dimensions
|
||||
Default dimensions are unspecified. For HTML output, charts fill the entire page by default. For PNG output, defaults to `1000*1000`.
|
||||
|
||||
### Theme
|
||||
Default theme: `'light'`. VChart supports multiple themes. See [Themes](https://www.visactor.io/vchart/guide/tutorial_docs/Theme/Theme_Extension).
|
||||
|
||||
## Testing
|
||||
|
||||
Two test tasks with different difficulty levels are provided:
|
||||
|
||||
### Basic Chart Generation Task
|
||||
|
||||
Generates charts from given data and specific requirements. Execute with:
|
||||
```bash
|
||||
python -m app.tool.chart_visualization.test.simple_chart
|
||||
```
|
||||
Results will be saved in `./data`, containing 9 different chart types.
|
||||
|
||||
### Simple Data Report Task
|
||||
|
||||
Processes raw data with basic analysis requirements. Execute with:
|
||||
```bash
|
||||
python -m app.tool.chart_visualization.test.simple_report
|
||||
```
|
||||
Results will also be saved in `./data`.
|
||||
@@ -0,0 +1,74 @@
|
||||
# 图表可视化工具
|
||||
|
||||
图表可视化工具,通过python生成数据处理代码,最终调用[@visactor/vmind](https://github.com/VisActor/VMind)得到图表的spec结果,图表渲染使用[@visactor/vchart](https://github.com/VisActor/VChart)
|
||||
|
||||
## 安装
|
||||
|
||||
1. 安装node >= 18
|
||||
|
||||
```bash
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
|
||||
# 安装完成后重启终端,然后安装 Node 最新 LTS 版本:
|
||||
nvm install --lts
|
||||
```
|
||||
|
||||
2. 安装依赖
|
||||
|
||||
```bash
|
||||
cd app/tool/chart_visualization
|
||||
npm install
|
||||
```
|
||||
|
||||
## 工具参数
|
||||
```typescript
|
||||
{
|
||||
// 用于生产数据处理的python代码,最终得到csv文件
|
||||
code: string;
|
||||
// 解析用户意图,得到图表描述
|
||||
chart_description: string;
|
||||
// 最终产物png或者html;html下支持vchart渲染和交互
|
||||
output_type: 'png' | 'html'
|
||||
}
|
||||
```
|
||||
|
||||
## 输出
|
||||
最终以'png'或者'html'的形式保存在本地,供后续agent使用
|
||||
|
||||
## VMind配置
|
||||
|
||||
### LLM
|
||||
|
||||
VMind本身也需要通过调用大模型得到智能图表生成结果,目前默认会使用`config.llm["default"]`配置
|
||||
|
||||
### 生成配置
|
||||
|
||||
主要生成配置包括图表的宽高、主题以及生成方式;
|
||||
### 生成方式
|
||||
默认为png,目前支持大模型根据上下文自己选择`output_type`
|
||||
|
||||
### 宽高
|
||||
目前默认不指定宽高,`html`下默认占满整个页面,'png'下默认为`1000 * 1000`
|
||||
|
||||
### 主题
|
||||
目前默认主题为`'light'`,VChart图表支持多种主题,详见[主题](https://www.visactor.io/vchart/guide/tutorial_docs/Theme/Theme_Extension)
|
||||
|
||||
|
||||
## 测试
|
||||
|
||||
当前设置了两种不能难度的任务用于测试
|
||||
|
||||
### 简单图表生成任务
|
||||
|
||||
给予数据和具体的图表生成需求,测试结果,执行命令:
|
||||
```bash
|
||||
python -m app.tool.chart_visualization.test.simple_chart
|
||||
```
|
||||
结果应位于`./data`下,涉及到9种不同的图表结果
|
||||
|
||||
### 简单数据报表任务
|
||||
|
||||
给予简单原始数据可分析需求,需要对数据进行简单加工处理,执行命令:
|
||||
```bash
|
||||
python -m app.tool.chart_visualization.test.simple_report
|
||||
```
|
||||
结果同样位于`./data`下
|
||||
@@ -0,0 +1,5 @@
|
||||
from app.tool.chart_visualization.chart_visualization import ChartVisualization
|
||||
|
||||
__all__ = [
|
||||
"ChartVisualization",
|
||||
]
|
||||
@@ -0,0 +1,230 @@
|
||||
import subprocess
|
||||
import json
|
||||
import threading
|
||||
import base64
|
||||
import pandas as pd
|
||||
import aiofiles
|
||||
import os
|
||||
from typing import Any, Dict, Hashable
|
||||
from app.tool.base import BaseTool
|
||||
from app.config import config
|
||||
|
||||
|
||||
def extract_executable_code(code_str: str) -> str:
|
||||
"""
|
||||
Extract executable code from function call's parameters
|
||||
|
||||
Args:
|
||||
code_str (string): The python code generated by llm.
|
||||
|
||||
Returns:
|
||||
String: Python code can execute directly.
|
||||
"""
|
||||
lines = code_str.strip().splitlines()
|
||||
start_idx = -1
|
||||
end_idx = -1
|
||||
|
||||
# Find first occurrence of ```
|
||||
for i, line in enumerate(lines):
|
||||
if "```" in line.strip() or '"""' in line.strip():
|
||||
start_idx = i
|
||||
break
|
||||
|
||||
# Find last occurrence of ```
|
||||
for i in reversed(range(len(lines))):
|
||||
if "```" in line.strip() or '"""' in line.strip():
|
||||
end_idx = i
|
||||
break
|
||||
|
||||
if start_idx != -1 and end_idx != -1 and start_idx < end_idx:
|
||||
lines = lines[start_idx + 1 : end_idx]
|
||||
elif start_idx != -1:
|
||||
lines = lines[start_idx + 1 :]
|
||||
elif end_idx != -1:
|
||||
lines = lines[:end_idx]
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
class ChartVisualization(BaseTool):
|
||||
name: str = "generate_data_visualization"
|
||||
description: str = """Visualize a statistical chart using csv data and chart description. The tool accepts code to generate csv data and description of the chart, and output a chart in png or html.
|
||||
Note: Each tool call generates a single chart.
|
||||
"""
|
||||
parameters: dict = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "string",
|
||||
"description": """Python code template EXCLUSIVELY for CSV generation. MUST CONTAIN:
|
||||
1. Data loading logic (handle dataframe/dict/file/url/json)
|
||||
2. Data processing (cleaning/transformation)
|
||||
3. CSV saving with path print (Only csv path)
|
||||
""",
|
||||
# example
|
||||
"examples": [
|
||||
"""import pandas as pd
|
||||
# Create safe output directory if not exists
|
||||
output_dir = './data/chart_generation_temp'
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# Data loading
|
||||
# Add your data loding logic here
|
||||
|
||||
# Data processing placeholder
|
||||
# Add your cleaning/transformation logic here
|
||||
|
||||
# Final: Generic output handling
|
||||
output_path = os.path.join(output_dir, 'csv_file_name.csv')
|
||||
df.to_csv(output_path, index=False)
|
||||
|
||||
print(output_path)"""
|
||||
],
|
||||
},
|
||||
"chart_description": {
|
||||
"type": "string",
|
||||
"description": "The chart title or description should be concise and clear",
|
||||
"examples": ["Product sales distribution", "Monthly revenue trend"],
|
||||
},
|
||||
"output_type": {
|
||||
"description": "Rendering format (html=interactive)",
|
||||
"type": "string",
|
||||
"default": "html",
|
||||
"enum": ["png", "html"],
|
||||
},
|
||||
},
|
||||
"required": ["code", "chart_description"],
|
||||
}
|
||||
llm: dict = config.llm["default"]
|
||||
|
||||
async def execute(self, code: str, chart_description: str, output_type: str) -> str:
|
||||
code_result = await self.execute_code(code=code)
|
||||
if "success" in code_result and code_result["success"] is False:
|
||||
return code_result
|
||||
if code_result["observation"].startswith("Error"):
|
||||
return {"observation": code_result["observation"], "success": False}
|
||||
|
||||
try:
|
||||
data_path = (
|
||||
code_result["observation"].replace("\n", "").replace("\r", "").strip()
|
||||
)
|
||||
if not data_path.endswith(".csv"):
|
||||
return {
|
||||
"observation": "Error: Code should ONLY output CSV data path",
|
||||
"success": False,
|
||||
}
|
||||
df = pd.read_csv(data_path)
|
||||
df = df.astype(object)
|
||||
df = df.where(pd.notnull(df), None)
|
||||
data_dict_list = df.to_json(orient="records", force_ascii=False)
|
||||
result = await self.invoke_vmind(
|
||||
data_dict_list, chart_description, output_type
|
||||
)
|
||||
if "error" in result:
|
||||
return {
|
||||
"observation": f"Error: {result["error"]}",
|
||||
"success": False,
|
||||
}
|
||||
chart_file_path = data_path.replace(".csv", f".{output_type}")
|
||||
while os.path.exists(chart_file_path):
|
||||
chart_file_path = chart_file_path.replace(
|
||||
f".{output_type}", f"_new.{output_type}"
|
||||
)
|
||||
if output_type == "png":
|
||||
byte_data = base64.b64decode(result["res"])
|
||||
async with aiofiles.open(chart_file_path, "wb") as file:
|
||||
await file.write(byte_data)
|
||||
else:
|
||||
async with aiofiles.open(
|
||||
chart_file_path, "w", encoding="utf-8"
|
||||
) as file:
|
||||
await file.write(result["res"])
|
||||
return {"observation": f"chart successfully saved to {chart_file_path}"}
|
||||
except Exception as e:
|
||||
return {
|
||||
"observation": f"Error: {e}",
|
||||
"success": False,
|
||||
}
|
||||
|
||||
async def execute_code(
|
||||
self,
|
||||
code: str,
|
||||
timeout: int = 5,
|
||||
) -> Dict:
|
||||
"""
|
||||
Executes the provided Python code with a timeout.
|
||||
|
||||
Args:
|
||||
code (str): The Python code to execute.
|
||||
timeout (int): Execution timeout in seconds.
|
||||
|
||||
Returns:
|
||||
Dict: Contains 'output' with execution output or error message and 'success' status.
|
||||
"""
|
||||
result = {"observation": ""}
|
||||
be_extracted_code = extract_executable_code(code)
|
||||
|
||||
def run_code():
|
||||
try:
|
||||
safe_globals = {"__builtins__": dict(__builtins__)}
|
||||
|
||||
import sys
|
||||
from io import StringIO
|
||||
|
||||
output_buffer = StringIO()
|
||||
sys.stdout = output_buffer
|
||||
|
||||
exec(be_extracted_code, safe_globals, {})
|
||||
|
||||
sys.stdout = sys.__stdout__
|
||||
|
||||
result["observation"] = output_buffer.getvalue()
|
||||
|
||||
except Exception as e:
|
||||
result["observation"] = str(e)
|
||||
result["success"] = False
|
||||
|
||||
thread = threading.Thread(target=run_code)
|
||||
thread.start()
|
||||
thread.join(timeout)
|
||||
|
||||
if thread.is_alive():
|
||||
return {
|
||||
"observation": f"Execution timeout after {timeout} seconds",
|
||||
"success": False,
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
async def invoke_vmind(
|
||||
self,
|
||||
dict_data: list[dict[Hashable, Any]],
|
||||
chart_description: str,
|
||||
output_type: str,
|
||||
):
|
||||
vmind_options = {
|
||||
"url": self.llm.base_url + "/chat/completions",
|
||||
"model": self.llm.model,
|
||||
"headers": {
|
||||
"api-key": self.llm.api_key,
|
||||
"Authorization": f"Bearer {self.llm.api_key}",
|
||||
},
|
||||
}
|
||||
vmind_params = {
|
||||
"options": vmind_options,
|
||||
"user_prompt": chart_description,
|
||||
"dataset": dict_data,
|
||||
"output_type": output_type,
|
||||
}
|
||||
process = subprocess.run(
|
||||
["npx", "ts-node", "src/chartVisualize.ts"],
|
||||
input=json.dumps(vmind_params),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
cwd=os.path.dirname(__file__),
|
||||
)
|
||||
if process.returncode == 0:
|
||||
return json.loads(process.stdout)
|
||||
else:
|
||||
return {"error": f"Node.js Error: {process.stderr}"}
|
||||
+8221
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "chart_visualization",
|
||||
"version": "1.0.0",
|
||||
"main": "src/index.ts",
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.10.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@visactor/vchart": "^1.13.7",
|
||||
"@visactor/vmind": "^2.0.4",
|
||||
"canvas": "^2.11.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": ""
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
import Canvas from "canvas";
|
||||
import path from "path";
|
||||
import { readFileSync } from "fs";
|
||||
import VMind from "@visactor/vmind";
|
||||
import { getFieldInfoFromDataset } from "@visactor/vmind/cjs/utils/field.js";
|
||||
import VChart from "@visactor/vchart";
|
||||
import { isString } from "@visactor/vutils";
|
||||
|
||||
const getBase64 = async (spec: any, width?: number, height?: number) => {
|
||||
spec.animation = false;
|
||||
width && (spec.width = width);
|
||||
height && (spec.height = height);
|
||||
const cs = new VChart(spec, {
|
||||
mode: "node",
|
||||
modeParams: Canvas,
|
||||
animation: false,
|
||||
dpr: 2,
|
||||
});
|
||||
|
||||
await cs.renderAsync();
|
||||
|
||||
const buffer = await cs.getImageBuffer();
|
||||
return Buffer.from(buffer, "utf8").toString("base64");
|
||||
};
|
||||
|
||||
const serializeSpec = (spec: any) => {
|
||||
return JSON.stringify(spec, (key, value) => {
|
||||
if (typeof value === "function") {
|
||||
const funcStr = value
|
||||
.toString()
|
||||
.replace(/(\r\n|\n|\r)/gm, "")
|
||||
.replace(/\s+/g, " ");
|
||||
|
||||
return `__FUNCTION__${funcStr}`;
|
||||
}
|
||||
return value;
|
||||
});
|
||||
};
|
||||
|
||||
async function getHtmlVChart(spec: any, width: number, height: number) {
|
||||
return `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>VChart 示例</title>
|
||||
<script src="${path.join(
|
||||
__dirname,
|
||||
"../node_modules/@visactor/vchart/build/index.min.js"
|
||||
)}"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="chart-container" style="width: ${
|
||||
width ? `${width}px` : "100%"
|
||||
}; height: ${height ? `${height}px` : "100%"};"></div>
|
||||
<script>
|
||||
// parse spec with function
|
||||
function parseSpec(stringSpec) {
|
||||
return JSON.parse(stringSpec, (k, v) => {
|
||||
if (typeof v === 'string' && v.startsWith('__FUNCTION__')) {
|
||||
const funcBody = v.slice(12); // 移除标记
|
||||
try {
|
||||
return new Function('return (' + funcBody + ')')();
|
||||
} catch(e) {
|
||||
console.error('函数解析失败:', e);
|
||||
return () => {};
|
||||
}
|
||||
}
|
||||
return v;
|
||||
});
|
||||
}
|
||||
const spec = parseSpec('${serializeSpec(spec)}');
|
||||
const chart = new VChart.VChart(spec, {
|
||||
dom: 'chart-container'
|
||||
});
|
||||
chart.renderSync();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
}
|
||||
|
||||
async function generateChart() {
|
||||
const inputData = JSON.parse(readFileSync(process.stdin.fd, "utf-8"));
|
||||
try {
|
||||
const {
|
||||
options,
|
||||
user_prompt: userPrompt,
|
||||
dataset,
|
||||
output_type: outputType = "png",
|
||||
width,
|
||||
height,
|
||||
} = inputData;
|
||||
const vmind = new VMind(options);
|
||||
const jsonDataset = isString(dataset) ? JSON.parse(dataset) : dataset;
|
||||
const { spec, error, vizSchema, cell } = await vmind.generateChart(
|
||||
userPrompt,
|
||||
getFieldInfoFromDataset(jsonDataset),
|
||||
jsonDataset,
|
||||
{
|
||||
enableDataQuery: false,
|
||||
theme: "light",
|
||||
}
|
||||
);
|
||||
if (error || !spec) {
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
error: error || "Spec of Chart was Empty!",
|
||||
options,
|
||||
userPrompt,
|
||||
fieldInfo: getFieldInfoFromDataset(jsonDataset),
|
||||
dataset: jsonDataset,
|
||||
op: {
|
||||
enableDataQuery: false,
|
||||
theme: "light",
|
||||
},
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (outputType === "png") {
|
||||
console.log(
|
||||
JSON.stringify({ res: await getBase64(spec, width, height) })
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
JSON.stringify({ res: await getHtmlVChart(spec, width, height) })
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(JSON.stringify({ error }));
|
||||
}
|
||||
}
|
||||
|
||||
async function test() {
|
||||
const inputData = {
|
||||
options: {
|
||||
url: "https://ark.cn-beijing.volces.com/api/v3/chat/completions",
|
||||
model: "ep-20250218181138-t86qb",
|
||||
headers: {
|
||||
"api-key": "5165caca-92dc-4c6b-936f-517a00d4aae3",
|
||||
Authorization: "Bearer 5165caca-92dc-4c6b-936f-517a00d4aae3",
|
||||
},
|
||||
},
|
||||
user_prompt: "帮我展示不同区域各商品销售额",
|
||||
fieldInfo: [
|
||||
{
|
||||
fieldName: "商品名称",
|
||||
type: "string",
|
||||
role: "dimension",
|
||||
domain: ["可乐", "雪碧", "芬达", "醒目"],
|
||||
},
|
||||
{
|
||||
fieldName: "region",
|
||||
type: "string",
|
||||
role: "dimension",
|
||||
domain: ["south", "east", "west", "north"],
|
||||
},
|
||||
{
|
||||
fieldName: "销售额",
|
||||
type: "int",
|
||||
role: "measure",
|
||||
domain: [28, 2350],
|
||||
},
|
||||
],
|
||||
dataset: [
|
||||
{ 商品名称: "可乐", region: "south", 销售额: 2350 },
|
||||
{ 商品名称: "可乐", region: "east", 销售额: 1027 },
|
||||
{ 商品名称: "可乐", region: "west", 销售额: 1027 },
|
||||
{ 商品名称: "可乐", region: "north", 销售额: 1027 },
|
||||
{ 商品名称: "雪碧", region: "south", 销售额: 215 },
|
||||
{ 商品名称: "雪碧", region: "east", 销售额: 654 },
|
||||
{ 商品名称: "雪碧", region: "west", 销售额: 159 },
|
||||
{ 商品名称: "雪碧", region: "north", 销售额: 28 },
|
||||
{ 商品名称: "芬达", region: "south", 销售额: 345 },
|
||||
{ 商品名称: "芬达", region: "east", 销售额: 654 },
|
||||
{ 商品名称: "芬达", region: "west", 销售额: 2100 },
|
||||
{ 商品名称: "芬达", region: "north", 销售额: 1679 },
|
||||
{ 商品名称: "醒目", region: "south", 销售额: 1476 },
|
||||
{ 商品名称: "醒目", region: "east", 销售额: 830 },
|
||||
{ 商品名称: "醒目", region: "west", 销售额: 532 },
|
||||
{ 商品名称: "醒目", region: "north", 销售额: 498 },
|
||||
],
|
||||
output_type: "html",
|
||||
};
|
||||
const vmind2 = new VMind(inputData.options);
|
||||
const res = await vmind2.generateChart(
|
||||
inputData.user_prompt,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
inputData.fieldInfo as any,
|
||||
inputData.dataset,
|
||||
{
|
||||
enableDataQuery: false,
|
||||
theme: "light",
|
||||
}
|
||||
);
|
||||
console.log(res);
|
||||
}
|
||||
// test();
|
||||
generateChart();
|
||||
@@ -0,0 +1,190 @@
|
||||
import asyncio
|
||||
|
||||
from app.agent.manus import Manus
|
||||
from app.logger import logger
|
||||
|
||||
prefix = "帮我生成图表并保存在本地./data下,具体为:"
|
||||
tasks = [
|
||||
{
|
||||
"prompt": "帮我展示不同区域各商品销售额",
|
||||
"data": """商品名称,region,销售额
|
||||
可乐,south,2350
|
||||
可乐,east,1027
|
||||
可乐,west,1027
|
||||
可乐,north,1027
|
||||
雪碧,south,215
|
||||
雪碧,east,654
|
||||
雪碧,west,159
|
||||
雪碧,north,28
|
||||
芬达,south,345
|
||||
芬达,east,654
|
||||
芬达,west,2100
|
||||
芬达,north,1679
|
||||
醒目,south,1476
|
||||
醒目,east,830
|
||||
醒目,west,532
|
||||
醒目,north,498
|
||||
""",
|
||||
},
|
||||
{
|
||||
"prompt": "展示各品牌市场占有率",
|
||||
"data": """品牌名称,市场份额,平均价格,净利润
|
||||
Apple,0.5,7068,314531
|
||||
Samsung,0.2,6059,362345
|
||||
Vivo,0.05,3406,234512
|
||||
Nokia,0.01,1064,-1345
|
||||
Xiaomi,0.1,4087,131345""",
|
||||
},
|
||||
{
|
||||
"prompt": "请帮我展示各产品的销售趋势",
|
||||
"data": """date,type,value
|
||||
2023-01-01,产品 A,52.9
|
||||
2023-01-01,产品 B,63.6
|
||||
2023-01-01,产品 C,11.2
|
||||
2023-01-02,产品 A,45.7
|
||||
2023-01-02,产品 B,89.1
|
||||
2023-01-02,产品 C,21.4
|
||||
2023-01-03,产品 A,67.2
|
||||
2023-01-03,产品 B,82.4
|
||||
2023-01-03,产品 C,31.7
|
||||
2023-01-04,产品 A,80.7
|
||||
2023-01-04,产品 B,55.1
|
||||
2023-01-04,产品 C,21.1
|
||||
2023-01-05,产品 A,65.6
|
||||
2023-01-05,产品 B,78
|
||||
2023-01-05,产品 C,31.3
|
||||
2023-01-06,产品 A,75.6
|
||||
2023-01-06,产品 B,89.1
|
||||
2023-01-06,产品 C,63.5
|
||||
2023-01-07,产品 A,67.3
|
||||
2023-01-07,产品 B,77.2
|
||||
2023-01-07,产品 C,43.7
|
||||
2023-01-08,产品 A,96.1
|
||||
2023-01-08,产品 B,97.6
|
||||
2023-01-08,产品 C,59.9
|
||||
2023-01-09,产品 A,96.1
|
||||
2023-01-09,产品 B,100.6
|
||||
2023-01-09,产品 C,66.8
|
||||
2023-01-10,产品 A,101.6
|
||||
2023-01-10,产品 B,108.3
|
||||
2023-01-10,产品 C,56.9 """,
|
||||
},
|
||||
{
|
||||
"prompt": "展示搜索关键词热度",
|
||||
"data": """关键词,热度
|
||||
热词,1000
|
||||
燥了我们,800
|
||||
娆贱货,400
|
||||
我的心愿是世界和平,400
|
||||
咻咻咻,400
|
||||
神舟十一号,400
|
||||
百鸟朝风,400
|
||||
中国女排,400
|
||||
我的关呐,400
|
||||
腿咚,400
|
||||
火锅英雄,400
|
||||
宝宝心里苦,400
|
||||
奥运会,400
|
||||
厉害了我的哥,400
|
||||
诗和远方,400
|
||||
宋仲基,400
|
||||
PPAP,400
|
||||
蓝瘦香菇,400
|
||||
雨露均沾,400
|
||||
友谊的小船说翻就翻就翻,400
|
||||
北京瘫,400
|
||||
敬业,200
|
||||
Apple,200
|
||||
狗带,200
|
||||
老司机,200
|
||||
吃瓜群众,200
|
||||
疯狂动物城,200
|
||||
城会玩,200
|
||||
套路,200
|
||||
水逆,200
|
||||
你咋不上天呢,200
|
||||
蛇精男,200
|
||||
你咋不上天呢,200
|
||||
三星爆炸门,200
|
||||
小李子奥斯卡,200
|
||||
人丑就要多读书,200
|
||||
男友力,200
|
||||
一脸懵逼,200
|
||||
太阳的后裔,200""",
|
||||
},
|
||||
{
|
||||
"prompt": "帮我比较不同电动汽车品牌性能,使用散点图",
|
||||
"data": """续航里程,充电时间,品牌名称,平均价格
|
||||
2904,46,品牌1,2350
|
||||
1231,146,品牌2,1027
|
||||
5675,324,品牌3,1242
|
||||
543,57,品牌4,6754
|
||||
326,234,品牌5,215
|
||||
1124,67,品牌6,654
|
||||
3426,81,品牌7,159
|
||||
2134,24,品牌8,28
|
||||
1234,52,品牌9,345
|
||||
2345,27,品牌10,654
|
||||
526,145,品牌11,2100
|
||||
234,93,品牌12,1679
|
||||
567,94,品牌13,1476
|
||||
789,45,品牌14,830
|
||||
469,75,品牌15,532
|
||||
5689,54,品牌16,498
|
||||
""",
|
||||
},
|
||||
{
|
||||
"prompt": "展示各个流程转化率",
|
||||
"data": """流程,转化率,Month
|
||||
Step1,100,1
|
||||
Step2,80,1
|
||||
Step3,60,1
|
||||
Step4,40,1""",
|
||||
},
|
||||
{
|
||||
"prompt": "展示男女早餐饭量不同",
|
||||
"data": """时间,男-早餐,女-早餐
|
||||
周一,15,22
|
||||
周二,12,10
|
||||
周三,15,20
|
||||
周四,10,12
|
||||
周五,13,15
|
||||
周六,10,15
|
||||
周日,12,14""",
|
||||
},
|
||||
{
|
||||
"prompt": "帮我展示这个人在不同方面的绩效,他是否是六边形战士",
|
||||
"data": """dimension,performance
|
||||
Strength,5
|
||||
Speed,5
|
||||
Shooting,3
|
||||
Endurance,5
|
||||
Precision,5
|
||||
Growth,5""",
|
||||
},
|
||||
{
|
||||
"prompt": "展示数据流动",
|
||||
"data": """始发地,终点站,value
|
||||
Node A,Node 1,10
|
||||
Node A,Node 2,5
|
||||
Node B,Node 2,8
|
||||
Node B,Node 3,2
|
||||
Node C,Node 2,4
|
||||
Node A,Node C,2
|
||||
Node C,Node 1,2""",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
async def main():
|
||||
for index, item in enumerate(tasks):
|
||||
logger.info(f"Begin task {index} / {len(tasks)}!")
|
||||
agent = Manus()
|
||||
await agent.run(
|
||||
f"{prefix},chart_description:{item["prompt"]},Data:{item["data"]}"
|
||||
)
|
||||
logger.info(f"Finish with {item["prompt"]}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,21 @@
|
||||
import asyncio
|
||||
|
||||
from app.agent.manus import Manus
|
||||
|
||||
|
||||
async def main():
|
||||
agent = Manus()
|
||||
await agent.run(
|
||||
"""分析以下数据并生成一个图文数据报告在本地./data文件夹下,格式为html.Requriment:展示3个团队半年内的人效变化,并且将相邻两个月各团队的环比上升或者下降的比例体现出来
|
||||
Data:月份 团队A 团队B 团队C
|
||||
1月 1200小时 1350小时 1100小时
|
||||
2月 1250小时 1400小时 1150小时
|
||||
3月 1180小时 1300小时 1300小时
|
||||
4月 1220小时 1280小时 1400小时
|
||||
5月 1230小时 1320小时 1450小时
|
||||
6月 1200小时 1250小时 1500小时"""
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
],
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
/* Language and Environment */
|
||||
"target": "ES2021", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
/* Modules */
|
||||
"module": "commonjs", /* Specify what module code is generated. */
|
||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"src/types"
|
||||
], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
||||
// "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */
|
||||
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||
// "noUncheckedSideEffectImports": true, /* Check side effect imports. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
/* JavaScript Support */
|
||||
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
"checkJs": false, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
||||
// "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user