核心工作流
- 确定实体名称和属性列表(用户未提供属性时 → 按下方属性推断规则自动补全,不询问用户)
- 属性为空时:只画一个实体矩形,不画属性椭圆和连接线
- 首选:调用
scripts/er_draw.py生成 PNG 图片(路径:相对于 SKILL.md 所在目录的scripts/er_draw.py) - 回退:如果
scripts/er_draw.py不存在(单文件分发场景),使用下方的"回退方案:手写 Python 代码"生成图片 - 向用户展示结果图片路径
路径检测
用以下方式检测脚本是否存在,决定走哪条路:
import os, sys
script_path = os.path.join(os.path.dirname(__file__), 'scripts', 'er_draw.py')
# 或用相对路径检测
has_script = os.path.isfile('scripts/er_draw.py') # 或尝试其他常见相对路径
触发方式
用户说以下内容时触发本 skill(不用本工具画多实体关系图):
| 触发条件 | 示例 |
|---|---|
| 用户说"画XX的ER图" | "画一个电影的ER图" |
| 用户说"画XX的实体属性图" | "给学生的表画个实体属性图" |
| 用户说"给XX画个数据库ER图" | "给图书画个数据库ER图" |
| 用户说"画个单实体图" | "画个单实体图" |
| 用户只提实体名要求画ER图 | "画课程实体"、"画个学生" |
| 用户说"画XX的图,属性是..." | 带自定义属性 |
不触发:多实体关系图("画学生和课程的关系图")、生成文档("生成ER图Word")、只问概念不画图。
使用说明
字体问题(中文显示为方框)
- 检查系统是否装了中文字体,推荐安装
SimHei(黑体)或Microsoft YaHei(微软雅黑) - 通过
--font参数指定已安装的字体,如--font "Microsoft YaHei" - 回退方案代码会自动检测常见中文字体,检测不到时会警告但继续运行
属性太多(布局拥挤)
| 属性数量 | 自动处理 | 建议 |
|---|---|---|
| 10 个以下 | 自动选择 120°/180°/360° 最佳布局 | 默认参数即可 |
| 10~25 个 | 自动拉远间距,防止椭圆重叠 | 可加 --attr-size 14 缩小字号 |
| 超过 25 个 | 自动截断到 25 个并给出警告 | 建议精简属性,只保留关键字段 |
如果生成后椭圆还是挤在一起,可手动调整:
--layout 360 --attr-size 14 --ellipse-width 3.5 --ellipse-height 2.5
属性名太长(超出椭圆)
- 脚本会自动检测过长属性名并给出警告
- 可调大椭圆尺寸:
--ellipse-width 5.5 --ellipse-height 3.5 - 或缩小字号:
--attr-size 14 - 或使用更短的属性名
生成失败
- 缺依赖 → 运行
pip install numpy matplotlib - 输入有误 → 检查实体名是否为空、属性是否重复
- 降级也失败 → 减少属性数量或用更短属性名重试
样式调整(按喜好改字体、颜色、大小)
用户可通过参数自由调整样式,不需要改脚本代码:
| 想要的效果 | 参数示例 |
|---|---|
| 换字体 | --font "Microsoft YaHei" |
| 换颜色 | --color "#2E75B6"(蓝色)或 #FF0000(红色) |
| 画大一点 | --ellipse-width 6.0 --ellipse-height 4.0 --rect-width 8.0 --rect-height 5.0 |
| 字大一些 | --title-size 26 --attr-size 20 |
| 换布局 | --layout 180(半圆)或 --layout 360(整圆) |
所有颜色用十六进制色值(如 #000000 黑色),更多自定义参数见下方 样式自定义 章节。
效果案例
以下是本工具生成的 ER 图示例:
默认样式
实体:电影,属性:电影编号、电影名称、类型、时长、上映日期、导演
自定义样式
实体:学生,属性:学号、姓名、性别、年龄、专业、班级
- 颜色:蓝色 --color "#2E75B6"
- 布局:半圆 --layout 180
- 椭圆加大:--ellipse-width 5.0 --ellipse-height 3.5
能力边界(看清楚再问)
能做
| 场景 | 说明 |
|---|---|
| 单实体+多个属性 | 如"画一个电影的ER图" → 自动推断属性后生成 |
| 自定义属性 | 如"画一个学生实体,属性是学号、姓名、年龄" |
| 调整样式 | 改颜色、大小、字体、布局(通过参数) |
| 数据库课程设计 | 标准单实体ER图,符合教材规范 |
不能做
| 场景 | 原因 |
|---|---|
| 多实体关系图(ER图含联系) | 本工具只画一个实体+它的属性,不画实体间的关系 |
| 带主键下划线/标识的图 | 本工具设计为无主键标注版 |
| 生成 Word/PDF 文档 | 只输出 PNG 图片 |
| 修改已有图片 | 每次都重新生成 |
如果用户的请求涉及多个实体+关系(如"画学生和课程的关系图"),不要用本 skill。
用户怎么说(触发示例)
用户可能这样问,下面都是你应该触发本 skill 的典型表述:
| 用户说 | 你应该怎么做 |
|---|---|
| "画一个电影的ER图" | 从属性表推断属性,调用脚本生成 |
| "给学生的表画个实体属性图" | 推断属性:学号、姓名、性别、年龄、专业、班级、入学年份 |
| "画个图书的ER图,属性是书号、书名、作者、出版社" | 直接用用户给的属性 |
| "画观众实体图,把椭圆和矩形画大一点" | 推断属性后加 --ellipse-width 6.0 --ellipse-height 4.0 |
| "帮我画个ER图,实体是课程" | 推断课程属性 |
| "画个蓝色的学生ER图" | 加 --color "#2E75B6" |
| "画大一点" | 加 --ellipse-width 6.0 --ellipse-height 4.0 --rect-width 8.0 --rect-height 5.0 |
不触发的情况:用户说"画学生和课程的关系图"(多实体)、"生成ER图文档"(需Word)、"设计数据库"(范围太大)。
0. 属性推断规则
用户提供了属性列表 → 直接使用。
用户只提供了实体名称(如"画一个电影的ER图")→ 自行推断典型属性,无需询问用户。
常见实体默认属性参考(不在表中的根据名称合理推断4~8个,原则:编号+名称+业务相关字段):
| 实体 | 默认属性 |
|---|---|
| 电影 | 电影编号、电影名称、类型、时长、上映日期、导演、主演、语言 |
| 观众/用户/顾客 | 编号、姓名、性别、年龄、手机号、电子邮箱、注册日期 |
| 影院 | 影院编号、影院名称、地址、联系电话 |
| 影厅 | 影厅编号、影厅名称、座位数、影厅类型 |
| 员工/工作人员 | 员工编号、姓名、性别、职位、入职日期、联系电话 |
| 会员 | 会员编号、等级、积分、开通日期、到期日期 |
| 票务/订单 | 订单编号、电影名称、影厅、场次时间、座位号、价格、状态 |
| 公告 | 公告编号、标题、内容、发布时间、发布人 |
| 评论 | 评论编号、内容、评分、评论时间 |
| 排片/场次 | 场次编号、电影名称、影厅、开始时间、结束时间、票价 |
| 活动 | 活动编号、活动名称、开始时间、结束时间、活动描述 |
| 积分 | 积分编号、变动数量、变动原因、变动时间 |
| 课程 | 课程编号、课程名称、学分、学时、授课教师 |
| 学生 | 学号、姓名、性别、年龄、专业、班级、入学年份 |
| 教师 | 教师编号、姓名、性别、职称、所属院系、联系电话 |
| 图书 | 图书编号、书名、作者、出版社、出版年份、定价、库存 |
| 宿舍 | 宿舍编号、楼栋、楼层、房间号、床位数、住宿费 |
1. 调用方式
python scripts/er_draw.py \
--entity "电影" \
--attributes "电影编号,电影名称,类型,时长,上映日期,导演,主演,语言" \
[--font "SimHei"] \
[--color "#000000"] \
[--layout "auto"] \
[--ellipse-width 4.5] \
[--ellipse-height 3.0] \
[--rect-width 6.0] \
[--rect-height 4.0] \
[--title-size 22] \
[--attr-size 17] \
[--output "电影ER图.png"] \
[--no-fallback]
参数说明
| 参数 | 默认值 | 说明 |
|---|---|---|
--entity |
必填 | 实体名称 |
--attributes / --attrs |
必填 | 属性列表,逗号分隔(如 编号,名称,类型) |
--font |
自动检测 | 中文字体名称 |
--color |
#000000 |
线条和文字颜色 |
--layout |
auto |
排布模式:auto自动 / 120窄弧 / 180半圆 / 360整圆 |
--ellipse-width |
4.5 | 椭圆宽(cm) |
--ellipse-height |
3.0 | 椭圆高(cm) |
--rect-width |
6.0 | 矩形宽(cm) |
--rect-height |
4.0 | 矩形高(cm) |
--title-size |
22 | 实体文字字号(pt) |
--attr-size |
17 | 属性文字字号(pt) |
--output |
{实体名}ER图.png |
输出路径 |
--no-fallback |
(关闭) | 禁用降级模式 |
--debug |
(关闭) | 调试模式,输出更多诊断信息 |
2. 排布规则(角度均分优先级排布)
脚本的 --layout auto 模式自动遵循以下规则:
| 属性数量 | 排布方式 | 角度范围 |
|---|---|---|
| 0 | 只画实体矩形,无属性椭圆 | — |
| ≤ 4 | 窄弧 120° 排布(上方) | 150° → 30° |
| 5~6 | 半圈 180° 均分 | 180° → 0° |
| > 6 | 整圆 360° 均分 | 360°/n 每个 |
- 180° 半圈优先 → 若计算发现椭圆重叠 → 自动切到 360° 整圈
- 属性超过 10 个时,距离会自动拉远留出更多空间
- 数量超过 25 个时自动截断并给出警告
3. 异常处理(核心规则)
最终兜底:生成失败或图片有误时告诉用户原因
脚本运行完成但生成的图片不正确、或降级全部失败时,直接告诉用户出错原因即可,不要反复重试或自行修改脚本。
黄金法则:永不向用户展示原始报错
脚本的输出可能包含中英文混排的异常信息,这些不能直接转述给用户。
你必须先读取返回码,然后用自己的话告诉用户发生了什么、怎么解决。
返回码与你的应对话术
| 返回码 | 含义 | 你对用户说的话 |
|---|---|---|
| 0 | 成功 | "已生成 ER 图,保存在 [路径]" |
| 1 | 输入有误 | 告诉用户具体哪里错了(实体名空/属性重复/数值越界等),让用户改后重试 |
| 2 | 缺依赖包 | "需要安装 Python 库,运行 pip install numpy matplotlib 就行" |
| 3 | 降级也失败 | "自动生成失败了,建议减少属性数量或用更短的属性名再试一次" |
| 4 | 用户中断 | "已取消" |
返回码 1(输入错误)→ 你的回复模板
输入有误:[具体原因]
→ 请检查后重新运行,例如:
python er_draw.py --entity "电影" --attributes "编号,名称,类型"
具体原因从脚本输出的 ! 行里提取(如"实体名称不能为空"、"存在重复属性")。
返回码 3(生成失败)→ 你的回复模板
目前无法自动生成 ER 图。
[原因简述]
→ 建议减少属性数量或用更短的属性名再试一次。
→ 如果仍不行,可参考以下结构手动绘制:
实体:[实体名称]
属性:[属性列表]
超时处理(返回码 4 或调用超时)
如果脚本 30 秒没返回:
1. 中断它
2. 告诉用户:"绘图耗时较长,尝试用简化参数重试..."
3. 调用 --layout 360 --attr-size 14 --ellipse-width 3.5 --ellipse-height 2.2 简化参数后重试
4. 如果还超时,建议用户减少属性数量
4. 样式自定义(当用户要求调整样式时)
用户可以通过以下参数自定义样式,不需要修改脚本代码:
| 用户需求 | 对应参数 |
|---|---|
| "画大一点/小一点" | --ellipse-width, --ellipse-height, --rect-width, --rect-height |
| "换个颜色" | --color "#FF0000"(红色)等十六进制色值 |
| "字大一些" | --title-size 26 --attr-size 20(超出形状边界时自动缩小到能容纳的大小) |
| "换个布局" | --layout 360(整圆均分) |
| "换个字体" | --font "Microsoft YaHei" |
| "换个文件名" | --output "my_er.png" |
实体/关系合法性校验:脚本会自动检查空实体名、空属性列表、重复属性、过长属性文字,并给出中文警告。
5. 回退方案:手写 Python 代码
当 scripts/er_draw.py 不存在时,直接编写 Python 脚本生成 PNG。使用下方模板,它内置了校验、错误处理和降级逻辑。
完整代码模板
import math, os, sys, traceback
# ── 输入校验 ──
entity = "电影" # 替换为实际实体名称
attrs = ["编号", "名称", "类型"] # 替换为实际属性列表
errors = []
if not entity or not entity.strip():
errors.append("错误:实体名称不能为空")
# 属性为空时只画实体矩形,不报错
if len(attrs) > 25:
print("警告:属性超过25个,将自动截断")
attrs = attrs[:25]
seen = set()
for a in attrs:
if a in seen:
errors.append(f"错误:存在重复属性 '{a}'")
seen.add(a)
if errors:
for e in errors:
print(e)
sys.exit(1)
# ── 依赖检查 ──
try:
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import numpy as np
except ImportError as e:
print(f"错误:缺少依赖 {e.name},请运行: pip install {e.name}")
sys.exit(1)
# ── 中文字体检测 ──
font_name = None
for font in ['SimHei', 'Microsoft YaHei', 'WenQuanYi Micro Hei',
'Noto Sans CJK SC', 'PingFang SC', 'STHeiti', 'SimSun']:
if font in {f.name for f in fm.fontManager.ttflist}:
font_name = font
break
if font_name:
plt.rcParams['font.family'] = [font_name]
plt.rcParams['font.sans-serif'] = [font_name]
else:
print("警告:未检测到中文字体,中文可能显示为方框")
plt.rcParams['axes.unicode_minus'] = False
# ── 参数(可按需调整)──
COLOR = '#000000'
ELLIPSE_W, ELLIPSE_H = 4.5, 3.0 # 椭圆宽高(cm)
RECT_W, RECT_H = 6.0, 4.0 # 矩形宽高(cm)
TITLE_SIZE, ATTR_SIZE = 22, 17 # 字号
MARGIN = 4.0 # 留白(cm)
n = len(attrs)
# 画布(无属性时缩小,只包住实体矩形)
if n == 0:
canvas = max(RECT_W, RECT_H) * 0.8 + MARGIN
dist = 0
else:
# 角度计算
if n <= 4:
angles = np.linspace(150, 30, n) # 120度窄弧
spacing = 120 / max(n - 1, 1)
elif n <= 6:
angles = np.linspace(180, 0, n) # 180度半圆
spacing = 180 / max(n - 1, 1)
else:
angles = np.linspace(0, 360, n, endpoint=False) # 360度整圆
spacing = 360 / n
# 防止重叠的最小距离
if n > 1 and spacing > 0:
sin_v = math.sin(math.radians(spacing / 2))
min_dist = (ELLIPSE_W + 0.3) / (2 * sin_v) + 0.3 if sin_v > 0.001 else RECT_W + 1.0
else:
min_dist = RECT_W + 1.0
dist = max(RECT_W * 1.1, min_dist)
if n > 10:
dist *= 1.0 + (n - 10) * 0.04
# 画布
canvas = dist + max(ELLIPSE_H + 1.0, MARGIN)
fig, ax = plt.subplots(1, 1, figsize=(max(canvas*2.8, 14), max(canvas*2.2, 11)))
ax.set_xlim(-canvas, canvas)
ax.set_ylim(-canvas, canvas)
ax.set_aspect('equal')
ax.axis('off')
from matplotlib.patches import FancyBboxPatch, Ellipse
# 绘制实体矩形
rect = FancyBboxPatch(
(-RECT_W/2, -RECT_H/2), RECT_W, RECT_H,
boxstyle="round,pad=0", edgecolor=COLOR, linewidth=2.5,
facecolor='#FFFFFF', zorder=5)
ax.add_patch(rect)
ax.text(0, 0, entity, ha='center', va='center',
fontsize=TITLE_SIZE, fontweight='bold', color=COLOR, zorder=6)
# 绘制属性椭圆与连接线(无属性时跳过)
if n > 0:
rx_box, ry_box = RECT_W/2, RECT_H/2
rx_el, ry_el = ELLIPSE_W/2, ELLIPSE_H/2
for i, attr in enumerate(attrs):
rad = math.radians(angles[i])
ex, ey = dist * math.cos(rad), dist * math.sin(rad)
# 方向向量(指向中心)
dx_n, dy_n = -ex / math.hypot(ex, ey), -ey / math.hypot(ex, ey)
# 椭圆边缘点
s = 1.0 / math.sqrt((dx_n/rx_el)**2 + (dy_n/ry_el)**2)
ex_e = ex + dx_n * s * 0.95
ey_e = ey + dy_n * s * 0.95
# 矩形边缘点
tx = rx_box / abs(dx_n) if abs(dx_n) > 1e-10 else float('inf')
ty = ry_box / abs(dy_n) if abs(dy_n) > 1e-10 else float('inf')
t = min(tx, ty)
rx_e, ry_e = dx_n * t * 0.95, dy_n * t * 0.95
# 连接线
ax.plot([ex_e, rx_e], [ey_e, ry_e], color=COLOR, linewidth=1.2, zorder=4)
# 属性椭圆
ax.add_patch(Ellipse((ex, ey), ELLIPSE_W, ELLIPSE_H,
edgecolor=COLOR, linewidth=1.0, facecolor='#FFFFFF', zorder=5))
ax.text(ex, ey, attr, ha='center', va='center',
fontsize=ATTR_SIZE, color=COLOR, zorder=6)
output = f"{entity}ER图.png"
plt.tight_layout(pad=0.5)
fig.savefig(output, dpi=200, bbox_inches='tight', facecolor='#FFFFFF', edgecolor='none')
plt.close(fig)
print(f"成功:{output}")
手写模式下的错误处理
| 问题 | 处理方式 |
|---|---|
| ImportError | 告诉用户 pip install numpy matplotlib |
| 中文字体缺失 | 警告但继续(中文变方框),建议安装字体 |
| 绘图异常 | try/except 包裹,分三级降级:①缩小尺寸→②最小配置→③文字说明 |
| 全部失败 | 输出实体和属性列表,建议用户手动绘制 |
手写模式下同样遵守黄金法则:永不向用户展示原始报错信息。
手写代码要点
- 连接线只连矩形边缘,不穿过内部
- 不标主键、不下划线、无标题
- 颜色用十六进制
'#000000'字符串,禁止用fontproperties - 完成后删除临时
.py文件
关键设计原则
- 连接线只连接到实体矩形的边缘,不穿过矩形内部
- 椭圆统一加大,文字居中无溢出
- 不标注主键、不画下划线、不写任何标识
- 底部不添加任何标题
- 四周留白边距 ≥ 4cm,确保最外侧椭圆完整可见,不被裁剪
- 所有颜色必须显式指定十六进制色值(如
#000000),不使用颜色名称字符串 - 保存为 PNG 文件,文件名格式:
{实体名称}ER图.png - 属性为空时只画实体矩形,不画属性椭圆和连接线
示例
标准调用:
python scripts/er_draw.py --entity "观众" --attributes "观众ID,姓名,性别,年龄,手机号,电子邮箱,注册日期,会员等级"
→ 生成 观众ER图.png,8个属性360度均分(每45°一个),线条从椭圆边缘连到矩形边缘,无标题。
自定义样式:
python scripts/er_draw.py --entity "学生" --attributes "学号,姓名,性别,年龄,专业" --layout 180 --color "#2E75B6" --ellipse-width 5.0 --ellipse-height 3.5 --title-size 24 --attr-size 18
当用户说"画大一点":
python scripts/er_draw.py --entity "图书" --attributes "书号,书名,作者,出版社" --ellipse-width 6.0 --ellipse-height 4.0 --rect-width 8.0 --rect-height 5.0 --title-size 26 --attr-size 20