使用 Coze 工作流开发科研数据可视化智能体:从文件上传到柱状图展示
1. 项目背景
在科研写作过程中,经常需要把实验结果表格转换为可视化图表,例如:
- 不同数据集上的 Precision、Recall、F1-Score 对比;
- 不同方法的性能指标对比;
- 不同类别的分类效果展示;
- 论文实验结果的柱状图、折线图或雷达图生成。
传统做法通常需要手动整理 Excel、Python 画图、导出图片,再插入论文或报告中。为了减少重复操作,可以使用 Coze 搭建一个“数据可视化智能体”,让用户上传实验结果文件后,自动完成数据解析、指标抽取和图表生成。
本文介绍一个基于 Coze 工作流实现的科研数据可视化智能体,核心流程如下:
上传数据文件
→ 文本提取
→ 数据整理
→ Python 代码生成图表 URL
→ 结束节点输出
→ 页面图片组件展示
Coze 的低代码工作流支持通过可视化画布编排节点,开始节点和结束节点用于定义工作流的输入输出,代码节点可以编写自定义逻辑并返回结果,用户界面编辑器则可以拖拽组件并绑定工作流数据。(扣子)
2. 智能体功能目标
本智能体的目标是:
用户上传一个包含实验指标的数据文件,智能体自动读取其中的类别、Precision、Recall、F1-Score,并生成对应的柱状图,在页面中直接预览。
例如,用户上传的数据内容可能是:
Dataset,TON-loT
Class,Precision,Recall,F1-Score
Backdoor,1.00,0.99,0.99
DDoS,0.90,0.87,0.88
DoS,0.99,0.88,0.83
injection,0.77,0.76,0.68
mitm,0.61,0.72,0.69
password,0.82,0.88,0.85
ransomware,0.75,0.69,0.72
scanning,0.98,0.78,0.87
xss,0.95,0.86,0.90
系统最终生成一张分组柱状图:
-
横轴:类别名称;
-
纵轴:指标得分;
-
每个类别包含三根柱子:
- Precision;
- Recall;
- F1-Score。
3. 整体架构设计
当前工作流可以拆成 5 个核心模块:
开始节点
↓
文本提取节点
↓
数据整理节点
↓
代码节点
↓
结束节点
对应的功能如下:
| 节点 | 作用 |
|---|---|
| 开始节点 | 接收用户上传的文档或文件 |
| 文本提取节点 | 从 docx、pdf、txt、csv 等文件中提取文本内容 |
| 数据整理节点 | 使用大模型把原始文本整理成结构化 CSV |
| 代码节点 | 使用 Python 解析 CSV,并生成柱状图 URL |
| 结束节点 | 输出 Markdown 结果和纯图片 URL |
4. 工作流节点配置
4.1 开始节点配置
开始节点用于定义整个工作流需要接收哪些输入。官方文档中说明,开始节点负责配置低代码工作流启动时所需的信息,结束节点负责输出工作流结果。(扣子)
本项目中,开始节点建议只保留一个输入参数:
| 参数名 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| input | Doc / File | 是 | 用户上传的数据文件 |
如果你的 Coze 页面中使用的是文件上传组件,则在用户界面中需要将上传组件的 value 绑定到这个参数。文件上传组件的值可以传递给工作流的文件类型入参,系统会自动解析并获取文件 URL,再传递给下游节点。(扣子)
推荐配置:
参数名:input
参数类型:Doc 或 File
必填:开启
4.2 文本提取节点配置
文本提取节点负责从上传的文件中读取文本内容。
输入配置:
url / file / input:引用开始节点 input
输出一般包括:
message:提取出来的文本内容
如果上传的是 .docx、.txt、.csv 或论文实验结果表格,文本提取节点会把其中内容转换为后续大模型节点可以处理的文本。
4.3 数据整理节点配置
数据整理节点的作用是把文本提取结果整理成统一格式。
输入:
input = 文本提取节点.message
推荐使用大模型节点进行结构化整理。Coze 的大模型节点可以根据输入参数和提示词生成回复,适合做文本抽取、格式转换和结构化输出。(扣子)
推荐提示词
你是一个科研实验数据整理助手。
你的任务是从用户上传的文本中提取分类实验指标,并整理为标准 CSV 格式。
请只输出如下 JSON,不要输出解释、说明或 Markdown:
{
"input": "Dataset,数据集名称\nClass,Precision,Recall,F1-Score\n类别1,precision,recall,f1\n类别2,precision,recall,f1"
}
要求:
1. 如果文本中存在 Dataset 或数据集名称,请填入 Dataset 行;
2. 如果没有数据集名称,则 Dataset 填写 Unknown;
3. 只保留 Class、Precision、Recall、F1-Score 四类指标;
4. 数值保留原始小数格式;
5. 不要添加额外字段;
6. 不要输出代码块;
7. 不要输出自然语言解释。
推荐输出格式
{
"input": "Dataset,TON-loT\nClass,Precision,Recall,F1-Score\nBackdoor,1.00,0.99,0.99\nDDoS,0.90,0.87,0.88\nDoS,0.99,0.88,0.83"
}
这样做的好处是:后续代码节点只需要处理统一格式,不需要面对复杂的原始文档结构。
4.4 代码节点配置
代码节点用于解析大模型输出,并生成图表链接。Coze 的代码节点支持通过编写代码来生成返回值,适合处理输入参数并输出结构化结果。(扣子)
输入配置
| 参数名 | 类型 | 绑定来源 |
|---|---|---|
| input | String | 数据整理节点.output 或 reasoning_content |
输出配置
建议配置两个输出:
| 输出变量 | 类型 | 作用 |
|---|---|---|
| output | String | Markdown 图片展示内容 |
| chart_url | String | 纯图片 URL,供图片组件绑定 |
这里一定要注意:
output可以是;chart_url必须是纯 URL;- 图片组件要绑定
chart_url,不要绑定 Markdown 字符串。
5. 代码节点完整 Python 代码
下面代码可以同时兼容两种常见输入格式。
格式一:标准 CSV
Dataset,Class,Precision,Recall,F1-Score
TON-loT,Backdoor,1.00,0.99,0.99
TON-loT,DDoS,0.90,0.87,0.88
格式二:两段式 CSV
Dataset,TON-loT
Class,Precision,Recall,F1-Score
Backdoor,1.00,0.99,0.99
DDoS,0.90,0.87,0.88
Python 代码
import json
import re
import csv
from io import StringIO
from urllib.parse import quote
async def main(args: Args) -> Output:
try:
params = args.params or {}
raw_input = params.get("input", "")
# 1. 兼容输入为 dict、字符串、JSON 字符串
if isinstance(raw_input, dict):
csv_text = raw_input.get("input", "")
else:
csv_text = raw_input
if not isinstance(csv_text, str):
csv_text = str(csv_text or "")
csv_text = csv_text.strip()
# 2. 去掉可能的大模型 Markdown 代码块
csv_text = csv_text.replace("```json", "").replace("```csv", "").replace("```", "").strip()
# 3. 如果是 JSON 字符串,提取其中的 input 字段
if csv_text.startswith("{") and csv_text.endswith("}"):
try:
obj = json.loads(csv_text)
if isinstance(obj, dict) and "input" in obj:
csv_text = obj.get("input", "")
except Exception:
pass
# 4. 处理转义换行
csv_text = csv_text.replace("\n", "\n").replace('\"', '"').strip()
if not csv_text:
return {
"output": "未接收到有效输入。",
"chart_url": ""
}
def parse_value(val):
if val is None:
return 0.0
s = str(val).strip()
if not s:
return 0.0
if s.endswith("%"):
try:
return float(s[:-1]) / 100.0
except Exception:
return 0.0
try:
return float(s)
except Exception:
return 0.0
def clean_key(key):
key = str(key or "").replace("\ufeff", "").strip().lower()
key = re.sub(r"[^a-z0-9]+", "", key)
return key
lines = [line.strip() for line in csv_text.split("\n") if line.strip()]
rows = []
dataset_name = "Unknown"
# 5. 兼容两段式格式:
# Dataset,TON-loT
# Class,Precision,Recall,F1-Score
# Backdoor,1.00,0.99,0.99
if len(lines) >= 3 and lines[0].lower().startswith("dataset,") and lines[1].lower().startswith("class,"):
first_row = next(csv.reader([lines[0]]))
if len(first_row) > 1:
dataset_name = first_row[1].strip() or "Unknown"
headers = next(csv.reader([lines[1]]))
data_lines = lines[2:]
for line in data_lines:
values = next(csv.reader([line]))
while len(values) < len(headers):
values.append("")
row = dict(zip(headers, values))
rows.append(row)
# 6. 兼容标准 CSV 格式:
# Dataset,Class,Precision,Recall,F1-Score
# TON-loT,Backdoor,1.00,0.99,0.99
else:
reader = csv.DictReader(StringIO(csv_text))
rows = list(reader)
if not rows:
return {
"output": "未解析到有效数据。",
"chart_url": ""
}
labels = []
precision_data = []
recall_data = []
f1_data = []
for row in rows:
normalized = {}
for k, v in row.items():
normalized[clean_key(k)] = v
current_dataset = normalized.get("dataset", "")
if current_dataset:
dataset_name = str(current_dataset).strip()
class_name = (
normalized.get("class")
or normalized.get("category")
or normalized.get("label")
or ""
)
class_name = str(class_name).strip()
if not class_name:
continue
class_key = re.sub(r"[^a-z0-9]+", "", class_name.lower())
if class_key in ["overall", "accuracy", "macroavg", "weightedavg"]:
continue
labels.append(class_name)
precision_data.append(parse_value(normalized.get("precision", 0)))
recall_data.append(parse_value(normalized.get("recall", 0)))
f1_data.append(parse_value(normalized.get("f1score", normalized.get("f1", 0))))
if not labels:
return {
"output": "未解析到类别和指标数据,请检查输入中是否包含 Class、Precision、Recall、F1-Score。",
"chart_url": ""
}
chart_config = {
"type": "bar",
"data": {
"labels": labels,
"datasets": [
{
"label": "Precision",
"data": precision_data
},
{
"label": "Recall",
"data": recall_data
},
{
"label": "F1-Score",
"data": f1_data
}
]
},
"options": {
"responsive": True,
"plugins": {
"title": {
"display": True,
"text": f"{dataset_name} - Classification Metrics"
},
"legend": {
"position": "top"
}
},
"scales": {
"y": {
"beginAtZero": True,
"max": 1,
"title": {
"display": True,
"text": "Score"
}
},
"x": {
"ticks": {
"maxRotation": 45,
"minRotation": 45
},
"title": {
"display": True,
"text": "Class"
}
}
}
}
}
config_str = json.dumps(chart_config, ensure_ascii=False, separators=(",", ":"))
chart_url = (
"https://quickchart.io/chart"
"?width=1200"
"&height=600"
"&format=png"
"&backgroundColor=white"
"&c="
+ quote(config_str, safe="")
)
return {
"output": f"",
"chart_url": chart_url
}
except Exception as e:
return {
"output": f"生成柱状图失败:{str(e)}",
"chart_url": ""
}
6. 结束节点配置
结束节点负责把代码节点的结果返回给页面。
建议结束节点配置两个输出字段:
| 输出名 | 类型 | 绑定来源 |
|---|---|---|
| output | String | 代码节点.output |
| chart_url | String | 代码节点.chart_url |
配置完成后,整个工作流最终会输出:
{
"output": "",
"chart_url": "https://quickchart.io/chart?..."
}
其中:
output适合绑定 Markdown 或文本组件;chart_url适合绑定图片组件。
7. 用户界面设计
在 Coze 的用户界面中,可以设计一个简单页面:
文件上传组件
↓
生成柱状图按钮
↓
图片预览组件
用户界面编辑器支持通过拖放组件构建页面,并设置组件属性和样式;文件上传组件可以作为组件值传递给工作流输入。(扣子)
7.1 文件上传组件配置
组件名称建议:
FileUpload1
配置建议:
| 配置项 | 推荐值 |
|---|---|
| 支持格式 | doc、docx、txt、csv、pdf |
| 最大数量 | 1 |
| 是否必填 | 是 |
| 文案 | 请上传实验数据文件 |
7.2 按钮组件配置
按钮名称建议:
Button1
按钮文案:
生成柱状图
按钮事件配置:
事件类型:点击时
执行动作:调用工作流
Workflow:shujukeshihua
工作流入参配置:
| 工作流参数 | 绑定值 |
|---|---|
| input | FileUpload1.value |
注意,这里不要绑定:
Button1.content
也不要绑定:
FileUpload1
而是要绑定:
FileUpload1.value
原因是 FileUpload1 表示整个上传组件对象,而 FileUpload1.value 才是用户上传文件对应的值。
如果按钮在工作流运行过程中可以被重复点击,建议将按钮的禁用态和加载态绑定到工作流 loading 状态,避免重复触发。Coze 的 UI Builder FAQ 也建议内容生成类场景为生成按钮绑定工作流 loading 值。(扣子)
7.3 图片组件配置
组件名称建议:
Image1
图片组件属性配置:
| 配置项 | 推荐值 |
|---|---|
| 来源 | 绑定数据 |
| 绑定值 | 工作流输出 chart_url |
| 填充方式 | 包含 / 覆盖 |
| 宽度 | 100% 或固定宽度 |
| 高度 | 自适应或固定高度 |
| 支持大图 | 可开启 |
关键点:
Image1 的数据源要绑定到工作流输出的 chart_url
不要绑定:
{{ Button1.content }}
因为 Button1.content 只是按钮文案“生成柱状图”,不是图片地址。
也不要绑定:
output
如果 output 是 Markdown 格式:

图片组件通常需要的是纯图片地址,所以应该绑定:
chart_url
8. 推荐页面布局
页面可以设计为:
标题:科研数据可视化助手
上传区域:
[文件上传组件]
操作区域:
[生成柱状图按钮]
结果区域:
[图片组件 Image1]
[可选 Markdown 组件 output]
如果想让页面更适合科研写作,可以增加说明文字:
请上传包含 Class、Precision、Recall、F1-Score 的实验结果文件,系统将自动生成分类指标柱状图。
9. 完整运行流程
用户实际使用时的流程如下:
1. 用户上传 data.docx / data.csv / data.txt
2. 点击“生成柱状图”
3. 工作流读取上传文件
4. 文本提取节点提取实验数据
5. 数据整理节点把文本转换为统一 CSV
6. 代码节点解析 CSV 并生成图表 URL
7. 结束节点返回 output 和 chart_url
8. 页面图片组件读取 chart_url 并展示柱状图
10. 常见问题与解决方案
10.1 点击发布时提示 UI 页面没有绑定工作流
原因通常是只搭建了业务逻辑工作流,但用户界面中的按钮没有绑定工作流。
解决方法:
用户界面
→ 选中按钮
→ 事件
→ 新建事件
→ 点击时
→ 调用工作流
→ 选择 shujukeshihua
→ 绑定 input = FileUpload1.value
10.2 工作流入参中出现 USER_INPUT、CONVERSATION_NAME
这通常是旧的绑定关系或旧 schema 残留。
解决方法:
1. 删除按钮原来的调用工作流事件
2. 保存页面
3. 刷新页面
4. 重新创建按钮事件
5. 重新选择工作流
6. 重新绑定 input = FileUpload1.value
10.3 图片组件显示空白
优先检查三点:
第一,结束节点是否输出了 chart_url。
第二,chart_url 是否是纯 URL,例如:
https://quickchart.io/chart?width=1200&height=600...
第三,图片组件是否绑定到了:
工作流输出 chart_url
不要绑定:
Button1.content
也不要绑定 Markdown 字符串:

10.4 代码节点提示没有解析到数据
常见原因是大模型输出格式不稳定。
建议在数据整理节点提示词中明确要求:
只输出 JSON,不要输出 Markdown,不要输出解释。
同时要求固定结构:
{
"input": "Dataset,TON-loT\nClass,Precision,Recall,F1-Score\nBackdoor,1.00,0.99,0.99"
}
10.5 上传文件后传入的不是文件
如果运行日志中看到类似:
{
"input": {
"hidden": false,
"value": {
"file_name": "data.docx"
}
}
}
说明传的是整个组件对象,而不是文件值。
应该改成:
input = FileUpload1.value
而不是:
input = FileUpload1
11. 项目扩展方向
当前版本主要生成 Precision、Recall、F1-Score 的柱状图,后续可以扩展为:
| 扩展功能 | 说明 |
|---|---|
| 多图表类型 | 支持柱状图、折线图、雷达图、热力图 |
| 多数据集对比 | 同时比较 TON-IoT、CICIDS、UNSW-NB15 等数据集 |
| 多模型对比 | 比较不同算法在同一数据集上的性能 |
| 自动论文描述 | 根据图表自动生成论文实验分析段落 |
| LaTeX 表格生成 | 自动把数据转换为 IEEE 风格表格 |
| 图表标题优化 | 自动生成适合论文的图题 |
| 图片下载 | 输出可下载的 PNG 链接 |
| Markdown 报告 | 一次性生成图表、表格和分析文本 |
12. 总结
本文实现了一个面向科研写作场景的数据可视化智能体。它通过 Coze 工作流完成文件读取、数据整理和图表生成,并通过用户界面中的图片组件展示最终结果。
核心实现思路是:
文件上传组件 value
→ 工作流 input
→ 文本提取
→ 大模型整理为 CSV
→ Python 代码生成 chart_url
→ 结束节点输出
→ 图片组件绑定 chart_url
其中最关键的两个点是:
1. 上传文件时,按钮事件要绑定 FileUpload1.value
2. 图片展示时,Image1 要绑定工作流输出 chart_url
这样,一个适用于科研实验结果可视化的 Coze 智能体就可以完整运行起来。