科研作图标准操作规程
学术图表生成的标准操作规程。包含四种渲染引擎:Python(数据可视化)、Mermaid(流程图)、AI 图像(通过 NanoBanana/OpenRouter 生成复杂图表)、SVG(矢量图)。包括引擎选择决策树、ReAct 自我纠正、NanoBanana 配置、环境检测和学术风格规则。
文件预览
---
name: Plotting SOP
description: >-
Standard operating procedure for academic figure generation.
Four rendering engines: Python (data viz), Mermaid (flowcharts),
AI Image via NanoBanana/OpenRouter (complex diagrams), SVG (vector).
Includes engine selection decision tree, ReAct self-correction,
NanoBanana configuration, environment detection, and academic style rules.
---
# Plotting SOP — 科研作图标准操作规程
<!-- SKILL MAINTENANCE NOTES:
- This is a SYSTEM-LEVEL skill (lives in research-claw/skills/, NOT research-plugins)
- Covers all figure generation workflows for academic research
- NanoBanana = OpenRouter API endpoint for Gemini image generation
- ReAct pattern: generate → execute → error? → inject error → retry (max 3)
- References: writing-sop (embeds figures), workspace-sop (saves figures)
- AGENTS.md §3 Quick Path points here for "画图/作图/figure"
- Update AGENTS.md pointers when modifying this skill
-->
## When to Read This Skill
Read this skill when the user asks to:
- Draw, plot, or visualize any figure or diagram
- Create charts for a paper (bar, line, scatter, heatmap, radar, etc.)
- Draw flowcharts, architecture diagrams, or concept maps
- Generate figures during academic writing (called from writing-sop Phase 2)
- Convert data into visual representations
---
## §1 Environment Detection (run once per session)
Before generating any figure, **MUST** check available capabilities.
Run these checks silently (do not show output to user unless something fails):
```
Check 1: python3 -c "import matplotlib; print('matplotlib', matplotlib.__version__)"
Check 2: python3 -c "import seaborn; print('seaborn', seaborn.__version__)"
Check 3: which mmdc 2>/dev/null || npx --yes @mermaid-js/mermaid-cli mmdc --version 2>/dev/null
Check 4: python3 -c "import cairosvg; print('cairosvg OK')" 2>/dev/null
```
Record results in session memory. Do NOT repeat these checks for subsequent figures.
**If matplotlib is missing** (common on native macOS/WSL2 installs):
1. Tell user: "Python 科学绘图库未安装。是否允许我运行安装脚本?(约 30 秒)"
2. If user agrees: `bash scripts/setup-plotting-env.sh`
3. If setup script not found: `pip install --user matplotlib seaborn numpy pandas`
4. If user declines: skip Python Engine, use Mermaid or AI Image instead
**If mmdc is missing**: This is normal. Use `npx --yes @mermaid-js/mermaid-cli mmdc` as fallback.
If npx also fails: save `.mmd` file and tell user to paste at https://mermaid.live
---
## §2 Engine Selection Decision Tree
**MUST** follow this decision tree for every figure request. Do NOT skip steps.
```
User requests a figure
│
├─ 1. Is it DATA VISUALIZATION (charts with numbers/statistics)?
│ YES → §4 Python Engine
│ Examples: bar chart, line plot, scatter, heatmap, violin, radar, histogram, box plot
│
├─ 2. Is it a SIMPLE STRUCTURED DIAGRAM (≤15 nodes AND aesthetics not critical)?
│ YES → §5 Mermaid Engine
│ Examples: simple flowchart, sequence diagram, class diagram, Gantt chart
│
├─ 3. Is it a COMPLEX diagram (>15 nodes OR requires visual polish)?
│ (architecture diagram, research methodology flow, concept map,
│ multi-layer system diagram, publication-quality illustration)
│
│ → Is NanoBanana/OpenRouter configured?
│ YES → §6 AI Image Engine (NanoBanana)
│ NO → Recommend NanoBanana to user (see §3)
│ → User provides API key? → §6 AI Image Engine
│ → User declines?
│ → WARN: "本地引擎生成复杂流程图的质量有限,可能需要手动调整。"
│ → Fall back to §5 Mermaid Engine (best effort)
│
└─ 4. Is it a CUSTOM VECTOR graphic (geometric shapes, coordinate annotations)?
YES → §7 SVG Engine
```
---
## §3 NanoBanana Configuration Guide
NanoBanana provides access to Gemini's native image generation through OpenRouter.
It is the **only reliable path** for complex, publication-quality academic diagrams
because it generates images directly (pixel-level), bypassing code generation entirely.
### Why recommend NanoBanana
- LLM-generated Mermaid/Python code for complex diagrams **frequently has syntax errors**
- Even when correct, code-rendered diagrams look mechanical and unprofessional
- Gemini image generation produces **visually polished, publication-ready** figures
- **Low-IQ models benefit most**: the API quality is independent of the local model's coding ability
### Configuration
User needs to provide an **OpenRouter API Key**.
Store in MEMORY.md under `## Global > ### Environment`:
```
NanoBanana: configured
OpenRouter API Key: [stored in environment, not in memory]
```
**Recommended model**: `google/gemini-2.5-flash-preview-image-generation`
**Alternative model**: `google/gemini-2.5-pro-preview-image-generation`
**API endpoint**: `https://openrouter.ai/api/v1/chat/completions`
### How to tell the user
When the user requests a complex diagram and NanoBanana is not configured:
> "这类复杂的学术图表,推荐使用 NanoBanana(基于 Gemini 图片生成)来获得最佳效果。
> 您只需提供一个 OpenRouter API Key(https://openrouter.ai/settings/keys)。
> 如果您不想配置,我也可以使用本地工具(Mermaid/Python)尝试生成,但质量可能有限。"
---
## §4 Python Engine — Data Visualization
For charts based on numerical data: bar, line, scatter, heatmap, violin, radar, box, histogram, pie, area, etc.
### Output Path Resolution (MUST set before generating code)
Set `output_path` per §9 naming convention:
```
output_path = "outputs/figures/{topic}-fig{N}.png"
```
Example: `outputs/figures/model-comparison-fig1.png`
### Code Template (MUST follow this structure)
The generated Python code **MUST** include all of the following elements.
Low-IQ models: copy this template exactly, then fill in the plotting section.
```python
import matplotlib
matplotlib.use('Agg') # Non-interactive backend — MUST be before pyplot import
import matplotlib.pyplot as plt
import numpy as np
# ── Academic style settings ──────────────────────────────────
plt.rcParams.update({
'font.family': 'sans-serif',
'font.size': 12,
'figure.dpi': 300,
'axes.linewidth': 1.2,
'axes.grid': True,
'grid.alpha': 0.3,
'legend.framealpha': 0.9,
})
fig, ax = plt.subplots(figsize=(10, 6))
# ── [YOUR PLOTTING CODE HERE] ───────────────────────────────
# Generate reasonable example data if user did not provide actual data.
# If using example data, add text annotation: "Example Data" in bottom-right.
# ── Labels and title (ALL ENGLISH) ──────────────────────────
ax.set_xlabel('X Label', fontsize=13)
ax.set_ylabel('Y Label', fontsize=13)
ax.set_title('Chart Title', fontsize=14, fontweight='bold')
# ── Save ─────────────────────────────────────────────────────
plt.tight_layout()
plt.savefig('{output_path}', format='png', dpi=300, bbox_inches='tight')
plt.close()
print('OK')
```
### Execution Protocol
1. **Generate code** following the template above
2. Save code to temp file: `system.run` with inline Python or write to `/tmp/rc_plot_{hash}.py`
3. **Execute**: `python3 /tmp/rc_plot_{hash}.py`
4. **Check result**:
- Exit code 0 AND "OK" in stdout AND output file exists → **SUCCESS**
- Otherwise → **FAILURE** → enter ReAct loop
### ReAct Self-Correction (max 3 attempts)
If execution fails:
**Attempt 2-3**: Inject the previous code and error into your next generation:
```
## Previous attempt FAILED. Fix the code.
### Previous code:
[paste the code that failed]
### Error output:
[paste stderr, truncated to 500 chars]
### Fix instructions:
- Analyze the error carefully
- Fix syntax errors, indentation, missing imports
- Ensure matplotlib.use('Agg') is BEFORE pyplot import
- Ensure savefig path is correct: {output_path}
- Do NOT use libraries that are not installed (stick to matplotlib, numpy, pandas, seaborn)
```
If all 3 attempts fail → inform user: "Python 作图失败。请检查数据格式或简化图表要求。"
### Color Palettes (academic standard)
| Use case | Palette | Code |
|----------|---------|------|
| Categorical (≤10) | tab10 | `plt.cm.tab10` |
| Categorical (≤8) | Set2 | `plt.cm.Set2` |
| Sequential | viridis | `cmap='viridis'` |
| Diverging | RdYlBu | `cmap='RdYlBu'` |
| Colorblind-safe | Paired | `plt.cm.Paired` |
**NEVER** use red-green only contrast. Always use colorblind-safe palettes.
### Chart Type Quick Reference
| User says | Chart type | Key code |
|-----------|-----------|----------|
| 柱状图/bar chart | Grouped bar | `ax.bar(x, y)` |
| 折线图/line chart | Line plot | `ax.plot(x, y)` |
| 散点图/scatter | Scatter | `ax.scatter(x, y)` |
| 热力图/heatmap | Heatmap | `import seaborn as sns; sns.heatmap(data)` |
| 箱线图/box plot | Box | `ax.boxplot(data)` or `sns.boxplot()` |
| 小提琴图/violin | Violin | `sns.violinplot()` |
| 雷达图/radar | Radar | Custom with `ax = fig.add_subplot(111, polar=True)` |
| 饼图/pie | Pie | `ax.pie(sizes, labels=labels)` |
| 直方图/histogram | Histogram | `ax.hist(data, bins=30)` |
| 面积图/area | Stacked area | `ax.stackplot(x, y1, y2)` |
---
## §5 Mermaid Engine — Structured Diagrams
For flowcharts, sequence diagrams, class diagrams, state machines, Gantt charts.
### Supported Diagram Types
| Type | Keyword | Best for |
|------|---------|----------|
| Flowchart (vertical) | `flowchart TD` | Process flows, decision trees |
| Flowchart (horizontal) | `flowchart LR` | Pipelines, architectures |
| Sequence diagram | `sequenceDiagram` | API calls, message passing |
| Class diagram | `classDiagram` | OOP design, data models |
| State diagram | `stateDiagram-v2` | State machines, lifecycle |
| Gantt chart | `gantt` | Project timelines |
### Syntax Rules (critical for low-IQ models)
1. Node IDs: use simple letters — `A`, `B`, `C` (NOT Chinese, NOT spaces)
2. Labels: wrap in `[]` — `A[Start Process]`
3. Arrows: `-->` or `-->|label|`
4. **NEVER** use these characters inside labels: `&`, `<`, `>`
5. For decision nodes (diamond shape): use single braces — `C{Is valid?}`
6. Keep diagrams **≤15 nodes AND aesthetics not critical**. For larger or visually polished diagrams → recommend NanoBanana (§6).
### Rendering Protocol
1. **Generate** Mermaid code
2. Save to temp file: `/tmp/rc_mermaid_{hash}.mmd`
3. **Render** (try in order):
- `mmdc -i /tmp/rc_mermaid_{hash}.mmd -o {output_path} -w 1920 -H 1080 --backgroundColor white`
- `npx --yes @mermaid-js/mermaid-cli -i /tmp/rc_mermaid_{hash}.mmd -o {output_path}`
- Both fail → save `.mmd` file to workspace + tell user: "Mermaid 渲染工具未安装。已保存源文件,请粘贴到 https://mermaid.live 查看。"
4. **Check result**: file exists + size > 1KB → SUCCESS
### ReAct for Mermaid
If mmdc returns an error:
- Parse the error message (usually "Parse error on line N")
- Fix the syntax issue (often: special characters in labels, missing brackets)
- Retry (max 3 attempts)
---
## §6 AI Image Engine — NanoBanana (Complex Diagrams)
For complex academic diagrams that require visual polish: architecture diagrams,
research methodology flows, concept maps, multi-layer system diagrams.
### When to Use
- Diagram has >15 nodes or complex spatial layout
- User wants "美观/professional/publication-ready" quality
- User explicitly requests AI-generated figure
- Low-IQ model is active (AI Image quality is model-independent)
### API Call Protocol
**Step 0 — Confirm with user** before calling the API (costs money):
> "即将调用 NanoBanana (Gemini) 生成图片,预计消耗约 $0.01 API 额度。是否继续?"
Wait for user confirmation. If user declines → fall back to §5 Mermaid.
**Step 1 — Generate and execute** a Python script via `system.run`:
```python
import requests, base64, sys, os
API_KEY = os.environ.get('OPENROUTER_API_KEY', '')
if not API_KEY:
print('ERROR: OPENROUTER_API_KEY not set', file=sys.stderr)
sys.exit(1)
resp = requests.post(
'https://openrouter.ai/api/v1/chat/completions',
headers={
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json',
},
json={
'model': 'google/gemini-2.5-flash-preview-image-generation',
'messages': [{
'role': 'user',
'content': 'Generate a professional academic diagram: {description}.\n\n'
'Style: clean, minimal, publication-ready, white background, '
'no watermark, clear English labels, professional color scheme '
'(blues, grays, muted tones), high resolution for academic paper.'
}],
},
timeout=120,
)
resp.raise_for_status()
data = resp.json()
# Extract image from multimodal response
content = data['choices'][0]['message']['content']
b64_data = None
if isinstance(content, list):
for part in content:
if part.get('type') == 'image_url':
url = part['image_url']['url']
b64_data = url.split(',', 1)[1]
break
elif part.get('type') == 'image' and 'data' in part:
b64_data = part['data']
break
elif isinstance(content, str) and 'data:image' in content:
b64_data = content.split(',', 1)[1]
if not b64_data:
print('ERROR: No image found in API response', file=sys.stderr)
sys.exit(1)
with open('{output_path}', 'wb') as f:
f.write(base64.b64decode(b64_data))
print('OK')
```
**Step 2 — Check**: file exists + size > 1KB → SUCCESS.
**If API call fails** (timeout, auth error, quota exceeded):
- Log the error
- **WARN user**: "NanoBanana API 调用失败。降级到 Mermaid 引擎。"
- Fall back to §5 Mermaid Engine
---
## §7 SVG Engine — Custom Vector Graphics
For geometric shapes, coordinate-annotated diagrams, simple custom illustrations.
### Generation Method
Generate Python code using `svgwrite`:
```python
import svgwrite
dwg = svgwrite.Drawing('{output_path}', size=('800px', '600px'))
dwg.add(dwg.rect(insert=(0, 0), size=('100%', '100%'), fill='white'))
# [YOUR SVG ELEMENTS HERE]
dwg.save()
print('OK')
```
If `svgwrite` is not available, generate raw SVG XML and save directly.
**PNG conversion** (optional):
- Try: `python3 -c "import cairosvg; cairosvg.svg2png(url='{svg_path}', write_to='{png_path}', dpi=300)"`
- If cairosvg unavailable: keep `.svg` file, inform user
---
## §8 Quality Checklist (run after EVERY figure)
After generating a figure, **MUST** verify:
| # | Check | How to verify | If FAIL |
|---|-------|---------------|---------|
| 1 | File exists | `ls -la {output_path}` | Re-run generation |
| 2 | File size > 1KB | Same command | File is corrupt → regenerate |
| 3 | Labels in English | Review generated code | Fix labels → re-run |
| 4 | DPI ≥ 300 (Python) | Check savefig params in code | Fix → re-run |
| 5 | No overlapping text | Visual inspection if possible | Add `tight_layout()` or adjust |
If checks fail → fix and re-run (counts as one ReAct iteration).
---
## §9 Academic Figure Standards
### File Naming
Save all figures to: `outputs/figures/{topic}-fig{N}.{ext}`
Examples:
- `outputs/figures/transformer-fig1.png`
- `outputs/figures/model-comparison-fig2.png`
- `outputs/figures/methodology-flow-fig3.png`
### Caption Rule
Every figure **MUST** have an English caption. Present to user as:
> **Figure {N}.** {Caption text describing what the figure shows.}
### Citation Format
When embedding in text: "as shown in Figure {N}" or "see Figure {N}".
### Style Rules
- **Font**: sans-serif (Arial, Helvetica), ≥ 10pt for all text
- **DPI**: 300 minimum (publication standard)
- **Background**: white (no transparency)
- **Colors**: colorblind-safe palettes (viridis, Set2, tab10, Paired)
- **Borders**: thin axis lines (1-1.5pt), no box frames around plots
- **Grid**: light gray (alpha 0.3), optional but recommended for data plots
- **Legend**: positioned to avoid overlapping data; semi-transparent background
---
## §10 Integration with Writing SOP
When called from **writing-sop Phase 2** (first draft generation):
1. Writing-sop identifies a section needs a figure
2. It invokes this skill (plotting-sop) with the figure description
3. This skill generates the figure → `workspace_save` to `outputs/figures/`
4. Return to writing-sop with: figure path + caption
5. Writing-sop embeds the figure reference in the draft
**Pattern for inline invocation:**
```
[Writing-sop Phase 2, writing Methods section]
→ "This section needs a methodology flowchart"
→ [Load plotting-sop] → Engine selection → Generate → Save
→ [Return to writing-sop] → "See Figure 1" inserted in text
```
---
## RC Local Tools Reference
| Task | Tool | Example |
|:-----|:-----|:--------|
| Run Python plot | `system.run` | `python3 /tmp/rc_plot_abc.py` |
| Run Mermaid compile | `system.run` | `npx --yes @mermaid-js/mermaid-cli -i input.mmd -o output.png` |
| Call NanoBanana API | `system.run` | Python requests POST to OpenRouter endpoint |
| Save figure | `workspace_save` | `outputs/figures/{name}.png` |
| Check file | `system.run` | `ls -la outputs/figures/{name}.png` |
| Install deps (if needed) | `system.run` | `pip install matplotlib seaborn` |
SKILL.md
| name | Plotting SOP |
|---|---|
| description | 学术图表生成的标准操作规程。 四种渲染引擎:Python(数据可视化)、Mermaid(流程图)、 AI 图像(通过 NanoBanana/OpenRouter 生成复杂图表)、SVG(矢量图)。 包括引擎选择决策树、ReAct 自我纠正、 NanoBanana 配置、环境检测和学术风格规则。 |
Plotting SOP — 科研作图标准操作规程
<!-- 技能维护说明: - 这是一个系统级技能(位于 research-claw/skills/,而不是 research-plugins) - 覆盖所有科研图表生成流程 - NanoBanana = OpenRouter API 端点,用于 Gemini 图像生成 - ReAct 模式:生成 → 执行 → 错误? → 注入错误 → 重试(最多 3 次) - 参考:writing-sop(内嵌图表)、workspace-sop(保存图表) - AGENTS.md §3 快捷路径指向此处,用于“画图/作图/figure” - 修改本技能时请更新 AGENTS.md 指针 -->何时阅读本技能
当用户要求以下操作时阅读本技能:
- 绘制任何图表或图示
- 为论文创建图表(柱状图、折线图、散点图、热力图、雷达图等)
- 绘制流程图、架构图或概念图
- 在学术写作过程中生成图表(由 writing-sop Phase 2 调用)
- 将数据转换为可视化表示
§1 环境检测(每个会话运行一次)
生成任何图形之前,必须检查可用能力。 静默运行以下检查(除非有失败,否则不向用户显示输出):
Check 1: python3 -c "import matplotlib; print('matplotlib', matplotlib.__version__)"
Check 2: python3 -c "import seaborn; print('seaborn', seaborn.__version__)"
Check 3: which mmdc 2>/dev/null || npx --yes @mermaid-js/mermaid-cli mmdc --version 2>/dev/null
Check 4: python3 -c "import cairosvg; print('cairosvg OK')" 2>/dev/null将会话记录结果。后续图表无需重复这些检查。
如果缺少 matplotlib(在原生 macOS/WSL2 安装中常见):
- 告诉用户:“Python 科学绘图库未安装。是否允许我运行安装脚本?(约 30 秒)”
- 如果用户同意:
bash scripts/setup-plotting-env.sh - 如果未找到安装脚本:
pip install --user matplotlib seaborn numpy pandas - 如果用户拒绝:跳过 Python 引擎,改用 Mermaid 或 AI 图像
如果缺少 mmdc:这是正常的。使用 npx --yes @mermaid-js/mermaid-cli mmdc 作为备选。
如果 npx 也失败:保存 .mmd 文件,并告知用户粘贴到 https://mermaid.live 查看
§2 引擎选择决策树
对于每个图表请求,必须遵循此决策树。不要跳过步骤。
用户请求图表
│
├─ 1. 是数据可视化(包含数字/统计的图表)吗?
│ 是 → §4 Python 引擎
│ 示例:柱状图、折线图、散点图、热力图、小提琴图、雷达图、直方图、箱线图
│
├─ 2. 是简单结构化图表(节点 ≤15 且美观性不关键)吗?
│ 是 → §5 Mermaid 引擎
│ 示例:简单流程图、时序图、类图、甘特图
│
├─ 3. 是复杂图表(节点 >15 或需要视觉美化)吗?
│ (架构图、研究方法流程、概念图、
│ 多层系统图、出版级插图)
│
│ → NanoBanana/OpenRouter 是否已配置?
│ 是 → §6 AI 图像引擎(NanoBanana)
│ 否 → 向用户推荐 NanoBanana(参见 §3)
│ → 用户提供 API 密钥? → §6 AI 图像引擎
│ → 用户拒绝?
│ → 警告:“本地引擎生成复杂流程图的质量有限,可能需要手动调整。”
│ → 回退到 §5 Mermaid 引擎(尽力而为)
│
└─ 4. 是定制矢量图形(几何形状、坐标注释)吗?
是 → §7 SVG 引擎§3 NanoBanana 配置指南
NanoBanana 通过 OpenRouter 提供对 Gemini 原生图像生成功能的访问。 它是生成复杂、出版级学术图表的唯一可靠途径, 因为它直接生成图像(像素级),完全绕过代码生成。
为什么推荐 NanoBanana
- LLM 生成的 Mermaid/Python 代码用于复杂图表经常出现语法错误
- 即使正确,代码渲染的图表也显得呆板、不专业
- Gemini 图像生成能生成视觉精美、可直接出版的图表
- 低智商模型受益最大:API 的质量与本地模型的编码能力无关
配置
用户需要提供一个 OpenRouter API 密钥。
存储在 MEMORY.md 的 ## Global > ### Environment 下:
NanoBanana: 已配置
OpenRouter API 密钥:[存储在环境中,而非内存中]推荐模型:google/gemini-2.5-flash-preview-image-generation
替代模型:google/gemini-2.5-pro-preview-image-generation
API 端点:https://openrouter.ai/api/v1/chat/completions
如何告知用户
当用户请求复杂图表且 NanoBanana 未配置时:
“这类复杂的学术图表,推荐使用 NanoBanana(基于 Gemini 图片生成)来获得最佳效果。 您只需提供一个 OpenRouter API Key(https://openrouter.ai/settings/keys)。 如果您不想配置,我也可以使用本地工具(Mermaid/Python)尝试生成,但质量可能有限。”
§4 Python 引擎 — 数据可视化
适用于基于数值数据的图表:柱状图、折线图、散点图、热力图、小提琴图、雷达图、箱线图、直方图、饼图、面积图等。
输出路径解析(生成代码前必须设置)
根据 §9 命名规范设置 output_path:
output_path = "outputs/figures/{topic}-fig{N}.png"示例:outputs/figures/model-comparison-fig1.png
代码模板(必须遵循此结构)
生成的 Python 代码必须包含以下所有元素。 低智商模型:一字不差地复制此模板,然后填入绘图部分。
import matplotlib
matplotlib.use('Agg') # 非交互后端 — 必须在导入 pyplot 之前
import matplotlib.pyplot as plt
import numpy as np
# ── 学术样式设置 ──────────────────────────────────
plt.rcParams.update({
'font.family': 'sans-serif',
'font.size': 12,
'figure.dpi': 300,
'axes.linewidth': 1.2,
'axes.grid': True,
'grid.alpha': 0.3,
'legend.framealpha': 0.9,
})
fig, ax = plt.subplots(figsize=(10, 6))
# ── [你的绘图代码在此] ───────────────────────────────
# 如果用户未提供实际数据,则生成合理的示例数据。
# 如果使用示例数据,请在右下角添加文字注释:“示例数据”。
# ── 标签与标题(全部英文) ──────────────────────────
ax.set_xlabel('X Label', fontsize=13)
ax.set_ylabel('Y Label', fontsize=13)
ax.set_title('Chart Title', fontsize=14, fontweight='bold')
# ── 保存 ─────────────────────────────────────────────────────
plt.tight_layout()
plt.savefig('{output_path}', format='png', dpi=300, bbox_inches='tight')
plt.close()
print('OK')执行协议
- 生成代码,遵循上述模板
- 将代码保存到临时文件:
system.run使用内联 Python 或写入/tmp/rc_plot_{hash}.py - 执行:
python3 /tmp/rc_plot_{hash}.py - 检查结果:
- 退出代码 0 且 stdout 中有“OK”且输出文件存在 → 成功
- 否则 → 失败 → 进入 ReAct 循环
ReAct 自我纠正(最多 3 次尝试)
如果执行失败:
尝试 2-3:将上一次的代码和错误注入到你的下一次生成中:
## 上一次尝试失败。请修正代码。
### 上一次的代码:
[粘贴失败的代码]
### 错误输出:
[粘贴 stderr,截断到 500 字符]
### 修正指令:
- 仔细分析错误
- 修正语法错误、缩进、缺失的导入
- 确保 matplotlib.use('Agg') 在导入 pyplot 之前
- 确保 savefig 路径正确:{output_path}
- 不要使用未安装的库(仅限于 matplotlib、numpy、pandas、seaborn)如果所有 3 次尝试均失败 → 告知用户:“Python 作图失败。请检查数据格式或简化图表要求。”
配色方案(学术标准)
| 用途 | 调色板 | 代码 |
|---|---|---|
| 分类(≤10) | tab10 | plt.cm.tab10 |
| 分类(≤8) | Set2 | plt.cm.Set2 |
| 顺序 | viridis | cmap='viridis' |
| 发散 | RdYlBu | cmap='RdYlBu' |
| 色盲安全 | Paired | plt.cm.Paired |
绝对不要仅使用红绿对比。始终使用色盲安全的调色板。
图表类型快速参考
| 用户说 | 图表类型 | 关键代码 |
|---|---|---|
| 柱状图/bar chart | 分组柱状图 | ax.bar(x, y) |
| 折线图/line chart | 折线图 | ax.plot(x, y) |
| 散点图/scatter | 散点图 | ax.scatter(x, y) |
| 热力图/heatmap | 热力图 | import seaborn as sns; sns.heatmap(data) |
| 箱线图/box plot | 箱线图 | ax.boxplot(data) 或 sns.boxplot() |
| 小提琴图/violin | 小提琴图 | sns.violinplot() |
| 雷达图/radar | 雷达图 | 自定义,使用 ax = fig.add_subplot(111, polar=True) |
| 饼图/pie | 饼图 | ax.pie(sizes, labels=labels) |
| 直方图/histogram | 直方图 | ax.hist(data, bins=30) |
| 面积图/area | 堆叠面积图 | ax.stackplot(x, y1, y2) |
§5 Mermaid 引擎 — 结构化图表
适用于流程图、时序图、类图、状态机、甘特图。
支持的图表类型
| 类型 | 关键字 | 最适合 |
|---|---|---|
| 流程图(垂直) | flowchart TD | 流程、决策树 |
| 流程图(水平) | flowchart LR | 管道、架构 |
| 时序图 | sequenceDiagram | API 调用、消息传递 |
| 类图 | classDiagram | OOP 设计、数据模型 |
| 状态图 | stateDiagram-v2 | 状态机、生命周期 |
| 甘特图 | gantt | 项目时间线 |
语法规则(对低智商模型至关重要)
- 节点 ID:使用简单字母 —
A、B、C(不能用中文,不能有空格) - 标签:用
[]包裹 —A[开始处理] - 箭头:
-->或-->|标签| - 绝对不要在标签中使用这些字符:
&、<、> - 决策节点(菱形):使用单层花括号 —
C{是否有效?} - 保持图表节点 ≤15 且美观性不关键。对于更大或需要视觉美化的图表 → 推荐 NanoBanana(§6)。
渲染协议
- 生成 Mermaid 代码
- 保存到临时文件:
/tmp/rc_mermaid_{hash}.mmd - 渲染(按顺序尝试):
mmdc -i /tmp/rc_mermaid_{hash}.mmd -o {output_path} -w 1920 -H 1080 --backgroundColor whitenpx --yes @mermaid-js/mermaid-cli -i /tmp/rc_mermaid_{hash}.mmd -o {output_path}- 两者均失败 → 保存
.mmd文件到工作区 + 告知用户:“Mermaid 渲染工具未安装。已保存源文件,请粘贴到 https://mermaid.live 查看。”
- 检查结果:文件存在且大小 > 1KB → 成功
Mermaid 的 ReAct
如果 mmdc 返回错误:
- 解析错误信息(通常是“第 N 行解析错误”)
- 修正语法问题(通常是标签中的特殊字符、缺失的括号)
- 重试(最多 3 次尝试)
§6 AI 图像引擎 — NanoBanana(复杂图表)
适用于需要视觉美化的复杂学术图表:架构图、研究方法流程、概念图、多层系统图。
何时使用
- 图表节点 >15 或空间布局复杂
- 用户希望“美观/专业/可直接出版”质量
- 用户明确要求 AI 生成的图形
- 低智商模型处于活跃状态(AI 图像质量与模型无关)
API 调用协议
步骤 0 — 与用户确认(调用 API 前,因为需要花钱):
“即将调用 NanoBanana (Gemini) 生成图片,预计消耗约 $0.01 API 额度。是否继续?” 等待用户确认。如果用户拒绝 → 回退到 §5 Mermaid。
步骤 1 — 生成并执行 Python 脚本,通过 system.run:
import requests, base64, sys, os
API_KEY = os.environ.get('OPENROUTER_API_KEY', '')
if not API_KEY:
print('ERROR: OPENROUTER_API_KEY not set', file=sys.stderr)
sys.exit(1)
resp = requests.post(
'https://openrouter.ai/api/v1/chat/completions',
headers={
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json',
},
json={
'model': 'google/gemini-2.5-flash-preview-image-generation',
'messages': [{
'role': 'user',
'content': '生成一张专业的学术图表:{description}.\n\n'
'风格:简洁、极简、可直接出版、白色背景、'
'无水印、清晰的英文标签、专业的配色方案 '
'(蓝色、灰色、柔和色调)、学术论文所需的高分辨率。'
}],
},
timeout=120,
)
resp.raise_for_status()
data = resp.json()
# 从多模态响应中提取图像
content = data['choices'][0]['message']['content']
b64_data = None
if isinstance(content, list):
for part in content:
if part.get('type') == 'image_url':
url = part['image_url']['url']
b64_data = url.split(',', 1)[1]
break
elif part.get('type') == 'image' and 'data' in part:
b64_data = part['data']
break
elif isinstance(content, str) and 'data:image' in content:
b64_data = content.split(',', 1)[1]
if not b64_data:
print('ERROR: No image found in API response', file=sys.stderr)
sys.exit(1)
with open('{output_path}', 'wb') as f:
f.write(base64.b64decode(b64_data))
print('OK')步骤 2 — 检查:文件存在且大小 > 1KB → 成功。
如果 API 调用失败(超时、认证错误、配额超出):
- 记录错误
- 警告用户:“NanoBanana API 调用失败。降级到 Mermaid 引擎。”
- 回退到 §5 Mermaid 引擎
§7 SVG 引擎 — 定制矢量图形
适用于几何形状、坐标注释图、简单的自定义插图。
生成方法
使用 svgwrite 生成 Python 代码:
import svgwrite
dwg = svgwrite.Drawing('{output_path}', size=('800px', '600px'))
dwg.add(dwg.rect(insert=(0, 0), size=('100%', '100%'), fill='white'))
# [你的 SVG 元素在此]
dwg.save()
print('OK')如果 svgwrite 不可用,生成原始 SVG XML 并直接保存。
PNG 转换(可选):
- 尝试:
python3 -c "import cairosvg; cairosvg.svg2png(url='{svg_path}', write_to='{png_path}', dpi=300)" - 如果 cairosvg 不可用:保留
.svg文件,告知用户
§8 质量检查清单(每张图表生成后执行)
生成图表后,必须验证:
| # | 检查项 | 如何验证 | 如果失败 |
|---|---|---|---|
| 1 | 文件存在 | ls -la {output_path} | 重新生成 |
| 2 | 文件大小 > 1KB | 同上 | 文件损坏 → 重新生成 |
| 3 | 英文标签 | 检查生成的代码 | 修正标签 → 重新运行 |
| 4 | DPI ≥ 300(Python) | 检查代码中的 savefig 参数 | 修正 → 重新运行 |
| 5 | 无重叠文本 | 可能的话进行视觉检查 | 添加 tight_layout() 或调整 |
如果检查失败 → 修正并重新运行(计为一次 ReAct 迭代)。
§9 学术图表标准
文件命名
所有图表保存到:outputs/figures/{topic}-fig{N}.{ext}
示例:
outputs/figures/transformer-fig1.pngoutputs/figures/model-comparison-fig2.pngoutputs/figures/methodology-flow-fig3.png
标题规则
每张图表必须有英文标题。呈现给用户的方式:
图 {N}. {描述图表内容的标题文本。}
引用格式
在正文中引用时:“as shown in Figure {N}” 或 “see Figure {N}”。
样式规则
- 字体:无衬线体(Arial、Helvetica),所有文字 ≥ 10pt
- DPI:最低 300(出版标准)
- 背景:白色(无透明度)
- 颜色:色盲安全调色板(viridis、Set2、tab10、Paired)
- 边框:细轴线(1-1.5pt),图表周围不要加方框
- 网格:浅灰色(透明度 0.3),对于数据图推荐但可选
- 图例:放置位置避免遮挡数据;使用半透明背景
§10 与写作 SOP 的集成
当从 writing-sop Phase 2(初稿生成)调用时:
- Writing-sop 识别某个部分需要图表
- 它调用本技能(plotting-sop)并传入图表描述
- 本技能生成图表 →
workspace_save到outputs/figures/ - 返回给 writing-sop:图表路径 + 标题
- Writing-sop 在草稿中嵌入图表引用
内联调用模式:
[Writing-sop Phase 2,正在撰写 Methods 部分]
→ “本部分需要方法流程图”
→ [加载 plotting-sop] → 引擎选择 → 生成 → 保存
→ [返回 writing-sop] → 在文本中插入“见图 1”RC 本地工具参考
| 任务 | 工具 | 示例 |
|---|---|---|
| 运行 Python 绘图 | system.run | python3 /tmp/rc_plot_abc.py |
| 运行 Mermaid 编译 | system.run | npx --yes @mermaid-js/mermaid-cli -i input.mmd -o output.png |
| 调用 NanoBanana API | system.run | 使用 requests 向 OpenRouter 端点发送 POST 请求的 Python 脚本 |
| 保存图表 | workspace_save | outputs/figures/{name}.png |
| 检查文件 | system.run | ls -la outputs/figures/{name}.png |
| 安装依赖(如有必要) | system.run | pip install matplotlib seaborn |